diff --git a/swh/loader/cvs/rcsparse/rcsparse.c b/swh/loader/cvs/rcsparse/rcsparse.c
index b490832..992c60c 100644
--- a/swh/loader/cvs/rcsparse/rcsparse.c
+++ b/swh/loader/cvs/rcsparse/rcsparse.c
@@ -1,1439 +1,1441 @@
/*
* This file is part of rcsparse.
*
* rcsparse is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* rcsparse is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with rcsparse. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "queue.h"
#include "tree.h"
#include "rcsparse.h"
struct line {
char *str;
size_t len;
size_t atcount;
};
struct stringinfo {
size_t size;
size_t pos;
struct line lines[0];
};
#define STRNFO_LINES 8
struct rcsdelta {
SLIST_ENTRY(rcsdelta) link;
int action;
size_t pos;
size_t len;
struct line *lines;
};
static int cmprev(struct rcsrev *, struct rcsrev *);
static int cmptokpair(struct rcstokpair *, struct rcstokpair *);
static struct rcstoken *checktok(struct rcsfile *);
static int skipws(struct rcsfile *);
static struct rcstoken *parsestring(struct rcsfile *, struct stringinfo **);
static struct rcstoken *parsetoken(struct rcsfile *);
static int tokcmp(struct rcstoken *, struct rcstoken *);
static int tokeqtok(struct rcstoken *, struct rcstoken *);
static int tokeqstr(struct rcstoken *, const char *);
static int tokeqstrn(struct rcstoken *, const char *, size_t);
static int expecttok(struct rcsfile *, int);
static int opttok(struct rcsfile *, int);
static int expecttokstr(struct rcsfile *, const char *);
static int opttokstr(struct rcsfile *, const char *);
static char *strnfo2txtbuf(struct stringinfo *, size_t *);
static int applydelta(struct stringinfo **, struct stringinfo *);
RB_GENERATE(rcsrevtree, rcsrev, link, cmprev);
RB_GENERATE(rcstokmap, rcstokpair, link, cmptokpair);
static int
cmprev(struct rcsrev *rev1, struct rcsrev *rev2)
{
return tokcmp(rev1->rev, rev2->rev);
}
static int
cmptokpair(struct rcstokpair *pair1, struct rcstokpair *pair2)
{
return tokcmp(pair1->first, pair2->first);
}
static struct rcstoken *
checktok(struct rcsfile *rcs)
{
if (rcs->tok == NULL)
rcs->lasttok = rcs->tok = calloc(1, sizeof(struct rcstoken));
return rcs->tok;
}
static int
resizestrnfo(struct stringinfo **si, size_t len)
{
size_t newsize;
newsize = (*si)->size;
while (len > newsize)
newsize *= 2;
if (newsize > (*si)->size) {
struct stringinfo *nsi;
nsi = realloc(*si, sizeof(struct stringinfo) +
newsize * sizeof(struct line));
if (nsi == NULL)
return -1;
nsi->size = newsize;
*si = nsi;
}
return 0;
}
static struct stringinfo *
copystrnfo(struct stringinfo *si)
{
struct stringinfo *nsi;
size_t size;
size = sizeof(*si) + si->size * sizeof(si->lines[0]);
nsi = malloc(size);
if (nsi == NULL)
return NULL;
memcpy(nsi, si, size);
return nsi;
}
static int
skipws(struct rcsfile *rcs)
{
for (; rcs->pos < rcs->end; rcs->pos++) {
switch (*rcs->pos) {
case ' ':
case '\b':
case '\t':
case '\n':
case '\r':
case '\v':
case '\f':
continue;
}
break;
}
return rcs->pos == rcs->end ? -1 : 0;
}
static struct rcstoken *
parsestring(struct rcsfile *rcs, struct stringinfo **sip)
{
int atcount;
struct stringinfo *si = NULL;
struct rcstoken *tok;
if (skipws(rcs) < 0)
return NULL;
if (*rcs->pos != '@')
return NULL;
tok = checktok(rcs);
if (tok == NULL)
return NULL;
rcs->pos++;
if (sip != NULL) {
*sip = NULL;
si = malloc(sizeof(struct stringinfo) + STRNFO_LINES * sizeof(struct line));
if (si == NULL)
return NULL;
si->size = STRNFO_LINES;
si->pos = 0;
si->lines[0].str = rcs->pos;
si->lines[0].atcount = 0;
}
tok->str = rcs->pos;
atcount = 0;
for (; rcs->pos < rcs->end; rcs->pos++) {
switch (*rcs->pos) {
case '\n':
if (si != NULL) {
if (resizestrnfo(&si, si->pos + 2) < 0)
goto fail;
si->lines[si->pos].len = rcs->pos - si->lines[si->pos].str + 1;
si->pos++;
si->lines[si->pos].str = rcs->pos + 1;
si->lines[si->pos].atcount = 0;
}
continue;
case '@':
if (rcs->pos + 1 == rcs->end)
goto fail;
rcs->pos++;
if (*rcs->pos != '@')
break;
atcount++;
if (si != NULL)
si->lines[si->pos].atcount++;
continue;
default:
continue;
}
/* If we reached this point, we need to finish */
break;
}
if (si != NULL) {
si->lines[si->pos].len = rcs->pos - si->lines[si->pos].str - 1;
if (si->lines[si->pos].len != 0) /* last line didn't end with '\n' */
si->pos++;
*sip = si;
}
tok->len = rcs->pos - tok->str - 1;
tok->type = atcount > 0 ? TOK_STRINGAT : TOK_STRING;
return tok;
fail:
if (si != NULL)
free(si);
return NULL;
}
static struct rcstoken *
parsetoken(struct rcsfile *rcs)
{
int ch;
int type;
int finish;
struct rcstoken *tok;
if (skipws(rcs) < 0)
return NULL;
tok = checktok(rcs);
+ if (tok == NULL)
+ return NULL;
ch = *rcs->pos;
switch (ch) {
case ';':
case ':':
case ',':
case '$':
tok->type = ch;
tok->str = rcs->pos;
rcs->pos++;
tok->len = 1;
return tok;
case '@':
return parsestring(rcs, NULL);
}
tok->str = rcs->pos;
type = 0;
finish = 0;
while (rcs->pos < rcs->end && !finish) {
ch = *rcs->pos;
switch (ch) {
case ' ':
case '\b':
case '\t':
case '\n':
case '\r':
case '\v':
case '\f':
case ';':
case ':':
case ',':
case '$':
case '@':
finish = 1;
continue;
}
if (isdigit(ch))
type |= TOK_DIGIT;
else if (ch == '.')
type |= TOK_DOT;
else
type |= TOK_PRINT;
rcs->pos++;
}
tok->type = type;
tok->len = rcs->pos - tok->str;
return tok;
}
static int
tokcmp(struct rcstoken *tok1, struct rcstoken *tok2)
{
char *pos1, *pos2, *end1, *end2;
pos1 = tok1->str;
end1 = pos1 + tok1->len;
pos2 = tok2->str;
end2 = pos2 + tok2->len;
for (; pos1 < end1 && pos2 < end2; pos1++, pos2++)
if (*pos1 != *pos2)
return *pos1 - *pos2;
if (pos1 == end1) {
if (pos2 == end2)
return 0;
else
return -1;
} else {
return 1;
}
}
static int
tokeqtok(struct rcstoken *tok1, struct rcstoken *tok2)
{
return tokcmp(tok1, tok2) == 0;
}
static int
tokeqstr(struct rcstoken *tok, const char *str)
{
char *pos, *endpos;
pos = tok->str;
endpos = pos + tok->len;
for (; pos < endpos && *str; pos++, str++) {
if (*str != *pos)
return 0;
}
if (pos == endpos && *str == '\0')
return 1;
else
return 0;
}
static int
tokeqstrn(struct rcstoken *tok, const char *str, size_t len)
{
char *pos, *endpos;
if (tok->len < len)
return 0;
pos = tok->str;
endpos = pos + tok->len;
for (; len && *str; pos++, str++, len--) {
if (*str != *pos)
return 0;
}
return len == 0 ? 1 : 0;
}
static int
expecttok(struct rcsfile *rcs, int type)
{
if (parsetoken(rcs) == NULL)
return -2;
if (rcs->tok->type == type)
return 0;
else
return -1;
}
static int
opttok(struct rcsfile *rcs, int type)
{
return expecttok(rcs, type) + 1;
}
static int
expecttokstr(struct rcsfile *rcs, const char *str)
{
if (parsetoken(rcs) == NULL)
return -2;
return tokeqstr(rcs->tok, str) ? 0 : -1;
}
static int
opttokstr(struct rcsfile *rcs, const char *str)
{
int ret;
ret = expecttokstr(rcs, str);
if (ret == -1)
rcs->pos = rcs->tok->str;
return ret + 1;
}
static char *
tokstripat(struct rcstoken *tok)
{
char *ret;
ret = malloc(tok->len + 1);
if (ret == NULL)
return NULL;
if (tok->type == TOK_STRING) {
bcopy(tok->str, ret, tok->len);
ret[tok->len] = '\0';
} else {
char *endpos, *at, *ipos, *opos;
ipos = tok->str;
endpos = ipos + tok->len;
opos = ret;
while ((at = memchr(ipos, '@', endpos - ipos)) != NULL) {
bcopy(ipos, opos, at - ipos + 1);
opos += at - ipos + 1;
ipos = at + 2;
}
bcopy(ipos, opos, endpos - ipos);
opos += endpos - ipos;
*opos = '\0';
}
return ret;
}
static char *
strnfo2txtbuf(struct stringinfo *si, size_t *plen)
{
struct line *curline;
size_t lineno, len;
char *pos, *ret;
for (len = lineno = 0; lineno < si->pos; lineno++)
len += si->lines[lineno].len;
ret = malloc(len + 1);
if (ret == NULL)
return NULL;
ret[len] = '\0';
pos = ret;
for (lineno = 0, curline = si->lines; lineno < si->pos; lineno++, curline++) {
if (curline->atcount == 0) {
bcopy(curline->str, pos, curline->len);
pos += curline->len;
} else {
char *oldpos, *newpos, *endpos;
size_t atn;
oldpos = curline->str;
endpos = oldpos + curline->len;
for (atn = 0; atn < curline->atcount; atn++) {
newpos = memchr(oldpos, '@', endpos - oldpos);
bcopy(oldpos, pos, newpos - oldpos + 1);
pos += newpos - oldpos + 1;
oldpos = newpos + 2;
}
bcopy(oldpos, pos, endpos - oldpos);
pos += endpos - oldpos;
}
}
if (plen != NULL)
*plen = pos - ret;
return ret;
}
static int
applydelta(struct stringinfo **text, struct stringinfo *deltatext)
{
SLIST_HEAD(, rcsdelta) deltas;
struct rcsdelta *curdelta;
struct stringinfo *curtext;
size_t lineno;
int ret = -1;
curdelta = NULL;
SLIST_INIT(&deltas);
curtext = *text;
for (lineno = 0; lineno < deltatext->pos; lineno++) {
char *pos, *endpos;
pos = deltatext->lines[lineno].str;
endpos = pos + deltatext->lines[lineno].len;
if (endpos - pos < 5) /* check for minimum */
goto fail;
curdelta = calloc(1, sizeof(struct rcsdelta));
if (curdelta == NULL)
goto fail;
if (*pos != 'a' && *pos != 'd')
goto fail;
curdelta->action = *pos;
pos++;
for (curdelta->pos = 0; pos < endpos && isdigit(*pos); pos++)
curdelta->pos = curdelta->pos * 10 + *pos - '0';
if (pos == endpos || *pos++ != ' ' || pos == endpos)
goto fail;
for (curdelta->len = 0; pos < endpos && isdigit(*pos); pos++)
curdelta->len = curdelta->len * 10 + *pos - '0';
if (pos == endpos || *pos != '\n')
goto fail;
if (curdelta->len == 0)
goto fail;
if (curdelta->action == 'a') {
curdelta->lines = &deltatext->lines[lineno + 1];
lineno += curdelta->len;
}
SLIST_INSERT_HEAD(&deltas, curdelta, link);
curdelta = NULL;
}
if (lineno != deltatext->pos)
goto fail;
while ((curdelta = SLIST_FIRST(&deltas)) != NULL) {
SLIST_REMOVE_HEAD(&deltas, link);
switch (curdelta->action) {
case 'a':
if (resizestrnfo(&curtext, curtext->pos + curdelta->len) < 0)
goto fail;
bcopy(&curtext->lines[curdelta->pos],
&curtext->lines[curdelta->pos + curdelta->len],
(curtext->pos - curdelta->pos) * sizeof(struct line));
bcopy(curdelta->lines,
&curtext->lines[curdelta->pos],
curdelta->len * sizeof(struct line));
curtext->pos += curdelta->len;
break;
case 'd':
if (curdelta->pos <= 0 ||
curdelta->pos > curtext->pos ||
curdelta->pos + curdelta->len - 1 > curtext->pos)
goto fail;
bcopy(&curtext->lines[curdelta->pos + curdelta->len - 1],
&curtext->lines[curdelta->pos - 1],
(curtext->pos - curdelta->pos - curdelta->len + 1) *
sizeof(struct line));
curtext->pos -= curdelta->len;
break;
}
free(curdelta);
}
ret = 0;
fail:
if (curdelta != NULL)
free(curdelta);
while ((curdelta = SLIST_FIRST(&deltas)) != NULL) {
SLIST_REMOVE_HEAD(&deltas, link);
free(curdelta);
}
*text = curtext;
return ret;
}
static void
rcsfreerev(struct rcsrev *rev)
{
struct rcstoken *tok;
free(rev->rev);
free(rev->date);
free(rev->author);
if (rev->state != NULL)
free(rev->state);
if (rev->next != NULL)
free(rev->next);
while ((tok = SLIST_FIRST(&rev->branches)) != NULL) {
SLIST_REMOVE_HEAD(&rev->branches, link);
free(tok);
}
if (rev->commitid != NULL)
free(rev->commitid);
if (rev->log != NULL)
free(rev->log);
if (rev->rawtext != NULL)
free(rev->rawtext);
if (rev->text != NULL)
free(rev->text);
free(rev);
}
int
rcsparseadmin(struct rcsfile *rcs)
{
if (rcs->revpos != NULL)
return 0;
if (expecttokstr(rcs, "head") < 0)
return -1;
if (opttok(rcs, ';') == 0) {
if ((rcs->tok->type & ~TOK_NUM) != 0)
return -1;
rcs->admin.head = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
return -1;
}
if (opttokstr(rcs, "branch") > 0) {
if (opttok(rcs, ';') == 0) {
if ((rcs->tok->type & ~TOK_NUM) != 0)
return -1;
rcs->admin.branch = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
return -1;
}
}
if (expecttokstr(rcs, "access") < 0)
return -1;
while (opttok(rcs, ';') == 0) {
SLIST_INSERT_HEAD(&rcs->admin.access, rcs->tok, link);
rcs->tok = NULL;
}
if (expecttokstr(rcs, "symbols") < 0)
return -1;
while (opttok(rcs, ';') == 0) {
struct rcstokpair *pair;
if ((rcs->tok->type & TOK_DOT) != 0)
return -1;
pair = calloc(1, sizeof(struct rcstokpair));
if (pair == NULL)
return -1;
pair->first = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ':') < 0 ||
parsetoken(rcs) == NULL ||
(rcs->tok->type & ~TOK_NUM) != 0) {
free(pair);
return -1;
}
pair->second = rcs->tok;
rcs->tok = NULL;
RB_INSERT(rcstokmap, &rcs->admin.symbols, pair);
}
if (expecttokstr(rcs, "locks") < 0)
return -1;
while (opttok(rcs, ';') == 0) {
struct rcstokpair *pair;
pair = calloc(1, sizeof(struct rcstokpair));
if (pair == NULL)
return -1;
pair->first = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ':') < 0 ||
parsetoken(rcs) == NULL ||
(rcs->tok->type & ~TOK_NUM) != 0) {
free(pair);
return -1;
}
pair->second = rcs->tok;
rcs->tok = NULL;
RB_INSERT(rcstokmap, &rcs->admin.locks, pair);
}
if (opttokstr(rcs, "strict") > 0) {
rcs->admin.strict = 1;
if (expecttok(rcs, ';') < 0)
return -1;
}
if (opttokstr(rcs, "comment") > 0) {
if (opttok(rcs, ';') == 0) {
rcs->admin.comment = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
return -1;
}
}
if (opttokstr(rcs, "expand") > 0) {
if (opttok(rcs, ';') == 0) {
rcs->admin.expand = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
return -1;
}
}
for (;;) {
rcs->revpos = rcs->pos;
if (parsetoken(rcs) == NULL)
return -1;
if (tokeqstr(rcs->tok, "desc")) {
rcs->pos = rcs->tok->str;
break;
}
if ((rcs->tok->type & ~TOK_NUM) == 0) {
rcs->pos = rcs->tok->str;
break;
}
while (opttok(rcs, ';') == 0)
;
}
return 0;
}
int
rcsparsetree(struct rcsfile *rcs)
{
struct rcsrev searchrev;
struct rcsrev *rev = NULL;
if (rcs->deltapos != NULL)
return 0;
if (rcsparseadmin(rcs) < 0)
return -1;
rcs->pos = rcs->revpos;
for (;;) {
if (parsetoken(rcs) == NULL)
return -1;
if (tokeqstr(rcs->tok, "desc")) {
rcs->pos = rcs->tok->str;
break;
}
rev = calloc(1, sizeof(struct rcsrev));
if (rev == NULL)
return -1;
if ((rcs->tok->type & ~TOK_NUM) != 0)
goto fail;
rev->rev = rcs->tok;
rcs->tok = NULL;
if (expecttokstr(rcs, "date") < 0)
goto fail;
if (expecttok(rcs, TOK_NUM) < 0)
goto fail;
if (rcs->tok->len != 17 && rcs->tok->len != 19)
goto fail;
rev->date = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
goto fail;
if (expecttokstr(rcs, "author") < 0)
goto fail;
if (parsetoken(rcs) == NULL)
goto fail;
if ((rcs->tok->type & (TOK_STRING | TOK_PRINT)) != TOK_PRINT)
goto fail;
rev->author = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
goto fail;
if (expecttokstr(rcs, "state") < 0)
goto fail;
if (opttok(rcs, ';') == 0) {
rev->state = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
goto fail;
}
if (expecttokstr(rcs, "branches") < 0)
goto fail;
while (opttok(rcs, ';') == 0) {
SLIST_INSERT_HEAD(&rev->branches, rcs->tok, link);
rcs->tok = NULL;
}
if (expecttokstr(rcs, "next") < 0)
goto fail;
if (opttok(rcs, ';') == 0) {
rev->next = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
goto fail;
}
if (expecttokstr(rcs, "commitid") < 0) {
/* No hit, rewind */
rcs->pos = rcs->tok->str;
} else {
if (parsetoken(rcs) == NULL)
goto fail;
rev->commitid = rcs->tok;
rcs->tok = NULL;
if (expecttok(rcs, ';') < 0)
goto fail;
}
for (;;) {
if (parsetoken(rcs) == NULL)
return -1;
if (tokeqstr(rcs->tok, "desc")) {
rcs->pos = rcs->tok->str;
break;
}
if ((rcs->tok->type & ~TOK_NUM) == 0) {
rcs->pos = rcs->tok->str;
break;
}
while (opttok(rcs, ';') == 0)
;
}
RB_INSERT(rcsrevtree, &rcs->admin.revs, rev);
rev = NULL;
}
parsetoken(rcs); /* We *know* it is good */
if (parsetoken(rcs) == NULL)
return -1;
rcs->admin.desc = rcs->tok;
rcs->tok = NULL;
rcs->deltapos = rcs->pos;
/* There are empty RCS files around */
if (rcs->admin.head == NULL)
return 0;
if (parsetoken(rcs) == NULL)
goto fail;
if ((rcs->tok->type & ~TOK_NUM) != 0)
goto fail;
if (!tokeqtok(rcs->tok, rcs->admin.head))
goto fail;
searchrev.rev = rcs->tok;
rev = RB_FIND(rcsrevtree, &rcs->admin.revs, &searchrev);
if (rev == NULL)
goto fail;
rev->logpos = rcs->pos;
return 0;
fail:
if (rev != NULL)
rcsfreerev(rev);
return -1;
}
static int
rcsparsetext(struct rcsfile *rcs, struct rcsrev *rrev)
{
struct rcsrev searchrev;
if (rrev->log != NULL)
return 0;
if (rrev->logpos == NULL)
return -1;
rcs->pos = rrev->logpos;
if (expecttokstr(rcs, "log") < 0)
return -1;
if (parsestring(rcs, NULL) == NULL)
return -1;
if (rrev->log == NULL) {
rrev->log = rcs->tok;
rcs->tok = NULL;
}
for (;;) {
if (parsetoken(rcs) == NULL)
return -1;
if (tokeqstr(rcs->tok, "text"))
break;
while (opttok(rcs, ';') == 0)
;
}
if (parsestring(rcs, &rrev->rawtext) == NULL)
return -1;
if (parsetoken(rcs) == NULL)
return (0); /* could be end of file */
if ((rcs->tok->type & ~TOK_NUM) != 0)
return -1;
searchrev.rev = rcs->tok;
rrev->nextlog = RB_FIND(rcsrevtree, &rcs->admin.revs, &searchrev);
if (rrev->nextlog == NULL)
return (-1);
rrev->nextlog->logpos = rcs->pos;
return 0;
}
char *
rcscheckout(struct rcsfile *rcs, const char *revstr, size_t *len)
{
struct rcsrev searchrev;
struct rcstoken searchtok;
struct rcsrev *currcsrev, *curtextrev;
struct stringinfo *curtext;
struct rcstoken *nextrev;
char *branchrev, *tmpstr;
char *retbuf, *rev;
if (rcsparsetree(rcs) < 0)
return NULL;
curtextrev = NULL;
curtext = NULL;
nextrev = NULL;
branchrev = NULL;
retbuf = NULL;
rev = rcsrevfromsym(rcs, revstr);
if (rev == NULL)
goto fail;
searchtok.str = rev;
searchtok.len = strlen(rev);
searchrev.rev = &searchtok;
currcsrev = RB_FIND(rcsrevtree, &rcs->admin.revs, &searchrev);
if (currcsrev == NULL)
goto fail;
curtext = currcsrev->text;
if (curtext != NULL)
goto done;
branchrev = strdup(rev);
if (branchrev == NULL)
goto fail;
tmpstr = strchr(branchrev, '.');
if (tmpstr != NULL)
tmpstr = strchr(tmpstr + 1, '.');
if (tmpstr != NULL)
*tmpstr = '\0';
searchrev.rev = rcs->admin.head;
currcsrev = RB_FIND(rcsrevtree, &rcs->admin.revs, &searchrev);
if (currcsrev == NULL)
goto fail;
for (; currcsrev != NULL; currcsrev = currcsrev->nextlog) {
if (rcsparsetext(rcs, currcsrev) < 0)
goto fail;
if (curtext == NULL) {
curtext = currcsrev->rawtext;
curtextrev = currcsrev;
} else {
if (nextrev == NULL)
goto fail;
if (!tokeqtok(currcsrev->rev, nextrev))
continue;
if (currcsrev->text) {
/* Was expanded before */
if (curtextrev != NULL) {
free(curtextrev->text);
curtextrev->text = NULL;
}
curtext = currcsrev->text;
curtextrev = currcsrev;
} else {
if (currcsrev->rawtext == NULL)
goto fail;
currcsrev->text = copystrnfo(curtext);
if (currcsrev->text == NULL)
goto fail;
if (applydelta(&currcsrev->text, currcsrev->rawtext) < 0)
goto fail;
if (curtextrev != NULL) {
free(curtextrev->text);
curtextrev->text = NULL;
}
curtext = currcsrev->text;
curtextrev = currcsrev;
}
}
if (tokeqstr(currcsrev->rev, rev))
break;
if (tokeqstr(currcsrev->rev, branchrev)) {
size_t cmplen;
*tmpstr = '.';
tmpstr = strchr(tmpstr + 1, '.');
if (tmpstr != NULL)
cmplen = tmpstr - branchrev + 1;
else
cmplen = strlen(branchrev) + 1;
SLIST_FOREACH(nextrev, &currcsrev->branches, link)
if (tokeqstrn(nextrev, branchrev, cmplen))
break;
if (tmpstr != NULL) {
tmpstr = strchr(tmpstr + 1, '.');
if (tmpstr != NULL)
*tmpstr = '\0';
}
} else {
nextrev = currcsrev->next;
}
}
if (currcsrev == NULL)
goto fail;
done:
if (tokeqstr(currcsrev->state, "dead")) {
/* TODO: optimize this case */
retbuf = strdup("");
if (len != NULL)
*len = 0;
} else {
retbuf = strnfo2txtbuf(curtext, len);
}
fail:
if (rev != NULL)
free(rev);
free(branchrev);
return retbuf;
}
char *
rcsrevfromsym(struct rcsfile *rcs, const char *sym)
{
struct rcsrev findrev, *rev;
struct rcstokpair findpair, *pair;
struct rcstoken findtok, branchtok, *tok;
char *pos, *endpos;
char *lastdot, *last2dot;
char *retrev;
size_t dotcount;
int issym, searchbranch;
/* To check head we only need admin info */
if (rcsparseadmin(rcs) < 0)
return NULL;
/* Handle special symbol "HEAD" */
if (sym == NULL || strcmp(sym, "HEAD") == 0) {
if (rcs->admin.branch == NULL) {
tok = rcs->admin.head;
goto found;
} else {
findtok = *rcs->admin.branch;
}
} else {
findtok.str = (char *)(unsigned long)sym;
findtok.len = strlen(sym);
}
/* We really need to wade in the revs, so parse them as well */
if (rcsparsetree(rcs) < 0)
return NULL;
dotcount = 0;
issym = 0;
lastdot = last2dot = NULL;
for (pos = findtok.str, endpos = pos + findtok.len; pos < endpos; pos++) {
if (*pos == '.') {
/* Two adjacent dots are invalid */
if (pos == lastdot + 1)
return NULL;
dotcount++;
last2dot = lastdot;
lastdot = pos;
} else if (!isdigit(*pos)) {
issym = 1;
}
}
if (issym && dotcount > 0)
return NULL;
if (*findtok.str == '.' || findtok.len == 0 || findtok.str[findtok.len - 1] == '.')
return NULL;
if (issym) {
findpair.first = &findtok;
pair = RB_FIND(rcstokmap, &rcs->admin.symbols, &findpair);
if (pair == NULL)
return NULL;
findtok = *pair->second;
for (pos = findtok.str, endpos = pos + findtok.len; pos < endpos; pos++) {
if (*pos == '.') {
/* Two adjacent dots are invalid */
if (pos == lastdot + 1)
return NULL;
dotcount++;
last2dot = lastdot;
lastdot = pos;
} else if (!isdigit(*pos)) {
return NULL;
}
}
}
searchbranch = 0;
if (dotcount == 0) {
branchtok = findtok;
findtok = *rcs->admin.head;
} else if (dotcount % 2 == 0 ||
(last2dot != NULL && lastdot - last2dot == 2 && *(last2dot + 1) == '0')) {
/*
* We are explicitly searching for a branch or
* seeking a magic branch.
*/
branchtok.str = lastdot + 1;
branchtok.len = findtok.str + findtok.len - branchtok.str;
if (dotcount % 2 == 0)
findtok.len = lastdot - findtok.str;
else
findtok.len = last2dot - findtok.str;
searchbranch = 1;
}
findrev.rev = &findtok;
rev = RB_FIND(rcsrevtree, &rcs->admin.revs, &findrev);
if (rev == NULL)
return NULL;
if (searchbranch) {
struct rcsrev *nextrev;
char *branchstr;
size_t branchlen;
/* First locate the right branch, then climb up */
branchlen = rev->rev->len + branchtok.len + 3;
branchstr = malloc(branchlen);
if (branchstr == NULL)
return NULL;
bcopy(rev->rev->str, branchstr, rev->rev->len);
branchstr[rev->rev->len] = '.';
bcopy(branchtok.str, branchstr + rev->rev->len + 1, branchtok.len);
branchstr[branchlen - 2] = '.';
branchstr[branchlen - 1] = '\0';
SLIST_FOREACH(tok, &rev->branches, link)
if (tokeqstrn(tok, branchstr, branchlen - 1))
break;
free(branchstr);
findrev.rev = tok;
while (findrev.rev != NULL &&
(nextrev = RB_FIND(rcsrevtree, &rcs->admin.revs, &findrev)) != NULL) {
rev = nextrev;
findrev.rev = rev->next;
}
}
if (dotcount == 0) {
for (;;) {
if (rev->rev->len > branchtok.len + 1 &&
memcmp(rev->rev->str, branchtok.str, branchtok.len) == 0 &&
rev->rev->str[branchtok.len] == '.')
break;
if (rev->next == NULL)
return NULL;
findrev.rev = rev->next;
rev = RB_FIND(rcsrevtree, &rcs->admin.revs, &findrev);
if (rev == NULL)
return NULL;
}
}
tok = rev->rev;
found:
retrev = malloc(tok->len + 1);
if (retrev == NULL)
return NULL;
bcopy(tok->str, retrev, tok->len);
retrev[tok->len] = '\0';
return retrev;
}
char *
rcsgetlog(struct rcsfile *rcs, const char *logrev)
{
struct rcstoken findtok;
struct rcsrev findrev, *rev;
if (rcsparsetree(rcs) < 0)
return NULL;
findtok.str = (char *)(long)logrev;
findtok.len = strlen(logrev);
findrev.rev = &findtok;
rev = RB_FIND(rcsrevtree, &rcs->admin.revs, &findrev);
if (rev == NULL)
return NULL;
if (rev->log != NULL)
goto done;
findrev.rev = rcs->admin.head;
rev = RB_FIND(rcsrevtree, &rcs->admin.revs, &findrev);
for (; rev != NULL; rev = rev->nextlog) {
if (rcsparsetext(rcs, rev) < 0)
return NULL;
if (tokeqstr(rev->rev, logrev))
break;
}
if (rev == NULL)
return NULL;
done:
return tokstripat(rev->log);
}
struct rcsfile *
rcsopen(const char *filename)
{
struct stat st;
struct rcsfile *rcs;
rcs = calloc(1, sizeof(struct rcsfile));
if (rcs == NULL)
goto fail;
rcs->file = open(filename, O_RDONLY);
if (rcs->file < 0)
goto fail;
if (fstat(rcs->file, &st) < 0)
goto fail;
rcs->size = st.st_size;
rcs->data = mmap(NULL, rcs->size, PROT_READ, MAP_PRIVATE, rcs->file, 0);
if (rcs->data == MAP_FAILED)
goto fail;
rcs->end = rcs->data + rcs->size;
rcs->pos = rcs->data;
SLIST_INIT(&rcs->admin.access);
RB_INIT(&rcs->admin.symbols);
RB_INIT(&rcs->admin.locks);
RB_INIT(&rcs->admin.revs);
return rcs;
fail:
if (rcs != NULL && rcs->file >= 0)
close(rcs->file);
if (rcs != NULL)
free(rcs);
return NULL;
}
void
rcsclose(struct rcsfile *rcs)
{
struct rcstoken *tok;
struct rcstokpair *pair;
struct rcsrev *rev;
if (rcs->tok != NULL) {
free(rcs->tok);
if (rcs->lasttok != NULL && rcs->lasttok != rcs->tok)
free(rcs->lasttok);
}
if (rcs->admin.head != NULL)
free(rcs->admin.head);
if (rcs->admin.branch != NULL)
free(rcs->admin.branch);
while ((tok = SLIST_FIRST(&rcs->admin.access)) != NULL) {
SLIST_REMOVE_HEAD(&rcs->admin.access, link);
free(tok);
}
if (rcs->admin.comment != NULL)
free(rcs->admin.comment);
if (rcs->admin.expand != NULL)
free(rcs->admin.expand);
if (rcs->admin.desc != NULL)
free(rcs->admin.desc);
while ((pair = RB_MIN(rcstokmap, &rcs->admin.symbols)) != NULL) {
RB_REMOVE(rcstokmap, &rcs->admin.symbols, pair);
free(pair->first);
free(pair->second);
free(pair);
}
while ((pair = RB_MIN(rcstokmap, &rcs->admin.locks)) != NULL) {
RB_REMOVE(rcstokmap, &rcs->admin.locks, pair);
free(pair->first);
free(pair->second);
free(pair);
}
while ((rev = RB_MIN(rcsrevtree, &rcs->admin.revs)) != NULL) {
RB_REMOVE(rcsrevtree, &rcs->admin.revs, rev);
rcsfreerev(rev);
}
munmap(rcs->data, rcs->size);
close(rcs->file);
free(rcs);
}
#ifdef TESTING
int
main(int argc, char **argv)
{
struct rcsfile *rcs;
char *buf, *rev, *log;
int i;
size_t len;
if (argc < 3)
errx(1, "invalid arguments");
rcs = rcsopen(argv[1]);
if (rcs == NULL)
return 1;
if (rcsparseadmin(rcs) < 0)
return 1;
if (rcsparsetree(rcs) < 0)
return 2;
for (i = 2; i < argc; i++) {
if (strcmp(argv[i], "all") == 0) {
struct rcsrev *rrev;
RB_FOREACH(rrev, rcsrevtree, &rcs->admin.revs) {
rev = malloc(rrev->rev->len + 1);
memcpy(rev, rrev->rev->str, rrev->rev->len);
rev[rrev->rev->len] = 0;
log = rcsgetlog(rcs, rev);
free(log);
buf = rcscheckout(rcs, rev, &len);
free(buf);
free(rev);
}
} else {
rev = rcsrevfromsym(rcs, argv[i]);
if (rev == NULL)
return 3;
log = rcsgetlog(rcs, rev);
if (log == NULL)
return 5;
printf("%s\n", log);
free(log);
buf = rcscheckout(rcs, rev, &len);
if (buf == NULL)
return 4;
fwrite(buf, 1, len, stdout);
free(buf);
free(rev);
}
}
rcsclose(rcs);
return 0;
}
#endif