freebsd-skq/contrib/csup/rcsparse.c
Ulf Lilleengen 9178dc306f A few bugfixes:
- Instead of including protocol info in diffs, strip them before adding
  deltatext and take this into account when applying the diff later.
- Don't use strlen when the string in the RCS file may contain garbage. This got
  caught in the checksumming before, but was not fixed until now. Instead of
  using strlen, pass the token length when adding log and text entries to a
  delta. Add an extra length parameter to duptext() to record the token length.
- When adding new branches to a file, add them in at the tail instead of the
  head of the list to get correct ordering when writing out.
- Input stream when diffing was opened twice.
- Don't expand keywords in diffs between deltas.
2008-12-03 22:47:33 +00:00

355 lines
8.2 KiB
C

/*-
* Copyright (c) 2008, Ulf Lilleengen <lulf@FreeBSD.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.
*
* 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.
*
* $FreeBSD$
*/
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "misc.h"
#include "queue.h"
#include "rcsfile.h"
#include "rcsparse.h"
#include "rcstokenizer.h"
/*
* This is an RCS-parser using lex for tokenizing and makes sure the RCS syntax
* is correct as it constructs an RCS file that is used by csup.
*/
static void asserttoken(yyscan_t *, int);
static int parse_admin(struct rcsfile *, yyscan_t *);
static int parse_deltas(struct rcsfile *, yyscan_t *, int);
static int parse_deltatexts(struct rcsfile *, yyscan_t *, int);
static char *duptext(yyscan_t *, int *);
struct string {
char *str;
STAILQ_ENTRY(string) next;
};
static void
asserttoken(yyscan_t *sp, int token)
{
int t;
t = token;
t = rcslex(*sp);
assert(t == token);
}
static char *
duptext(yyscan_t *sp, int *arglen)
{
char *tmp, *val;
int len;
tmp = rcsget_text(*sp);
len = rcsget_leng(*sp);
val = xmalloc(len + 1);
memcpy(val, tmp, len);
val[len] = '\0';
if (arglen != NULL)
*arglen = len;
return (val);
}
/*
* Start up parser, and use the rcsfile hook to add objects.
*/
int
rcsparse_run(struct rcsfile *rf, FILE *infp)
{
yyscan_t scanner;
char *desc;
int error, tok;
error = 0;
rcslex_init(&scanner);
rcsset_in(infp, scanner);
tok = parse_admin(rf, &scanner);
tok = parse_deltas(rf, &scanner, tok);
assert(tok == KEYWORD);
asserttoken(&scanner, STRING);
desc = duptext(&scanner, NULL);
rcsfile_setval(rf, RCSFILE_DESC, desc);
free(desc);
tok = rcslex(scanner);
error = parse_deltatexts(rf, &scanner, tok);
if (error)
return (error);
rcslex_destroy(scanner);
return (0);
}
/*
* Parse the admin part of a RCS file.
*/
static int
parse_admin(struct rcsfile *rf, yyscan_t *sp)
{
char *branch, *comment, *expand, *head, *id, *revnum, *tag, *tmp;
int strict, token;
strict = 0;
branch = NULL;
/* head {num}; */
asserttoken(sp, KEYWORD);
asserttoken(sp, NUM);
head = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_HEAD, head);
free(head);
asserttoken(sp, SEMIC);
/* { branch {num}; } */
token = rcslex(*sp);
if (token == KEYWORD_TWO) {
asserttoken(sp, NUM);
branch = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_BRANCH, branch);
free(branch);
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
/* access {id]*; */
assert(token == KEYWORD);
token = rcslex(*sp);
while (token == ID) {
id = duptext(sp, NULL);
rcsfile_addaccess(rf, id);
free(id);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* symbols {sym : num}*; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
while (token == ID) {
tag = duptext(sp, NULL);
asserttoken(sp, COLON);
asserttoken(sp, NUM);
revnum = duptext(sp, NULL);
rcsfile_importtag(rf, tag, revnum);
free(tag);
free(revnum);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* locks {id : num}*; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
while (token == ID) {
/* XXX: locks field is skipped */
asserttoken(sp, COLON);
asserttoken(sp, NUM);
token = rcslex(*sp);
}
assert(token == SEMIC);
token = rcslex(*sp);
while (token == KEYWORD) {
tmp = rcsget_text(*sp);
/* {strict ;} */
if (!strcmp(tmp, "strict")) {
rcsfile_setval(rf, RCSFILE_STRICT, tmp);
asserttoken(sp, SEMIC);
/* { comment {string}; } */
} else if (!strcmp(tmp, "comment")) {
token = rcslex(*sp);
if (token == STRING) {
comment = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_COMMENT, comment);
free(comment);
}
asserttoken(sp, SEMIC);
/* { expand {string}; } */
} else if (!strcmp(tmp, "expand")) {
token = rcslex(*sp);
if (token == STRING) {
expand = duptext(sp, NULL);
rcsfile_setval(rf, RCSFILE_EXPAND, expand);
free(expand);
}
asserttoken(sp, SEMIC);
}
/* {newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: newphrases ignored */
while (token == ID || token == NUM || token == STRING ||
token == COLON) {
token = rcslex(*sp);
}
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
}
return (token);
}
/*
* Parse RCS deltas.
*/
static int
parse_deltas(struct rcsfile *rf, yyscan_t *sp, int token)
{
STAILQ_HEAD(, string) branchlist;
char *revnum, *revdate, *author, *state, *next;
/* In case we don't have deltas. */
if (token != NUM)
return (token);
do {
next = NULL;
state = NULL;
/* num */
assert(token == NUM);
revnum = duptext(sp, NULL);
/* date num; */
asserttoken(sp, KEYWORD);
asserttoken(sp, NUM);
revdate = duptext(sp, NULL);
asserttoken(sp, SEMIC);
/* author id; */
asserttoken(sp, KEYWORD);
asserttoken(sp, ID);
author = duptext(sp, NULL);
asserttoken(sp, SEMIC);
/* state {id}; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
if (token == ID) {
state = duptext(sp, NULL);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* branches {num}*; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
STAILQ_INIT(&branchlist);
while (token == NUM)
token = rcslex(*sp);
assert(token == SEMIC);
/* next {num}; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
if (token == NUM) {
next = duptext(sp, NULL);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* {newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: newphrases ignored. */
while (token == ID || token == NUM || token == STRING ||
token == COLON) {
token = rcslex(*sp);
}
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
rcsfile_importdelta(rf, revnum, revdate, author, state, next);
free(revnum);
free(revdate);
free(author);
if (state != NULL)
free(state);
if (next != NULL)
free(next);
} while (token == NUM);
return (token);
}
/*
* Parse RCS deltatexts.
*/
static int
parse_deltatexts(struct rcsfile *rf, yyscan_t *sp, int token)
{
struct delta *d;
char *log, *revnum, *text;
int error, len;
error = 0;
/* In case we don't have deltatexts. */
if (token != NUM)
return (token);
do {
/* num */
assert(token == NUM);
revnum = duptext(sp, NULL);
/* Get delta we're adding text to. */
d = rcsfile_getdelta(rf, revnum);
free(revnum);
/* log string */
asserttoken(sp, KEYWORD);
asserttoken(sp, STRING);
log = duptext(sp, &len);
error = rcsdelta_addlog(d, log, len);
free(log);
if (error)
return (-1);
/* { newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: newphrases ignored. */
while (token == ID || token == NUM || token == STRING ||
token == COLON) {
token = rcslex(*sp);
}
asserttoken(sp, SEMIC);
token = rcslex(*sp);
}
/* text string */
assert(token == KEYWORD);
asserttoken(sp, STRING);
text = duptext(sp, &len);
error = rcsdelta_addtext(d, text, len);
/*
* If this happens, something is wrong with the RCS file, and it
* should be resent.
*/
free(text);
if (error)
return (-1);
token = rcslex(*sp);
} while (token == NUM);
return (0);
}