cc806ac0f2
of faulting, check for NULL. However, returning an error would cause csup to just abort the entire update. Instead, break out of the loop and return ok. The attempts to update the file will trigger a MD5 failure which will cause csup to download the entire file as a fixup. Reviewed by: lulf MFC after: 1 week
366 lines
8.4 KiB
C
366 lines
8.4 KiB
C
/*-
|
|
* Copyright (c) 2008-2009, 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, int ro)
|
|
{
|
|
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);
|
|
/* Parse deltatexts if we need to edit. */
|
|
if (!ro) {
|
|
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 (-1);
|
|
do {
|
|
/* num */
|
|
assert(token == NUM);
|
|
revnum = duptext(sp, NULL);
|
|
/* Get delta we're adding text to. */
|
|
d = rcsfile_getdelta(rf, revnum);
|
|
free(revnum);
|
|
|
|
/*
|
|
* XXX: The RCS file is corrupt, but lie and say it is ok.
|
|
* If it is actually broken, then the MD5 mismatch will
|
|
* trigger a fixup.
|
|
*/
|
|
if (d == NULL)
|
|
return (0);
|
|
|
|
/* 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);
|
|
}
|