Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F8392369
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
29 KB
Subscribers
None
View Options
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 <http://www.gnu.org/licenses/>.
*/
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <ctype.h>
#include <err.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jun 4 2025, 6:58 PM (11 w, 4 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3298784
Attached To
rDLDCVS CVS Loader
Event Timeline
Log In to Comment