Merge support for CVSMode (aka. mirror mode) into csup. This means csup can now

fetch a complete CVS repository. Support for rsync update of regular files are
also included, but are not yet enabled. The change should not have an impact on
existing csup usage, as little of the existing code has changed.
This commit is contained in:
lulf 2009-01-05 15:18:16 +00:00
commit 62eb8b9f1f
31 changed files with 6812 additions and 83 deletions

View File

@ -12,8 +12,9 @@ GROUP?= 0
UNAME= $(shell uname -s)
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 pathcomp.c \
parse.c proto.c status.c stream.c threads.c token.c updater.c
globtree.c idcache.c keyword.c lex.rcs.c lister.c main.c misc.c mux.c \
pathcomp.c parse.c proto.c rcsfile.c rcsparse.c rsyncfile.c status.c \
stream.c threads.c token.c updater.c
OBJS= $(SRCS:.c=.o)
WARNS= -Wall -W -Wno-unused-parameter -Wmissing-prototypes -Wpointer-arith \

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 rsyncfile.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

@ -28,4 +28,3 @@ MISSING FEATURES:
checkout files (files in CVS/ subdirectores), a command line override
to only update a specific collection and a third verbosity level to
display commit log messages.
- Add support for CVS mode (maybe?).

View File

@ -133,7 +133,6 @@ config_init(const char *file, struct coll *override, int overridemask)
coll->co_options &= ~CO_CHECKRCS;
/* In recent versions, we always try to set the file modes. */
coll->co_options |= CO_SETMODE;
/* XXX We don't support the rsync updating algorithm yet. */
coll->co_options |= CO_NORSYNC;
error = config_parse_refusefiles(coll);
if (error)
@ -444,10 +443,6 @@ coll_add(char *name)
"\"%s\"\n", cur_coll->co_name);
exit(1);
}
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,13 +30,21 @@
#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"
#include "fixups.h"
#include "globtree.h"
#include "misc.h"
#include "mux.h"
#include "proto.h"
#include "rcsfile.h"
#include "rsyncfile.h"
#include "status.h"
#include "stream.h"
@ -56,8 +64,16 @@ 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_dofile_rsync(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)
@ -186,8 +202,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,12 +229,14 @@ 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, *path, *target;
int error, attic;
rd = d->rd;
wr = d->wr;
attic = 0;
line = stream_getln(rd, NULL);
if (line == NULL)
return (DETAILER_ERR_READ);
@ -226,17 +249,84 @@ detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
/* Delete file. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
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;
@ -261,14 +351,110 @@ 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 a file with the rsync algorithm.
*/
static int
detailer_dofile_rsync(struct detailer *d, char *name, char *path)
{
struct stream *wr;
struct rsyncfile *rf;
wr = d->wr;
rf = rsync_open(path, 0, 1);
if (rf == NULL) {
/* Fallback if we fail in opening it. */
proto_printf(wr, "A %s\n", name);
return (0);
}
proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf),
rsync_blocksize(rf));
/* Detail the blocks. */
while (rsync_nextblock(rf) != 0)
proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf));
proto_printf(wr, ".\n");
rsync_close(rf);
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, 1);
free(path);
if (rf == NULL) {
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];
struct stream *wr;
struct fattr *fa;
struct statusrec *sr;
char md5[MD5_DIGEST_SIZE];
char *path;
int error, ret;
@ -337,3 +523,81 @@ 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 if (!(coll->co_options & CO_NORSYNC) &&
!globtree_test(coll->co_norsync, name)) {
detailer_dofile_rsync(d, 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,9 +26,12 @@
* $FreeBSD$
*/
#include <sys/limits.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@ -36,15 +39,20 @@
#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,20 +63,28 @@ 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 void 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;
char *line;
size_t size;
char *line;
int empty, error, noeol;
memset(&ec, 0, sizeof(ec));
@ -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--;
}
@ -124,10 +140,10 @@ diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
}
line = stream_getln(rd, NULL);
}
if (line == NULL)
if (comode && line == NULL)
return (-1);
/* If we got ".+", there's no ending newline. */
if (strcmp(line, ".+") == 0 && !empty)
if (comode && strcmp(line, ".+") == 0 && !empty)
noeol = 1;
ec.where = 0;
while ((line = stream_getln(orig, &size)) != NULL)
@ -143,6 +159,198 @@ diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
return (0);
}
/*
* Reverse a diff using the same algorithm as in cvsup.
*/
static int
diff_write_reverse(struct stream *dest, struct diffstart *ds)
{
struct editcmd *ec, *nextec;
long editline, endline, firstoutputlinedeleted;
long num_added, num_deleted, startline;
int num;
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. */
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 void
diff_free(struct diffstart *ds)
{
struct editcmd *ec;
while(!LIST_EMPTY(&ds->dhead)) {
ec = LIST_FIRST(&ds->dhead);
LIST_REMOVE(ec, next);
free(ec);
}
}
/*
* 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;
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);
if (error)
break;
if (ec.cmd == EC_ADD) {
addec = xmalloc(sizeof(struct editcmd));
*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);
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);
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));
*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)
line = stream_getln(rd, NULL);
if (delec != NULL) {
diff_insert_edit(&ds, delec);
delec = NULL;
}
addec = xmalloc(sizeof(struct editcmd));
/* 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;
diff_write_reverse(dest, &ds);
diff_free(&ds);
stream_flush(dest);
return (0);
}
/* Get an editing command from the diff. */
static int
diff_geteditcmd(struct editcmd *ec, char *line)
@ -181,8 +389,8 @@ diff_geteditcmd(struct editcmd *ec, char *line)
static int
diff_copyln(struct editcmd *ec, lineno_t to)
{
char *line;
size_t size;
char *line;
while (ec->editline < to) {
line = stream_getln(ec->orig, &size);
@ -194,12 +402,28 @@ 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)
{
size_t size;
char *line;
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)
{
char *line, *newline;
size_t newsize;
char *line, *newline;
int ret;
line = buf;

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 a 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,18 +739,28 @@ 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);
}
@ -823,6 +840,19 @@ fattr_install(struct fattr *fa, const char *topath, const char *frompath)
}
#endif
/*
* If it is changed from a file to a symlink, remove the file
* and create the symlink.
*/
if (inplace && (fa->type == FT_SYMLINK) &&
(old->type == FT_FILE)) {
error = unlink(topath);
if (error)
goto bad;
error = symlink(fa->linktarget, topath);
if (error)
goto bad;
}
/* Determine whether we need to remove the target first. */
if (!inplace && (fa->type == FT_DIRECTORY) !=
(old->type == FT_DIRECTORY)) {
@ -853,8 +883,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;
@ -901,6 +932,9 @@ fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
mask = fa1->mask & fa2->mask;
if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
return (0);
if (mask & FA_FILETYPE)
if (fa1->type != fa2->type)
return (0);
if (mask & FA_MODTIME)
if (fa1->modtime != fa2->modtime)
return (0);
@ -936,3 +970,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,10 @@ 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 *);
static int lister_dorcsdead(struct lister *, struct coll *,
struct statusrec *);
void *
lister(void *arg)
@ -189,6 +193,24 @@ lister_coll(struct lister *l, struct coll *coll, struct status *st)
goto bad;
}
break;
case SR_FILEDEAD:
if (depth < prunedepth) {
if (!(coll->co_options & CO_CHECKOUTMODE)) {
error = lister_dorcsdead(l, coll, sr);
if (error)
goto bad;
}
}
break;
case SR_FILELIVE:
if (depth < prunedepth) {
if (!(coll->co_options & CO_CHECKOUTMODE)) {
error = lister_dorcsfile(l, coll, sr);
if (error)
goto bad;
}
}
break;
}
}
if (ret == -1) {
@ -383,6 +405,60 @@ lister_dofile(struct lister *l, struct coll *coll, struct statusrec *sr)
return (0);
}
/* Handle a rcs file live entry found in the status file. */
static int
lister_dorcsfile(struct lister *l, struct coll *coll, struct statusrec *sr)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr;
struct fattr *fa;
char *path, *spath;
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, 0);
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;
if (fa != NULL && fattr_equal(fa, sr->sr_clientattr)) {
/*
* If the file is an RCS file, we use "loose" equality, so sizes
* may disagress because of differences in whitespace.
*/
if (isrcs(sr->sr_file, &len) &&
!(coll->co_options & CO_NORCS) &&
!(coll->co_options & CO_STRICTCHECKRCS)) {
fattr_maskout(fa, FA_SIZE);
}
sendattr = fa;
} else {
/*
* If different, the user may have changed it, so we report
* bogus attributes to force a full comparison.
*/
sendattr = fattr_bogus;
}
error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr,
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)
@ -437,3 +513,57 @@ lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr)
return (LISTER_ERR_WRITE);
return (0);
}
/* Handle a rcs file dead entry found in the status file. */
static int
lister_dorcsdead(struct lister *l, struct coll *coll, struct statusrec *sr)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr;
struct fattr *fa;
char *path, *spath;
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, 1);
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;
if (fattr_equal(fa, sr->sr_clientattr)) {
/*
* If the file is an RCS file, we use "loose" equality, so sizes
* may disagress because of differences in whitespace.
*/
if (isrcs(sr->sr_file, &len) &&
!(coll->co_options & CO_NORCS) &&
!(coll->co_options & CO_STRICTCHECKRCS)) {
fattr_maskout(fa, FA_SIZE);
}
sendattr = fa;
} else {
/*
* If different, the user may have changed it, so we report
* bogus attributes to force a full comparison.
*/
sendattr = fattr_bogus;
}
error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}

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,73 @@ bt_free(struct backoff_timer *bt)
free(bt);
}
/* Compare two revisions. */
int
rcsnum_cmp(char *revision1, char *revision2)
{
char *ptr1, *ptr2, *dot1, *dot2;
int num1len, num2len, ret;
ptr1 = revision1;
ptr2 = revision2;
while (*ptr1 != '\0' && *ptr2 != '\0') {
dot1 = strchr(ptr1, '.');
dot2 = strchr(ptr2, '.');
if (dot1 == NULL)
dot1 = strchr(ptr1, '\0');
if (dot2 == NULL)
dot2 = strchr(ptr2, '\0');
num1len = dot1 - ptr1;
num2len = dot2 - ptr2;
/* Check the distance between each, showing how many digits */
if (num1len > num2len)
return (1);
else if (num1len < num2len)
return (-1);
/* Equal distance means we must check each character. */
ret = strncmp(ptr1, ptr2, num1len);
if (ret != 0)
return (ret);
ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
}
if (*ptr1 != '\0' && *ptr2 == '\0')
return (1);
if (*ptr1 == '\0' && *ptr2 != '\0')
return (-1);
return (0);
}
/* 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

@ -99,22 +99,30 @@ struct backoff_timer;
struct pattlist;
struct tm;
int asciitoint(const char *, int *, int);
int lprintf(int, const char *, ...) __printflike(2, 3);
int MD5_File(char *, char *);
void MD5_End(char *, MD5_CTX *);
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 *);
char *checkoutpath(const char *, const 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 asciitoint(const char *, int *, int);
int lprintf(int, const char *, ...) __printflike(2, 3);
int MD5_File(char *, char *);
void MD5_End(char *, MD5_CTX *);
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);
const char *pathlast(const char *);
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

@ -365,6 +365,8 @@ proto_xchgcoll(struct config *config)
s = config->server;
lprintf(2, "Exchanging collection information\n");
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
proto_printf(s, "COLL %s %s %o %d\n", coll->co_name,
coll->co_release, coll->co_umask, coll->co_options);
for (i = 0; i < pattlist_size(coll->co_accepts); i++) {
@ -768,6 +770,8 @@ proto_printf(struct stream *wr, const char *format, ...)
va_list ap;
char *cp, *s, *attr;
ssize_t n;
size_t size;
off_t off;
int rv, val, ignore;
char c;
@ -801,6 +805,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, "%llu", off);
break;
case 'S':
s = va_arg(ap, char *);
assert(s != NULL);
@ -829,6 +837,11 @@ proto_printf(struct stream *wr, const char *format, ...)
rv = proto_escape(wr, attr);
free(attr);
break;
case 'z':
size = va_arg(ap, size_t);
rv = stream_printf(wr, "%zu", size);
break;
case '%':
n = stream_write(wr, "%", 1);
if (n == -1)
@ -938,6 +951,26 @@ proto_get_int(char **s, int *val, int base)
return (error);
}
/*
* Get a size_t token.
*/
int
proto_get_sizet(char **s, size_t *val, int base)
{
unsigned long long tmp;
char *cp, *end;
cp = proto_get_ascii(s);
if (cp == NULL)
return (-1);
errno = 0;
tmp = strtoll(cp, &end, base);
if (errno || *end != '\0')
return (-1);
*val = (size_t)tmp;
return (0);
}
/*
* Get a time_t token.
*

View File

@ -44,6 +44,7 @@ int proto_printf(struct stream *, const char *, ...);
char *proto_get_ascii(char **);
char *proto_get_rest(char **);
int proto_get_int(char **, int *, int);
int proto_get_sizet(char **, size_t *, int);
int proto_get_time(char **, time_t *);
#endif /* !_PROTO_H_ */

1367
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-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$
*/
#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);
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);
int rcsdelta_addtext(struct delta *, char *, int);
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_ */

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

@ -0,0 +1,357 @@
/*-
* 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 (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);
}

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

@ -0,0 +1,41 @@
/*-
* 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$
*/
#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 *, int);
#endif /* !_RCSPARSE_H_ */

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-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$
*/
/*
* 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]+ ;
%%

223
contrib/csup/rsyncfile.c Normal file
View File

@ -0,0 +1,223 @@
/*-
* 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 <errno.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>
#include "misc.h"
#include "rsyncfile.h"
#define MINBLOCKSIZE 1024
#define MAXBLOCKSIZE (16 * 1024)
#define RECEIVEBUFFERSIZE (15 * 1024)
#define BLOCKINFOSIZE 26
#define SEARCHREGION 10
#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE)
#define CHAR_OFFSET 3
#define RSUM_SIZE 9
struct rsyncfile {
char *start;
char *buf;
char *end;
size_t blocksize;
size_t fsize;
int fd;
char *blockptr;
int blocknum;
char blockmd5[MD5_DIGEST_SIZE];
char rsumstr[RSUM_SIZE];
uint32_t rsum;
};
static size_t rsync_chooseblocksize(size_t);
static uint32_t rsync_rollsum(char *, size_t);
/* Open a file and initialize variable for rsync operation. */
struct rsyncfile *
rsync_open(char *path, size_t blocksize, int rdonly)
{
struct rsyncfile *rf;
struct stat st;
int error;
rf = xmalloc(sizeof(*rf));
error = stat(path, &st);
if (error) {
free(rf);
return (NULL);
}
rf->fsize = st.st_size;
rf->fd = open(path, rdonly ? O_RDONLY : O_RDWR);
if (rf->fd < 0) {
free(rf);
return (NULL);
}
rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0);
if (rf->buf == MAP_FAILED) {
free(rf);
return (NULL);
}
rf->start = rf->buf;
rf->end = rf->buf + rf->fsize;
rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) :
blocksize);
rf->blockptr = rf->buf;
rf->blocknum = 0;
return (rf);
}
/* Close and free all resources related to an rsync file transfer. */
int
rsync_close(struct rsyncfile *rf)
{
int error;
error = munmap(rf->buf, rf->fsize);
if (error)
return (error);
close(rf->fd);
free(rf);
return (0);
}
/*
* Choose the most appropriate block size for an rsync transfer. Modeled
* algorithm after cvsup.
*/
static size_t
rsync_chooseblocksize(size_t fsize)
{
size_t bestrem, blocksize, bs, hisearch, losearch, rem;
blocksize = fsize / MAXBLOCKS;
losearch = blocksize - SEARCHREGION;
hisearch = blocksize + SEARCHREGION;
if (losearch < MINBLOCKSIZE) {
losearch = MINBLOCKSIZE;
hisearch = losearch + (2 * SEARCHREGION);
} else if (hisearch > MAXBLOCKSIZE) {
hisearch = MAXBLOCKSIZE;
losearch = hisearch - (2 * SEARCHREGION);
}
bestrem = MAXBLOCKSIZE;
for (bs = losearch; bs <= hisearch; bs++) {
rem = fsize % bs;
if (rem < bestrem) {
bestrem = rem;
blocksize = bs;
}
}
return (bestrem);
}
/* Get the next rsync block of a file. */
int
rsync_nextblock(struct rsyncfile *rf)
{
MD5_CTX ctx;
size_t blocksize;
if (rf->blockptr >= rf->end)
return (0);
blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize);
/* Calculate MD5 of the block. */
MD5_Init(&ctx);
MD5_Update(&ctx, rf->blockptr, blocksize);
MD5_End(rf->blockmd5, &ctx);
rf->rsum = rsync_rollsum(rf->blockptr, blocksize);
snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum);
rf->blocknum++;
rf->blockptr += blocksize;
return (1);
}
/* Get the rolling checksum of a file. */
static uint32_t
rsync_rollsum(char *buf, size_t len)
{
uint32_t a, b;
char *ptr, *limit;
a = b = 0;
ptr = buf;
limit = buf + len;
while (ptr < limit) {
a += *ptr + CHAR_OFFSET;
b += a;
ptr++;
}
return ((b << 16) | a);
}
/* Get running sum so far. */
char *
rsync_rsum(struct rsyncfile *rf)
{
return (rf->rsumstr);
}
/* Get MD5 of current block. */
char *
rsync_blockmd5(struct rsyncfile *rf)
{
return (rf->blockmd5);
}
/* Accessor for blocksize. */
size_t
rsync_blocksize(struct rsyncfile *rf)
{
return (rf->blocksize);
}
/* Accessor for filesize. */
size_t
rsync_filesize(struct rsyncfile *rf)
{
return (rf->fsize);
}

41
contrib/csup/rsyncfile.h Normal file
View File

@ -0,0 +1,41 @@
/*-
* 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$
*/
#ifndef _RSYNCFILE_H_
#define _RSYNCFILE_H_
struct rsyncfile;
struct rsyncfile *rsync_open(char *, size_t, int);
int rsync_nextblock(struct rsyncfile *);
char *rsync_rsum(struct rsyncfile *);
char *rsync_blockmd5(struct rsyncfile *);
int rsync_close(struct rsyncfile *);
size_t rsync_blocksize(struct rsyncfile *);
size_t rsync_filesize(struct rsyncfile *);
#endif /* !_RSYNCFILE_H_ */

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)
@ -197,6 +206,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 +227,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 +308,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 +372,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[0] = ' ';
space[1] = '\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,9 @@ SRCS= attrstack.c \
parse.y \
pathcomp.c \
proto.c \
rcsfile.c \
rcsparse.c \
rsyncfile.c \
status.c \
stream.c \
threads.c \
@ -28,7 +32,7 @@ SRCS= attrstack.c \
CFLAGS+= -I. -I${.CURDIR}/../../contrib/csup
CFLAGS+= -DHAVE_FFLAGS -DNDEBUG
WARNS?= 6
WARNS?= 1
DPADD= ${LIBCRYPTO} ${LIBZ} ${LIBPTHREAD}
LDADD= -lcrypto -lz -lpthread