/*-
* Copyright (c) 2000-2005 MAEKAWA Masahide <maekawa@cvsync.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the author nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <stdlib.h>
#include <ctype.h>
#include <limits.h>
#include <string.h>
#include <time.h>
#include "compat_stdbool.h"
#include "compat_stdint.h"
#include "rcslib.h"
struct rcs_keyword {
const char *name;
const size_t namelen;
};
static const struct rcs_keyword rcs_keywords[] = {
{ "access", 6 },
{ "author", 6 },
{ "branch", 6 },
{ "branches", 8 },
{ "comment", 7 },
{ "date", 4 },
{ "desc", 4 },
{ "expand", 6 },
{ "head", 4 },
{ "locks", 5 },
{ "log", 3 },
{ "next", 4 },
{ "state", 5 },
{ "strict", 6 },
{ "symbols", 7 },
{ "text", 4 },
{ NULL, 0 }
};
enum {
RCS_ACCESS = 0,
RCS_AUTHOR,
RCS_BRANCH,
RCS_BRANCHES,
RCS_COMMENT,
RCS_DATE,
RCS_DESC,
RCS_EXPAND,
RCS_HEAD,
RCS_LOCKS,
RCS_LOG,
RCS_NEXT,
RCS_STATE,
RCS_STRICT,
RCS_SYMBOLS,
RCS_TEXT
};
#define RCS_SKIP(p, e) \
while (isspace((int)(*(p)))) { \
if (++(p) > (e)) \
return (NULL); \
}
#define RCS_SKIP_NORET(p, e) \
while (isspace((int)(*(p)))) { \
if (++(p) > (e)) \
break; \
}
#define IS_RCS_IDCHAR(p) \
(isalnum((int)(p)) || \
(strchr("!\"#%&'()*+-/<=>?[\\]^_`{|}~", (p)) != NULL))
void rcslib_destroy_admin(struct rcslib_file *);
void rcslib_destroy_delta(struct rcslib_file *);
bool rcslib_parse_rcstext(struct rcslib_file *, char *, const char *);
char *rcslib_parse_admin(struct rcslib_file *, char *, const char *);
char *rcslib_parse_delta(struct rcslib_file *, char *, const char *);
char *rcslib_parse_deltatext(struct rcslib_file *, char *, const char *);
char *rcslib_parse_access(char *, const char *, struct rcslib_access *);
char *rcslib_parse_symbols(char *, const char *, struct rcslib_symbols *);
char *rcslib_parse_locks(char *, const char *, struct rcslib_locks *);
char *rcslib_parse_date(char *, const char *, struct rcslib_date *);
char *rcslib_parse_branches(char *, const char *, struct rcslib_branches *);
char *rcslib_parse_newphrase(char *, const char *);
char *rcslib_parse_word(char *, const char *);
char *rcslib_parse_num(char *, const char *, struct rcsnum *);
char *rcslib_parse_id(char *, const char *, struct rcsid *);
char *rcslib_parse_sym(char *, const char *, struct rcssym *);
char *rcslib_parse_string(char *, const char *, struct rcsstr *);
void rcslib_sort_revision(struct rcslib_file *);
typedef int (*rcslib_cmp_func)(const void *, const void *);
struct rcslib_file *
rcslib_init(void *addr, off_t addrlen)
{
struct rcslib_file *rcs;
char *sp = addr, *bp = sp + (size_t)(addrlen - 1);
if ((addrlen == 0) || (*bp != '\n'))
return (NULL);
if ((rcs = malloc(sizeof(*rcs))) == NULL)
return (NULL);
(void)memset(rcs, 0, sizeof(*rcs));
if (!rcslib_parse_rcstext(rcs, sp, bp)) {
free(rcs);
return (NULL);
}
return (rcs);
}
void
rcslib_destroy(struct rcslib_file *rcs)
{
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
free(rcs);
}
void
rcslib_destroy_admin(struct rcslib_file *rcs)
{
if (rcs->access.ra_id != NULL)
free(rcs->access.ra_id);
if (rcs->symbols.rs_symbols != NULL)
free(rcs->symbols.rs_symbols);
if (rcs->locks.rl_locks != NULL)
free(rcs->locks.rl_locks);
}
void
rcslib_destroy_delta(struct rcslib_file *rcs)
{
struct rcslib_revision *rev;
size_t i;
if (rcs->delta.rd_count > 0) {
for (i = 0 ; i < rcs->delta.rd_count ; i++) {
rev = &rcs->delta.rd_rev[i];
if (rev->branches.rb_num != NULL)
free(rev->branches.rb_num);
}
}
free(rcs->delta.rd_rev);
}
bool
rcslib_parse_rcstext(struct rcslib_file *rcs, char *sp, const char *bp)
{
const struct rcs_keyword *rcskey = &rcs_keywords[RCS_DESC];
struct rcslib_revision *rev;
char *p;
size_t i;
if ((sp = rcslib_parse_admin(rcs, sp, bp)) == NULL)
return (false);
if ((sp = rcslib_parse_delta(rcs, sp, bp)) == NULL) {
rcslib_destroy_admin(rcs);
return (false);
}
if (rcs->delta.rd_count > 0)
rcslib_sort_revision(rcs);
/* desc string */
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
sp += rcskey->namelen;
p = sp;
while (isspace((int)(*sp))) {
if (++sp > bp) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
}
if (sp == p) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
if ((sp = rcslib_parse_string(sp, bp, &rcs->desc)) == NULL) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
while (isspace((int)(*sp))) {
if (++sp > bp) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
}
if ((sp = rcslib_parse_deltatext(rcs, sp, bp)) == NULL) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
for (p = sp ; isspace((int)(*p)) ; p++) {
if (p == bp)
break;
}
if (p != bp) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
if (rcs->delta.rd_count == 0)
return (true);
for (i = 0 ; i < rcs->delta.rd_count ; i++) {
rev = &rcs->delta.rd_rev[i];
if (!(rev->rv_flags & RCSLIB_REVISION_DELTATEXT)) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
if (rev->next.n_len == 0)
continue;
if (i + 1 == rcs->delta.rd_count)
continue;
if (rcslib_cmp_num(&rcs->delta.rd_rev[i + 1].num,
&rev->next) == 0) {
rev->rv_next = &rcs->delta.rd_rev[i + 1];
continue;
}
rev->rv_next = rcslib_lookup_revision(rcs, &rev->next);
if (rev->rv_next == NULL) {
rcslib_destroy_admin(rcs);
rcslib_destroy_delta(rcs);
return (false);
}
}
return (true);
}
char *
rcslib_parse_admin(struct rcslib_file *rcs, char *sp, const char *bp)
{
const struct rcs_keyword *rcskey;
char *p;
RCS_SKIP(sp, bp)
/* head {num}; */
rcskey = &rcs_keywords[RCS_HEAD];
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if ((sp == p) && (*sp != ';'))
return (NULL);
p = sp;
if ((sp = rcslib_parse_num(sp, bp, &rcs->head)) != NULL) {
RCS_SKIP(sp, bp)
} else {
sp = p;
}
if (*sp++ != ';')
return (NULL);
RCS_SKIP(sp, bp)
/* { branch {num}; } */
rcskey = &rcs_keywords[RCS_BRANCH];
if ((sp + rcskey->namelen < bp) &&
(memcmp(sp, rcskey->name, rcskey->namelen) == 0)) {
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if ((sp == p) && (*sp != ';'))
return (NULL);
p = sp;
if ((sp = rcslib_parse_num(sp, bp, &rcs->branch)) != NULL) {
RCS_SKIP(sp, bp)
} else {
sp = p;
}
if (*sp++ != ';')
return (NULL);
RCS_SKIP(sp, bp)
}
/* access {id}*; */
if ((sp = rcslib_parse_access(sp, bp, &rcs->access)) == NULL)
return (NULL);
/* symbols {sym : num}*; */
if ((sp = rcslib_parse_symbols(sp, bp, &rcs->symbols)) == NULL)
return (NULL);
/* locks {id : num}*; {strict ;} */
if ((sp = rcslib_parse_locks(sp, bp, &rcs->locks)) == NULL)
return (NULL);
/* { comment {string}; } */
rcskey = &rcs_keywords[RCS_COMMENT];
if ((sp + rcskey->namelen < bp) &&
(memcmp(sp, rcskey->name, rcskey->namelen) == 0)) {
sp += rcskey->namelen;
RCS_SKIP(sp, bp)
p = sp;
if ((sp = rcslib_parse_string(sp, bp, &rcs->comment)) != NULL) {
RCS_SKIP(sp, bp)
} else {
sp = p;
}
if (*sp++ != ';')
return (NULL);
RCS_SKIP(sp, bp)
}
/* { expand {string}; } */
rcskey = &rcs_keywords[RCS_EXPAND];
if ((sp + rcskey->namelen < bp) &&
(memcmp(sp, rcskey->name, rcskey->namelen) == 0)) {
sp += rcskey->namelen;
RCS_SKIP(sp, bp)
p = sp;
if ((sp = rcslib_parse_string(sp, bp, &rcs->expand)) != NULL) {
RCS_SKIP(sp, bp)
} else {
sp = p;
}
if (*sp++ != ';')
return (NULL);
RCS_SKIP(sp, bp)
}
/* { newphrase }* */
if ((sp = rcslib_parse_newphrase(sp, bp)) == NULL)
return (NULL);
return (sp);
}
char *
rcslib_parse_delta(struct rcslib_file *rcs, char *sp, const char *bp)
{
const struct rcs_keyword *rcskey;
struct rcslib_delta *delta = &rcs->delta;
struct rcslib_revision *rev;
char *p;
for (;;) {
if (delta->rd_count == delta->rd_size) {
size_t size = delta->rd_size, new;
new = (size == 0) ? 32 : size * 4;
rev = malloc(new * RCSLIB_REVISION_SIZE);
if (rev == NULL) {
if (delta->rd_rev != NULL)
free(delta->rd_rev);
return (NULL);
}
if (size > 0) {
(void)memcpy(rev, delta->rd_rev,
size * RCSLIB_REVISION_SIZE);
free(delta->rd_rev);
}
(void)memset(rev + size, 0,
(new - size) * RCSLIB_REVISION_SIZE);
delta->rd_rev = rev;
delta->rd_size = new;
}
/* num */
rev = &delta->rd_rev[delta->rd_count++];
if ((sp = rcslib_parse_num(sp, bp, &rev->num)) == NULL) {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
/* date num; */
if ((sp = rcslib_parse_date(sp, bp, &rev->date)) == NULL) {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
if (*sp++ != ';') {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
/* author id; */
rcskey = &rcs_keywords[RCS_AUTHOR];
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
free(delta->rd_rev);
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP_NORET(sp, bp)
if ((sp > bp) || (sp == p)) {
free(delta->rd_rev);
return (NULL);
}
if ((sp = rcslib_parse_id(sp, bp, &rev->author)) == NULL) {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if ((sp > bp) || (*sp++ != ';')) {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
/* state {id}; */
rcskey = &rcs_keywords[RCS_STATE];
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
free(delta->rd_rev);
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP_NORET(sp, bp)
if ((sp > bp) || ((sp == p) && (*sp != ';'))) {
free(delta->rd_rev);
return (NULL);
}
p = sp;
if ((sp = rcslib_parse_id(sp, bp, &rev->state)) != NULL) {
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
} else {
sp = p;
}
if (*sp++ != ';') {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
/* branches {num}*; */
if ((sp = rcslib_parse_branches(sp, bp,
&rev->branches)) == NULL) {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
/* next {num}; */
rcskey = &rcs_keywords[RCS_NEXT];
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
free(delta->rd_rev);
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP_NORET(sp, bp)
if ((sp > bp) || ((sp == p) && (*sp != ';'))) {
free(delta->rd_rev);
return (NULL);
}
p = sp;
if ((sp = rcslib_parse_num(sp, bp, &rev->next)) != NULL) {
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
} else {
sp = p;
}
if (*sp++ != ';') {
free(delta->rd_rev);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(delta->rd_rev);
return (NULL);
}
/* { newphrase }* */
p = sp;
if ((sp = rcslib_parse_newphrase(sp, bp)) == NULL) {
sp = p;
break;
}
}
if (delta->rd_count == 0) {
free(delta->rd_rev);
delta->rd_rev = NULL;
}
return (sp);
}
char *
rcslib_parse_deltatext(struct rcslib_file *rcs, char *sp, const char *bp)
{
const struct rcs_keyword *rcskey;
struct rcslib_delta *delta = &rcs->delta;
struct rcslib_revision *rev = NULL;
struct rcsnum num;
char *p;
if (delta->rd_count == 0)
return (sp);
for (;;) {
/* num */
if ((sp = rcslib_parse_num(sp, bp, &num)) == NULL)
return (NULL);
if ((rev = rcslib_lookup_revision(rcs, &num)) == NULL)
return (NULL);
p = sp;
RCS_SKIP(sp, bp)
if (sp == p)
return (NULL);
/* log string */
rcskey = &rcs_keywords[RCS_LOG];
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if (sp == p)
return (NULL);
if ((sp = rcslib_parse_string(sp, bp, &rev->log)) == NULL)
return (NULL);
RCS_SKIP(sp, bp)
/* { newphrase }* */
p = sp;
if ((sp = rcslib_parse_newphrase(sp, bp)) == NULL)
sp = p;
/* text string */
rcskey = &rcs_keywords[RCS_TEXT];
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if (sp == p)
return (NULL);
if ((sp = rcslib_parse_string(sp, bp, &rev->text)) == NULL)
return (NULL);
rev->rv_flags |= RCSLIB_REVISION_DELTATEXT;
while (isspace((int)(*sp))) {
if (sp == bp)
break;
sp++;
}
if (sp == bp)
break;
}
return (sp);
}
char *
rcslib_parse_access(char *sp, const char *bp, struct rcslib_access *access)
{
const struct rcs_keyword *rcskey = &rcs_keywords[RCS_ACCESS];
struct rcsid *id;
char *p;
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if ((sp == p) && (*sp != ';'))
return (NULL);
if (*sp == ';') {
sp++;
RCS_SKIP(sp, bp)
return (sp);
}
for (;;) {
if (access->ra_count == access->ra_size) {
size_t size = access->ra_size, new = size + 4;
if ((id = malloc(new * RCSID_SIZE)) == NULL) {
if (access->ra_id != NULL)
free(access->ra_id);
return (NULL);
}
if (size > 0) {
(void)memcpy(id, access->ra_id,
size * RCSID_SIZE);
free(access->ra_id);
}
access->ra_id = id;
access->ra_size = new;
}
id = &access->ra_id[access->ra_count++];
if ((sp = rcslib_parse_id(sp, bp, id)) == NULL) {
free(access->ra_id);
return (NULL);
}
p = sp;
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(access->ra_id);
return (NULL);
}
if (*sp == ';')
break;
}
if (*sp++ != ';') {
free(access->ra_id);
return (NULL);
}
RCS_SKIP_NORET(sp, bp);
if (sp > bp) {
free(access->ra_id);
return (NULL);
}
if (access->ra_count > 0) {
qsort(access->ra_id, access->ra_count, RCSID_SIZE,
(rcslib_cmp_func)rcslib_cmp_id);
}
return (sp);
}
char *
rcslib_parse_symbols(char *sp, const char *bp, struct rcslib_symbols *symbols)
{
const struct rcs_keyword *rcskey = &rcs_keywords[RCS_SYMBOLS];
struct rcslib_symbol *sym;
char *p;
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if ((sp == p) && (*sp != ';'))
return (NULL);
if (*sp == ';') {
sp++;
RCS_SKIP(sp, bp)
return (sp);
}
for (;;) {
if (symbols->rs_count == symbols->rs_size) {
size_t size = symbols->rs_size, new;
new = (size == 0) ? 32 : size * 4;
sym = malloc(new * RCSLIB_SYMBOL_SIZE);
if (sym == NULL) {
if (symbols->rs_symbols != NULL)
free(symbols->rs_symbols);
return (NULL);
}
if (size > 0) {
(void)memcpy(sym, symbols->rs_symbols,
size * RCSLIB_SYMBOL_SIZE);
free(symbols->rs_symbols);
}
symbols->rs_symbols = sym;
symbols->rs_size = new;
}
sym = &symbols->rs_symbols[symbols->rs_count++];
if ((sp = rcslib_parse_sym(sp, bp, &sym->sym)) == NULL) {
free(symbols->rs_symbols);
return (NULL);
}
if (*sp++ != ':')
return (NULL);
if ((sp = rcslib_parse_num(sp, bp, &sym->num)) == NULL) {
free(symbols->rs_symbols);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(symbols->rs_symbols);
return (NULL);
}
if (*sp == ';')
break;
}
if (*sp++ != ';')
return (NULL);
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(symbols->rs_symbols);
return (NULL);
}
if (symbols->rs_count > 0) {
qsort(symbols->rs_symbols, symbols->rs_count,
RCSLIB_SYMBOL_SIZE, (rcslib_cmp_func)rcslib_cmp_symbol);
}
return (sp);
}
char *
rcslib_parse_locks(char *sp, const char *bp, struct rcslib_locks *locks)
{
const struct rcs_keyword *rcskey = &rcs_keywords[RCS_LOCKS];
struct rcslib_lock *lock;
char *p;
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if ((sp == p) && (*sp != ';'))
return (NULL);
if (*sp == ';') {
sp++;
goto do_parse_strict;
}
for (;;) {
if (locks->rl_count == locks->rl_size) {
size_t size = locks->rl_size, new = size + 4;
if ((lock = malloc(new * RCSLIB_LOCK_SIZE)) == NULL) {
if (locks->rl_locks != NULL)
free(locks->rl_locks);
return (NULL);
}
if (size > 0) {
(void)memcpy(lock, locks->rl_locks,
size * RCSLIB_LOCK_SIZE);
free(locks->rl_locks);
}
locks->rl_locks = lock;
locks->rl_size = new;
}
lock = &locks->rl_locks[locks->rl_count++];
if ((sp = rcslib_parse_id(sp, bp, &lock->id)) == NULL) {
free(locks->rl_locks);
return (NULL);
}
if (*sp++ != ':') {
free(locks->rl_locks);
return (NULL);
}
if ((sp = rcslib_parse_num(sp, bp, &lock->num)) == NULL) {
free(locks->rl_locks);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(locks->rl_locks);
return (NULL);
}
if (*sp == ';')
break;
}
if (*sp++ != ';') {
free(locks->rl_locks);
return (NULL);
}
do_parse_strict:
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
if (locks->rl_locks != NULL)
free(locks->rl_locks);
return (NULL);
}
rcskey = &rcs_keywords[RCS_STRICT];
if ((sp + rcskey->namelen < bp) &&
(memcmp(sp, rcskey->name, rcskey->namelen) == 0)) {
sp += rcskey->namelen;
RCS_SKIP_NORET(sp, bp)
if ((sp > bp) || (*sp++ != ';')) {
if (locks->rl_locks != NULL)
free(locks->rl_locks);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
if (locks->rl_locks != NULL)
free(locks->rl_locks);
return (NULL);
}
locks->rl_strict = 1;
}
if (locks->rl_count > 0) {
qsort(locks, locks->rl_count, RCSLIB_LOCK_SIZE,
(rcslib_cmp_func)rcslib_cmp_lock);
}
return (sp);
}
char *
rcslib_parse_date(char *sp, const char *bp, struct rcslib_date *date)
{
const struct rcs_keyword *rcskey = &rcs_keywords[RCS_DATE];
char *sv_sp, *p;
int n, c;
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if (sp == p)
return (NULL);
sv_sp = sp;
/*
* Y.mm.dd.hh.mm.ss
* Y: year, mm: 01-12, dd: 01-31, hh: 00-23, mm: 00-59, ss: 00-60
* Dates use the Gregorian calendar. Times use UTC.
*/
/* year */
n = 0;
while (isdigit((int)(*sp))) {
c = *sp - '0';
if (INT_MAX / 10 < n)
return (NULL);
n *= 10;
if (INT_MAX - n < c)
return (NULL);
n += c;
if (++sp > bp)
return (NULL);
}
date->rd_tm.tm_year = n;
if (n > 100)
date->rd_tm.tm_year -= 1900;
if (*sp++ != '.')
return (NULL);
/* month */
if (sp + 2 > bp)
return (NULL);
if (!isdigit((int)(*sp)))
return (NULL);
n = (*sp++ - '0') * 10;
if (!isdigit((int)(*sp)))
return (NULL);
n += *sp++ - '0';
if ((n < 1) || (n > 12))
return (NULL);
date->rd_tm.tm_mon = n - 1;
if (*sp++ != '.')
return (NULL);
/* day */
if (sp + 2 > bp)
return (NULL);
if (!isdigit((int)(*sp)))
return (NULL);
n = (*sp++ - '0') * 10;
if (!isdigit((int)(*sp)))
return (NULL);
n += *sp++ - '0';
if ((n < 1) || (n > 31))
return (NULL);
date->rd_tm.tm_mday = n;
if (*sp++ != '.')
return (NULL);
/* hour */
if (sp + 2 > bp)
return (NULL);
if (!isdigit((int)(*sp)))
return (NULL);
n = (*sp++ - '0') * 10;
if (!isdigit((int)(*sp)))
return (NULL);
n += *sp++ - '0';
if (n > 23)
return (NULL);
date->rd_tm.tm_hour = n;
if (*sp++ != '.')
return (NULL);
/* minute */
if (sp + 2 > bp)
return (NULL);
if (!isdigit((int)(*sp)))
return (NULL);
n = (*sp++ - '0') * 10;
if (!isdigit((int)(*sp)))
return (NULL);
n += *sp++ - '0';
if (n > 59)
return (NULL);
date->rd_tm.tm_min = n;
if (*sp++ != '.')
return (NULL);
/* second */
if (sp + 2 > bp)
return (NULL);
if (!isdigit((int)(*sp)))
return (NULL);
n = (*sp++ - '0') * 10;
if (!isdigit((int)(*sp)))
return (NULL);
n += *sp++ - '0';
if (n > 60)
return (NULL);
date->rd_tm.tm_sec = n;
date->rd_num.n_str = sv_sp;
date->rd_num.n_len = sp - sv_sp;
return (sp);
}
char *
rcslib_parse_branches(char *sp, const char *bp,
struct rcslib_branches *branches)
{
const struct rcs_keyword *rcskey = &rcs_keywords[RCS_BRANCHES];
struct rcsnum *num;
char *p;
if ((sp + rcskey->namelen > bp) ||
(memcmp(sp, rcskey->name, rcskey->namelen) != 0)) {
return (NULL);
}
sp += rcskey->namelen;
p = sp;
RCS_SKIP(sp, bp)
if ((sp == p) && (*sp != ';'))
return (NULL);
if (*sp == ';') {
sp++;
RCS_SKIP(sp, bp)
return (sp);
}
for (;;) {
if (branches->rb_count == branches->rb_size) {
size_t size = branches->rb_size, new = size + 4;
if ((num = malloc(new * RCSNUM_SIZE)) == NULL) {
if (branches->rb_num != NULL)
free(branches->rb_num);
return (NULL);
}
if (size > 0) {
(void)memcpy(num, branches->rb_num,
size * RCSNUM_SIZE);
free(branches->rb_num);
}
branches->rb_num = num;
branches->rb_size = new;
}
num = &branches->rb_num[branches->rb_count++];
if ((sp = rcslib_parse_num(sp, bp, num)) == NULL) {
free(branches->rb_num);
return (NULL);
}
RCS_SKIP_NORET(sp, bp)
if (sp > bp) {
free(branches->rb_num);
return (NULL);
}
if (*sp == ';')
break;
}
if (*sp++ != ';') {
free(branches->rb_num);
return (NULL);
}
return (sp);
}
char *
rcslib_parse_newphrase(char *sp, const char *bp)
{
const struct rcs_keyword *rcskey;
struct rcsid id;
char *p;
for (;;) {
p = sp;
if ((sp = rcslib_parse_id(sp, bp, &id)) == NULL) {
sp = p;
break;
}
for (rcskey = rcs_keywords ; rcskey->name != NULL ; rcskey++) {
if ((id.i_len >= rcskey->namelen) &&
(memcmp(id.i_id, rcskey->name, id.i_len) == 0)) {
return (NULL);
}
}
p = sp;
RCS_SKIP(sp, bp)
if ((sp == p) && (*sp != ';'))
return (NULL);
for (;;) {
p = sp;
if ((sp = rcslib_parse_word(sp, bp)) == NULL) {
sp = p;
break;
}
RCS_SKIP(sp, bp)
if (*sp == ';')
break;
}
if (*sp++ == ';')
break;
}
RCS_SKIP(sp, bp)
return (sp);
}
char *
rcslib_parse_word(char *sp, const char *bp)
{
char *sv_sp = sp;
if ((sp = rcslib_parse_id(sv_sp, bp, NULL)) != NULL)
return (sp);
if ((sp = rcslib_parse_num(sv_sp, bp, NULL)) != NULL)
return (sp);
if ((sp = rcslib_parse_string(sv_sp, bp, NULL)) != NULL)
return (sp);
if (*sv_sp == ':')
return (sv_sp + 1);
return (NULL);
}
char *
rcslib_parse_num(char *sp, const char *bp, struct rcsnum *num)
{
char *sv_sp = sp;
size_t i;
while (isdigit((int)(*sp)) || (*sp == '.')) {
if (++sp > bp)
return (NULL);
}
if (sp == sv_sp)
return (NULL);
if (num != NULL) {
num->n_str = sv_sp;
num->n_len = sp - sv_sp;
num->n_level = 0;
num->n_num[num->n_level] = 0;
sp = sv_sp;
for (i = 0 ; i < num->n_len ; i++) {
if (*sp == '.') {
if (num->n_level == RCSNUM_MAXLEVEL)
return (NULL);
num->n_num[++num->n_level] = 0;
} else {
num->n_num[num->n_level] *= 10;
num->n_num[num->n_level] += *sp - '0';
if (num->n_num[num->n_level] > RCSNUM_MAX)
return (NULL);
}
sp++;
}
num->n_level++;
}
return (sp);
}
char *
rcslib_parse_id(char *sp, const char *bp, struct rcsid *id)
{
char *sv_sp = sp, *p;
if ((sp = rcslib_parse_num(sp, bp, NULL)) == NULL)
sp = sv_sp;
if (!IS_RCS_IDCHAR(*sp))
return (NULL);
for (;;) {
p = sp;
while (IS_RCS_IDCHAR(*sp)) {
if (++sp > bp)
return (NULL);
}
if (sp != p)
continue;
if ((sp = rcslib_parse_num(sp, bp, NULL)) == NULL)
sp = p;
if (sp == p)
break;
}
if (id != NULL) {
id->i_id = sv_sp;
id->i_len = sp - sv_sp;
}
return (sp);
}
char *
rcslib_parse_sym(char *sp, const char *bp, struct rcssym *sym)
{
char *sv_sp = sp, *p;
while (isdigit((int)(*sp))) {
if (++sp > bp)
return (NULL);
}
if (!IS_RCS_IDCHAR(*sp))
return (NULL);
for (;;) {
p = sp;
while (IS_RCS_IDCHAR(*sp)) {
if (++sp > bp)
return (NULL);
}
while (isdigit((int)(*sp))) {
if (++sp > bp)
return (NULL);
}
if (sp == p)
break;
}
if (sym != NULL) {
sym->s_sym = sv_sp;
sym->s_len = sp - sv_sp;
}
return (sp);
}
char *
rcslib_parse_string(char *sp, const char *bp, struct rcsstr *str)
{
char *sv_sp;
if (*sp++ != '@')
return (NULL);
sv_sp = sp;
while (sp < bp) {
if (*sp != '@') {
sp++;
continue;
}
if (*(sp + 1) != '@')
break;
sp += 2;
}
if (sp == bp)
return (NULL);
if (str != NULL) {
str->s_str = sv_sp;
str->s_len = sp++ - sv_sp;
}
return (sp);
}
struct rcslib_revision *
rcslib_lookup_revision(struct rcslib_file *rcs, const struct rcsnum *num)
{
struct rcslib_delta *delta = &rcs->delta;
struct rcslib_revision *rev;
size_t l = 0, r = delta->rd_count, i;
int rv;
if ((delta->rd_count == 0) || (num->n_len == 0))
return (NULL);
if (delta->rd_count < 100) {
for (i = 0 ; i < delta->rd_count ; i++) {
rev = &delta->rd_rev[i];
if ((num->n_len == rev->num.n_len) &&
(memcmp(num->n_str, rev->num.n_str,
num->n_len) == 0)) {
return (rev);
}
}
} else {
while (l <= r) {
i = (l + r) / 2;
rev = &delta->rd_rev[i];
if ((rv = rcslib_cmp_num(num, &rev->num)) == 0)
return (rev);
if (rv < 0) {
if (l == i)
break;
r = i - 1;
} else { /* rv > 0 */
if (r == i)
break;
l = i + 1;
}
}
}
return (NULL);
}
struct rcslib_revision *
rcslib_lookup_symbol(struct rcslib_file *rcs, void *symstr, size_t symlen)
{
struct rcslib_revision *rev;
struct rcslib_symbol *symbol;
struct rcsnum *num = NULL, *br_num = NULL, t_num;
size_t sv_level, n, i;
for (i = 0 ; i < rcs->symbols.rs_count ; i++) {
symbol = &rcs->symbols.rs_symbols[i];
if ((symlen == symbol->sym.s_len) &&
(memcmp(symstr, symbol->sym.s_sym, symlen) == 0)) {
num = &symbol->num;
break;
}
}
if (num == NULL) {
if (((symlen == 4) && (memcmp(symstr, "HEAD", 4) == 0)) ||
((symlen == 1) && (memcmp(symstr, ".", 1) == 0))) {
if (rcs->head.n_len != 0) {
rev = rcslib_lookup_revision(rcs, &rcs->head);
} else {
if (rcs->delta.rd_count == 0)
return (NULL);
rev = &rcs->delta.rd_rev[0];
}
if (rev == NULL)
return (NULL);
if (rcs->branch.n_len == 0)
return (rev);
num = &rcs->branch;
} else {
if (!rcslib_str2num(symstr, symlen, &t_num))
return (NULL);
return (rcslib_lookup_revision(rcs, &t_num));
}
}
if ((num->n_level % 2) != 0) {
for (n = num->n_len - 1 ; n > 0 ; n--) {
if (num->n_str[n] == '.')
break;
}
if (n == 0)
return (NULL);
sv_level = num->n_level;
} else {
for (sv_level = 0 ; sv_level < num->n_level ; sv_level += 2) {
if (num->n_num[sv_level] == 0)
break;
}
if (sv_level == num->n_level)
return (rcslib_lookup_revision(rcs, num));
i = sv_level;
for (n = 0 ; n < num->n_len ; n++) {
if (num->n_str[n] == '.') {
if (--i == 0)
break;
}
}
}
if (!rcslib_str2num(num->n_str, n, &t_num))
return (NULL);
if ((rev = rcslib_lookup_revision(rcs, &t_num)) == NULL)
return (NULL);
for (n = 0 ; n < rev->branches.rb_count ; n++) {
br_num = &rev->branches.rb_num[n];
for (i = 0 ; i < sv_level ; i++) {
if (br_num->n_num[i] != num->n_num[i])
break;
}
if (i == sv_level)
break;
}
if (n == rev->branches.rb_count)
return (rev);
if ((rev = rcslib_lookup_revision(rcs, br_num)) == NULL)
return (NULL);
while (rev->rv_next != NULL)
rev = rev->rv_next;
return (rev);
}
bool
rcslib_write_delta(int fd, const struct rcslib_revision *rev)
{
const struct rcslib_branches *branches = &rev->branches;
struct iovec iov[8];
size_t len, i;
ssize_t wn;
iov[0].iov_base = rev->num.n_str;
iov[0].iov_len = rev->num.n_len;
iov[1].iov_base = "\ndate\t";
iov[1].iov_len = 6;
iov[2].iov_base = rev->date.rd_num.n_str;
iov[2].iov_len = rev->date.rd_num.n_len;
iov[3].iov_base = ";\tauthor ";
iov[3].iov_len = 9;
iov[4].iov_base = rev->author.i_id;
iov[4].iov_len = rev->author.i_len;
iov[5].iov_base = ";\tstate ";
if (rev->state.i_len > 0)
iov[5].iov_len = 8;
else
iov[5].iov_len = 7;
iov[6].iov_base = rev->state.i_id;
iov[6].iov_len = rev->state.i_len;
iov[7].iov_base = ";\nbranches";
iov[7].iov_len = 10;
len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len +
iov[3].iov_len + iov[4].iov_len + iov[5].iov_len +
iov[6].iov_len + iov[7].iov_len;
if ((wn = writev(fd, iov, 8)) == -1)
return (false);
if ((size_t)wn != len)
return (false);
iov[0].iov_base = "\n\t";
iov[0].iov_len = 2;
for (i = 0 ; i < branches->rb_count ; i++) {
iov[1].iov_base = branches->rb_num[i].n_str;
iov[1].iov_len = branches->rb_num[i].n_len;
len = iov[0].iov_len + iov[1].iov_len;
if ((wn = writev(fd, iov, 2)) == -1)
return (false);
if ((size_t)wn != len)
return (false);
}
iov[0].iov_base =";\nnext\t";
iov[0].iov_len = 7;
iov[1].iov_base = rev->next.n_str;
iov[1].iov_len = rev->next.n_len;
iov[2].iov_base = ";\n\n";
iov[2].iov_len = 3;
len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len;
if ((wn = writev(fd, iov, 3)) == -1)
return (false);
if ((size_t)wn != len)
return (false);
return (true);
}
bool
rcslib_write_deltatext(int fd, const struct rcslib_revision *rev)
{
struct iovec iov[7];
size_t len;
ssize_t wn;
iov[0].iov_base = "\n\n";
iov[0].iov_len = 2;
iov[1].iov_base = rev->num.n_str;
iov[1].iov_len = rev->num.n_len;
iov[2].iov_base = "\nlog\n@";
iov[2].iov_len = 6;
iov[3].iov_base = rev->log.s_str;
iov[3].iov_len = rev->log.s_len;
iov[4].iov_base = "@\ntext\n@";
iov[4].iov_len = 8;
iov[5].iov_base = rev->text.s_str;
iov[5].iov_len = rev->text.s_len;
iov[6].iov_base = "@\n";
iov[6].iov_len = 2;
len = iov[0].iov_len + iov[1].iov_len + iov[2].iov_len +
iov[3].iov_len + iov[4].iov_len + iov[5].iov_len +
iov[6].iov_len;
if ((wn = writev(fd, iov, 7)) == -1)
return (false);
if ((size_t)wn != len)
return (false);
return (true);
}
bool
rcslib_str2num(void *buffer, size_t bufsize, struct rcsnum *num)
{
char *sp = buffer;
size_t i;
num->n_str = buffer;
num->n_len = bufsize;
num->n_level = 0;
num->n_num[num->n_level] = 0;
for (i = 0 ; i < num->n_len ; i++) {
if (*sp == '.') {
if (num->n_level == RCSNUM_MAXLEVEL)
return (false);
num->n_num[++num->n_level] = 0;
} else if (isdigit((int)(*sp))) {
num->n_num[num->n_level] *= 10;
num->n_num[num->n_level] += *sp - '0';
if (num->n_num[num->n_level] > RCSNUM_MAX)
return (false);
} else {
return (false);
}
sp++;
}
num->n_level++;
return (true);
}
int
rcslib_cmp_lock(const struct rcslib_lock *l1, const struct rcslib_lock *l2)
{
int rv;
if ((rv = rcslib_cmp_id(&l1->id, &l2->id)) != 0)
return (rv);
return (rcslib_cmp_num(&l1->num, &l2->num));
}
int
rcslib_cmp_symbol(const struct rcslib_symbol *s1,
const struct rcslib_symbol *s2)
{
int rv;
if ((rv = rcslib_cmp_sym(&s1->sym, &s2->sym)) != 0)
return (rv);
return (rcslib_cmp_num(&s1->num, &s2->num));
}
int
rcslib_cmp_id(const struct rcsid *i1, const struct rcsid *i2)
{
int rv;
if (i1->i_len == i2->i_len)
return (memcmp(i1->i_id, i2->i_id, i1->i_len));
if (i1->i_len > i2->i_len) {
if ((rv = memcmp(i1->i_id, i2->i_id, i2->i_len)) == 0)
rv = 1;
} else {
if ((rv = memcmp(i1->i_id, i2->i_id, i1->i_len)) == 0)
rv = -1;
}
return (rv);
}
int
rcslib_cmp_num(const struct rcsnum *n1, const struct rcsnum *n2)
{
size_t i;
if (n1->n_level == n2->n_level) {
if (n1->n_level == 2) { /* main trunk */
for (i = 0 ; i < n1->n_level ; i++) {
if (n1->n_num[i] == n2->n_num[i])
continue;
if (n1->n_num[i] > n2->n_num[i])
return (-1);
else
return (1);
}
} else {
for (i = 0 ; i < n1->n_level ; i++) {
if (n1->n_num[i] == n2->n_num[i])
continue;
if (n1->n_num[i] > n2->n_num[i])
return (1);
else
return (-1);
}
}
} else {
if (n1->n_level > n2->n_level)
return (1);
else
return (-1);
}
return (0);
}
int
rcslib_cmp_sym(const struct rcssym *s1, const struct rcssym *s2)
{
int rv;
if (s1->s_len == s2->s_len)
return (memcmp(s1->s_sym, s2->s_sym, s1->s_len));
if (s1->s_len > s2->s_len) {
if ((rv = memcmp(s1->s_sym, s2->s_sym, s2->s_len)) == 0)
rv = 1;
} else {
if ((rv = memcmp(s1->s_sym, s2->s_sym, s1->s_len)) == 0)
rv = -1;
}
return (rv);
}
bool
rcslib_is_ancestor(const struct rcsnum *n1, const struct rcsnum *n2)
{
size_t n, i;
if (n1->n_level > n2->n_level)
return (false);
n = n1->n_level - 1;
for (i = 0 ; i < n ; i++) {
if (n1->n_num[i] != n2->n_num[i])
return (false);
}
if (n1->n_level == 2) {
if (n1->n_num[n] < n2->n_num[n])
return (false);
} else {
if (n1->n_num[n] > n2->n_num[n])
return (false);
}
return (true);
}
void
rcslib_sort_revision(struct rcslib_file *rcs)
{
struct rcslib_revision *rev1, *rev2;
size_t n = rcs->delta.rd_count - 1, i;
for (i = 0 ; i < n ; i++) {
rev1 = &rcs->delta.rd_rev[i];
rev2 = &rcs->delta.rd_rev[i + 1];
if (rcslib_cmp_num(&rev1->num, &rev2->num) >= 0) {
break;
}
}
if (i == n) {
/* already sorted */
return;
}
qsort(rcs->delta.rd_rev, rcs->delta.rd_count, RCSLIB_REVISION_SIZE,
(rcslib_cmp_func)rcslib_cmp_num);
}
syntax highlighted by Code2HTML, v. 0.9.1