summaryrefslogtreecommitdiff
path: root/usr.bin/cvs
diff options
context:
space:
mode:
authorTobias Stoeckmann <tobias@cvs.openbsd.org>2010-10-15 08:44:13 +0000
committerTobias Stoeckmann <tobias@cvs.openbsd.org>2010-10-15 08:44:13 +0000
commitce3620dabc66a20fd61e167be9dc1b1ed110f626 (patch)
tree44aff8e7c42d7644e22decbf6736a55ab997e751 /usr.bin/cvs
parent09cece3bceb9f3b13f794e4ab00c313e2f4ec2c8 (diff)
Replaced RCS parser code with new rcsparse.{c,h}:
- be very strict about things we parse - print more information about errors if they occur - do not fatal() directly in parser, give caller a chance to react - fix an rcs design issue when it comes to login names tested by many on tech@ ok xsa
Diffstat (limited to 'usr.bin/cvs')
-rw-r--r--usr.bin/cvs/Makefile8
-rw-r--r--usr.bin/cvs/rcs.c1013
-rw-r--r--usr.bin/cvs/rcsparse.c1262
-rw-r--r--usr.bin/cvs/rcsparse.h21
4 files changed, 1307 insertions, 997 deletions
diff --git a/usr.bin/cvs/Makefile b/usr.bin/cvs/Makefile
index 79117100072..bc8b984b5e3 100644
--- a/usr.bin/cvs/Makefile
+++ b/usr.bin/cvs/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.47 2008/06/21 15:39:15 joris Exp $
+# $OpenBSD: Makefile,v 1.48 2010/10/15 08:44:12 tobias Exp $
PROG= opencvs
MAN= cvsintro.7 # cvs.1 cvs.5
@@ -8,9 +8,9 @@ SRCS= cvs.c add.c admin.c annotate.c atomicio.c commit.c config.c \
checkout.c client.c buf.c cmd.c date.y diff.c diff3.c \
diff_internals.c edit.c entries.c fatal.c file.c getlog.c hash.c \
hash_func.c history.c log.c logmsg.c modules.c import.c init.c \
- release.c remove.c repository.c rcs.c rcsnum.c remote.c root.c \
- server.c status.c tag.c trigger.c worklist.c util.c update.c version.c \
- watch.c xmalloc.c
+ release.c remove.c repository.c rcs.c rcsnum.c rcsparse.c remote.c \
+ root.c server.c status.c tag.c trigger.c worklist.c util.c update.c \
+ version.c watch.c xmalloc.c
CFLAGS+=-Wall
CFLAGS+=-Wstrict-prototypes -Wmissing-prototypes
diff --git a/usr.bin/cvs/rcs.c b/usr.bin/cvs/rcs.c
index e136a30e4a1..bd052f5303b 100644
--- a/usr.bin/cvs/rcs.c
+++ b/usr.bin/cvs/rcs.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rcs.c,v 1.302 2010/09/29 09:23:54 tobias Exp $ */
+/* $OpenBSD: rcs.c,v 1.303 2010/10/15 08:44:12 tobias Exp $ */
/*
* Copyright (c) 2004 Jean-Francois Brousseau <jfb@openbsd.org>
* All rights reserved.
@@ -38,63 +38,14 @@
#include "cvs.h"
#include "diff.h"
#include "rcs.h"
+#include "rcsparse.h"
-#define RCS_BUFSIZE 16384
-#define RCS_BUFEXTSIZE 8192
#define RCS_KWEXP_SIZE 1024
-/* RCS token types */
-#define RCS_TOK_ERR -1
-#define RCS_TOK_EOF 0
-#define RCS_TOK_NUM 1
-#define RCS_TOK_ID 2
-#define RCS_TOK_STRING 3
-#define RCS_TOK_SCOLON 4
-#define RCS_TOK_COLON 5
-
-#define RCS_TOK_HEAD 8
-#define RCS_TOK_BRANCH 9
-#define RCS_TOK_ACCESS 10
-#define RCS_TOK_SYMBOLS 11
-#define RCS_TOK_LOCKS 12
-#define RCS_TOK_COMMENT 13
-#define RCS_TOK_EXPAND 14
-#define RCS_TOK_DATE 15
-#define RCS_TOK_AUTHOR 16
-#define RCS_TOK_STATE 17
-#define RCS_TOK_NEXT 18
-#define RCS_TOK_BRANCHES 19
-#define RCS_TOK_DESC 20
-#define RCS_TOK_LOG 21
-#define RCS_TOK_TEXT 22
-#define RCS_TOK_STRICT 23
-
-#define RCS_ISKEY(t) (((t) >= RCS_TOK_HEAD) && ((t) <= RCS_TOK_BRANCHES))
-
-#define RCS_NOSCOL 0x01 /* no terminating semi-colon */
-#define RCS_VOPT 0x02 /* value is optional */
-
#define ANNOTATE_NEVER 0
#define ANNOTATE_NOW 1
#define ANNOTATE_LATER 2
-/* opaque parse data */
-struct rcs_pdata {
- char *rp_buf;
- size_t rp_blen;
- char *rp_bufend;
- size_t rp_tlen;
-
- /* pushback token buffer */
- char rp_ptok[128];
- int rp_pttype; /* token type, RCS_TOK_ERR if no token */
-
- FILE *rp_file;
-};
-
-#define RCS_TOKSTR(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_buf
-#define RCS_TOKLEN(rfp) ((struct rcs_pdata *)rfp->rf_pdata)->rp_tlen
-
/* invalid characters in RCS symbol names */
static const char rcs_sym_invch[] = RCS_SYM_INVALCHAR;
@@ -184,59 +135,15 @@ struct rcs_kw rcs_expkw[] = {
#define NB_COMTYPES (sizeof(rcs_comments)/sizeof(rcs_comments[0]))
-static struct rcs_key {
- char rk_str[16];
- int rk_id;
- int rk_val;
- int rk_flags;
-} rcs_keys[] = {
- { "access", RCS_TOK_ACCESS, RCS_TOK_ID, RCS_VOPT },
- { "author", RCS_TOK_AUTHOR, RCS_TOK_ID, 0 },
- { "branch", RCS_TOK_BRANCH, RCS_TOK_NUM, RCS_VOPT },
- { "branches", RCS_TOK_BRANCHES, RCS_TOK_NUM, RCS_VOPT },
- { "comment", RCS_TOK_COMMENT, RCS_TOK_STRING, RCS_VOPT },
- { "date", RCS_TOK_DATE, RCS_TOK_NUM, 0 },
- { "desc", RCS_TOK_DESC, RCS_TOK_STRING, RCS_NOSCOL },
- { "expand", RCS_TOK_EXPAND, RCS_TOK_STRING, RCS_VOPT },
- { "head", RCS_TOK_HEAD, RCS_TOK_NUM, RCS_VOPT },
- { "locks", RCS_TOK_LOCKS, RCS_TOK_ID, 0 },
- { "log", RCS_TOK_LOG, RCS_TOK_STRING, RCS_NOSCOL },
- { "next", RCS_TOK_NEXT, RCS_TOK_NUM, RCS_VOPT },
- { "state", RCS_TOK_STATE, RCS_TOK_ID, RCS_VOPT },
- { "strict", RCS_TOK_STRICT, 0, 0, },
- { "symbols", RCS_TOK_SYMBOLS, 0, 0 },
- { "text", RCS_TOK_TEXT, RCS_TOK_STRING, RCS_NOSCOL },
-};
-
-#define RCS_NKEYS (sizeof(rcs_keys)/sizeof(rcs_keys[0]))
-
static RCSNUM *rcs_get_revision(const char *, RCSFILE *);
int rcs_patch_lines(struct rcs_lines *, struct rcs_lines *,
struct rcs_line **, struct rcs_delta *);
-static void rcs_parse_init(RCSFILE *);
-static int rcs_parse_admin(RCSFILE *);
-static int rcs_parse_delta(RCSFILE *);
-static void rcs_parse_deltas(RCSFILE *, RCSNUM *);
-static int rcs_parse_deltatext(RCSFILE *);
-static void rcs_parse_deltatexts(RCSFILE *, RCSNUM *);
-static void rcs_parse_desc(RCSFILE *);
-
-static int rcs_parse_access(RCSFILE *);
-static int rcs_parse_symbols(RCSFILE *);
-static int rcs_parse_locks(RCSFILE *);
-static int rcs_parse_branches(RCSFILE *, struct rcs_delta *);
static void rcs_freedelta(struct rcs_delta *);
-static void rcs_freepdata(struct rcs_pdata *);
-static int rcs_gettok(RCSFILE *);
-static int rcs_pushtok(RCSFILE *, const char *, int);
-static void rcs_growbuf(RCSFILE *);
static void rcs_strprint(const u_char *, size_t, FILE *);
static void rcs_kwexp_line(char *, struct rcs_delta *, struct rcs_lines *,
struct rcs_line *, int mode);
-static int rcs_ignore_keys = 0;
-
RCSFILE *
rcs_open(const char *path, int fd, int flags, ...)
{
@@ -277,8 +184,10 @@ rcs_open(const char *path, int fd, int flags, ...)
TAILQ_INIT(&(rfp->rf_symbols));
TAILQ_INIT(&(rfp->rf_locks));
- if (!(rfp->rf_flags & RCS_CREATE))
- rcs_parse_init(rfp);
+ if (!(rfp->rf_flags & RCS_CREATE)) {
+ if (rcsparse_init(rfp))
+ fatal("could not parse admin data");
+ }
/* fill in rd_locker */
TAILQ_FOREACH(lkr, &(rfp->rf_locks), rl_list) {
@@ -352,7 +261,7 @@ rcs_close(RCSFILE *rfp)
if (rfp->rf_desc != NULL)
xfree(rfp->rf_desc);
if (rfp->rf_pdata != NULL)
- rcs_freepdata(rfp->rf_pdata);
+ rcsparse_free(rfp);
xfree(rfp);
}
@@ -384,7 +293,8 @@ rcs_write(RCSFILE *rfp)
return;
/* Write operations need the whole file parsed */
- rcs_parse_deltatexts(rfp, NULL);
+ if (rcsparse_deltatexts(rfp, NULL))
+ fatal("rcs_write: rcsparse_deltatexts");
if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
fatal("rcs_write: truncation");
@@ -1425,7 +1335,8 @@ rcs_findrev(RCSFILE *rfp, RCSNUM *rev)
(!isbrev && rcsnum_cmp(rdp->rd_num, rev, 0) == -1) ||
((isbrev && rdp->rd_num->rn_len < 4) ||
(isbrev && rcsnum_differ(rev, rdp->rd_num)))) {
- rcs_parse_deltas(rfp, rev);
+ if (rcsparse_deltas(rfp, rev))
+ fatal("error parsing deltas");
}
TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
@@ -1528,711 +1439,6 @@ rcs_kflag_get(const char *flags)
return (fl);
}
-/* rcs_parse_deltas()
- *
- * Parse deltas. If <rev> is not NULL, parse only as far as that
- * revision. If <rev> is NULL, parse all deltas.
- */
-static void
-rcs_parse_deltas(RCSFILE *rfp, RCSNUM *rev)
-{
- int ret;
- struct rcs_delta *enddelta;
-
- if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
- return;
-
- for (;;) {
- ret = rcs_parse_delta(rfp);
- if (rev != NULL) {
- enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
- if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
- break;
- }
-
- if (ret == 0) {
- rfp->rf_flags |= PARSED_DELTAS;
- break;
- }
- else if (ret == -1)
- fatal("error parsing deltas");
- }
-}
-
-/* rcs_parse_deltatexts()
- *
- * Parse deltatexts. If <rev> is not NULL, parse only as far as that
- * revision. If <rev> is NULL, parse everything.
- */
-static void
-rcs_parse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
-{
- int ret;
- struct rcs_delta *rdp;
-
- if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
- (rfp->rf_flags & RCS_CREATE))
- return;
-
- if (!(rfp->rf_flags & PARSED_DESC))
- rcs_parse_desc(rfp);
- for (;;) {
- if (rev != NULL) {
- rdp = rcs_findrev(rfp, rev);
- if (rdp->rd_text != NULL)
- break;
- else
- ret = rcs_parse_deltatext(rfp);
- } else
- ret = rcs_parse_deltatext(rfp);
- if (ret == 0) {
- rfp->rf_flags |= PARSED_DELTATEXTS;
- break;
- }
- else if (ret == -1)
- fatal("problem parsing deltatexts");
- }
-}
-
-/* rcs_parse_desc()
- *
- * Parse RCS description.
- */
-static void
-rcs_parse_desc(RCSFILE *rfp)
-{
- int ret = 0;
-
- if ((rfp->rf_flags & PARSED_DESC) || (rfp->rf_flags & RCS_CREATE))
- return;
- if (!(rfp->rf_flags & PARSED_DELTAS))
- rcs_parse_deltas(rfp, NULL);
- /* do parsing */
- ret = rcs_gettok(rfp);
- if (ret != RCS_TOK_DESC)
- fatal("token `%s' found where RCS desc expected",
- RCS_TOKSTR(rfp));
-
- ret = rcs_gettok(rfp);
- if (ret != RCS_TOK_STRING)
- fatal("token `%s' found where RCS desc expected",
- RCS_TOKSTR(rfp));
-
- rfp->rf_desc = xstrdup(RCS_TOKSTR(rfp));
- rfp->rf_flags |= PARSED_DESC;
-}
-
-/*
- * rcs_parse_init()
- *
- * Initial parsing of file <path>, which is in the RCS format.
- * Just does admin section.
- */
-static void
-rcs_parse_init(RCSFILE *rfp)
-{
- struct rcs_pdata *pdp;
-
- if (rfp->rf_flags & RCS_PARSED)
- return;
-
- pdp = xcalloc(1, sizeof(*pdp));
-
- pdp->rp_pttype = RCS_TOK_ERR;
-
- if ((pdp->rp_file = fdopen(rfp->rf_fd, "r")) == NULL)
- fatal("fdopen: `%s'", rfp->rf_path);
-
- pdp->rp_buf = xmalloc((size_t)RCS_BUFSIZE);
- pdp->rp_blen = RCS_BUFSIZE;
- pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
-
- /* ditch the strict lock */
- rfp->rf_flags &= ~RCS_SLOCK;
- rfp->rf_pdata = pdp;
-
- if (rcs_parse_admin(rfp) < 0) {
- rcs_freepdata(pdp);
- fatal("could not parse admin data");
- }
-
- if (rfp->rf_flags & RCS_PARSE_FULLY) {
- rcs_parse_deltatexts(rfp, NULL);
- (void)close(rfp->rf_fd);
- rfp->rf_fd = -1;
- }
-
- rfp->rf_flags |= RCS_SYNCED;
-}
-
-/*
- * rcs_parse_admin()
- *
- * Parse the administrative portion of an RCS file.
- * Returns the type of the first token found after the admin section on
- * success, or -1 on failure.
- */
-static int
-rcs_parse_admin(RCSFILE *rfp)
-{
- u_int i;
- int tok, ntok, hmask;
- struct rcs_key *rk;
-
- rfp->rf_head = NULL;
- rfp->rf_branch = NULL;
-
- /* hmask is a mask of the headers already encountered */
- hmask = 0;
- for (;;) {
- tok = rcs_gettok(rfp);
- if (tok == RCS_TOK_ERR) {
- cvs_log(LP_ERR, "parse error in RCS admin section");
- goto fail;
- } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
- /*
- * Assume this is the start of the first delta or
- * that we are dealing with an empty RCS file and
- * we just found the description.
- */
- if (!(hmask & (1 << RCS_TOK_HEAD))) {
- cvs_log(LP_ERR, "head missing");
- goto fail;
- }
- rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
- return (tok);
- }
-
- rk = NULL;
- for (i = 0; i < RCS_NKEYS; i++)
- if (rcs_keys[i].rk_id == tok)
- rk = &(rcs_keys[i]);
-
- if (hmask & (1 << tok)) {
- cvs_log(LP_ERR, "duplicate RCS key");
- goto fail;
- }
- hmask |= (1 << tok);
-
- switch (tok) {
- case RCS_TOK_HEAD:
- case RCS_TOK_BRANCH:
- case RCS_TOK_COMMENT:
- case RCS_TOK_EXPAND:
- ntok = rcs_gettok(rfp);
- if (ntok == RCS_TOK_SCOLON)
- break;
- if (ntok != rk->rk_val) {
- cvs_log(LP_ERR,
- "invalid value type for RCS key `%s'",
- rk->rk_str);
- }
-
- if (tok == RCS_TOK_HEAD) {
- if (rfp->rf_head == NULL)
- rfp->rf_head = rcsnum_alloc();
- if (RCS_TOKSTR(rfp)[0] == '\0' ||
- rcsnum_aton(RCS_TOKSTR(rfp), NULL,
- rfp->rf_head) < 0) {
- rcsnum_free(rfp->rf_head);
- rfp->rf_head = NULL;
- }
- } else if (tok == RCS_TOK_BRANCH) {
- if (rfp->rf_branch == NULL)
- rfp->rf_branch = rcsnum_alloc();
- if (rcsnum_aton(RCS_TOKSTR(rfp), NULL,
- rfp->rf_branch) < 0)
- goto fail;
- } else if (tok == RCS_TOK_COMMENT) {
- rfp->rf_comment = xstrdup(RCS_TOKSTR(rfp));
- } else if (tok == RCS_TOK_EXPAND) {
- rfp->rf_expand = xstrdup(RCS_TOKSTR(rfp));
- }
-
- /* now get the expected semi-colon */
- ntok = rcs_gettok(rfp);
- if (ntok != RCS_TOK_SCOLON) {
- cvs_log(LP_ERR,
- "missing semi-colon after RCS `%s' key",
- rk->rk_str);
- goto fail;
- }
- break;
- case RCS_TOK_ACCESS:
- if (rcs_parse_access(rfp) < 0)
- goto fail;
- break;
- case RCS_TOK_SYMBOLS:
- rcs_ignore_keys = 1;
- if (rcs_parse_symbols(rfp) < 0)
- goto fail;
- break;
- case RCS_TOK_LOCKS:
- if (rcs_parse_locks(rfp) < 0)
- goto fail;
- break;
- default:
- cvs_log(LP_ERR,
- "unexpected token `%s' in RCS admin section",
- RCS_TOKSTR(rfp));
- goto fail;
- }
-
- rcs_ignore_keys = 0;
-
- }
-
-fail:
- return (-1);
-}
-
-/*
- * rcs_parse_delta()
- *
- * Parse an RCS delta section and allocate the structure to store that delta's
- * information in the <rfp> delta list.
- * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
- * -1 on error.
- */
-static int
-rcs_parse_delta(RCSFILE *rfp)
-{
- int ret, tok, ntok, hmask;
- u_int i;
- char *tokstr;
- RCSNUM *datenum;
- struct rcs_delta *rdp;
- struct rcs_key *rk;
-
- tok = rcs_gettok(rfp);
- if (tok == RCS_TOK_DESC) {
- rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
- return (0);
- } else if (tok != RCS_TOK_NUM) {
- cvs_log(LP_ERR, "unexpected token `%s' at start of delta",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- rdp = xcalloc(1, sizeof(*rdp));
-
- rdp->rd_num = rcsnum_alloc();
- rdp->rd_next = rcsnum_alloc();
-
- TAILQ_INIT(&(rdp->rd_branches));
-
- rcsnum_aton(RCS_TOKSTR(rfp), NULL, rdp->rd_num);
-
- hmask = 0;
- ret = 0;
- tokstr = NULL;
-
- for (;;) {
- tok = rcs_gettok(rfp);
- if (tok == RCS_TOK_ERR) {
- cvs_log(LP_ERR, "parse error in RCS delta section");
- rcs_freedelta(rdp);
- return (-1);
- } else if (tok == RCS_TOK_NUM || tok == RCS_TOK_DESC) {
- rcs_pushtok(rfp, RCS_TOKSTR(rfp), tok);
- ret = (tok == RCS_TOK_NUM ? 1 : 0);
- break;
- }
-
- rk = NULL;
- for (i = 0; i < RCS_NKEYS; i++)
- if (rcs_keys[i].rk_id == tok)
- rk = &(rcs_keys[i]);
-
- if (hmask & (1 << tok)) {
- cvs_log(LP_ERR, "duplicate RCS key");
- rcs_freedelta(rdp);
- return (-1);
- }
- hmask |= (1 << tok);
-
- switch (tok) {
- case RCS_TOK_DATE:
- case RCS_TOK_AUTHOR:
- case RCS_TOK_STATE:
- case RCS_TOK_NEXT:
- ntok = rcs_gettok(rfp);
- if (ntok == RCS_TOK_SCOLON) {
- if (rk->rk_flags & RCS_VOPT)
- break;
- else {
- cvs_log(LP_ERR, "missing mandatory "
- "value to RCS key `%s'",
- rk->rk_str);
- rcs_freedelta(rdp);
- return (-1);
- }
- }
-
- if (ntok != rk->rk_val) {
- cvs_log(LP_ERR,
- "invalid value type for RCS key `%s'",
- rk->rk_str);
- rcs_freedelta(rdp);
- return (-1);
- }
-
- if (tokstr != NULL)
- xfree(tokstr);
- tokstr = xstrdup(RCS_TOKSTR(rfp));
- /* now get the expected semi-colon */
- ntok = rcs_gettok(rfp);
- if (ntok != RCS_TOK_SCOLON) {
- cvs_log(LP_ERR,
- "missing semi-colon after RCS `%s' key",
- rk->rk_str);
- xfree(tokstr);
- rcs_freedelta(rdp);
- return (-1);
- }
-
- if (tok == RCS_TOK_DATE) {
- if ((datenum = rcsnum_parse(tokstr)) == NULL) {
- xfree(tokstr);
- rcs_freedelta(rdp);
- return (-1);
- }
- if (datenum->rn_len != 6) {
- cvs_log(LP_ERR,
- "RCS date specification has %s "
- "fields",
- (datenum->rn_len > 6) ? "too many" :
- "missing");
- xfree(tokstr);
- rcs_freedelta(rdp);
- rcsnum_free(datenum);
- return (-1);
- }
- rdp->rd_date.tm_year = datenum->rn_id[0];
- if (rdp->rd_date.tm_year >= 1900)
- rdp->rd_date.tm_year -= 1900;
- rdp->rd_date.tm_mon = datenum->rn_id[1] - 1;
- rdp->rd_date.tm_mday = datenum->rn_id[2];
- rdp->rd_date.tm_hour = datenum->rn_id[3];
- rdp->rd_date.tm_min = datenum->rn_id[4];
- rdp->rd_date.tm_sec = datenum->rn_id[5];
- rcsnum_free(datenum);
- } else if (tok == RCS_TOK_AUTHOR) {
- rdp->rd_author = tokstr;
- tokstr = NULL;
- } else if (tok == RCS_TOK_STATE) {
- rdp->rd_state = tokstr;
- tokstr = NULL;
- } else if (tok == RCS_TOK_NEXT) {
- rcsnum_aton(tokstr, NULL, rdp->rd_next);
- }
- break;
- case RCS_TOK_BRANCHES:
- if (rcs_parse_branches(rfp, rdp) < 0) {
- rcs_freedelta(rdp);
- return (-1);
- }
- break;
- default:
- cvs_log(LP_ERR, "unexpected token `%s' in RCS delta",
- RCS_TOKSTR(rfp));
- rcs_freedelta(rdp);
- return (-1);
- }
- }
-
- if (tokstr != NULL)
- xfree(tokstr);
-
- TAILQ_INSERT_TAIL(&(rfp->rf_delta), rdp, rd_list);
- rfp->rf_ndelta++;
-
- return (ret);
-}
-
-/*
- * rcs_parse_deltatext()
- *
- * Parse an RCS delta text section and fill in the log and text field of the
- * appropriate delta section.
- * Returns 1 if the section was parsed OK, 0 if it is the last delta, and
- * -1 on error.
- */
-static int
-rcs_parse_deltatext(RCSFILE *rfp)
-{
- int tok;
- RCSNUM *tnum;
- struct rcs_delta *rdp;
-
- tok = rcs_gettok(rfp);
- if (tok == RCS_TOK_EOF)
- return (0);
-
- if (tok != RCS_TOK_NUM) {
- cvs_log(LP_ERR,
- "unexpected token `%s' at start of RCS delta text",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- tnum = rcsnum_alloc();
- rcsnum_aton(RCS_TOKSTR(rfp), NULL, tnum);
-
- TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
- if (rcsnum_cmp(tnum, rdp->rd_num, 0) == 0)
- break;
- }
- rcsnum_free(tnum);
-
- if (rdp == NULL) {
- cvs_log(LP_ERR, "RCS delta text `%s' has no matching delta",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- tok = rcs_gettok(rfp);
- if (tok != RCS_TOK_LOG) {
- cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- tok = rcs_gettok(rfp);
- if (tok != RCS_TOK_STRING) {
- cvs_log(LP_ERR, "unexpected token `%s' where RCS log expected",
- RCS_TOKSTR(rfp));
- return (-1);
- }
- rdp->rd_log = xstrdup(RCS_TOKSTR(rfp));
- tok = rcs_gettok(rfp);
- if (tok != RCS_TOK_TEXT) {
- cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- tok = rcs_gettok(rfp);
- if (tok != RCS_TOK_STRING) {
- cvs_log(LP_ERR, "unexpected token `%s' where RCS text expected",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- if (RCS_TOKLEN(rfp) == 0) {
- rdp->rd_text = xmalloc(1);
- rdp->rd_text[0] = '\0';
- rdp->rd_tlen = 0;
- } else {
- rdp->rd_text = xmalloc(RCS_TOKLEN(rfp));
- memcpy(rdp->rd_text, RCS_TOKSTR(rfp), RCS_TOKLEN(rfp));
- rdp->rd_tlen = RCS_TOKLEN(rfp);
- }
-
- return (1);
-}
-
-/*
- * rcs_parse_access()
- *
- * Parse the access list given as value to the `access' keyword.
- * Returns 0 on success, or -1 on failure.
- */
-static int
-rcs_parse_access(RCSFILE *rfp)
-{
- int type;
-
- while ((type = rcs_gettok(rfp)) != RCS_TOK_SCOLON) {
- if (type != RCS_TOK_ID) {
- cvs_log(LP_ERR, "unexpected token `%s' in access list",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- if (rcs_access_add(rfp, RCS_TOKSTR(rfp)) < 0)
- return (-1);
- }
-
- return (0);
-}
-
-/*
- * rcs_parse_symbols()
- *
- * Parse the symbol list given as value to the `symbols' keyword.
- * Returns 0 on success, or -1 on failure.
- */
-static int
-rcs_parse_symbols(RCSFILE *rfp)
-{
- int type;
- struct rcs_sym *symp;
-
- for (;;) {
- type = rcs_gettok(rfp);
- if (type == RCS_TOK_SCOLON)
- break;
-
- if (type != RCS_TOK_ID) {
- cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- symp = xmalloc(sizeof(*symp));
- symp->rs_name = xstrdup(RCS_TOKSTR(rfp));
- symp->rs_num = rcsnum_alloc();
-
- type = rcs_gettok(rfp);
- if (type != RCS_TOK_COLON) {
- cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
- RCS_TOKSTR(rfp));
- rcsnum_free(symp->rs_num);
- xfree(symp->rs_name);
- xfree(symp);
- return (-1);
- }
-
- type = rcs_gettok(rfp);
- if (type != RCS_TOK_NUM) {
- cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
- RCS_TOKSTR(rfp));
- rcsnum_free(symp->rs_num);
- xfree(symp->rs_name);
- xfree(symp);
- return (-1);
- }
-
- if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, symp->rs_num) < 0) {
- cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
- RCS_TOKSTR(rfp));
- rcsnum_free(symp->rs_num);
- xfree(symp->rs_name);
- xfree(symp);
- return (-1);
- }
-
- TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
- }
-
- return (0);
-}
-
-/*
- * rcs_parse_locks()
- *
- * Parse the lock list given as value to the `locks' keyword.
- * Returns 0 on success, or -1 on failure.
- */
-static int
-rcs_parse_locks(RCSFILE *rfp)
-{
- int type;
- struct rcs_lock *lkp;
-
- for (;;) {
- type = rcs_gettok(rfp);
- if (type == RCS_TOK_SCOLON)
- break;
-
- if (type != RCS_TOK_ID) {
- cvs_log(LP_ERR, "unexpected token `%s' in lock list",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- lkp = xmalloc(sizeof(*lkp));
- lkp->rl_name = xstrdup(RCS_TOKSTR(rfp));
- lkp->rl_num = rcsnum_alloc();
-
- type = rcs_gettok(rfp);
- if (type != RCS_TOK_COLON) {
- cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
- RCS_TOKSTR(rfp));
- rcsnum_free(lkp->rl_num);
- xfree(lkp->rl_name);
- xfree(lkp);
- return (-1);
- }
-
- type = rcs_gettok(rfp);
- if (type != RCS_TOK_NUM) {
- cvs_log(LP_ERR, "unexpected token `%s' in symbol list",
- RCS_TOKSTR(rfp));
- rcsnum_free(lkp->rl_num);
- xfree(lkp->rl_name);
- xfree(lkp);
- return (-1);
- }
-
- if (rcsnum_aton(RCS_TOKSTR(rfp), NULL, lkp->rl_num) < 0) {
- cvs_log(LP_ERR, "failed to parse RCS NUM `%s'",
- RCS_TOKSTR(rfp));
- rcsnum_free(lkp->rl_num);
- xfree(lkp->rl_name);
- xfree(lkp);
- return (-1);
- }
-
- TAILQ_INSERT_HEAD(&(rfp->rf_locks), lkp, rl_list);
- }
-
- /* check if we have a `strict' */
- type = rcs_gettok(rfp);
- if (type != RCS_TOK_STRICT) {
- rcs_pushtok(rfp, RCS_TOKSTR(rfp), type);
- } else {
- rfp->rf_flags |= RCS_SLOCK;
-
- type = rcs_gettok(rfp);
- if (type != RCS_TOK_SCOLON) {
- cvs_log(LP_ERR,
- "missing semi-colon after `strict' keyword");
- return (-1);
- }
- }
-
- return (0);
-}
-
-/*
- * rcs_parse_branches()
- *
- * Parse the list of branches following a `branches' keyword in a delta.
- * Returns 0 on success, or -1 on failure.
- */
-static int
-rcs_parse_branches(RCSFILE *rfp, struct rcs_delta *rdp)
-{
- int type;
- struct rcs_branch *brp;
-
- for (;;) {
- type = rcs_gettok(rfp);
- if (type == RCS_TOK_SCOLON)
- break;
-
- if (type != RCS_TOK_NUM) {
- cvs_log(LP_ERR,
- "unexpected token `%s' in list of branches",
- RCS_TOKSTR(rfp));
- return (-1);
- }
-
- brp = xmalloc(sizeof(*brp));
- brp->rb_num = rcsnum_parse(RCS_TOKSTR(rfp));
- if (brp->rb_num == NULL) {
- xfree(brp);
- return (-1);
- }
-
- TAILQ_INSERT_TAIL(&(rdp->rd_branches), brp, rb_list);
- }
-
- return (0);
-}
-
/*
* rcs_freedelta()
*
@@ -2269,190 +1475,6 @@ rcs_freedelta(struct rcs_delta *rdp)
}
/*
- * rcs_freepdata()
- *
- * Free the contents of the parser data structure.
- */
-static void
-rcs_freepdata(struct rcs_pdata *pd)
-{
- if (pd->rp_file != NULL)
- (void)fclose(pd->rp_file);
- if (pd->rp_buf != NULL)
- xfree(pd->rp_buf);
- xfree(pd);
-}
-
-/*
- * rcs_gettok()
- *
- * Get the next RCS token from the string <str>.
- */
-static int
-rcs_gettok(RCSFILE *rfp)
-{
- u_int i;
- int ch, last, type;
- size_t len;
- char *bp;
- struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
-
- type = RCS_TOK_ERR;
- bp = pdp->rp_buf;
- pdp->rp_tlen = 0;
- *bp = '\0';
-
- if (pdp->rp_pttype != RCS_TOK_ERR) {
- type = pdp->rp_pttype;
- if (strlcpy(pdp->rp_buf, pdp->rp_ptok, pdp->rp_blen) >=
- pdp->rp_blen)
- fatal("rcs_gettok: strlcpy");
- pdp->rp_pttype = RCS_TOK_ERR;
- return (type);
- }
-
- /* skip leading whitespace */
- /* XXX we must skip backspace too for compatibility, should we? */
- do {
- ch = getc(pdp->rp_file);
- } while (isspace(ch));
-
- if (ch == EOF) {
- type = RCS_TOK_EOF;
- } else if (ch == ';') {
- type = RCS_TOK_SCOLON;
- } else if (ch == ':') {
- type = RCS_TOK_COLON;
- } else if (isalpha(ch)) {
- type = RCS_TOK_ID;
- *(bp++) = ch;
- for (;;) {
- ch = getc(pdp->rp_file);
- if (ch == EOF) {
- type = RCS_TOK_EOF;
- break;
- } else if (!isgraph(ch) ||
- strchr(rcs_sym_invch, ch) != NULL) {
- ungetc(ch, pdp->rp_file);
- break;
- }
- *(bp++) = ch;
- pdp->rp_tlen++;
- if (bp == pdp->rp_bufend - 1) {
- len = bp - pdp->rp_buf;
- rcs_growbuf(rfp);
- bp = pdp->rp_buf + len;
- }
- }
- *bp = '\0';
-
- if (type != RCS_TOK_ERR && rcs_ignore_keys != 1) {
- for (i = 0; i < RCS_NKEYS; i++) {
- if (strcmp(rcs_keys[i].rk_str,
- pdp->rp_buf) == 0) {
- type = rcs_keys[i].rk_id;
- break;
- }
- }
- }
- } else if (ch == '@') {
- /* we have a string */
- type = RCS_TOK_STRING;
- for (;;) {
- ch = getc(pdp->rp_file);
- if (ch == EOF) {
- type = RCS_TOK_EOF;
- break;
- } else if (ch == '@') {
- ch = getc(pdp->rp_file);
- if (ch != '@') {
- ungetc(ch, pdp->rp_file);
- break;
- }
- }
-
- *(bp++) = ch;
- pdp->rp_tlen++;
- if (bp == pdp->rp_bufend - 1) {
- len = bp - pdp->rp_buf;
- rcs_growbuf(rfp);
- bp = pdp->rp_buf + len;
- }
- }
-
- *bp = '\0';
- } else if (isdigit(ch)) {
- *(bp++) = ch;
- last = ch;
- type = RCS_TOK_NUM;
-
- for (;;) {
- ch = getc(pdp->rp_file);
- if (ch == EOF) {
- type = RCS_TOK_EOF;
- break;
- }
- if (bp == pdp->rp_bufend)
- break;
- if (!isdigit(ch) && ch != '.') {
- ungetc(ch, pdp->rp_file);
- break;
- }
-
- if (last == '.' && ch == '.') {
- type = RCS_TOK_ERR;
- break;
- }
- last = ch;
- *(bp++) = ch;
- pdp->rp_tlen++;
- }
- *bp = '\0';
- }
-
- return (type);
-}
-
-/*
- * rcs_pushtok()
- *
- * Push a token back in the parser's token buffer.
- */
-static int
-rcs_pushtok(RCSFILE *rfp, const char *tok, int type)
-{
- struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
-
- if (pdp->rp_pttype != RCS_TOK_ERR)
- return (-1);
-
- pdp->rp_pttype = type;
- if (strlcpy(pdp->rp_ptok, tok, sizeof(pdp->rp_ptok)) >=
- sizeof(pdp->rp_ptok))
- fatal("rcs_pushtok: strlcpy");
- return (0);
-}
-
-
-/*
- * rcs_growbuf()
- *
- * Attempt to grow the internal parse buffer for the RCS file <rf> by
- * RCS_BUFEXTSIZE.
- * In case of failure, the original buffer is left unmodified.
- */
-static void
-rcs_growbuf(RCSFILE *rf)
-{
- struct rcs_pdata *pdp = (struct rcs_pdata *)rf->rf_pdata;
-
- pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
- pdp->rp_blen + RCS_BUFEXTSIZE);
- pdp->rp_blen += RCS_BUFEXTSIZE;
- pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
-}
-
-/*
* rcs_strprint()
*
* Output an RCS string <str> of size <slen> to the stream <stream>. Any
@@ -2495,7 +1517,8 @@ rcs_deltatext_set(RCSFILE *rfp, RCSNUM *rev, BUF *bp)
struct rcs_delta *rdp;
/* Write operations require full parsing */
- rcs_parse_deltatexts(rfp, NULL);
+ if (rcsparse_deltatexts(rfp, NULL))
+ return (-1);
if ((rdp = rcs_findrev(rfp, rev)) == NULL)
return (-1);
@@ -2727,7 +1750,8 @@ rcs_rev_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
fatal("rcs_rev_getlines: no HEAD revision");
tnum = frev;
- rcs_parse_deltatexts(rfp, hrdp->rd_num);
+ if (rcsparse_deltatexts(rfp, hrdp->rd_num))
+ fatal("rcs_rev_getlines: rcsparse_deltatexts");
/* revision on branch, get the branch root */
nextroot = 2;
@@ -2792,7 +1816,8 @@ again:
}
if (rdp->rd_tlen == 0) {
- rcs_parse_deltatexts(rfp, rdp->rd_num);
+ if (rcsparse_deltatexts(rfp, rdp->rd_num))
+ fatal("rcs_rev_getlines: rcsparse_deltatexts");
if (rdp->rd_tlen == 0) {
if (!rcsnum_differ(rdp->rd_num, bnum))
break;
@@ -2962,7 +1987,9 @@ rcs_annotate_getlines(RCSFILE *rfp, RCSNUM *frev, struct rcs_line ***alines)
}
if (rdp->rd_tlen == 0) {
- rcs_parse_deltatexts(rfp, rdp->rd_num);
+ if (rcsparse_deltatexts(rfp, rdp->rd_num))
+ fatal("rcs_annotate_getlines: "
+ "rcsparse_deltatexts");
if (rdp->rd_tlen == 0) {
if (!rcsnum_differ(rdp->rd_num, bnum))
break;
diff --git a/usr.bin/cvs/rcsparse.c b/usr.bin/cvs/rcsparse.c
new file mode 100644
index 00000000000..7b3de255e68
--- /dev/null
+++ b/usr.bin/cvs/rcsparse.c
@@ -0,0 +1,1262 @@
+/* $OpenBSD: rcsparse.c,v 1.1 2010/10/15 08:44:12 tobias Exp $ */
+/*
+ * Copyright (c) 2010 Tobias Stoeckmann <tobias@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/queue.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <pwd.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "rcs.h"
+#include "rcsparse.h"
+#include "xmalloc.h"
+
+#define RCS_BUFSIZE 16384
+#define RCS_BUFEXTSIZE 8192
+
+/* RCS token types */
+#define RCS_TOK_HEAD (1 << 0)
+#define RCS_TOK_BRANCH (1 << 1)
+#define RCS_TOK_ACCESS (1 << 2)
+#define RCS_TOK_SYMBOLS (1 << 3)
+#define RCS_TOK_LOCKS (1 << 4)
+#define RCS_TOK_STRICT (1 << 5)
+#define RCS_TOK_COMMENT (1 << 6)
+#define RCS_TOK_COMMITID (1 << 7)
+#define RCS_TOK_EXPAND (1 << 8)
+#define RCS_TOK_DESC (1 << 9)
+#define RCS_TOK_DATE (1 << 10)
+#define RCS_TOK_AUTHOR (1 << 11)
+#define RCS_TOK_STATE (1 << 12)
+#define RCS_TOK_BRANCHES (1 << 13)
+#define RCS_TOK_NEXT (1 << 14)
+#define RCS_TOK_LOG (1 << 15)
+#define RCS_TOK_TEXT (1 << 16)
+#define RCS_TOK_COLON (1 << 17)
+#define RCS_TOK_COMMA (1 << 18)
+#define RCS_TOK_SCOLON (1 << 19)
+
+#define RCS_TYPE_STRING (1 << 20)
+#define RCS_TYPE_NUMBER (1 << 21)
+#define RCS_TYPE_BRANCH (1 << 22)
+#define RCS_TYPE_REVISION (1 << 23)
+#define RCS_TYPE_LOGIN (1 << 24)
+#define RCS_TYPE_STATE (1 << 25)
+#define RCS_TYPE_SYMBOL (1 << 26)
+#define RCS_TYPE_DATE (1 << 27)
+#define RCS_TYPE_KEYWORD (1 << 28)
+#define RCS_TYPE_COMMITID (1 << 29)
+
+#define MANDATORY 0
+#define OPTIONAL 1
+
+/* opaque parse data */
+struct rcs_pdata {
+ char *rp_buf;
+ size_t rp_blen;
+ char *rp_bufend;
+ size_t rp_tlen;
+
+ struct rcs_delta *rp_delta;
+ FILE *rp_file;
+ int rp_lineno;
+ int rp_msglineno;
+ int rp_token;
+
+ union {
+ RCSNUM *rev;
+ char *str;
+ struct tm date;
+ } rp_value;
+};
+
+struct rcs_keyword {
+ const char *k_name;
+ int k_val;
+};
+
+struct rcs_section {
+ int token;
+ int (*parse)(RCSFILE *, struct rcs_pdata *);
+ int opt;
+};
+
+/* this has to be sorted always */
+static const struct rcs_keyword keywords[] = {
+ { "access", RCS_TOK_ACCESS},
+ { "author", RCS_TOK_AUTHOR},
+ { "branch", RCS_TOK_BRANCH},
+ { "branches", RCS_TOK_BRANCHES},
+ { "comment", RCS_TOK_COMMENT},
+ { "date", RCS_TOK_DATE},
+ { "desc", RCS_TOK_DESC},
+ { "expand", RCS_TOK_EXPAND},
+ { "head", RCS_TOK_HEAD},
+ { "locks", RCS_TOK_LOCKS},
+ { "log", RCS_TOK_LOG},
+ { "next", RCS_TOK_NEXT},
+ { "state", RCS_TOK_STATE},
+ { "strict", RCS_TOK_STRICT},
+ { "symbols", RCS_TOK_SYMBOLS},
+ { "text", RCS_TOK_TEXT}
+};
+
+/* parser functions specified in rcs_section structs */
+static int rcsparse_head(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_branch(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_access(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_symbols(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_locks(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_strict(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_comment(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_commitid(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_expand(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_deltarevision(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_date(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_author(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_state(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_branches(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_next(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_textrevision(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_log(RCSFILE *, struct rcs_pdata *);
+static int rcsparse_text(RCSFILE *, struct rcs_pdata *);
+
+static int rcsparse_delta(RCSFILE *);
+static int rcsparse_deltatext(RCSFILE *);
+static int rcsparse_desc(RCSFILE *);
+
+static int kw_cmp(const void *, const void *);
+static int rcsparse(RCSFILE *, struct rcs_section *);
+static void rcsparse_growbuf(RCSFILE *);
+static int rcsparse_string(RCSFILE *, int);
+static int rcsparse_token(RCSFILE *, int);
+static void rcsparse_warnx(RCSFILE *, char *, ...);
+static int valid_login(char *);
+
+/*
+ * head [REVISION];
+ * [branch BRANCH];
+ * access [LOGIN ...];
+ * symbols [SYMBOL:REVISION ...];
+ * locks [LOGIN:REVISION ...];
+ * [strict;]
+ * [comment [@[...]@];]
+ * [expand [@[...]@];]
+ */
+static struct rcs_section sec_admin[] = {
+ { RCS_TOK_HEAD, rcsparse_head, MANDATORY },
+ { RCS_TOK_BRANCH, rcsparse_branch, OPTIONAL },
+ { RCS_TOK_ACCESS, rcsparse_access, MANDATORY },
+ { RCS_TOK_SYMBOLS, rcsparse_symbols, MANDATORY },
+ { RCS_TOK_LOCKS, rcsparse_locks, MANDATORY },
+ { RCS_TOK_STRICT, rcsparse_strict, OPTIONAL },
+ { RCS_TOK_COMMENT, rcsparse_comment, OPTIONAL },
+ { RCS_TOK_EXPAND, rcsparse_expand, OPTIONAL },
+ { 0, NULL, 0 }
+};
+
+/*
+ * REVISION
+ * date [YY]YY.MM.DD.HH.MM.SS;
+ * author LOGIN;
+ * state STATE;
+ * branches [REVISION ...];
+ * next [REVISION];
+ * [commitid ID;]
+ */
+static struct rcs_section sec_delta[] = {
+ { RCS_TYPE_REVISION, rcsparse_deltarevision, MANDATORY },
+ { RCS_TOK_DATE, rcsparse_date, MANDATORY },
+ { RCS_TOK_AUTHOR, rcsparse_author, MANDATORY },
+ { RCS_TOK_STATE, rcsparse_state, MANDATORY },
+ { RCS_TOK_BRANCHES, rcsparse_branches, MANDATORY },
+ { RCS_TOK_NEXT, rcsparse_next, MANDATORY },
+ { RCS_TOK_COMMITID, rcsparse_commitid, OPTIONAL },
+ { 0, NULL, 0 }
+};
+
+/*
+ * REVISION
+ * log @[...]@
+ * text @[...]@
+ */
+static struct rcs_section sec_deltatext[] = {
+ { RCS_TYPE_REVISION, rcsparse_textrevision, MANDATORY },
+ { RCS_TOK_LOG, rcsparse_log, MANDATORY },
+ { RCS_TOK_TEXT, rcsparse_text, MANDATORY },
+ { 0, NULL, 0 }
+};
+
+/*
+ * rcsparse_init()
+ *
+ * Initializes the parsing data structure and parses the admin section of
+ * RCS file <rfp>.
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+int
+rcsparse_init(RCSFILE *rfp)
+{
+ struct rcs_pdata *pdp;
+
+ if (rfp->rf_flags & RCS_PARSED)
+ return (0);
+
+ pdp = xmalloc(sizeof(*pdp));
+ pdp->rp_buf = xmalloc(RCS_BUFSIZE);
+ pdp->rp_blen = RCS_BUFSIZE;
+ pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
+ pdp->rp_token = -1;
+ pdp->rp_lineno = 1;
+ pdp->rp_msglineno = 1;
+ if ((pdp->rp_file = fdopen(rfp->rf_fd, "r")) == NULL) {
+ xfree(pdp);
+ return (1);
+ }
+ /* ditch the strict lock */
+ rfp->rf_flags &= ~RCS_SLOCK;
+ rfp->rf_pdata = pdp;
+
+ if (rcsparse(rfp, sec_admin)) {
+ rcsparse_free(rfp);
+ return (1);
+ }
+
+ if ((rfp->rf_flags & RCS_PARSE_FULLY) &&
+ rcsparse_deltatexts(rfp, NULL)) {
+ rcsparse_free(rfp);
+ return (1);
+ }
+
+ rfp->rf_flags |= RCS_SYNCED;
+ return (0);
+}
+
+/*
+ * rcsparse_deltas()
+ *
+ * Parse deltas. If <rev> is not NULL, parse only as far as that
+ * revision. If <rev> is NULL, parse all deltas.
+ *
+ * Returns 0 on success or 1 on error.
+ */
+int
+rcsparse_deltas(RCSFILE *rfp, RCSNUM *rev)
+{
+ int ret;
+ struct rcs_delta *enddelta;
+
+ if ((rfp->rf_flags & PARSED_DELTAS) || (rfp->rf_flags & RCS_CREATE))
+ return (0);
+
+ for (;;) {
+ ret = rcsparse_delta(rfp);
+ if (rev != NULL) {
+ enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
+ if (enddelta == NULL)
+ return (1);
+
+ if (rcsnum_cmp(enddelta->rd_num, rev, 0) == 0)
+ break;
+ }
+
+ if (ret == 0) {
+ rfp->rf_flags |= PARSED_DELTAS;
+ break;
+ }
+ else if (ret == -1)
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * rcsparse_deltatexts()
+ *
+ * Parse deltatexts. If <rev> is not NULL, parse only as far as that
+ * revision. If <rev> is NULL, parse everything.
+ *
+ * Returns 0 on success or 1 on error.
+ */
+int
+rcsparse_deltatexts(RCSFILE *rfp, RCSNUM *rev)
+{
+ int ret;
+ struct rcs_delta *rdp;
+
+ if ((rfp->rf_flags & PARSED_DELTATEXTS) ||
+ (rfp->rf_flags & RCS_CREATE))
+ return (0);
+
+ if (!(rfp->rf_flags & PARSED_DESC))
+ if (rcsparse_desc(rfp))
+ return (1);
+ for (;;) {
+ if (rev != NULL) {
+ rdp = rcs_findrev(rfp, rev);
+ if (rdp->rd_text != NULL)
+ break;
+ else
+ ret = rcsparse_deltatext(rfp);
+ } else
+ ret = rcsparse_deltatext(rfp);
+ if (ret == 0) {
+ rfp->rf_flags |= PARSED_DELTATEXTS;
+ break;
+ }
+ else if (ret == -1)
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * rcsparse_free()
+ *
+ * Free the contents of the <rfp>'s parser data structure.
+ */
+void
+rcsparse_free(RCSFILE *rfp)
+{
+ struct rcs_pdata *pdp;
+
+ pdp = rfp->rf_pdata;
+
+ if (pdp->rp_file != NULL)
+ (void)fclose(pdp->rp_file);
+ if (pdp->rp_buf != NULL)
+ xfree(pdp->rp_buf);
+ if (pdp->rp_token == RCS_TYPE_REVISION)
+ rcsnum_free(pdp->rp_value.rev);
+ xfree(pdp);
+}
+
+/*
+ * rcsparse_desc()
+ *
+ * Parse desc of the RCS file <rfp>. By calling rcsparse_desc, all deltas
+ * will be parsed in order to proceed the reading cursor to the desc keyword.
+ *
+ * desc @[...]@;
+ *
+ * Returns 0 on success or 1 on error.
+ */
+static int
+rcsparse_desc(RCSFILE *rfp)
+{
+ struct rcs_pdata *pdp;
+
+ if (rfp->rf_flags & PARSED_DESC)
+ return (0);
+
+ if (!(rfp->rf_flags & PARSED_DELTAS) && rcsparse_deltas(rfp, NULL))
+ return (1);
+
+ pdp = (struct rcs_pdata *)rfp->rf_pdata;
+
+ if (rcsparse_token(rfp, RCS_TOK_DESC) != RCS_TOK_DESC ||
+ rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
+ return (1);
+
+ rfp->rf_desc = pdp->rp_value.str;
+ rfp->rf_flags |= PARSED_DESC;
+
+ return (0);
+}
+
+/*
+ * rcsparse_deltarevision()
+ *
+ * Called upon reaching a new REVISION entry in the delta section.
+ * A new rcs_delta structure will be prepared in pdp->rp_delta for further
+ * parsing.
+ *
+ * REVISION
+ *
+ * Always returns 0.
+ */
+static int
+rcsparse_deltarevision(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ struct rcs_delta *rdp;
+
+ rdp = xcalloc(1, sizeof(*rdp));
+ TAILQ_INIT(&rdp->rd_branches);
+ rdp->rd_num = pdp->rp_value.rev;
+ pdp->rp_delta = rdp;
+
+ return (0);
+}
+
+/*
+ * rcsparse_date()
+ *
+ * Parses the specified date of current delta pdp->rp_delta.
+ *
+ * date YYYY.MM.DD.HH.MM.SS;
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_date(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ if (rcsparse_token(rfp, RCS_TYPE_DATE) != RCS_TYPE_DATE)
+ return (1);
+
+ pdp->rp_delta->rd_date = pdp->rp_value.date;
+
+ return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_author()
+ *
+ * Parses the specified author of current delta pdp->rp_delta.
+ *
+ * author LOGIN;
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_author(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ if (rcsparse_token(rfp, RCS_TYPE_LOGIN) != RCS_TYPE_LOGIN)
+ return (1);
+
+ pdp->rp_delta->rd_author = pdp->rp_value.str;
+
+ return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_state()
+ *
+ * Parses the specified state of current delta pdp->rp_delta.
+ *
+ * state STATE;
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_state(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ if (rcsparse_token(rfp, RCS_TYPE_STATE) != RCS_TYPE_STATE)
+ return (1);
+
+ pdp->rp_delta->rd_state = pdp->rp_value.str;
+
+ return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_branches()
+ *
+ * Parses the specified branches of current delta pdp->rp_delta.
+ *
+ * branches [REVISION ...];
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_branches(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ struct rcs_branch *rb;
+ int type;
+
+ while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_REVISION))
+ == RCS_TYPE_REVISION) {
+ rb = xmalloc(sizeof(*rb));
+ rb->rb_num = pdp->rp_value.rev;
+ TAILQ_INSERT_TAIL(&(pdp->rp_delta->rd_branches), rb, rb_list);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_next()
+ *
+ * Parses the specified next revision of current delta pdp->rp_delta.
+ *
+ * next [REVISION];
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_next(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int type;
+
+ type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
+ if (type == RCS_TYPE_REVISION) {
+ pdp->rp_delta->rd_next = pdp->rp_value.rev;
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ } else
+ pdp->rp_delta->rd_next = rcsnum_alloc();
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_commitid()
+ *
+ * Parses the specified commit id of current delta pdp->rp_delta. The
+ * commitid keyword is optional and can be omitted.
+ *
+ * [commitid ID;]
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_commitid(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ if (rcsparse_token(rfp, RCS_TYPE_COMMITID) != RCS_TYPE_COMMITID)
+ return (1);
+
+ /* XXX - do something with commitid */
+
+ return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_textrevision()
+ *
+ * Called upon reaching a new REVISION entry in the delta text section.
+ * pdp->rp_delta will be set to REVISION's delta (created in delta section)
+ * for further parsing.
+ *
+ * REVISION
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_textrevision(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ struct rcs_delta *rdp;
+
+ TAILQ_FOREACH(rdp, &rfp->rf_delta, rd_list) {
+ if (rcsnum_cmp(rdp->rd_num, pdp->rp_value.rev, 0) == 0)
+ break;
+ }
+ if (rdp == NULL) {
+ rcsparse_warnx(rfp, "delta for revision \"%s\" not found",
+ pdp->rp_buf);
+ rcsnum_free(pdp->rp_value.rev);
+ return (1);
+ }
+ pdp->rp_delta = rdp;
+
+ rcsnum_free(pdp->rp_value.rev);
+ return (0);
+}
+
+/*
+ * rcsparse_log()
+ *
+ * Parses the specified log of current deltatext pdp->rp_delta.
+ *
+ * log @[...]@
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_log(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
+ return (1);
+
+ pdp->rp_delta->rd_log = pdp->rp_value.str;
+
+ return (0);
+}
+
+/*
+ * rcsparse_text()
+ *
+ * Parses the specified text of current deltatext pdp->rp_delta.
+ *
+ * text @[...]@
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_text(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
+ return (1);
+
+ pdp->rp_delta->rd_tlen = pdp->rp_tlen - 1;
+ if (pdp->rp_delta->rd_tlen == 0) {
+ pdp->rp_delta->rd_text = xstrdup("");
+ } else {
+ pdp->rp_delta->rd_text = xmalloc(pdp->rp_delta->rd_tlen);
+ memcpy(pdp->rp_delta->rd_text, pdp->rp_buf,
+ pdp->rp_delta->rd_tlen);
+ }
+ xfree(pdp->rp_value.str);
+
+ return (0);
+}
+
+/*
+ * rcsparse_head()
+ *
+ * Parses the head revision of RCS file <rfp>.
+ *
+ * head [REVISION];
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_head(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int type;
+
+ type = rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON);
+ if (type == RCS_TYPE_REVISION) {
+ rfp->rf_head = pdp->rp_value.rev;
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_branch()
+ *
+ * Parses the default branch of RCS file <rfp>. The branch keyword is
+ * optional and can be omitted.
+ *
+ * [branch BRANCH;]
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_branch(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int type;
+
+ type = rcsparse_token(rfp, RCS_TYPE_BRANCH|RCS_TOK_SCOLON);
+ if (type == RCS_TYPE_BRANCH) {
+ rfp->rf_branch = pdp->rp_value.rev;
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_access()
+ *
+ * Parses the access list of RCS file <rfp>.
+ *
+ * access [LOGIN ...];
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_access(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ struct rcs_access *ap;
+ int type;
+
+ while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN))
+ == RCS_TYPE_LOGIN) {
+ ap = xmalloc(sizeof(*ap));
+ ap->ra_name = pdp->rp_value.str;
+ TAILQ_INSERT_TAIL(&(rfp->rf_access), ap, ra_list);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_symbols()
+ *
+ * Parses the symbol list of RCS file <rfp>.
+ *
+ * symbols [SYMBOL:REVISION ...];
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_symbols(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ struct rcs_sym *symp;
+ char *name;
+ int type;
+
+ while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_SYMBOL)) ==
+ RCS_TYPE_SYMBOL) {
+ name = pdp->rp_value.str;
+ if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
+ rcsparse_token(rfp, RCS_TYPE_NUMBER) != RCS_TYPE_NUMBER) {
+ xfree(name);
+ return (1);
+ }
+ symp = xmalloc(sizeof(*symp));
+ symp->rs_name = name;
+ symp->rs_num = pdp->rp_value.rev;
+ TAILQ_INSERT_TAIL(&(rfp->rf_symbols), symp, rs_list);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_locks()
+ *
+ * Parses the lock list of RCS file <rfp>.
+ *
+ * locks [SYMBOL:REVISION ...];
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_locks(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ struct rcs_lock *lkp;
+ char *name;
+ int type;
+
+ while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN)) ==
+ RCS_TYPE_LOGIN) {
+ name = pdp->rp_value.str;
+ if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
+ rcsparse_token(rfp, RCS_TYPE_REVISION) !=
+ RCS_TYPE_REVISION) {
+ xfree(name);
+ return (1);
+ }
+ lkp = xmalloc(sizeof(*lkp));
+ lkp->rl_name = name;
+ lkp->rl_num = pdp->rp_value.rev;
+ TAILQ_INSERT_TAIL(&(rfp->rf_locks), lkp, rl_list);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_locks()
+ *
+ * Parses the strict keyword of RCS file <rfp>. The strict keyword is
+ * optional and can be omitted.
+ *
+ * [strict;]
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_strict(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ rfp->rf_flags |= RCS_SLOCK;
+
+ return (rcsparse_token(rfp, RCS_TOK_SCOLON) != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_comment()
+ *
+ * Parses the comment of RCS file <rfp>. The comment keyword is optional
+ * and can be omitted.
+ *
+ * [comment [@[...]@];]
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_comment(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int type;
+
+ type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
+ if (type == RCS_TYPE_STRING) {
+ rfp->rf_comment = pdp->rp_value.str;
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+/*
+ * rcsparse_expand()
+ *
+ * Parses expand of RCS file <rfp>. The expand keyword is optional and
+ * can be omitted.
+ *
+ * [expand [@[...]@];]
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_expand(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int type;
+
+ type = rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON);
+ if (type == RCS_TYPE_STRING) {
+ rfp->rf_expand = pdp->rp_value.str;
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ }
+
+ return (type != RCS_TOK_SCOLON);
+}
+
+#define RBUF_PUTC(ch) \
+do { \
+ if (bp == pdp->rp_bufend - 1) { \
+ len = bp - pdp->rp_buf; \
+ rcsparse_growbuf(rfp); \
+ bp = pdp->rp_buf + len; \
+ } \
+ *(bp++) = (ch); \
+ pdp->rp_tlen++; \
+} while (0);
+
+static int
+rcsparse_string(RCSFILE *rfp, int allowed)
+{
+ struct rcs_pdata *pdp;
+ int c;
+ size_t len;
+ char *bp;
+
+ pdp = (struct rcs_pdata *)rfp->rf_pdata;
+
+ bp = pdp->rp_buf;
+ pdp->rp_tlen = 0;
+ *bp = '\0';
+
+ for (;;) {
+ c = getc(pdp->rp_file);
+ if (c == '@') {
+ c = getc(pdp->rp_file);
+ if (c == EOF) {
+ return (EOF);
+ } else if (c != '@') {
+ ungetc(c, pdp->rp_file);
+ break;
+ }
+ }
+
+ if (c == EOF) {
+ return (EOF);
+ } else if (c == '\n')
+ pdp->rp_lineno++;
+
+ RBUF_PUTC(c);
+ }
+
+ bp = pdp->rp_buf + pdp->rp_tlen;
+ RBUF_PUTC('\0');
+
+ if (!(allowed & RCS_TYPE_STRING)) {
+ rcsparse_warnx(rfp, "unexpected RCS string");
+ return (0);
+ }
+
+ pdp->rp_value.str = xstrdup(pdp->rp_buf);
+
+ return (RCS_TYPE_STRING);
+}
+
+static int
+rcsparse_token(RCSFILE *rfp, int allowed)
+{
+ const struct rcs_keyword *p;
+ struct rcs_pdata *pdp;
+ int c, pre, ret, type;
+ char *bp;
+ size_t len;
+ RCSNUM *datenum;
+
+ pdp = (struct rcs_pdata *)rfp->rf_pdata;
+
+ if (pdp->rp_token != -1) {
+ /* no need to check for allowed here */
+ type = pdp->rp_token;
+ pdp->rp_token = -1;
+ return (type);
+ }
+
+ /* skip whitespaces */
+ c = EOF;
+ do {
+ pre = c;
+ c = getc(pdp->rp_file);
+ if (c == EOF) {
+ if (ferror(pdp->rp_file)) {
+ rcsparse_warnx(rfp, "error during parsing");
+ return (0);
+ }
+ if (pre != '\n')
+ rcsparse_warnx(rfp,
+ "no newline at end of file");
+ return (EOF);
+ } else if (c == '\n')
+ pdp->rp_lineno++;
+ } while (isspace(c));
+
+ pdp->rp_msglineno = pdp->rp_lineno;
+ type = 0;
+ switch (c) {
+ case '@':
+ ret = rcsparse_string(rfp, allowed);
+ if (ret == EOF && ferror(pdp->rp_file)) {
+ rcsparse_warnx(rfp, "error during parsing");
+ return (0);
+ }
+ return (ret);
+ /* NOTREACHED */
+ case ':':
+ type = RCS_TOK_COLON;
+ if (type & allowed)
+ return (type);
+ rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
+ return (0);
+ /* NOTREACHED */
+ case ';':
+ type = RCS_TOK_SCOLON;
+ if (type & allowed)
+ return (type);
+ rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
+ return (0);
+ /* NOTREACHED */
+ case ',':
+ type = RCS_TOK_COMMA;
+ if (type & allowed)
+ return (type);
+ rcsparse_warnx(rfp, "unexpected token \"%c\"", c);
+ return (0);
+ /* NOTREACHED */
+ default:
+ if (!isgraph(c)) {
+ rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
+ return (0);
+ }
+ break;
+ }
+ allowed &= ~(RCS_TOK_COLON|RCS_TOK_SCOLON|RCS_TOK_COMMA);
+
+ bp = pdp->rp_buf;
+ pdp->rp_tlen = 0;
+ *bp = '\0';
+
+ for (;;) {
+ if (c == EOF) {
+ if (ferror(pdp->rp_file))
+ rcsparse_warnx(rfp, "error during parsing");
+ else
+ rcsparse_warnx(rfp, "unexpected end of file");
+ return (0);
+ } else if (c == '\n')
+ pdp->rp_lineno++;
+
+ RBUF_PUTC(c);
+
+ c = getc(pdp->rp_file);
+
+ if (isspace(c)) {
+ if (c == '\n')
+ pdp->rp_lineno++;
+ RBUF_PUTC('\0');
+ break;
+ } else if (c == ';' || c == ':' || c == ',') {
+ ungetc(c, pdp->rp_file);
+ RBUF_PUTC('\0');
+ break;
+ } else if (!isgraph(c)) {
+ rcsparse_warnx(rfp, "unexpected character 0x%.2X", c);
+ return (0);
+ }
+ }
+
+ switch (allowed) {
+ case RCS_TYPE_COMMITID:
+ /* XXX validate commitd */
+ break;
+ case RCS_TYPE_LOGIN:
+ if (!valid_login(pdp->rp_buf)) {
+ rcsparse_warnx(rfp, "invalid login \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ pdp->rp_value.str = xstrdup(pdp->rp_buf);
+ break;
+ case RCS_TYPE_SYMBOL:
+ if (!rcs_sym_check(pdp->rp_buf)) {
+ rcsparse_warnx(rfp, "invalid symbol \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ pdp->rp_value.str = xstrdup(pdp->rp_buf);
+ break;
+ /* FALLTHROUGH */
+ case RCS_TYPE_STATE:
+ if (rcs_state_check(pdp->rp_buf)) {
+ rcsparse_warnx(rfp, "invalid state \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ pdp->rp_value.str = xstrdup(pdp->rp_buf);
+ break;
+ case RCS_TYPE_DATE:
+ if ((datenum = rcsnum_parse(pdp->rp_buf)) == NULL) {
+ rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
+ return (0);
+ }
+ if (datenum->rn_len != 6) {
+ rcsnum_free(datenum);
+ rcsparse_warnx(rfp, "invalid date \"%s\"", pdp->rp_buf);
+ return (0);
+ }
+ pdp->rp_value.date.tm_year = datenum->rn_id[0];
+ if (pdp->rp_value.date.tm_year >= 1900)
+ pdp->rp_value.date.tm_year -= 1900;
+ pdp->rp_value.date.tm_mon = datenum->rn_id[1] - 1;
+ pdp->rp_value.date.tm_mday = datenum->rn_id[2];
+ pdp->rp_value.date.tm_hour = datenum->rn_id[3];
+ pdp->rp_value.date.tm_min = datenum->rn_id[4];
+ pdp->rp_value.date.tm_sec = datenum->rn_id[5];
+ rcsnum_free(datenum);
+ break;
+ case RCS_TYPE_NUMBER:
+ pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
+ if (pdp->rp_value.rev == NULL) {
+ rcsparse_warnx(rfp, "invalid number \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ break;
+ case RCS_TYPE_BRANCH:
+ pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
+ if (pdp->rp_value.rev == NULL) {
+ rcsparse_warnx(rfp, "invalid branch \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ if (!RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
+ rcsnum_free(pdp->rp_value.rev);
+ rcsparse_warnx(rfp, "expected branch, got \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ break;
+ case RCS_TYPE_KEYWORD:
+ if (islower(*pdp->rp_buf)) {
+ p = bsearch(pdp->rp_buf, keywords,
+ sizeof(keywords) / sizeof(keywords[0]),
+ sizeof(keywords[0]), kw_cmp);
+ if (p != NULL)
+ return (p->k_val);
+ }
+ allowed = RCS_TYPE_REVISION;
+ /* FALLTHROUGH */
+ case RCS_TYPE_REVISION:
+ pdp->rp_value.rev = rcsnum_parse(pdp->rp_buf);
+ if (pdp->rp_value.rev != NULL) {
+ if (RCSNUM_ISBRANCH(pdp->rp_value.rev)) {
+ rcsnum_free(pdp->rp_value.rev);
+ rcsparse_warnx(rfp,
+ "expected revision, got \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ break;
+ }
+ /* FALLTHROUGH */
+ default:
+ RBUF_PUTC('\0');
+ rcsparse_warnx(rfp, "unexpected token \"%s\"", pdp->rp_buf);
+ return (0);
+ /* NOTREACHED */
+ }
+
+ return (allowed);
+}
+
+static int
+rcsparse(RCSFILE *rfp, struct rcs_section *sec)
+{
+ struct rcs_pdata *pdp;
+ int i, token;
+
+ pdp = (struct rcs_pdata *)rfp->rf_pdata;
+ i = 0;
+
+ token = 0;
+ for (i = 0; sec[i].token != 0; i++) {
+ token = rcsparse_token(rfp, RCS_TYPE_KEYWORD);
+ if (token == 0)
+ return (1);
+
+ while (token != sec[i].token) {
+ if (sec[i].parse == NULL)
+ goto end;
+ if (sec[i].opt) {
+ i++;
+ continue;
+ }
+ if (token == EOF || (!(rfp->rf_flags & PARSED_DELTAS) &&
+ token == RCS_TOK_DESC))
+ goto end;
+ rcsparse_warnx(rfp, "unexpected token \"%s\"",
+ pdp->rp_buf);
+ return (1);
+ }
+
+ if (sec[i].parse(rfp, pdp))
+ return (1);
+ }
+end:
+ if (token == RCS_TYPE_REVISION)
+ pdp->rp_token = token;
+ else if (token == RCS_TOK_DESC)
+ pdp->rp_token = RCS_TOK_DESC;
+ else if (token == EOF)
+ rfp->rf_flags |= RCS_PARSED;
+
+ return (0);
+}
+
+static int
+rcsparse_deltatext(RCSFILE *rfp)
+{
+ struct rcs_pdata *pdp;
+ int ret;
+
+ if (rfp->rf_flags & PARSED_DELTATEXTS)
+ return (0);
+
+ if (!(rfp->rf_flags & PARSED_DESC))
+ if ((ret = rcsparse_desc(rfp)))
+ return (ret);
+
+ pdp = (struct rcs_pdata *)rfp->rf_pdata;
+
+ if (rcsparse(rfp, sec_deltatext))
+ return (-1);
+
+ if (rfp->rf_flags & RCS_PARSED)
+ rfp->rf_flags |= PARSED_DELTATEXTS;
+
+ return (1);
+}
+
+static int
+rcsparse_delta(RCSFILE *rfp)
+{
+ struct rcs_pdata *pdp;
+
+ if (rfp->rf_flags & PARSED_DELTAS)
+ return (0);
+
+ pdp = (struct rcs_pdata *)rfp->rf_pdata;
+ if (pdp->rp_token == RCS_TOK_DESC) {
+ rfp->rf_flags |= PARSED_DELTAS;
+ return (0);
+ }
+
+ if (rcsparse(rfp, sec_delta))
+ return (-1);
+
+ if (pdp->rp_delta != NULL) {
+ TAILQ_INSERT_TAIL(&rfp->rf_delta, pdp->rp_delta, rd_list);
+ pdp->rp_delta = NULL;
+ rfp->rf_ndelta++;
+ return (1);
+ }
+
+ return (0);
+}
+
+/*
+ * rcsparse_growbuf()
+ *
+ * Attempt to grow the internal parse buffer for the RCS file <rf> by
+ * RCS_BUFEXTSIZE.
+ * In case of failure, the original buffer is left unmodified.
+ */
+static void
+rcsparse_growbuf(RCSFILE *rfp)
+{
+ struct rcs_pdata *pdp = (struct rcs_pdata *)rfp->rf_pdata;
+
+ pdp->rp_buf = xrealloc(pdp->rp_buf, 1,
+ pdp->rp_blen + RCS_BUFEXTSIZE);
+ pdp->rp_blen += RCS_BUFEXTSIZE;
+ pdp->rp_bufend = pdp->rp_buf + pdp->rp_blen - 1;
+}
+
+/*
+ * Borrowed from src/usr.sbin/user/user.c:
+ * return 1 if `login' is a valid login name
+ */
+static int
+valid_login(char *login_name)
+{
+ unsigned char *cp;
+
+ /* The first character cannot be a hyphen */
+ if (*login_name == '-')
+ return 0;
+
+ for (cp = login_name ; *cp ; cp++) {
+ /* We allow '$' as the last character for samba */
+ if (!isalnum(*cp) && *cp != '.' && *cp != '_' && *cp != '-' &&
+ !(*cp == '$' && *(cp + 1) == '\0')) {
+ return 0;
+ }
+ }
+ if ((char *)cp - login_name > _PW_NAME_LEN)
+ return 0;
+ return 1;
+}
+
+static int
+kw_cmp(const void *k, const void *e)
+{
+ return (strcmp(k, ((const struct rcs_keyword *)e)->k_name));
+}
+
+static void
+rcsparse_warnx(RCSFILE *rfp, char *fmt, ...)
+{
+ struct rcs_pdata *pdp;
+ va_list ap;
+ char *nfmt;
+
+ pdp = (struct rcs_pdata *)rfp->rf_pdata;
+ va_start(ap, fmt);
+ if (asprintf(&nfmt, "%s:%d: %s", rfp->rf_path, pdp->rp_msglineno, fmt)
+ == -1)
+ nfmt = fmt;
+ cvs_vlog(LP_ERR, nfmt, ap);
+ va_end(ap);
+ if (nfmt != fmt)
+ free(nfmt);
+}
diff --git a/usr.bin/cvs/rcsparse.h b/usr.bin/cvs/rcsparse.h
new file mode 100644
index 00000000000..64c503a8304
--- /dev/null
+++ b/usr.bin/cvs/rcsparse.h
@@ -0,0 +1,21 @@
+/* $OpenBSD: rcsparse.h,v 1.1 2010/10/15 08:44:12 tobias Exp $ */
+/*
+ * Copyright (c) 2010 Tobias Stoeckmann <tobias@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+int rcsparse_deltas(RCSFILE *, RCSNUM *);
+int rcsparse_deltatexts(RCSFILE *, RCSNUM *);
+void rcsparse_free(RCSFILE *);
+int rcsparse_init(RCSFILE *);