- Import csup work from p4.
This commit is contained in:
parent
fc84e5f31b
commit
3d4e599494
@ -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...
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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_ */
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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_ */
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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
2094
contrib/csup/lex.rcs.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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 @@ send:
|
||||
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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 *);
|
||||
|
@ -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);
|
||||
|
@ -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
1381
contrib/csup/rcsfile.c
Normal file
File diff suppressed because it is too large
Load Diff
73
contrib/csup/rcsfile.h
Normal file
73
contrib/csup/rcsfile.h
Normal 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
358
contrib/csup/rcsparse.c
Normal 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
42
contrib/csup/rcsparse.h
Normal 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
333
contrib/csup/rcstokenizer.h
Normal 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 */
|
73
contrib/csup/rcstokenizer.l
Normal file
73
contrib/csup/rcstokenizer.l
Normal 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]+ ;
|
||||
%%
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
@ -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 \
|
||||
|
Loading…
x
Reference in New Issue
Block a user