- Import csup work from p4.

This commit is contained in:
Ulf Lilleengen 2008-10-19 09:08:59 +00:00
parent fc84e5f31b
commit 3d4e599494
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/projects/csup_cvsmode/; revision=184054
26 changed files with 6390 additions and 66 deletions

View File

@ -9,10 +9,11 @@ UNAME!= /usr/bin/uname -s
PROG= csup
SRCS= attrstack.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \
globtree.c idcache.c keyword.c lister.c main.c misc.c mux.c parse.y \
pathcomp.c proto.c status.c stream.c threads.c token.l updater.c
pathcomp.c proto.c status.c stream.c threads.c token.l updater.c \
rcsfile.c rcsparse.c lex.rcs.c
CFLAGS+= -I. -I${.CURDIR} -g -pthread -DHAVE_FFLAGS -DNDEBUG
WARNS?= 6
WARNS?= 1
# A bit of tweaking is needed to get this Makefile working
# with the bsd.prog.mk of all the *BSD OSes...

View File

@ -444,10 +444,10 @@ coll_add(char *name)
"\"%s\"\n", cur_coll->co_name);
exit(1);
}
if (!(cur_coll->co_options & CO_CHECKOUTMODE)) {
/* if (!(cur_coll->co_options & CO_CHECKOUTMODE)) {
lprintf(-1, "Client only supports checkout mode\n");
exit(1);
}
}*/
if (!STAILQ_EMPTY(&colls)) {
coll = STAILQ_LAST(&colls, coll, co_next);
if (strcmp(coll->co_host, cur_coll->co_host) != 0) {

View File

@ -30,6 +30,11 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "config.h"
#include "detailer.h"
@ -37,6 +42,7 @@
#include "misc.h"
#include "mux.h"
#include "proto.h"
#include "rcsfile.h"
#include "status.h"
#include "stream.h"
@ -56,8 +62,15 @@ struct detailer {
static int detailer_batch(struct detailer *);
static int detailer_coll(struct detailer *, struct coll *,
struct status *);
static int detailer_dofile(struct detailer *, struct coll *,
static int detailer_dofile_co(struct detailer *, struct coll *,
struct status *, char *);
static int detailer_dofile_rcs(struct detailer *, struct coll *,
char *, char *);
static int detailer_dofile_regular(struct detailer *, char *, char *);
static int detailer_checkrcsattr(struct detailer *, struct coll *, char *,
struct fattr *, int);
int detailer_send_details(struct detailer *, struct coll *, char *,
char *, struct fattr *);
void *
detailer(void *arg)
@ -93,6 +106,7 @@ detailer(void *arg)
xasprintf(&args->errmsg, "Detailer failed: "
"Network read failure: %s", strerror(errno));
}
lprintf(-1, "Error is '%s'\n", args->errmsg);
args->status = STATUS_TRANSIENTFAILURE;
break;
case DETAILER_ERR_WRITE:
@ -131,8 +145,9 @@ detailer_batch(struct detailer *d)
error = proto_get_time(&line, &coll->co_scantime);
if (error || line != NULL || strcmp(cmd, "COLL") != 0 ||
strcmp(collname, coll->co_name) != 0 ||
strcmp(release, coll->co_release) != 0)
strcmp(release, coll->co_release) != 0){
return (DETAILER_ERR_PROTO);
}
error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
coll->co_release);
if (error)
@ -147,8 +162,9 @@ detailer_batch(struct detailer *d)
return (DETAILER_ERR_MSG);
error = detailer_coll(d, coll, st);
status_close(st, NULL);
if (error)
if (error) {
return (error);
}
if (coll->co_options & CO_COMPRESS) {
stream_filter_stop(rd);
stream_filter_stop(wr);
@ -156,10 +172,12 @@ detailer_batch(struct detailer *d)
stream_flush(wr);
}
line = stream_getln(rd, NULL);
if (line == NULL)
if (line == NULL) {
return (DETAILER_ERR_READ);
if (strcmp(line, ".") != 0)
}
if (strcmp(line, ".") != 0) {
return (DETAILER_ERR_PROTO);
}
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
@ -186,8 +204,13 @@ detailer_batch(struct detailer *d)
}
if (fixup->f_coll != coll)
break;
error = proto_printf(wr, "Y %s %s %s\n", fixup->f_name,
coll->co_tag, coll->co_date);
if (coll->co_options & CO_CHECKOUTMODE)
error = proto_printf(wr, "Y %s %s %s\n",
fixup->f_name, coll->co_tag, coll->co_date);
else {
error = proto_printf(wr, "A %s\n",
fixup->f_name);
}
if (error)
return (DETAILER_ERR_WRITE);
fixup = NULL;
@ -208,35 +231,107 @@ detailer_batch(struct detailer *d)
static int
detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
{
struct fattr *rcsattr;
struct stream *rd, *wr;
char *cmd, *file, *line, *msg;
int error;
char *attr, *cmd, *file, *line, *msg, *target, *path;
int error, attic;
rd = d->rd;
wr = d->wr;
attic = 0;
line = stream_getln(rd, NULL);
if (line == NULL)
if (line == NULL) {
return (DETAILER_ERR_READ);
}
while (strcmp(line, ".") != 0) {
cmd = proto_get_ascii(&line);
if (cmd == NULL || strlen(cmd) != 1)
if (cmd == NULL || strlen(cmd) != 1) {
return (DETAILER_ERR_PROTO);
}
switch (cmd[0]) {
case 'D':
/* Delete file. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
if (file == NULL || line != NULL) {
return (DETAILER_ERR_PROTO);
}
error = proto_printf(wr, "D %s\n", file);
if (error)
return (DETAILER_ERR_WRITE);
break;
case 'I':
case 'i':
case 'j':
/* Directory operations. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "%s %s\n", cmd, file);
if (error)
return (DETAILER_ERR_WRITE);
break;
case 'J':
/* Set directory attributes. */
file = proto_get_ascii(&line);
attr = proto_get_ascii(&line);
if (file == NULL || line != NULL || attr == NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "%s %s %s\n", cmd, file, attr);
if (error)
return (DETAILER_ERR_WRITE);
break;
case 'H':
case 'h':
/* Create a hard link. */
file = proto_get_ascii(&line);
target = proto_get_ascii(&line);
if (file == NULL || target == NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "%s %s %s\n", cmd, file,
target);
break;
case 't':
file = proto_get_ascii(&line);
attr = proto_get_ascii(&line);
if (file == NULL || attr == NULL || line != NULL) {
return (DETAILER_ERR_PROTO);
}
rcsattr = fattr_decode(attr);
if (rcsattr == NULL) {
return (DETAILER_ERR_PROTO);
}
error = detailer_checkrcsattr(d, coll, file, rcsattr,
1);
break;
case 'T':
file = proto_get_ascii(&line);
attr = proto_get_ascii(&line);
if (file == NULL || attr == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
rcsattr = fattr_decode(attr);
if (rcsattr == NULL)
return (DETAILER_ERR_PROTO);
error = detailer_checkrcsattr(d, coll, file, rcsattr,
0);
break;
case 'U':
/* Add or update file. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
error = detailer_dofile(d, coll, st, file);
if (coll->co_options & CO_CHECKOUTMODE) {
error = detailer_dofile_co(d, coll, st, file);
} else {
path = cvspath(coll->co_prefix, file, 0);
rcsattr = fattr_frompath(path, FATTR_NOFOLLOW);
error = detailer_send_details(d, coll, file,
path, rcsattr);
if (rcsattr != NULL)
fattr_free(rcsattr);
free(path);
}
if (error)
return (error);
break;
@ -248,6 +343,7 @@ detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
lprintf(-1, "Server warning: %s\n", msg);
break;
default:
lprintf(-1, "Line: %s, cmd %s\n", line, cmd);
return (DETAILER_ERR_PROTO);
}
stream_flush(wr);
@ -261,8 +357,81 @@ detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
return (0);
}
/*
* Tell the server to update a regular file.
*/
static int
detailer_dofile(struct detailer *d, struct coll *coll, struct status *st,
detailer_dofile_regular(struct detailer *d, char *name, char *path)
{
struct stream *wr;
struct stat st;
char md5[MD5_DIGEST_SIZE];
int error;
wr = d->wr;
error = stat(path, &st);
/* If we don't have it or it's unaccessible, we want it again. */
if (error) {
proto_printf(wr, "A %s\n", name);
return (0);
}
/* If not, we want the file to be updated. */
error = MD5_File(path, md5);
if (error) {
lprintf(-1, "Error reading \"%s\"\n", name);
return (error);
}
error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/*
* Tell the server to update an RCS file that we have, or send it if we don't.
*/
static int
detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name,
char *path)
{
struct stream *wr;
struct fattr *fa;
struct rcsfile *rf;
int error;
wr = d->wr;
path = atticpath(coll->co_prefix, name);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
/* We don't have it, so send request to get it. */
error = proto_printf(wr, "A %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
free(path);
return (0);
}
rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag);
free(path);
if (rf == NULL) {
lprintf(-1, "Error parsing, resend file.\n");
error = proto_printf(wr, "A %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/* Tell to update the RCS file. The client version details follow. */
rcsfile_send_details(rf, wr);
rcsfile_free(rf);
fattr_free(fa);
return (0);
}
static int
detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st,
char *file)
{
char md5[MD5_DIGEST_SIZE];
@ -274,8 +443,9 @@ detailer_dofile(struct detailer *d, struct coll *coll, struct status *st,
wr = d->wr;
path = checkoutpath(coll->co_prefix, file);
if (path == NULL)
if (path == NULL) {
return (DETAILER_ERR_PROTO);
}
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
/* We don't have the file, so the only option at this
@ -337,3 +507,78 @@ detailer_dofile(struct detailer *d, struct coll *coll, struct status *st,
return (DETAILER_ERR_WRITE);
return (0);
}
int
detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name,
struct fattr *server_attr, int attic)
{
struct fattr *client_attr;
char *attr, *path;
int error;
/*
* I don't think we can use the status file, since it only records file
* attributes in cvsmode.
*/
client_attr = NULL;
path = cvspath(coll->co_prefix, name, attic);
if (path == NULL) {
return (DETAILER_ERR_PROTO);
}
if (access(path, F_OK) == 0 &&
((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) &&
fattr_equal(client_attr, server_attr)) {
attr = fattr_encode(client_attr, NULL, 0);
if (attic) {
error = proto_printf(d->wr, "l %s %s\n", name, attr);
} else {
error = proto_printf(d->wr, "L %s %s\n", name, attr);
}
free(attr);
free(path);
fattr_free(client_attr);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/* We don't have it, so tell the server to send it. */
error = detailer_send_details(d, coll, name, path, client_attr);
fattr_free(client_attr);
free(path);
return (error);
}
int
detailer_send_details(struct detailer *d, struct coll *coll, char *name,
char *path, struct fattr *fa)
{
int error;
size_t len;
/*
* Try to check if the file exists either live or dead to see if we can
* edit it and put it live or dead, rather than receiving the entire
* file.
*/
if (fa == NULL) {
path = atticpath(coll->co_prefix, name);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
}
if (fa == NULL) {
error = proto_printf(d->wr, "A %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
} else if (fattr_type(fa) == FT_FILE) {
if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) {
detailer_dofile_rcs(d, coll, name, path);
} else {
detailer_dofile_regular(d, name, path);
}
} else {
error = proto_printf(d->wr, "N %s\n", name);
if (error)
return (DETAILER_ERR_WRITE);
}
return (0);
}

View File

@ -26,25 +26,33 @@
* $FreeBSD$
*/
#include <sys/limits.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "diff.h"
#include "keyword.h"
#include "misc.h"
#include "stream.h"
#include "queue.h"
typedef long lineno_t;
#define EC_ADD 0
#define EC_DEL 1
#define MAXKEY LONG_MAX
/* Editing command and state. */
struct editcmd {
int cmd;
long key;
int havetext;
int offset;
lineno_t where;
lineno_t count;
lineno_t lasta;
@ -55,15 +63,23 @@ struct editcmd {
struct diffinfo *di;
struct stream *orig;
struct stream *dest;
LIST_ENTRY(editcmd) next;
};
struct diffstart {
LIST_HEAD(, editcmd) dhead;
};
static int diff_geteditcmd(struct editcmd *, char *);
static int diff_copyln(struct editcmd *, lineno_t);
static int diff_ignoreln(struct editcmd *, lineno_t);
static void diff_write(struct editcmd *, void *, size_t);
static int diff_insert_edit(struct diffstart *, struct editcmd *);
static int diff_free(struct diffstart *);
int
diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
struct keyword *keyword, struct diffinfo *di)
struct keyword *keyword, struct diffinfo *di, int comode)
{
struct editcmd ec;
lineno_t i;
@ -104,7 +120,7 @@ diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
line = stream_getln(rd, &size);
if (line == NULL)
return (-1);
if (line[0] == '.') {
if (comode && line[0] == '.') {
line++;
size--;
}
@ -143,6 +159,213 @@ diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
return (0);
}
static int
diff_write_reverse(struct stream *dest, struct diffstart *ds)
{
long firstoutputlinedeleted, endline, startline, editline, num_deleted,
num_added;
int num;
struct editcmd *ec, *nextec;
nextec = LIST_FIRST(&ds->dhead);
editline = 0;
num = 0;
while (nextec != NULL) {
ec = nextec;
nextec = LIST_NEXT(nextec, next);
if (nextec == NULL)
break;
num++;
num_deleted = 0;
if (ec->havetext)
num_deleted = ec->count;
num_added = num_deleted + nextec->offset - ec->offset;
if (num_deleted > 0) {
firstoutputlinedeleted = ec->key - num_deleted + 1;
stream_printf(dest, "d%ld %ld\n", firstoutputlinedeleted,
num_deleted);
if (num_added <= 0)
continue;
}
if (num_added > 0) {
stream_printf(dest, "a%ld %ld\n", ec->key, num_added);
startline = ec->key - num_deleted + 1 + ec->offset;
endline = startline + num_added - 1;
/* Copy lines from original file. First ignore some. */
ec->editline = editline;
diff_ignoreln(ec, startline - 1);
diff_copyln(ec, endline);
editline = ec->editline;
}
}
return (0);
}
/*
* Insert a diff into the list sorted on key. Should perhaps use quicker
* algorithms than insertion sort, but do this for now.
*/
static int
diff_insert_edit(struct diffstart *ds, struct editcmd *ec)
{
struct editcmd *curec;
if (ec == NULL)
return (0);
if (LIST_EMPTY(&ds->dhead)) {
LIST_INSERT_HEAD(&ds->dhead, ec, next);
return (0);
}
/* Insertion sort based on key. */
/* XXX: check if this gets too slow. */
LIST_FOREACH(curec, &ds->dhead, next) {
if (ec->key < curec->key) {
LIST_INSERT_BEFORE(curec, ec, next);
return (0);
}
if (LIST_NEXT(curec, next) == NULL)
break;
}
/* Just insert it after. */
LIST_INSERT_AFTER(curec, ec, next);
return (0);
}
static int
diff_free(struct diffstart *ds)
{
struct editcmd *ec;
int freecount = 0;
while(!LIST_EMPTY(&ds->dhead)) {
ec = LIST_FIRST(&ds->dhead);
LIST_REMOVE(ec, next);
free(ec);
freecount++;
}
return freecount;
}
/*
* Write the reverse diff from the diff in rd, and original file into
* destination. This algorithm is the same as used in cvsup.
*/
int
diff_reverse(struct stream *rd, struct stream *orig, struct stream *dest,
struct keyword *keyword, struct diffinfo *di)
{
struct diffstart ds;
struct editcmd ec, *addec, *delec;
lineno_t i;
char *line;
int error, offset;
int malloccount = 0, freecount = 0;
memset(&ec, 0, sizeof(ec));
ec.orig = orig;
ec.dest = dest;
ec.keyword = keyword;
ec.di = di;
addec = NULL;
delec = NULL;
ec.havetext = 0;
offset = 0;
LIST_INIT(&ds.dhead);
/* Start with next since we need it. */
line = stream_getln(rd, NULL);
/* First we build up the list of diffs from input. */
while (line != NULL) {
error = diff_geteditcmd(&ec, line);
/*fprintf(stderr, "Diff line '%s'\n", line);*/
if (error)
break;
if (ec.cmd == EC_ADD) {
addec = xmalloc(sizeof(struct editcmd));
malloccount++;
*addec = ec;
addec->havetext = 1;
/* Ignore the lines we was supposed to add. */
for (i = 0; i < ec.count; i++) {
line = stream_getln(rd, NULL);
/*fprintf(stderr, "Diff line '%s'\n", line);*/
if (line == NULL)
return (-1);
}
/* Get the next diff command if we have one. */
addec->key = addec->where + addec->count - offset;
if (delec != NULL && delec->key == addec->key - addec->count) {
delec->key = addec->key;
delec->havetext = addec->havetext;
delec->count = addec->count;
diff_insert_edit(&ds, delec);
free(addec);
freecount++;
delec = NULL;
addec = NULL;
} else {
if (delec != NULL) {
diff_insert_edit(&ds, delec);
}
delec = NULL;
addec->offset = offset;
diff_insert_edit(&ds, addec);
addec = NULL;
}
offset -= ec.count;
} else if (ec.cmd == EC_DEL) {
if (delec != NULL) {
/* Update offset to our next. */
diff_insert_edit(&ds, delec);
delec = NULL;
}
delec = xmalloc(sizeof(struct editcmd));
malloccount++;
*delec = ec;
delec->key = delec->where - 1 - offset;
delec->offset = offset;
delec->count = 0;
delec->havetext = 0;
/* Important to use the count we had before reset.*/
offset += ec.count;
}
line = stream_getln(rd, NULL);
}
while (line != NULL) {
/*fprintf(stderr, "Diff line '%s'\n", line);*/
line = stream_getln(rd, NULL);
}
/*fprintf(stderr, "Done with diff\n");*/
if (delec != NULL) {
diff_insert_edit(&ds, delec);
delec = NULL;
}
addec = xmalloc(sizeof(struct editcmd));
malloccount++;
/* Should be filesize, but we set it to max value. */
addec->key = MAXKEY;
addec->offset = offset;
addec->havetext = 0;
addec->count = 0;
diff_insert_edit(&ds, addec);
addec = NULL;
/*fprintf(stderr, "Done with last diff\n");*/
diff_write_reverse(dest, &ds);
freecount += diff_free(&ds);
/*fprintf(stderr, "Diff did a total of %d mallocs\n", malloccount);
fprintf(stderr, "Diff did a total of %d frees\n", freecount);*/
stream_flush(dest);
return (0);
}
/* Get an editing command from the diff. */
static int
diff_geteditcmd(struct editcmd *ec, char *line)
@ -194,6 +417,22 @@ diff_copyln(struct editcmd *ec, lineno_t to)
return (0);
}
/* Ignore lines from the original version of the file up to line "to". */
static int
diff_ignoreln(struct editcmd *ec, lineno_t to)
{
char *line;
size_t size;
while (ec->editline < to) {
line = stream_getln(ec->orig, &size);
if (line == NULL)
return (-1);
ec->editline++;
}
return (0);
}
/* Write a new line to the file, expanding RCS keywords appropriately. */
static void
diff_write(struct editcmd *ec, void *buf, size_t size)

View File

@ -45,6 +45,8 @@ struct diffinfo {
};
int diff_apply(struct stream *, struct stream *, struct stream *,
struct keyword *, struct diffinfo *);
struct keyword *, struct diffinfo *, int);
int diff_reverse(struct stream *, struct stream *,
struct stream *, struct keyword *, struct diffinfo *);
#endif /* !_DIFF_H_ */

View File

@ -44,7 +44,7 @@
/*
* Include the appropriate definition for the file attributes we support.
* There are two different files: fattr_bsd.h for BSD-like systems that
* support the extended file flags à la chflags() and fattr_posix.h for
* support the extended file flags ? la chflags() and fattr_posix.h for
* bare POSIX systems that don't.
*/
#ifdef HAVE_FFLAGS
@ -449,7 +449,7 @@ fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
piece++;
}
if (mask & FA_DEV) {
vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
vallen = snprintf(piece->val, sizeof(piece->val), "%llx",
(long long)fa->dev);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
@ -534,6 +534,13 @@ fattr_getlinkcount(const struct fattr *fa)
return (fa->linkcount);
}
char *
fattr_getlinktarget(const struct fattr *fa)
{
return (fa->linktarget);
}
/*
* Eat the specified attribute and put it in the file attribute
* structure. Returns NULL on error, or a pointer to the next
@ -732,23 +739,32 @@ fattr_makenode(const struct fattr *fa, const char *path)
mode_t modemask, mode;
int error;
error = 0;
if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
modemask = FA_SETIDMASK | FA_PERMMASK;
else
modemask = FA_PERMMASK;
/* We only implement fattr_makenode() for dirs for now. */
assert(fa->type == FT_DIRECTORY);
if (fa->mask & FA_MODE)
mode = fa->mode & modemask;
else
mode = 0700;
error = mkdir(path, mode);
if (fa->type == FT_DIRECTORY)
error = mkdir(path, mode);
else if (fa->type == FT_SYMLINK) {
error = symlink(fa->linktarget, path);
} else if (fa->type == FT_CDEV) {
lprintf(-1, "Character devices not supported!\n");
} else if (fa->type == FT_BDEV) {
lprintf(-1, "Block devices not supported!\n");
}
return (error);
}
int
fattr_delete(const char *path)
int fattr_delete(const char *path)
{
struct fattr *fa;
int error;
@ -830,8 +846,9 @@ fattr_install(struct fattr *fa, const char *topath, const char *frompath)
error = rmdir(topath);
else
error = unlink(topath);
if (error)
if (error) {
goto bad;
}
}
}
@ -842,8 +859,9 @@ fattr_install(struct fattr *fa, const char *topath, const char *frompath)
tv[1].tv_sec = fa->modtime; /* Modification time. */
tv[1].tv_usec = 0;
error = utimes(frompath, tv);
if (error)
if (error) {
goto bad;
}
}
if (mask & FA_OWNER || mask & FA_GROUP) {
uid = -1;
@ -853,8 +871,9 @@ fattr_install(struct fattr *fa, const char *topath, const char *frompath)
if (mask & FA_GROUP)
gid = fa->gid;
error = chown(frompath, uid, gid);
if (error)
if (error) {
goto bad;
}
}
if (mask & FA_MODE) {
newmode = fa->mode & modemask;
@ -936,3 +955,12 @@ fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
return (0);
return (1);
}
/*
* Must have to get the correct filesize sendt by the server.
*/
off_t
fattr_filesize(const struct fattr *fa)
{
return (fa->size);
}

View File

@ -101,6 +101,7 @@ int fattr_type(const struct fattr *);
void fattr_maskout(struct fattr *, int);
int fattr_getmask(const struct fattr *);
nlink_t fattr_getlinkcount(const struct fattr *);
char *fattr_getlinktarget(const struct fattr *);
void fattr_umask(struct fattr *, mode_t);
void fattr_merge(struct fattr *, const struct fattr *);
void fattr_mergedefault(struct fattr *);
@ -111,5 +112,7 @@ int fattr_install(struct fattr *, const char *, const char *);
int fattr_equal(const struct fattr *, const struct fattr *);
void fattr_free(struct fattr *);
int fattr_supported(int);
off_t fattr_filesize(const struct fattr *);
#endif /* !_FATTR_H_ */

View File

@ -152,6 +152,29 @@ keyword_decode_expand(const char *expand)
return (-1);
}
const char *
keyword_encode_expand(int expand)
{
switch (expand) {
case EXPAND_DEFAULT:
return (".");
case EXPAND_KEYVALUE:
return ("kv");
case EXPAND_KEYVALUELOCKER:
return ("kvl");
case EXPAND_KEY:
return ("k");
case EXPAND_OLD:
return ("o");
case EXPAND_BINARY:
return ("b");
case EXPAND_VALUE:
return ("v");
}
return (NULL);
}
void
keyword_free(struct keyword *keyword)
{

View File

@ -42,6 +42,7 @@ struct keyword;
struct keyword *keyword_new(void);
int keyword_decode_expand(const char *);
const char *keyword_encode_expand(int);
int keyword_alias(struct keyword *, const char *, const char *);
int keyword_enable(struct keyword *, const char *);
int keyword_disable(struct keyword *, const char *);

2094
contrib/csup/lex.rcs.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -64,6 +64,8 @@ static int lister_dofile(struct lister *, struct coll *,
struct statusrec *);
static int lister_dodead(struct lister *, struct coll *,
struct statusrec *);
static int lister_dorcsfile(struct lister *, struct coll *,
struct statusrec *, int);
void *
lister(void *arg)
@ -146,13 +148,14 @@ lister_coll(struct lister *l, struct coll *coll, struct status *st)
struct statusrec *sr;
struct fattr *fa;
size_t i;
int depth, error, ret, prunedepth;
int depth, error, ret, prunedepth, attic;
wr = l->wr;
depth = 0;
prunedepth = INT_MAX;
as = attrstack_new();
while ((ret = status_get(st, NULL, 0, 0, &sr)) == 1) {
attic = 0;
switch (sr->sr_type) {
case SR_DIRDOWN:
depth++;
@ -189,6 +192,20 @@ lister_coll(struct lister *l, struct coll *coll, struct status *st)
goto bad;
}
break;
#if 0
case SR_FILEDEAD:
attic = 1;
case SR_FILELIVE:
if (depth < prunedepth) {
if (!(coll->co_options & CO_CHECKOUTMODE)) {
error = lister_dorcsfile(l, coll, sr,
attic);
if (error)
goto bad;
}
}
break;
#endif
}
}
if (ret == -1) {
@ -383,6 +400,56 @@ lister_dofile(struct lister *l, struct coll *coll, struct statusrec *sr)
return (0);
}
/* Handle a file live or file dead entry found in the status file. */
static int
lister_dorcsfile(struct lister *l, struct coll *coll, struct statusrec *sr,
int attic)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr;
struct fattr *fa;
char *path, *spath;
char cmd;
size_t len;
int error;
if (!globtree_test(coll->co_filefilter, sr->sr_file))
return (0);
config = l->config;
wr = l->wr;
if (!coll->co_options & CO_TRUSTSTATUSFILE) {
path = cvspath(coll->co_prefix, sr->sr_file, attic);
if (path == NULL) {
spath = coll_statuspath(coll);
xasprintf(&l->errmsg, "Error in \"%s\": "
"Invalid filename \"%s\"", spath, sr->sr_file);
free(spath);
return (LISTER_ERR_STATUS);
}
fa = fattr_frompath(path, FATTR_NOFOLLOW);
free(path);
} else
fa = sr->sr_clientattr;
/* XXX: Perhaps check attic path name here. */
cmd = attic ? 'F' : 'f';
if (fattr_equal(fa, sr->sr_clientattr)) {
if (isrcs(sr->sr_file, &len) &&
!(coll->co_options & CO_NORCS) &&
!(coll->co_options & CO_STRICTCHECKRCS)) {
fattr_maskout(fa, FA_SIZE);
}
sendattr = fa;
} else {
sendattr = fattr_bogus;
}
error = proto_printf(wr, "%c %s %F\n", cmd, pathlast(sr->sr_file), fa,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* Handle a checkout dead entry found in the status file. */
static int
lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr)

View File

@ -202,10 +202,10 @@ commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
return (minlen);
}
char *
pathlast(char *path)
const char *
pathlast(const char *path)
{
char *s;
const char *s;
s = strrchr(path, '/');
if (s == NULL)
@ -248,6 +248,31 @@ rcsdatetotime(const char *revdate)
return (t);
}
/*
* Checks if a file is an RCS file.
*/
int
isrcs(const char *file, size_t *len)
{
const char *cp;
if (file[0] == '/')
return (0);
cp = file;
while ((cp = strstr(cp, "..")) != NULL) {
if (cp == file || cp[2] == '\0' ||
(cp[-1] == '/' && cp[2] == '/'))
return (0);
cp += 2;
}
*len = strlen(file);
if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
return (0);
}
return (1);
}
/*
* Returns a buffer allocated with malloc() containing the absolute
* pathname to the checkout file made from the prefix and the path
@ -257,26 +282,54 @@ rcsdatetotime(const char *revdate)
char *
checkoutpath(const char *prefix, const char *file)
{
const char *cp;
char *path;
size_t len;
if (file[0] == '/')
return (NULL);
cp = file;
while ((cp = strstr(cp, "..")) != NULL) {
if (cp == file || cp[2] == '\0' ||
(cp[-1] == '/' && cp[2] == '/'))
return (NULL);
cp += 2;
}
len = strlen(file);
if (len < 2 || file[len - 1] != 'v' || file[len - 2] != ',')
if (!isrcs(file, &len))
return (NULL);
xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
return (path);
}
/*
* Returns a cvs path allocated with malloc() containing absolute pathname to a
* file in cvs mode which can reside in the attic. XXX: filename has really no
* restrictions.
*/
char *
cvspath(const char *prefix, const char *file, int attic)
{
const char *last;
char *path;
last = pathlast(file);
if (attic)
xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
file, last);
else
xasprintf(&path, "%s/%s", prefix, file);
return (path);
}
/*
* Regular or attic path if regular fails.
* XXX: This should perhaps also check if the Attic file exists too, and return
* NULL if not.
*/
char *
atticpath(const char *prefix, const char *file)
{
char *path;
path = cvspath(prefix, file, 0);
if (access(path, F_OK) != 0) {
free(path);
path = cvspath(prefix, file, 1);
}
return (path);
}
int
mkdirhier(char *path, mode_t mask)
{
@ -520,3 +573,76 @@ bt_free(struct backoff_timer *bt)
free(bt);
}
/* Compare two revisions. */
int
rcsnum_cmp(char *revision1, char *revision2)
{
char *rev1, *rev2, *rev1orig, *rev2orig;
char *tmp1, *tmp2;
long num1, num2;
int retval;
rev1orig = xstrdup(revision1);
rev2orig = xstrdup(revision2);
retval = 0;
rev1 = rev1orig;
rev2 = rev2orig;
tmp1 = strsep(&rev1, ".");
tmp2 = strsep(&rev2, ".");
while (tmp1 != NULL && tmp2 != NULL) {
num1 = strtol(tmp1, NULL, 10);
num2 = strtol(tmp2, NULL, 10);
if (num1 > num2) {
retval = 1;
goto done;
} else if (num1 < num2) {
retval = -1;
goto done;
}
tmp1 = strsep(&rev1, ".");
tmp2 = strsep(&rev2, ".");
}
/* If one of them is longer (not null), the shortest is the highest
* ranked. */
if (tmp2 != NULL && tmp1 == NULL)
retval = -1;
else if (tmp2 == NULL && tmp1 != NULL)
retval = 1;
done:
free(rev1orig);
free(rev2orig);
return (retval);
}
/* Returns 0 if a rcsrev is not a trunk revision number. */
int
rcsrev_istrunk(char *revnum)
{
char *tmp;
tmp = strchr(revnum, '.');
tmp++;
if (strchr(tmp, '.') != NULL)
return (0);
return (1);
}
/* Return prefix of rcsfile. */
char *
rcsrev_prefix(char *revnum)
{
char *modrev, *pos;
modrev = xstrdup(revnum);
pos = strrchr(modrev, '.');
if (pos == NULL) {
free(modrev);
return (NULL);
}
*pos = '\0';
return (modrev);
}

View File

@ -107,14 +107,22 @@ int rcsdatetotm(const char *, struct tm *);
time_t rcsdatetotime(const char *);
int pathcmp(const char *, const char *);
size_t commonpathlength(const char *, size_t, const char *, size_t);
char *pathlast(char *);
const char *pathlast(const char *); /*XXX*/
int isrcs(const char *, size_t *);
char *checkoutpath(const char *, const char *);
char *cvspath(const char *, const char *, int);
char *atticpath(const char *, const char *);
char *path_prefix(char *);
char *path_first(char *);
int mkdirhier(char *, mode_t);
char *tempname(const char *);
void *xmalloc(size_t);
void *xrealloc(void *, size_t);
char *xstrdup(const char *);
int xasprintf(char **, const char *, ...) __printflike(2, 3);
int rcsnum_cmp(char *, char *);
int rcsrev_istrunk(char *);
char *rcsrev_prefix(char *);
struct pattlist *pattlist_new(void);
void pattlist_add(struct pattlist *, const char *);

View File

@ -785,6 +785,7 @@ sender_loop(void *arg)
int error, id, iovcnt, what = 0;
m = (struct mux *)arg;
what = 0;
again:
id = sender_waitforwork(m, &what);
chan = chan_get(m, id);

View File

@ -768,6 +768,7 @@ proto_printf(struct stream *wr, const char *format, ...)
va_list ap;
char *cp, *s, *attr;
ssize_t n;
off_t off;
int rv, val, ignore;
char c;
@ -801,6 +802,10 @@ proto_printf(struct stream *wr, const char *format, ...)
val = va_arg(ap, int);
rv = stream_printf(wr, "%o", val);
break;
case 'O':
off = va_arg(ap, off_t);
rv = stream_printf(wr, "%lu", off);
break;
case 'S':
s = va_arg(ap, char *);
assert(s != NULL);

1381
contrib/csup/rcsfile.c Normal file

File diff suppressed because it is too large Load Diff

73
contrib/csup/rcsfile.h Normal file
View File

@ -0,0 +1,73 @@
/*-
* Copyright (c) 2007-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$
*/
#ifndef _RCSFILE_H_
#define _RCSFILE_H_
/* RCSFILE fields. */
#define RCSFILE_HEAD 0
#define RCSFILE_BRANCH 1
#define RCSFILE_STRICT 2
#define RCSFILE_COMMENT 3
#define RCSFILE_EXPAND 4
#define RCSFILE_DESC 5
struct rcsfile;
struct delta;
struct stream;
/* Fetching, sending and writing an RCS file. */
struct rcsfile *rcsfile_frompath(char *, char *, char *, char *);
int rcsfile_send_details(struct rcsfile *, struct stream *);
int rcsfile_write(struct rcsfile *, struct stream *);
void rcsfile_print(struct rcsfile *);
void rcsfile_free(struct rcsfile *);
/* Used for adding and setting rcsfile values. */
void rcsfile_addaccess(struct rcsfile *, char *);
void rcsfile_addtag(struct rcsfile *, char *, char *);
void rcsfile_importtag(struct rcsfile *, char *, char *);
void rcsfile_deleterev(struct rcsfile *, char *);
void rcsfile_deletetag(struct rcsfile *, char *, char *);
struct delta *rcsfile_getdelta(struct rcsfile *, char *);
void rcsfile_setval(struct rcsfile *, int, char *);
/* Functions used for operating on RCS deltas. */
struct delta *rcsfile_addelta(struct rcsfile *, char *, char *, char *,
char *);
void rcsfile_importdelta(struct rcsfile *, char *, char *, char *,
char *, char *);
int rcsdelta_addlog(struct delta *, char *);
int rcsdelta_addtext(struct delta *, char *);
void rcsdelta_appendlog(struct delta *, char *, size_t);
void rcsdelta_appendtext(struct delta *, char *, size_t);
void rcsdelta_setstate(struct delta *, char *);
void rcsdelta_truncatetext(struct delta *, off_t);
void rcsdelta_truncatelog(struct delta *, off_t);
#endif /* !_RCSFILE_H_ */

358
contrib/csup/rcsparse.c Normal file
View File

@ -0,0 +1,358 @@
/*-
* 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 <stdlib.h>
#include <assert.h>
#include "rcstokenizer.h"
#include "rcsparse.h"
#include "rcsfile.h"
#include "misc.h"
#include "queue.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 *);
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)
{
char *tmp, *val;
int len;
tmp = rcsget_text(*sp);
len = rcsget_leng(*sp);
val = xmalloc(len + 2);
strlcpy(val, tmp, len + 1);
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);
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 *head;
char *branch;
char *comment;
char *id;
char *expand;
char *tag, *revnum;
char *tmp;
int strict, token;
strict = 0;
branch = NULL;
/* head {num}; */
asserttoken(sp, KEYWORD);
asserttoken(sp, NUM);
head = duptext(sp);
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);
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);
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);
asserttoken(sp, COLON);
asserttoken(sp, NUM);
revnum = duptext(sp);
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: skip locks */
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);
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);
rcsfile_setval(rf, RCSFILE_EXPAND, expand);
free(expand);
}
asserttoken(sp, SEMIC);
}
/* {newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: ignore for now. */
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);
/* date num; */
asserttoken(sp, KEYWORD);
asserttoken(sp, NUM);
revdate = duptext(sp);
asserttoken(sp, SEMIC);
/* author id; */
asserttoken(sp, KEYWORD);
asserttoken(sp, ID);
author = duptext(sp);
asserttoken(sp, SEMIC);
/* state {id}; */
asserttoken(sp, KEYWORD);
token = rcslex(*sp);
if (token == ID) {
state = duptext(sp);
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);
token = rcslex(*sp);
}
assert(token == SEMIC);
/* {newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: ignore for now. */
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 *revnum, *log, *text;
int error;
error = 0;
/* In case we don't have deltatexts. */
if (token != NUM) {
fprintf(stderr, "Tokens Was %d\n", token);
return (token);
}
do {
/* num */
assert(token == NUM);
revnum = duptext(sp);
/* 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);
error = rcsdelta_addlog(d, log);
free(log);
if (error)
return (-1);
/* { newphrase }* */
token = rcslex(*sp);
while (token == ID) {
token = rcslex(*sp);
/* XXX: ignore for now. */
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);
error = rcsdelta_addtext(d, text);
/*
* 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);
}

42
contrib/csup/rcsparse.h Normal file
View File

@ -0,0 +1,42 @@
/*-
* 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$
*
*/
#ifndef _RCSPARSE_H_
#define _RCSPARSE_H_
#define ID 0
#define NUM 1
#define KEYWORD 2
#define KEYWORD_TWO 3
#define STRING 4
#define SEMIC 5
#define COLON 6
struct rcsfile;
int rcsparse_run(struct rcsfile *, FILE *);
#endif

333
contrib/csup/rcstokenizer.h Normal file
View File

@ -0,0 +1,333 @@
#ifndef rcsHEADER_H
#define rcsHEADER_H 1
#define rcsIN_HEADER 1
#line 6 "rcstokenizer.h"
#define YY_INT_ALIGNED short int
/* A lexical scanner generated by flex */
#define FLEX_SCANNER
#define YY_FLEX_MAJOR_VERSION 2
#define YY_FLEX_MINOR_VERSION 5
#define YY_FLEX_SUBMINOR_VERSION 35
#if YY_FLEX_SUBMINOR_VERSION > 0
#define FLEX_BETA
#endif
/* First, we deal with platform-specific or compiler-specific issues. */
/* begin standard C headers. */
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
/* end standard C headers. */
/* flex integer type definitions */
#ifndef FLEXINT_H
#define FLEXINT_H
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
* if you want the limit (max/min) macros for int types.
*/
#ifndef __STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS 1
#endif
#include <inttypes.h>
typedef int8_t flex_int8_t;
typedef uint8_t flex_uint8_t;
typedef int16_t flex_int16_t;
typedef uint16_t flex_uint16_t;
typedef int32_t flex_int32_t;
typedef uint32_t flex_uint32_t;
#else
typedef signed char flex_int8_t;
typedef short int flex_int16_t;
typedef int flex_int32_t;
typedef unsigned char flex_uint8_t;
typedef unsigned short int flex_uint16_t;
typedef unsigned int flex_uint32_t;
#endif /* ! C99 */
/* Limits of integral types. */
#ifndef INT8_MIN
#define INT8_MIN (-128)
#endif
#ifndef INT16_MIN
#define INT16_MIN (-32767-1)
#endif
#ifndef INT32_MIN
#define INT32_MIN (-2147483647-1)
#endif
#ifndef INT8_MAX
#define INT8_MAX (127)
#endif
#ifndef INT16_MAX
#define INT16_MAX (32767)
#endif
#ifndef INT32_MAX
#define INT32_MAX (2147483647)
#endif
#ifndef UINT8_MAX
#define UINT8_MAX (255U)
#endif
#ifndef UINT16_MAX
#define UINT16_MAX (65535U)
#endif
#ifndef UINT32_MAX
#define UINT32_MAX (4294967295U)
#endif
#endif /* ! FLEXINT_H */
#ifdef __cplusplus
/* The "const" storage-class-modifier is valid. */
#define YY_USE_CONST
#else /* ! __cplusplus */
/* C99 requires __STDC__ to be defined as 1. */
#if defined (__STDC__)
#define YY_USE_CONST
#endif /* defined (__STDC__) */
#endif /* ! __cplusplus */
#ifdef YY_USE_CONST
#define yyconst const
#else
#define yyconst
#endif
/* An opaque pointer. */
#ifndef YY_TYPEDEF_YY_SCANNER_T
#define YY_TYPEDEF_YY_SCANNER_T
typedef void* yyscan_t;
#endif
/* For convenience, these vars (plus the bison vars far below)
are macros in the reentrant scanner. */
#define yyin yyg->yyin_r
#define yyout yyg->yyout_r
#define yyextra yyg->yyextra_r
#define yyleng yyg->yyleng_r
#define yytext yyg->yytext_r
#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
#define yy_flex_debug yyg->yy_flex_debug_r
/* Size of default input buffer. */
#ifndef YY_BUF_SIZE
#define YY_BUF_SIZE 16384
#endif
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
#define YY_TYPEDEF_YY_BUFFER_STATE
typedef struct yy_buffer_state *YY_BUFFER_STATE;
#endif
#ifndef YY_TYPEDEF_YY_SIZE_T
#define YY_TYPEDEF_YY_SIZE_T
typedef size_t yy_size_t;
#endif
#ifndef YY_STRUCT_YY_BUFFER_STATE
#define YY_STRUCT_YY_BUFFER_STATE
struct yy_buffer_state
{
FILE *yy_input_file;
char *yy_ch_buf; /* input buffer */
char *yy_buf_pos; /* current position in input buffer */
/* Size of input buffer in bytes, not including room for EOB
* characters.
*/
yy_size_t yy_buf_size;
/* Number of characters read into yy_ch_buf, not including EOB
* characters.
*/
int yy_n_chars;
/* Whether we "own" the buffer - i.e., we know we created it,
* and can realloc() it to grow it, and should free() it to
* delete it.
*/
int yy_is_our_buffer;
/* Whether this is an "interactive" input source; if so, and
* if we're using stdio for input, then we want to use getc()
* instead of fread(), to make sure we stop fetching input after
* each newline.
*/
int yy_is_interactive;
/* Whether we're considered to be at the beginning of a line.
* If so, '^' rules will be active on the next match, otherwise
* not.
*/
int yy_at_bol;
int yy_bs_lineno; /**< The line count. */
int yy_bs_column; /**< The column count. */
/* Whether to try to fill the input buffer when we reach the
* end of it.
*/
int yy_fill_buffer;
int yy_buffer_status;
};
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
void rcsrestart (FILE *input_file ,yyscan_t yyscanner );
void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
YY_BUFFER_STATE rcs_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
void rcs_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void rcs_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
void rcspush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
void rcspop_buffer_state (yyscan_t yyscanner );
YY_BUFFER_STATE rcs_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
YY_BUFFER_STATE rcs_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
YY_BUFFER_STATE rcs_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
void *rcsalloc (yy_size_t ,yyscan_t yyscanner );
void *rcsrealloc (void *,yy_size_t ,yyscan_t yyscanner );
void rcsfree (void * ,yyscan_t yyscanner );
/* Begin user sect3 */
#define rcswrap(n) 1
#define YY_SKIP_YYWRAP
#define yytext_ptr yytext_r
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
#define INITIAL 0
#endif
#ifndef YY_NO_UNISTD_H
/* Special case for "unistd.h", since it is non-ANSI. We include it way
* down here because we want the user's section 1 to have been scanned first.
* The user has a chance to override it with an option.
*/
#include <unistd.h>
#endif
#ifndef YY_EXTRA_TYPE
#define YY_EXTRA_TYPE void *
#endif
int rcslex_init (yyscan_t* scanner);
int rcslex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
/* Accessor methods to globals.
These are made visible to non-reentrant scanners for convenience. */
int rcslex_destroy (yyscan_t yyscanner );
int rcsget_debug (yyscan_t yyscanner );
void rcsset_debug (int debug_flag ,yyscan_t yyscanner );
YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner );
void rcsset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
FILE *rcsget_in (yyscan_t yyscanner );
void rcsset_in (FILE * in_str ,yyscan_t yyscanner );
FILE *rcsget_out (yyscan_t yyscanner );
void rcsset_out (FILE * out_str ,yyscan_t yyscanner );
int rcsget_leng (yyscan_t yyscanner );
char *rcsget_text (yyscan_t yyscanner );
int rcsget_lineno (yyscan_t yyscanner );
void rcsset_lineno (int line_number ,yyscan_t yyscanner );
/* Macros after this point can all be overridden by user definitions in
* section 1.
*/
#ifndef YY_SKIP_YYWRAP
#ifdef __cplusplus
extern "C" int rcswrap (yyscan_t yyscanner );
#else
extern int rcswrap (yyscan_t yyscanner );
#endif
#endif
#ifndef yytext_ptr
static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
#endif
#ifdef YY_NEED_STRLEN
static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
#endif
#ifndef YY_NO_INPUT
#endif
/* Amount of stuff to slurp up with each read. */
#ifndef YY_READ_BUF_SIZE
#define YY_READ_BUF_SIZE 8192
#endif
/* Number of entries by which start-condition stack grows. */
#ifndef YY_START_STACK_INCR
#define YY_START_STACK_INCR 25
#endif
/* Default declaration of generated scanner - a define so the user can
* easily add parameters.
*/
#ifndef YY_DECL
#define YY_DECL_IS_OURS 1
extern int rcslex (yyscan_t yyscanner);
#define YY_DECL int rcslex (yyscan_t yyscanner)
#endif /* !YY_DECL */
/* yy_get_previous_state - get the state just before the EOB char was reached */
#undef YY_NEW_FILE
#undef YY_FLUSH_BUFFER
#undef yy_set_bol
#undef yy_new_buffer
#undef yy_set_interactive
#undef YY_DO_BEFORE_ACTION
#ifdef YY_DECL_IS_OURS
#undef YY_DECL_IS_OURS
#undef YY_DECL
#endif
#line 73 "rcstokenizer.l"
#line 332 "rcstokenizer.h"
#undef rcsIN_HEADER
#endif /* rcsHEADER_H */

View File

@ -0,0 +1,73 @@
/*-
* Copyright (c) 2007-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$
*
*/
/*
* This tokenizer must be generated by a lexxer with support for reentrancy.
*/
%{
#include <string.h>
#include "misc.h"
#include "rcsparse.h"
%}
%option reentrant noyywrap
%option header-file="rcstokenizer.h"
everything (.|\n)*
num [0-9\.]+
whitespace [\t\n ]
digit [0-9]
idchar [^$,.:;\t\n ]
string @([^@]|\n|"@@")*@
keyword head|access|symbols|locks|comment|expand|strict|date|author|state|branches|next|desc|log|text
keyword2 branch
newline \n
%%
{keyword2} {
return (KEYWORD_TWO);
}
{keyword} {
return (KEYWORD);
}
{string} {
return (STRING);
}
{num} {
return (NUM);
}
{num}?{idchar}({idchar}|{num})* {
/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/
return (ID);
}
; { return (SEMIC); }
: { return (COLON); }
\n ;
[ \t]+ ;
%%

View File

@ -101,6 +101,15 @@ statusrec_cook(struct statusrec *sr, char *line)
char *clientattr, *serverattr;
switch (sr->sr_type) {
case SR_FILEDEAD:
case SR_FILELIVE:
clientattr = proto_get_ascii(&line);
if (clientattr == NULL || line != NULL)
return (-1);
sr->sr_clientattr = fattr_decode(clientattr);
if (sr->sr_clientattr == NULL)
return (-1);
break;
case SR_DIRDOWN:
/* Nothing to do. */
if (line != NULL)
@ -161,6 +170,7 @@ status_rd(struct status *st)
error = statusrec_cook(sr, line);
if (error) {
st->error = STATUS_ERR_PARSE;
printf("ERROR HERE\n");
return (NULL);
}
return (sr);
@ -197,6 +207,9 @@ status_rdraw(struct status *st, char **linep)
}
switch (cmd[0]) {
case 'A':
sr.sr_type = SR_FILELIVE;
break;
case 'D':
sr.sr_type = SR_DIRDOWN;
st->depth++;
@ -215,6 +228,12 @@ status_rdraw(struct status *st, char **linep)
}
st->depth--;
break;
case 'V':
sr.sr_type = SR_FILELIVE;
break;
case 'v':
sr.sr_type = SR_FILEDEAD;
break;
default:
st->error = STATUS_ERR_BAD_TYPE;
st->suberror = cmd[0];
@ -290,6 +309,14 @@ status_wr(struct status *st, struct statusrec *sr)
error = proto_printf(st->wr, "c %s %s %s %f\n", sr->sr_file,
sr->sr_tag, sr->sr_date, sr->sr_serverattr);
break;
case SR_FILELIVE:
error = proto_printf(st->wr, "V %s %f\n", sr->sr_file,
sr->sr_clientattr);
break;
case SR_FILEDEAD:
error = proto_printf(st->wr, "v %s %f\n", sr->sr_file,
sr->sr_clientattr);
break;
}
if (error)
goto bad;
@ -346,6 +373,12 @@ status_wrraw(struct status *st, struct statusrec *sr, char *line)
case SR_CHECKOUTDEAD:
cmd = 'c';
break;
case SR_FILELIVE:
cmd = 'V';
break;
case SR_FILEDEAD:
cmd = 'v';
break;
default:
assert(0);
return (-1);

View File

@ -97,6 +97,7 @@ struct buf {
struct stream {
void *cookie;
int fd;
int buf;
struct buf *rdbuf;
struct buf *wrbuf;
stream_readfn_t *readfn;
@ -126,10 +127,8 @@ struct stream_filter {
#define buf_count(buf) ((buf)->in)
#define buf_size(buf) ((buf)->size)
static struct buf *buf_new(size_t);
static void buf_more(struct buf *, size_t);
static void buf_less(struct buf *, size_t);
static void buf_free(struct buf *);
static void buf_grow(struct buf *, size_t);
/* Internal stream functions. */
@ -165,6 +164,12 @@ static int zfilter_flush(struct stream *, struct buf *,
struct md5filter {
MD5_CTX ctx;
char *md5;
char lastc;
#define PRINT 1
#define WS 2
#define STRING 3
#define SEEN 4
int state;
};
static int md5filter_init(struct stream *, void *);
@ -172,6 +177,8 @@ static void md5filter_fini(struct stream *);
static ssize_t md5filter_fill(struct stream *, struct buf *);
static int md5filter_flush(struct stream *, struct buf *,
stream_flush_t);
static int md5rcsfilter_flush(struct stream *, struct buf *,
stream_flush_t);
/* The available stream filters. */
struct stream_filter stream_filters[] = {
@ -195,12 +202,20 @@ struct stream_filter stream_filters[] = {
md5filter_fini,
md5filter_fill,
md5filter_flush
},
{
STREAM_FILTER_MD5RCS,
md5filter_init,
md5filter_fini,
md5filter_fill,
md5rcsfilter_flush
}
};
/* Create a new buffer. */
static struct buf *
struct buf *
buf_new(size_t size)
{
struct buf *buf;
@ -211,6 +226,7 @@ buf_new(size_t size)
* there in case the stream doesn't have an ending newline.
*/
buf->buf = xmalloc(size + 1);
memset(buf->buf, 0, size + 1);
buf->size = size;
buf->in = 0;
buf->off = 0;
@ -272,7 +288,7 @@ buf_less(struct buf *buf, size_t n)
}
/* Free a buffer. */
static void
void
buf_free(struct buf *buf)
{
@ -301,6 +317,7 @@ stream_new(stream_readfn_t *readfn, stream_writefn_t *writefn,
stream->wrbuf = NULL;
stream->cookie = NULL;
stream->fd = -1;
stream->buf = 0;
stream->readfn = readfn;
stream->writefn = writefn;
stream->closefn = closefn;
@ -335,6 +352,29 @@ stream_open_fd(int fd, stream_readfn_t *readfn, stream_writefn_t *writefn,
return (stream);
}
/* Associate a buf with a stream. */
struct stream *
stream_open_buf(struct buf *b)
{
struct stream *stream;
stream = stream_new(stream_read_buf, stream_append_buf, stream_close_buf);
stream->cookie = b;
stream->buf = 1;
b->in = 0;
return (stream);
}
/*
* Truncate a buffer, just decrease offset pointer.
* XXX: this can be dangerous if not used correctly.
*/
void
stream_truncate_buf(struct buf *b, off_t off)
{
b->off += off;
}
/* Like open() but returns a stream. */
struct stream *
stream_open_file(const char *path, int flags, ...)
@ -391,6 +431,57 @@ stream_fileno(struct stream *stream)
return (stream->fd);
}
/* Convenience read function for character buffers. */
ssize_t
stream_read_buf(void *cookie, void *buf, size_t size)
{
struct buf *b;
size_t avail;
/* Use in to be read offset. */
b = (struct buf *)cookie;
/* Just return what we have if the request is to large. */
avail = b->off - b->in;
if (avail < size) {
memcpy(buf, (b->buf + b->in), avail);
b->in += avail;
return (avail);
}
memcpy(buf, (b->buf + b->in), size);
b->in += size;
return (size);
}
/* Convenience write function for appending character buffers. */
ssize_t
stream_append_buf(void *cookie, const void *buf, size_t size)
{
struct buf *b;
size_t avail;
/* Use off to be write offset. */
b = (struct buf *)cookie;
avail = b->size - b->off;
if (size > avail)
buf_grow(b, b->size + size);
memcpy((b->buf + b->off), buf, size);
b->off += size;
b->buf[b->off] = '\0';
return (size);
}
/* Convenience close function for freeing character buffers. */
int
stream_close_buf(void *cookie)
{
void *data;
data = cookie;
/* Basically a NOP. */
return (0);
}
/* Convenience read function for file descriptors. */
ssize_t
stream_read_fd(void *cookie, void *buf, size_t size)
@ -446,6 +537,28 @@ stream_read(struct stream *stream, void *buf, size_t size)
return (n);
}
/* A blocking stream_read call. */
ssize_t
stream_read_blocking(struct stream *stream, void *buf, size_t size)
{
struct buf *rdbuf;
ssize_t ret;
size_t n;
rdbuf = stream->rdbuf;
while (buf_count(rdbuf) <= size) {
ret = stream_fill(stream);
if (ret <= 0)
return (-1);
}
/* XXX: Should be at least size bytes in the buffer, right? */
/* Just do this to make sure. */
n = min(size, buf_count(rdbuf));
memcpy(buf, rdbuf->buf + rdbuf->off, n);
buf_less(rdbuf, n);
return (n);
}
/*
* Read a line from the stream and return a pointer to it.
*
@ -638,6 +751,10 @@ stream_truncate_rel(struct stream *stream, off_t off)
struct stat sb;
int error;
if (stream->buf) {
stream_truncate_buf(stream->cookie, off);
return (0);
}
if (stream->fd == -1) {
errno = EINVAL;
return (-1);
@ -1043,6 +1160,8 @@ md5filter_init(struct stream *stream, void *data)
mf = xmalloc(sizeof(struct md5filter));
MD5_Init(&mf->ctx);
mf->md5 = data;
mf->lastc = ';';
mf->state = PRINT;
stream->fdata = mf;
return (0);
}
@ -1078,3 +1197,107 @@ md5filter_flush(struct stream *stream, struct buf *buf, stream_flush_t how)
error = stream_flush_default(stream, buf, how);
return (error);
}
/* MD5 flush for RCS, where whitespaces are omitted. */
static int
md5rcsfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how)
{
struct md5filter *mf;
char *ptr, *end;
char *start;
char space[2];
int error;
mf = stream->fdata;
space[1] = '\0';
space[0] = ' ';
ptr = buf->buf + buf->off;
end = buf->buf + buf->off + buf->in;
#define IS_WS(var) ((var) == ' ' || (var) == '\n' || (var) == '\t' || \
(var) == '\010' || (var) == '\013' || (var) == '\f' || \
(var) == '\r')
#define IS_SPECIAL(var) ((var) == '$' || (var) == ',' || (var) == ':' || \
(var) == ';' || (var) == '@')
#define IS_PRINT(var) (!IS_WS(var) && (var) != '@')
/* XXX: We can do better than this state machine. */
while (ptr < end) {
switch (mf->state) {
/* Outside RCS statements. */
case PRINT:
start = ptr;
while (ptr < end && IS_PRINT(*ptr)) {
mf->lastc = *ptr;
ptr++;
}
MD5_Update(&mf->ctx, start, (ptr - start));
if (ptr < end) {
if (*ptr == '@') {
MD5_Update(&mf->ctx, ptr, 1);
ptr++;
mf->state = STRING;
} else {
mf->state = WS;
}
}
break;
case WS:
while (ptr < end && IS_WS(*ptr)) {
ptr++;
}
if (ptr < end) {
if (*ptr == '@') {
if (mf->lastc == '@') {
MD5_Update(&mf->ctx,
space, 1);
}
MD5_Update(&mf->ctx, ptr, 1);
ptr++;
mf->state = STRING;
} else {
if (!IS_SPECIAL(*ptr) &&
!IS_SPECIAL(mf->lastc)) {
MD5_Update(&mf->ctx,
space, 1);
}
mf->state = PRINT;
}
}
break;
case STRING:
start = ptr;
while (ptr < end && *ptr != '@') {
ptr++;
}
MD5_Update(&mf->ctx, start, (ptr - start));
if (ptr < end) {
MD5_Update(&mf->ctx, ptr, 1);
ptr++;
mf->state = SEEN;
}
break;
case SEEN:
if (*ptr == '@') {
MD5_Update(&mf->ctx, ptr, 1);
ptr++;
mf->state = STRING;
} else if(IS_WS(*ptr)) {
mf->lastc = '@';
mf->state = WS;
} else {
mf->state = PRINT;
}
break;
default:
err(1, "Invalid state");
break;
}
}
error = stream_flush_default(stream, buf, how);
return (error);
}

View File

@ -34,10 +34,12 @@
typedef enum {
STREAM_FILTER_NULL,
STREAM_FILTER_ZLIB,
STREAM_FILTER_MD5
STREAM_FILTER_MD5,
STREAM_FILTER_MD5RCS
} stream_filter_t;
struct stream;
struct buf;
typedef ssize_t stream_readfn_t(void *, void *, size_t);
typedef ssize_t stream_writefn_t(void *, const void *, size_t);
@ -48,13 +50,20 @@ stream_readfn_t stream_read_fd;
stream_writefn_t stream_write_fd;
stream_closefn_t stream_close_fd;
/* Convenience functions for handling character buffers. */
stream_readfn_t stream_read_buf;
stream_writefn_t stream_append_buf;
stream_closefn_t stream_close_buf;
struct stream *stream_open(void *, stream_readfn_t *, stream_writefn_t *,
stream_closefn_t *);
struct stream *stream_open_fd(int, stream_readfn_t *, stream_writefn_t *,
stream_closefn_t *);
struct stream *stream_open_buf(struct buf *);
struct stream *stream_open_file(const char *, int, ...);
int stream_fileno(struct stream *);
ssize_t stream_read(struct stream *, void *, size_t);
ssize_t stream_read_blocking(struct stream *, void *, size_t);
ssize_t stream_write(struct stream *, const void *, size_t);
char *stream_getln(struct stream *, size_t *);
int stream_printf(struct stream *, const char *, ...)
@ -62,6 +71,7 @@ int stream_printf(struct stream *, const char *, ...)
int stream_flush(struct stream *);
int stream_sync(struct stream *);
int stream_truncate(struct stream *, off_t);
void stream_truncate_buf(struct buf *, off_t);
int stream_truncate_rel(struct stream *, off_t);
int stream_rewind(struct stream *);
int stream_eof(struct stream *);
@ -69,4 +79,6 @@ int stream_close(struct stream *);
int stream_filter_start(struct stream *, stream_filter_t, void *);
void stream_filter_stop(struct stream *);
struct buf *buf_new(size_t);
void buf_free(struct buf *);
#endif /* !_STREAM_H_ */

File diff suppressed because it is too large Load Diff

View File

@ -13,6 +13,7 @@ SRCS= attrstack.c \
globtree.c \
idcache.c \
keyword.c \
lex.rcs.c \
lister.c \
main.c \
misc.c \
@ -20,6 +21,8 @@ SRCS= attrstack.c \
parse.y \
pathcomp.c \
proto.c \
rcsfile.c \
rcsparse.c \
status.c \
stream.c \
threads.c \