Hi tech,
to keep things short: Please apply the diff and run OpenCVS with as many
different RCS files you can get your hands on. If you find bugs, please
try to seperate between new issues that arise with this diff and in
OpenCVS in general, i.e. try to reproduce any bugs with current OpenCVS
code, too.
To keep this huge diff and the RCS parsing code readable, the parser
functionality has been moved from rcs.c into rcsparse.c.
rcs_gettok (now rcsparse_token) has been rewritten in order to supply
properly checked values to the caller. Instead of parsing revision
numbers, symbols, states and so on in the caller, the caller supplies
information to rcsparse_token what to expect. This fixes already an
issue you can encounter by using cvs as user "1.5" or "1337" or whatever.
Beside of the rcs_gettok change, the parsing is split up into small
functions. They are called by rcsparse, a function that reads an array
of type rcs_section. Each section describes the expected keywords,
if they are optional and which function parses the arguments of that
keyword. Hopefully this increases readability of RCS handling for you.
This diff is OpenCVS-only right now. I expect to get this into OpenRCS
realy soon, but right now we are still using fatal() in parsing, which
has to be changed for OpenRCS -- and then in OpenCVS. Before I blow up
this diff even more, that'll be another story.
I have successfully parsed the whole OpenBSD repository, comparing the
resulting data structures with current OpenCVS; it works fine for us.
Please don't hesitate to give me feedback, as a user or as a developer.
Tobias Stoeckmann
Index: Makefile
===================================================================
RCS file: /home/tobias/anoncvs/src/usr.bin/cvs/Makefile,v
retrieving revision 1.47
diff -u -p -r1.47 Makefile
--- Makefile 21 Jun 2008 15:39:15 -0000 1.47
+++ Makefile 1 Oct 2010 10:09:13 -0000
@@ -8,9 +8,9 @@ SRCS= cvs.c add.c admin.c annotate.c ato
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
Index: rcs.c
===================================================================
RCS file: /home/tobias/anoncvs/src/usr.bin/cvs/rcs.c,v
retrieving revision 1.302
diff -u -p -r1.302 rcs.c
--- rcs.c 29 Sep 2010 09:23:54 -0000 1.302
+++ rcs.c 1 Oct 2010 11:10:48 -0000
@@ -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 f
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,7 @@ rcs_write(RCSFILE *rfp)
return;
/* Write operations need the whole file parsed */
- rcs_parse_deltatexts(rfp, NULL);
+ rcsparse_deltatexts(rfp, NULL);
if (strlcpy(tmpdir, rfp->rf_path, sizeof(tmpdir)) >= sizeof(tmpdir))
fatal("rcs_write: truncation");
@@ -1425,7 +1334,7 @@ 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);
+ rcsparse_deltas(rfp, rev);
}
TAILQ_FOREACH(rdp, &(rfp->rf_delta), rd_list) {
@@ -1528,711 +1437,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 +1473,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 +1515,7 @@ rcs_deltatext_set(RCSFILE *rfp, RCSNUM *
struct rcs_delta *rdp;
/* Write operations require full parsing */
- rcs_parse_deltatexts(rfp, NULL);
+ rcsparse_deltatexts(rfp, NULL);
if ((rdp = rcs_findrev(rfp, rev)) == NULL)
return (-1);
@@ -2727,7 +1747,7 @@ rcs_rev_getlines(RCSFILE *rfp, RCSNUM *f
fatal("rcs_rev_getlines: no HEAD revision");
tnum = frev;
- rcs_parse_deltatexts(rfp, hrdp->rd_num);
+ rcsparse_deltatexts(rfp, hrdp->rd_num);
/* revision on branch, get the branch root */
nextroot = 2;
@@ -2792,7 +1812,7 @@ again:
}
if (rdp->rd_tlen == 0) {
- rcs_parse_deltatexts(rfp, rdp->rd_num);
+ rcsparse_deltatexts(rfp, rdp->rd_num);
if (rdp->rd_tlen == 0) {
if (!rcsnum_differ(rdp->rd_num, bnum))
break;
@@ -2962,7 +1982,7 @@ rcs_annotate_getlines(RCSFILE *rfp, RCSN
}
if (rdp->rd_tlen == 0) {
- rcs_parse_deltatexts(rfp, rdp->rd_num);
+ rcsparse_deltatexts(rfp, rdp->rd_num);
if (rdp->rd_tlen == 0) {
if (!rcsnum_differ(rdp->rd_num, bnum))
break;
Index: rcsparse.c
===================================================================
RCS file: rcsparse.c
diff -N rcsparse.c
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ rcsparse.c 1 Oct 2010 12:02:10 -0000
@@ -0,0 +1,1248 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2010 Tobias Stoeckmann <[email protected]>
+ *
+ * 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 <ctype.h>
+#include <err.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "cvs.h"
+#include "rcs.h"
+#include "rcsparse.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_EXPAND (1 << 7)
+#define RCS_TOK_DESC (1 << 8)
+#define RCS_TOK_DATE (1 << 9)
+#define RCS_TOK_AUTHOR (1 << 10)
+#define RCS_TOK_STATE (1 << 11)
+#define RCS_TOK_BRANCHES (1 << 12)
+#define RCS_TOK_NEXT (1 << 13)
+#define RCS_TOK_LOG (1 << 14)
+#define RCS_TOK_TEXT (1 << 15)
+#define RCS_TOK_COLON (1 << 16)
+#define RCS_TOK_COMMA (1 << 17)
+#define RCS_TOK_SCOLON (1 << 18)
+
+#define RCS_TYPE_STRING (1 << 19)
+#define RCS_TYPE_NUMBER (1 << 20)
+#define RCS_TYPE_BRANCH (1 << 21)
+#define RCS_TYPE_REVISION (1 << 22)
+#define RCS_TYPE_LOGIN (1 << 23)
+#define RCS_TYPE_STATE (1 << 24)
+#define RCS_TYPE_SYMBOL (1 << 25)
+#define RCS_TYPE_DATE (1 << 26)
+#define RCS_TYPE_KEYWORD (1 << 27)
+
+#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;
+
+ FILE *rp_file;
+ int rp_token;
+ int rp_lineno;
+ int rp_msglineno;
+ struct rcs_delta *rp_delta;
+
+ 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_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 *, const char *, ...);
+static int valid_login(char *);
+
+/*
+ * head [REVISION];
+ * [branch REVISION];
+ * access [USER ...];
+ * symbols [SYMBOL:REVISION ...];
+ * locks [USER: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, MANDATORY },
+ { RCS_TOK_EXPAND, rcsparse_expand, OPTIONAL },
+ { 0, NULL, 0 }
+};
+
+/*
+ * REVISION
+ * date [YY]YY.MM.DD.HH.MM.SS;
+ * author USER;
+ * state STATE;
+ * branches [REVISION ...];
+ * next [REVISION];
+ */
+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 },
+ { 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);
+ (void)close(rfp->rf_fd);
+ rfp->rf_fd = -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.
+ */
+void
+rcsparse_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 = rcsparse_delta(rfp);
+ if (rev != NULL) {
+ enddelta = TAILQ_LAST(&(rfp->rf_delta), rcs_dlist);
+ if (enddelta == NULL)
+ fatal("error parsing deltas");
+
+ 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");
+ }
+}
+
+/*
+ * rcsparse_deltatexts()
+ *
+ * Parse deltatexts. If <rev> is not NULL, parse only as far as that
+ * revision. If <rev> is NULL, parse everything.
+ */
+void
+rcsparse_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))
+ if (rcsparse_desc(rfp))
+ fatal("error parsing desc");
+ 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)
+ fatal("problem parsing deltatexts");
+ }
+}
+
+/*
+ * 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);
+ 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) || (rfp->rf_flags & RCS_CREATE))
+ return (0);
+
+ if (!(rfp->rf_flags & PARSED_DELTAS))
+ rcsparse_deltas(rfp, NULL);
+
+ 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 USER;
+ *
+ * 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 ret, type;
+
+ switch (rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON)) {
+ case RCS_TYPE_REVISION:
+ pdp->rp_delta->rd_next = pdp->rp_value.rev;
+
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ ret = (type != RCS_TOK_SCOLON);
+ break;
+ case RCS_TOK_SCOLON:
+ pdp->rp_delta->rd_next = rcsnum_alloc();
+ ret = 0;
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * 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;
+
+ 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);
+ }
+
+ 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 ret, type;
+
+ switch (rcsparse_token(rfp, RCS_TYPE_REVISION|RCS_TOK_SCOLON)) {
+ case RCS_TYPE_REVISION:
+ rfp->rf_head = pdp->rp_value.rev;
+
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ ret = (type != RCS_TOK_SCOLON);
+ break;
+ case RCS_TOK_SCOLON:
+ ret = 0;
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * rcsparse_branch()
+ *
+ * Parses the default branch of RCS file <rfp>. The branch keyword is
+ * optional and can be omitted.
+ *
+ * [branch REVISION;]
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_branch(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int ret, type;
+
+ switch (rcsparse_token(rfp, RCS_TYPE_BRANCH|RCS_TOK_SCOLON)) {
+ case RCS_TYPE_BRANCH:
+ rfp->rf_branch = pdp->rp_value.rev;
+
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ ret = (type != RCS_TOK_SCOLON);
+ break;
+ case RCS_TOK_SCOLON:
+ ret = 0;
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+
+ return (0);
+}
+
+/*
+ * rcsparse_access()
+ *
+ * Parses the access list of RCS file <rfp>.
+ *
+ * access [USER ...];
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_access(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int type;
+
+ while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN))
+ == RCS_TYPE_LOGIN) {
+ if (rcs_access_add(rfp, pdp->rp_value.str)) {
+ rcsparse_warnx(rfp, "cannot add access \"%s\"",
+ pdp->rp_value.str);
+ xfree(pdp->rp_value.str);
+ return (1);
+ }
+ }
+
+ 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)
+{
+ char *sym;
+ int type;
+
+ while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_SYMBOL)) ==
+ RCS_TYPE_SYMBOL) {
+ sym = pdp->rp_value.str;
+ if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
+ rcsparse_token(rfp, RCS_TYPE_NUMBER) != RCS_TYPE_NUMBER) {
+ xfree(sym);
+ return (1);
+ }
+ (void)rcs_sym_add(rfp, sym, pdp->rp_value.rev);
+ }
+
+ 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)
+{
+ char *user;
+ int type;
+
+ while ((type = rcsparse_token(rfp, RCS_TOK_SCOLON|RCS_TYPE_LOGIN)) ==
+ RCS_TYPE_LOGIN) {
+ user = pdp->rp_value.str;
+ if (rcsparse_token(rfp, RCS_TOK_COLON) != RCS_TOK_COLON ||
+ rcsparse_token(rfp, RCS_TYPE_REVISION) !=
+ RCS_TYPE_REVISION) {
+ xfree(user);
+ return (1);
+ }
+ if (rcs_lock_add(rfp, user, pdp->rp_value.rev)) {
+ rcsparse_warnx(rfp, "cannot add %s's lock for %s", user,
+ pdp->rp_buf);
+ xfree(user);
+ rcsnum_free(pdp->rp_value.rev);
+ return (1);
+ }
+ }
+
+ 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>.
+ *
+ * comment @[...]@;
+ *
+ * Returns 0 on success or 1 on failure.
+ */
+static int
+rcsparse_comment(RCSFILE *rfp, struct rcs_pdata *pdp)
+{
+ int ret, type;
+
+ switch (rcsparse_token(rfp, RCS_TYPE_STRING|RCS_TOK_SCOLON)) {
+ case RCS_TYPE_STRING:
+ rfp->rf_comment = pdp->rp_value.str;
+
+ type = rcsparse_token(rfp, RCS_TOK_SCOLON);
+ ret = (type != RCS_TOK_SCOLON);
+ break;
+ case RCS_TOK_SCOLON:
+ ret = 0;
+ break;
+ default:
+ ret = 1;
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * 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)
+{
+ if (rcsparse_token(rfp, RCS_TYPE_STRING) != RCS_TYPE_STRING)
+ return (1);
+
+ rfp->rf_expand = pdp->rp_value.str;
+
+ return (rcsparse_token(rfp, RCS_TOK_SCOLON) != 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_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 (!isalpha(*pdp->rp_buf)) {
+ rcsparse_warnx(rfp, "invalid symbol \"%s\"",
+ pdp->rp_buf);
+ return (0);
+ }
+ /* FALLTHROUGH */
+ case RCS_TYPE_STATE:
+ for (bp = pdp->rp_buf; *bp != '\0'; bp++) {
+ if (strchr(RCS_SYM_INVALCHAR, *bp) != NULL) {
+ rcsparse_warnx(rfp, "invalid %s \"%s\"",
+ allowed == RCS_TYPE_STATE ? "state" :
+ "symbol", 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, const 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)
+ fatal("rcsparse_warnx asprintf");
+ vwarnx(nfmt, ap);
+ va_end(ap);
+ free(nfmt);
+}
Index: rcsparse.h
===================================================================
RCS file: rcsparse.h
diff -N rcsparse.h
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ rcsparse.h 1 Oct 2010 11:37:24 -0000
@@ -0,0 +1,21 @@
+/* $OpenBSD$ */
+/*
+ * Copyright (c) 2010 Tobias Stoeckmann <[email protected]>
+ *
+ * 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.
+ */
+
+void rcsparse_deltas(RCSFILE *, RCSNUM *);
+void rcsparse_deltatexts(RCSFILE *, RCSNUM *);
+void rcsparse_free(RCSFILE *);
+int rcsparse_init(RCSFILE *);