summaryrefslogtreecommitdiffstats
path: root/contrib/csup
diff options
context:
space:
mode:
authorlulf <lulf@FreeBSD.org>2008-10-19 09:08:59 +0000
committerlulf <lulf@FreeBSD.org>2008-10-19 09:08:59 +0000
commit811088f01868b1d89dec7a8a92b7d27d1395f55c (patch)
tree76e74988c8bcef8fa5297174a8219fc4055815b5 /contrib/csup
parent150ad2c538f0834132164fec5ed7ea55c88f2e51 (diff)
downloadFreeBSD-src-811088f01868b1d89dec7a8a92b7d27d1395f55c.zip
FreeBSD-src-811088f01868b1d89dec7a8a92b7d27d1395f55c.tar.gz
- Import csup work from p4.
Diffstat (limited to 'contrib/csup')
-rw-r--r--contrib/csup/Makefile5
-rw-r--r--contrib/csup/config.c4
-rw-r--r--contrib/csup/detailer.c275
-rw-r--r--contrib/csup/diff.c243
-rw-r--r--contrib/csup/diff.h4
-rw-r--r--contrib/csup/fattr.c46
-rw-r--r--contrib/csup/fattr.h3
-rw-r--r--contrib/csup/keyword.c23
-rw-r--r--contrib/csup/keyword.h1
-rw-r--r--contrib/csup/lex.rcs.c2094
-rw-r--r--contrib/csup/lister.c69
-rw-r--r--contrib/csup/misc.c156
-rw-r--r--contrib/csup/misc.h10
-rw-r--r--contrib/csup/mux.c1
-rw-r--r--contrib/csup/proto.c5
-rw-r--r--contrib/csup/rcsfile.c1381
-rw-r--r--contrib/csup/rcsfile.h73
-rw-r--r--contrib/csup/rcsparse.c358
-rw-r--r--contrib/csup/rcsparse.h42
-rw-r--r--contrib/csup/rcstokenizer.h333
-rw-r--r--contrib/csup/rcstokenizer.l73
-rw-r--r--contrib/csup/status.c33
-rw-r--r--contrib/csup/stream.c231
-rw-r--r--contrib/csup/stream.h14
-rw-r--r--contrib/csup/updater.c974
25 files changed, 6386 insertions, 65 deletions
diff --git a/contrib/csup/Makefile b/contrib/csup/Makefile
index b672c5d..3888396 100644
--- a/contrib/csup/Makefile
+++ b/contrib/csup/Makefile
@@ -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...
diff --git a/contrib/csup/config.c b/contrib/csup/config.c
index 5b71e74..3c5b83e 100644
--- a/contrib/csup/config.c
+++ b/contrib/csup/config.c
@@ -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) {
diff --git a/contrib/csup/detailer.c b/contrib/csup/detailer.c
index bf2ddb7..a07fe8b 100644
--- a/contrib/csup/detailer.c
+++ b/contrib/csup/detailer.c
@@ -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);
+ }
+ 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, "D %s\n", file);
+ 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_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(struct detailer *d, struct coll *coll, struct status *st,
+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);
+}
diff --git a/contrib/csup/diff.c b/contrib/csup/diff.c
index ea53c36..f4d73fa 100644
--- a/contrib/csup/diff.c
+++ b/contrib/csup/diff.c
@@ -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)
diff --git a/contrib/csup/diff.h b/contrib/csup/diff.h
index cbd9e50..b0c8c97 100644
--- a/contrib/csup/diff.h
+++ b/contrib/csup/diff.h
@@ -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_ */
diff --git a/contrib/csup/fattr.c b/contrib/csup/fattr.c
index b29d73d..53df588 100644
--- a/contrib/csup/fattr.c
+++ b/contrib/csup/fattr.c
@@ -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);
+}
diff --git a/contrib/csup/fattr.h b/contrib/csup/fattr.h
index 6015fbb..bd4e649 100644
--- a/contrib/csup/fattr.h
+++ b/contrib/csup/fattr.h
@@ -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_ */
diff --git a/contrib/csup/keyword.c b/contrib/csup/keyword.c
index dab44f0..049a011 100644
--- a/contrib/csup/keyword.c
+++ b/contrib/csup/keyword.c
@@ -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)
{
diff --git a/contrib/csup/keyword.h b/contrib/csup/keyword.h
index 3f152c1..033cb0a 100644
--- a/contrib/csup/keyword.h
+++ b/contrib/csup/keyword.h
@@ -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 *);
diff --git a/contrib/csup/lex.rcs.c b/contrib/csup/lex.rcs.c
new file mode 100644
index 0000000..5db7a7b
--- /dev/null
+++ b/contrib/csup/lex.rcs.c
@@ -0,0 +1,2094 @@
+
+#line 3 "lex.rcs.c"
+
+#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
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an unsigned
+ * integer for use as an array index. If the signed char is negative,
+ * we want to instead treat it as an 8-bit unsigned char, hence the
+ * double cast.
+ */
+#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c)
+
+/* 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
+
+/* Enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN yyg->yy_start = 1 + 2 *
+
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state. The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START ((yyg->yy_start - 1) / 2)
+#define YYSTATE YY_START
+
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE rcsrestart(yyin ,yyscanner )
+
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE 16384
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+ #define YY_LESS_LINENO(n)
+
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ *yy_cp = yyg->yy_hold_char; \
+ YY_RESTORE_YY_MORE_OFFSET \
+ yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+ YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner )
+
+#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;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+ /* When an EOF's been seen but there's still some text to process
+ * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+ * shouldn't try reading from the input source any more. We might
+ * still have a bunch of tokens to match, though, because of
+ * possible backing-up.
+ *
+ * When we actually see the EOF, we change the status to "new"
+ * (via rcsrestart()), so that the user can continue scanning by
+ * just pointing yyin at a new input file.
+ */
+#define YY_BUFFER_EOF_PENDING 2
+
+ };
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \
+ ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \
+ : NULL)
+
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top]
+
+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 );
+
+static void rcsensure_buffer_stack (yyscan_t yyscanner );
+static void rcs_load_buffer_state (yyscan_t yyscanner );
+static void rcs_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner );
+
+#define YY_FLUSH_BUFFER rcs_flush_buffer(YY_CURRENT_BUFFER ,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 );
+
+#define yy_new_buffer rcs_create_buffer
+
+#define yy_set_interactive(is_interactive) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){ \
+ rcsensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+ }
+
+#define yy_set_bol(at_bol) \
+ { \
+ if ( ! YY_CURRENT_BUFFER ){\
+ rcsensure_buffer_stack (yyscanner); \
+ YY_CURRENT_BUFFER_LVALUE = \
+ rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \
+ } \
+ YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+ }
+
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define rcswrap(n) 1
+#define YY_SKIP_YYWRAP
+
+typedef unsigned char YY_CHAR;
+
+typedef int yy_state_type;
+
+#define yytext_ptr yytext_r
+
+static yy_state_type yy_get_previous_state (yyscan_t yyscanner );
+static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner);
+static int yy_get_next_buffer (yyscan_t yyscanner );
+static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+ yyg->yytext_ptr = yy_bp; \
+ yyleng = (size_t) (yy_cp - yy_bp); \
+ yyg->yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yyg->yy_c_buf_p = yy_cp;
+
+#define YY_NUM_RULES 10
+#define YY_END_OF_BUFFER 11
+/* This struct is not used in this scanner,
+ but its presence is necessary. */
+struct yy_trans_info
+ {
+ flex_int32_t yy_verify;
+ flex_int32_t yy_nxt;
+ };
+static yyconst flex_int16_t yy_accept[89] =
+ { 0,
+ 0, 0, 11, 5, 9, 8, 10, 4, 4, 7,
+ 6, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 9, 5, 4, 4, 5,
+ 4, 4, 5, 0, 0, 5, 5, 3, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 3, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 2, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+ 5, 5, 5, 1, 5, 5, 5, 0
+ } ;
+
+static yyconst flex_int32_t yy_ec[256] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 2, 3,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 2, 1, 1, 1, 4, 1, 1, 1, 1,
+ 1, 1, 1, 4, 1, 5, 1, 6, 6, 6,
+ 6, 6, 6, 6, 6, 6, 6, 7, 8, 1,
+ 1, 1, 1, 9, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 10, 11, 12, 13,
+
+ 14, 1, 15, 16, 17, 1, 18, 19, 20, 21,
+ 22, 23, 1, 24, 25, 26, 27, 1, 1, 28,
+ 29, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int32_t yy_meta[30] =
+ { 0,
+ 1, 2, 2, 2, 1, 1, 2, 2, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1
+ } ;
+
+static yyconst flex_int16_t yy_base[94] =
+ { 0,
+ 0, 0, 136, 25, 127, 297, 297, 27, 29, 297,
+ 297, 34, 39, 41, 47, 44, 50, 54, 57, 66,
+ 68, 70, 76, 80, 82, 91, 84, 86, 90, 93,
+ 95, 97, 102, 58, 61, 0, 0, 107, 109, 112,
+ 114, 117, 120, 122, 125, 129, 137, 127, 135, 145,
+ 148, 74, 152, 155, 157, 162, 167, 174, 164, 178,
+ 182, 184, 187, 189, 191, 193, 196, 200, 204, 206,
+ 214, 211, 218, 224, 228, 230, 236, 239, 241, 243,
+ 245, 248, 250, 254, 260, 265, 267, 297, 76, 56,
+ 47, 292, 294
+
+ } ;
+
+static yyconst flex_int16_t yy_def[94] =
+ { 0,
+ 88, 1, 88, 89, 88, 88, 88, 90, 91, 88,
+ 88, 92, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 88, 89, 90, 91, 89,
+ 91, 91, 92, 93, 93, 33, 33, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 88, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 89, 89, 89,
+ 89, 89, 89, 89, 89, 89, 89, 0, 88, 88,
+ 88, 88, 88
+
+ } ;
+
+static yyconst flex_int16_t yy_nxt[327] =
+ { 0,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 4, 18, 4, 4, 19, 4,
+ 20, 4, 4, 4, 21, 22, 4, 4, 4, 24,
+ 25, 28, 29, 31, 32, 34, 35, 34, 36, 37,
+ 34, 34, 38, 24, 25, 24, 25, 30, 24, 25,
+ 39, 24, 25, 43, 24, 25, 27, 44, 24, 25,
+ 35, 24, 25, 35, 41, 40, 52, 46, 42, 52,
+ 24, 25, 24, 25, 24, 25, 23, 45, 47, 48,
+ 24, 25, 34, 51, 24, 25, 24, 25, 24, 25,
+ 28, 29, 26, 49, 31, 32, 50, 24, 25, 31,
+
+ 32, 31, 32, 34, 35, 34, 36, 37, 34, 34,
+ 38, 24, 25, 24, 25, 33, 24, 25, 24, 25,
+ 53, 24, 25, 55, 24, 25, 24, 25, 26, 24,
+ 25, 24, 25, 24, 25, 88, 56, 54, 60, 24,
+ 25, 24, 25, 88, 64, 57, 58, 59, 61, 24,
+ 25, 62, 24, 25, 63, 88, 24, 25, 65, 24,
+ 25, 24, 25, 88, 66, 68, 24, 25, 24, 25,
+ 69, 24, 25, 72, 88, 67, 88, 70, 24, 25,
+ 62, 71, 24, 25, 88, 62, 24, 25, 24, 25,
+ 62, 24, 25, 24, 25, 24, 25, 24, 25, 73,
+
+ 24, 25, 88, 76, 24, 25, 88, 75, 24, 25,
+ 24, 25, 62, 88, 74, 24, 25, 79, 24, 25,
+ 88, 62, 24, 25, 77, 78, 88, 80, 24, 25,
+ 88, 81, 24, 25, 24, 25, 88, 62, 88, 82,
+ 24, 25, 62, 24, 25, 24, 25, 24, 25, 24,
+ 25, 83, 24, 25, 24, 25, 84, 62, 24, 25,
+ 62, 88, 62, 85, 24, 25, 88, 87, 86, 24,
+ 25, 24, 25, 62, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 62, 88, 88, 88, 62,
+ 88, 62, 33, 33, 34, 34, 3, 88, 88, 88,
+
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88
+ } ;
+
+static yyconst flex_int16_t yy_chk[327] =
+ { 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 4,
+ 4, 8, 8, 9, 9, 12, 12, 12, 12, 12,
+ 12, 12, 12, 13, 13, 14, 14, 91, 16, 16,
+ 13, 15, 15, 16, 17, 17, 90, 16, 18, 18,
+ 34, 19, 19, 35, 14, 13, 34, 18, 15, 35,
+ 20, 20, 21, 21, 22, 22, 89, 17, 19, 20,
+ 23, 23, 52, 22, 24, 24, 25, 25, 27, 27,
+ 28, 28, 26, 21, 29, 29, 21, 30, 30, 31,
+
+ 31, 32, 32, 33, 33, 33, 33, 33, 33, 33,
+ 33, 38, 38, 39, 39, 38, 40, 40, 41, 41,
+ 39, 42, 42, 41, 43, 43, 44, 44, 5, 45,
+ 45, 48, 48, 46, 46, 3, 42, 40, 46, 49,
+ 49, 47, 47, 0, 49, 43, 44, 45, 47, 50,
+ 50, 47, 51, 51, 48, 0, 53, 53, 49, 54,
+ 54, 55, 55, 0, 50, 53, 56, 56, 59, 59,
+ 54, 57, 57, 59, 0, 51, 0, 55, 58, 58,
+ 57, 56, 60, 60, 0, 58, 61, 61, 62, 62,
+ 60, 63, 63, 64, 64, 65, 65, 66, 66, 61,
+
+ 67, 67, 0, 66, 68, 68, 0, 65, 69, 69,
+ 70, 70, 63, 0, 64, 72, 72, 70, 71, 71,
+ 0, 67, 73, 73, 68, 69, 0, 71, 74, 74,
+ 0, 72, 75, 75, 76, 76, 0, 74, 0, 75,
+ 77, 77, 73, 78, 78, 79, 79, 80, 80, 81,
+ 81, 76, 82, 82, 83, 83, 79, 81, 84, 84,
+ 77, 0, 78, 80, 85, 85, 0, 84, 83, 86,
+ 86, 87, 87, 82, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 85, 0, 0, 0, 86,
+ 0, 87, 92, 92, 93, 93, 88, 88, 88, 88,
+
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88, 88, 88, 88, 88,
+ 88, 88, 88, 88, 88, 88
+ } ;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+#line 1 "rcstokenizer.l"
+/*-
+ * 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.
+ */
+#line 34 "rcstokenizer.l"
+#include <string.h>
+#include "misc.h"
+#include "rcsparse.h"
+
+#line 567 "lex.rcs.c"
+
+#define INITIAL 0
+
+#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
+
+/* Holds the entire state of the reentrant scanner. */
+struct yyguts_t
+ {
+
+ /* User-defined. Not touched by flex. */
+ YY_EXTRA_TYPE yyextra_r;
+
+ /* The rest are the same as the globals declared in the non-reentrant scanner. */
+ FILE *yyin_r, *yyout_r;
+ size_t yy_buffer_stack_top; /**< index of top of stack. */
+ size_t yy_buffer_stack_max; /**< capacity of stack. */
+ YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */
+ char yy_hold_char;
+ int yy_n_chars;
+ int yyleng_r;
+ char *yy_c_buf_p;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+ int yy_start_stack_ptr;
+ int yy_start_stack_depth;
+ int *yy_start_stack;
+ yy_state_type yy_last_accepting_state;
+ char* yy_last_accepting_cpos;
+
+ int yylineno_r;
+ int yy_flex_debug_r;
+
+ char *yytext_r;
+ int yy_more_flag;
+ int yy_more_len;
+
+ }; /* end struct yyguts_t */
+
+static int yy_init_globals (yyscan_t yyscanner );
+
+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
+
+ static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner);
+
+#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
+
+#ifdef __cplusplus
+static int yyinput (yyscan_t yyscanner );
+#else
+static int input (yyscan_t yyscanner );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO fwrite( yytext, yyleng, 1, yyout )
+#endif
+
+/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \
+ { \
+ int c = '*'; \
+ int n; \
+ for ( n = 0; n < max_size && \
+ (c = getc( yyin )) != EOF && c != '\n'; ++n ) \
+ buf[n] = (char) c; \
+ if ( c == '\n' ) \
+ buf[n++] = (char) c; \
+ if ( c == EOF && ferror( yyin ) ) \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ result = n; \
+ } \
+ else \
+ { \
+ errno=0; \
+ while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \
+ { \
+ if( errno != EINTR) \
+ { \
+ YY_FATAL_ERROR( "input in flex scanner failed" ); \
+ break; \
+ } \
+ errno=0; \
+ clearerr(yyin); \
+ } \
+ }\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner)
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* 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 */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK break;
+#endif
+
+#define YY_RULE_SETUP \
+ YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp, *yy_bp;
+ register int yy_act;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+#line 51 "rcstokenizer.l"
+
+
+#line 791 "lex.rcs.c"
+
+ if ( !yyg->yy_init )
+ {
+ yyg->yy_init = 1;
+
+#ifdef YY_USER_INIT
+ YY_USER_INIT;
+#endif
+
+ if ( ! yyg->yy_start )
+ yyg->yy_start = 1; /* first start state */
+
+ if ( ! yyin )
+ yyin = stdin;
+
+ if ( ! yyout )
+ yyout = stdout;
+
+ if ( ! YY_CURRENT_BUFFER ) {
+ rcsensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ rcs_load_buffer_state(yyscanner );
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* Support of yytext. */
+ *yy_cp = yyg->yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of
+ * the current run.
+ */
+ yy_bp = yy_cp;
+
+ yy_current_state = yyg->yy_start;
+yy_match:
+ do
+ {
+ register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)];
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 89 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ ++yy_cp;
+ }
+ while ( yy_base[yy_current_state] != 297 );
+
+yy_find_action:
+ yy_act = yy_accept[yy_current_state];
+ if ( yy_act == 0 )
+ { /* have to back up */
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ yy_act = yy_accept[yy_current_state];
+ }
+
+ YY_DO_BEFORE_ACTION;
+
+do_action: /* This label is used only to access EOF actions. */
+
+ switch ( yy_act )
+ { /* beginning of action switch */
+ case 0: /* must back up */
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yyg->yy_hold_char;
+ yy_cp = yyg->yy_last_accepting_cpos;
+ yy_current_state = yyg->yy_last_accepting_state;
+ goto yy_find_action;
+
+case 1:
+YY_RULE_SETUP
+#line 53 "rcstokenizer.l"
+{
+ return (KEYWORD_TWO);
+}
+ YY_BREAK
+case 2:
+YY_RULE_SETUP
+#line 56 "rcstokenizer.l"
+{
+ return (KEYWORD);
+}
+ YY_BREAK
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+#line 59 "rcstokenizer.l"
+{
+ return (STRING);
+}
+ YY_BREAK
+case 4:
+YY_RULE_SETUP
+#line 62 "rcstokenizer.l"
+{
+ return (NUM);
+}
+ YY_BREAK
+case 5:
+YY_RULE_SETUP
+#line 65 "rcstokenizer.l"
+{
+/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/
+ return (ID);
+}
+ YY_BREAK
+case 6:
+YY_RULE_SETUP
+#line 69 "rcstokenizer.l"
+{ return (SEMIC); }
+ YY_BREAK
+case 7:
+YY_RULE_SETUP
+#line 70 "rcstokenizer.l"
+{ return (COLON); }
+ YY_BREAK
+case 8:
+/* rule 8 can match eol */
+YY_RULE_SETUP
+#line 71 "rcstokenizer.l"
+;
+ YY_BREAK
+case 9:
+YY_RULE_SETUP
+#line 72 "rcstokenizer.l"
+;
+ YY_BREAK
+case 10:
+YY_RULE_SETUP
+#line 73 "rcstokenizer.l"
+ECHO;
+ YY_BREAK
+#line 937 "lex.rcs.c"
+case YY_STATE_EOF(INITIAL):
+ yyterminate();
+
+ case YY_END_OF_BUFFER:
+ {
+ /* Amount of text matched not including the EOB char. */
+ int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1;
+
+ /* Undo the effects of YY_DO_BEFORE_ACTION. */
+ *yy_cp = yyg->yy_hold_char;
+ YY_RESTORE_YY_MORE_OFFSET
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+ {
+ /* We're scanning a new file or input source. It's
+ * possible that this happened because the user
+ * just pointed yyin at a new source and called
+ * rcslex(). If so, then we have to assure
+ * consistency between YY_CURRENT_BUFFER and our
+ * globals. Here is the right place to do so, because
+ * this is the first action (other than possibly a
+ * back-up) that will match for the new input source.
+ */
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+ }
+
+ /* Note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the
+ * end-of-buffer state). Contrast this with the test
+ * in input().
+ */
+ if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ { /* This was really a NUL. */
+ yy_state_type yy_next_state;
+
+ yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ /* Okay, we're now positioned to make the NUL
+ * transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we don't
+ * want to build jamming into it because then it
+ * will run more slowly).
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner);
+
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* Consume the NUL. */
+ yy_cp = ++yyg->yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+
+ else
+ {
+ yy_cp = yyg->yy_c_buf_p;
+ goto yy_find_action;
+ }
+ }
+
+ else switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yyg->yy_did_buffer_switch_on_eof = 0;
+
+ if ( rcswrap(yyscanner ) )
+ {
+ /* Note: because we've taken care in
+ * yy_get_next_buffer() to have set up
+ * yytext, we can now set up
+ * yy_c_buf_p so that if some total
+ * hoser (like flex itself) wants to
+ * call the scanner after we return the
+ * YY_NULL, it'll still work - another
+ * YY_NULL will get returned.
+ */
+ yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF(YY_START);
+ goto do_action;
+ }
+
+ else
+ {
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ break;
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p =
+ yyg->yytext_ptr + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yyg->yy_c_buf_p =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars];
+
+ yy_current_state = yy_get_previous_state( yyscanner );
+
+ yy_cp = yyg->yy_c_buf_p;
+ yy_bp = yyg->yytext_ptr + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--no action found" );
+ } /* end of action switch */
+ } /* end of scanning one token */
+} /* end of rcslex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+ register char *source = yyg->yytext_ptr;
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] )
+ YY_FATAL_ERROR(
+ "fatal flex scanner internal error--end of buffer missed" );
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+ { /* Don't try to fill the buffer, so this is an EOF. */
+ if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 )
+ {
+ /* We matched a single character, the EOB, so
+ * treat this as a final EOF.
+ */
+ return EOB_ACT_END_OF_FILE;
+ }
+
+ else
+ {
+ /* We matched some text prior to the EOB, first
+ * process it.
+ */
+ return EOB_ACT_LAST_MATCH;
+ }
+ }
+
+ /* Try to read more data. */
+
+ /* First move last chars to start of buffer. */
+ number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read =
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+ while ( num_to_read <= 0 )
+ { /* Not enough room in the buffer - grow it. */
+
+ /* just a shorter name for the current buffer */
+ YY_BUFFER_STATE b = YY_CURRENT_BUFFER;
+
+ int yy_c_buf_p_offset =
+ (int) (yyg->yy_c_buf_p - b->yy_ch_buf);
+
+ if ( b->yy_is_our_buffer )
+ {
+ int new_size = b->yy_buf_size * 2;
+
+ if ( new_size <= 0 )
+ b->yy_buf_size += b->yy_buf_size / 8;
+ else
+ b->yy_buf_size *= 2;
+
+ b->yy_ch_buf = (char *)
+ /* Include room in for 2 EOB chars. */
+ rcsrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner );
+ }
+ else
+ /* Can't grow it, we don't own it. */
+ b->yy_ch_buf = 0;
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR(
+ "fatal error - scanner input buffer overflow" );
+
+ yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+ num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+ number_to_move - 1;
+
+ }
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ /* Read in more data. */
+ YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+ yyg->yy_n_chars, (size_t) num_to_read );
+
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ if ( yyg->yy_n_chars == 0 )
+ {
+ if ( number_to_move == YY_MORE_ADJ )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ rcsrestart(yyin ,yyscanner);
+ }
+
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+ YY_BUFFER_EOF_PENDING;
+ }
+ }
+
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+ /* Extend the array by 50%, plus the number we really need. */
+ yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1);
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) rcsrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner );
+ if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+ }
+
+ yyg->yy_n_chars += number_to_move;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+ return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+ static yy_state_type yy_get_previous_state (yyscan_t yyscanner)
+{
+ register yy_state_type yy_current_state;
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_current_state = yyg->yy_start;
+
+ for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp )
+ {
+ register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1);
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 89 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ }
+
+ return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+ static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner)
+{
+ register int yy_is_jam;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */
+ register char *yy_cp = yyg->yy_c_buf_p;
+
+ register YY_CHAR yy_c = 1;
+ if ( yy_accept[yy_current_state] )
+ {
+ yyg->yy_last_accepting_state = yy_current_state;
+ yyg->yy_last_accepting_cpos = yy_cp;
+ }
+ while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state )
+ {
+ yy_current_state = (int) yy_def[yy_current_state];
+ if ( yy_current_state >= 89 )
+ yy_c = yy_meta[(unsigned int) yy_c];
+ }
+ yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c];
+ yy_is_jam = (yy_current_state == 88);
+
+ return yy_is_jam ? 0 : yy_current_state;
+}
+
+ static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner)
+{
+ register char *yy_cp;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ yy_cp = yyg->yy_c_buf_p;
+
+ /* undo effects of setting up yytext */
+ *yy_cp = yyg->yy_hold_char;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ /* +2 for EOB chars. */
+ register int number_to_move = yyg->yy_n_chars + 2;
+ register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+ register char *source =
+ &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+ while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += (int) (dest - source);
+ yy_bp += (int) (dest - source);
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+ if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ *--yy_cp = (char) c;
+
+ yyg->yytext_ptr = yy_bp;
+ yyg->yy_hold_char = *yy_cp;
+ yyg->yy_c_buf_p = yy_cp;
+}
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+ static int yyinput (yyscan_t yyscanner)
+#else
+ static int input (yyscan_t yyscanner)
+#endif
+
+{
+ int c;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+
+ if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] )
+ /* This was really a NUL. */
+ *yyg->yy_c_buf_p = '\0';
+
+ else
+ { /* need more input */
+ int offset = yyg->yy_c_buf_p - yyg->yytext_ptr;
+ ++yyg->yy_c_buf_p;
+
+ switch ( yy_get_next_buffer( yyscanner ) )
+ {
+ case EOB_ACT_LAST_MATCH:
+ /* This happens because yy_g_n_b()
+ * sees that we've accumulated a
+ * token and flags that we need to
+ * try matching the token before
+ * proceeding. But for input(),
+ * there's no matching to consider.
+ * So convert the EOB_ACT_LAST_MATCH
+ * to EOB_ACT_END_OF_FILE.
+ */
+
+ /* Reset buffer status. */
+ rcsrestart(yyin ,yyscanner);
+
+ /*FALLTHROUGH*/
+
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( rcswrap(yyscanner ) )
+ return EOF;
+
+ if ( ! yyg->yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+#ifdef __cplusplus
+ return yyinput(yyscanner);
+#else
+ return input(yyscanner);
+#endif
+ }
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yyg->yy_c_buf_p = yyg->yytext_ptr + offset;
+ break;
+ }
+ }
+ }
+
+ c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */
+ *yyg->yy_c_buf_p = '\0'; /* preserve yytext */
+ yyg->yy_hold_char = *++yyg->yy_c_buf_p;
+
+ return c;
+}
+#endif /* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * @param yyscanner The scanner object.
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+ void rcsrestart (FILE * input_file , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! YY_CURRENT_BUFFER ){
+ rcsensure_buffer_stack (yyscanner);
+ YY_CURRENT_BUFFER_LVALUE =
+ rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner);
+ }
+
+ rcs_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner);
+ rcs_load_buffer_state(yyscanner );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * @param yyscanner The scanner object.
+ */
+ void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* TODO. We should be able to replace this entire function body
+ * with
+ * rcspop_buffer_state();
+ * rcspush_buffer_state(new_buffer);
+ */
+ rcsensure_buffer_stack (yyscanner);
+ if ( YY_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+ rcs_load_buffer_state(yyscanner );
+
+ /* We don't actually know whether we did this switch during
+ * EOF (rcswrap()) processing, but the only time this flag
+ * is looked at is after rcswrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+static void rcs_load_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+ yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+ yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+ yyg->yy_hold_char = *yyg->yy_c_buf_p;
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * @param yyscanner The scanner object.
+ * @return the allocated buffer state.
+ */
+ YY_BUFFER_STATE rcs_create_buffer (FILE * file, int size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) rcsalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in rcs_create_buffer()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (char *) rcsalloc(b->yy_buf_size + 2 ,yyscanner );
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in rcs_create_buffer()" );
+
+ b->yy_is_our_buffer = 1;
+
+ rcs_init_buffer(b,file ,yyscanner);
+
+ return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with rcs_create_buffer()
+ * @param yyscanner The scanner object.
+ */
+ void rcs_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if ( ! b )
+ return;
+
+ if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+ YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+ if ( b->yy_is_our_buffer )
+ rcsfree((void *) b->yy_ch_buf ,yyscanner );
+
+ rcsfree((void *) b ,yyscanner );
+}
+
+#ifndef __cplusplus
+extern int isatty (int );
+#endif /* __cplusplus */
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a rcsrestart() or at EOF.
+ */
+ static void rcs_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner)
+
+{
+ int oerrno = errno;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ rcs_flush_buffer(b ,yyscanner);
+
+ b->yy_input_file = file;
+ b->yy_fill_buffer = 1;
+
+ /* If b is the current buffer, then rcs_init_buffer was _probably_
+ * called from rcsrestart() or through yy_get_next_buffer.
+ * In that case, we don't want to reset the lineno or column.
+ */
+ if (b != YY_CURRENT_BUFFER){
+ b->yy_bs_lineno = 1;
+ b->yy_bs_column = 0;
+ }
+
+ b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0;
+
+ errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * @param yyscanner The scanner object.
+ */
+ void rcs_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if ( ! b )
+ return;
+
+ b->yy_n_chars = 0;
+
+ /* We always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[0];
+
+ b->yy_at_bol = 1;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ if ( b == YY_CURRENT_BUFFER )
+ rcs_load_buffer_state(yyscanner );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ * the current state. This function will allocate the stack
+ * if necessary.
+ * @param new_buffer The new state.
+ * @param yyscanner The scanner object.
+ */
+void rcspush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (new_buffer == NULL)
+ return;
+
+ rcsensure_buffer_stack(yyscanner);
+
+ /* This block is copied from rcs_switch_to_buffer. */
+ if ( YY_CURRENT_BUFFER )
+ {
+ /* Flush out information for old buffer. */
+ *yyg->yy_c_buf_p = yyg->yy_hold_char;
+ YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p;
+ YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars;
+ }
+
+ /* Only push if top exists. Otherwise, replace top. */
+ if (YY_CURRENT_BUFFER)
+ yyg->yy_buffer_stack_top++;
+ YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+ /* copied from rcs_switch_to_buffer. */
+ rcs_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ * The next element becomes the new top.
+ * @param yyscanner The scanner object.
+ */
+void rcspop_buffer_state (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ if (!YY_CURRENT_BUFFER)
+ return;
+
+ rcs_delete_buffer(YY_CURRENT_BUFFER ,yyscanner);
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ if (yyg->yy_buffer_stack_top > 0)
+ --yyg->yy_buffer_stack_top;
+
+ if (YY_CURRENT_BUFFER) {
+ rcs_load_buffer_state(yyscanner );
+ yyg->yy_did_buffer_switch_on_eof = 1;
+ }
+}
+
+/* Allocates the stack if it does not exist.
+ * Guarantees space for at least one push.
+ */
+static void rcsensure_buffer_stack (yyscan_t yyscanner)
+{
+ int num_to_alloc;
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (!yyg->yy_buffer_stack) {
+
+ /* First allocation is just for 2 elements, since we don't know if this
+ * scanner will even need a stack. We use 2 instead of 1 to avoid an
+ * immediate realloc on the next call.
+ */
+ num_to_alloc = 1;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)rcsalloc
+ (num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in rcsensure_buffer_stack()" );
+
+ memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ yyg->yy_buffer_stack_top = 0;
+ return;
+ }
+
+ if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){
+
+ /* Increase the buffer to prepare for a possible push. */
+ int grow_size = 8 /* arbitrary grow size */;
+
+ num_to_alloc = yyg->yy_buffer_stack_max + grow_size;
+ yyg->yy_buffer_stack = (struct yy_buffer_state**)rcsrealloc
+ (yyg->yy_buffer_stack,
+ num_to_alloc * sizeof(struct yy_buffer_state*)
+ , yyscanner);
+ if ( ! yyg->yy_buffer_stack )
+ YY_FATAL_ERROR( "out of dynamic memory in rcsensure_buffer_stack()" );
+
+ /* zero only the new slots.*/
+ memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*));
+ yyg->yy_buffer_stack_max = num_to_alloc;
+ }
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE rcs_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+
+ if ( size < 2 ||
+ base[size-2] != YY_END_OF_BUFFER_CHAR ||
+ base[size-1] != YY_END_OF_BUFFER_CHAR )
+ /* They forgot to leave room for the EOB's. */
+ return 0;
+
+ b = (YY_BUFFER_STATE) rcsalloc(sizeof( struct yy_buffer_state ) ,yyscanner );
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in rcs_scan_buffer()" );
+
+ b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */
+ b->yy_buf_pos = b->yy_ch_buf = base;
+ b->yy_is_our_buffer = 0;
+ b->yy_input_file = 0;
+ b->yy_n_chars = b->yy_buf_size;
+ b->yy_is_interactive = 0;
+ b->yy_at_bol = 1;
+ b->yy_fill_buffer = 0;
+ b->yy_buffer_status = YY_BUFFER_NEW;
+
+ rcs_switch_to_buffer(b ,yyscanner );
+
+ return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to rcslex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ * rcs_scan_bytes() instead.
+ */
+YY_BUFFER_STATE rcs_scan_string (yyconst char * yystr , yyscan_t yyscanner)
+{
+
+ return rcs_scan_bytes(yystr,strlen(yystr) ,yyscanner);
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to rcslex() will
+ * scan from a @e copy of @a bytes.
+ * @param bytes the byte buffer to scan
+ * @param len the number of bytes in the buffer pointed to by @a bytes.
+ * @param yyscanner The scanner object.
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE rcs_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner)
+{
+ YY_BUFFER_STATE b;
+ char *buf;
+ yy_size_t n;
+ int i;
+
+ /* Get memory for full buffer, including space for trailing EOB's. */
+ n = _yybytes_len + 2;
+ buf = (char *) rcsalloc(n ,yyscanner );
+ if ( ! buf )
+ YY_FATAL_ERROR( "out of dynamic memory in rcs_scan_bytes()" );
+
+ for ( i = 0; i < _yybytes_len; ++i )
+ buf[i] = yybytes[i];
+
+ buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+ b = rcs_scan_buffer(buf,n ,yyscanner);
+ if ( ! b )
+ YY_FATAL_ERROR( "bad buffer in rcs_scan_bytes()" );
+
+ /* It's okay to grow etc. this buffer, and we should throw it
+ * away when we're done.
+ */
+ b->yy_is_our_buffer = 1;
+
+ return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner)
+{
+ (void) fprintf( stderr, "%s\n", msg );
+ exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+ do \
+ { \
+ /* Undo effects of setting up yytext. */ \
+ int yyless_macro_arg = (n); \
+ YY_LESS_LINENO(yyless_macro_arg);\
+ yytext[yyleng] = yyg->yy_hold_char; \
+ yyg->yy_c_buf_p = yytext + yyless_macro_arg; \
+ yyg->yy_hold_char = *yyg->yy_c_buf_p; \
+ *yyg->yy_c_buf_p = '\0'; \
+ yyleng = yyless_macro_arg; \
+ } \
+ while ( 0 )
+
+/* Accessor methods (get/set functions) to struct members. */
+
+/** Get the user-defined data for this scanner.
+ * @param yyscanner The scanner object.
+ */
+YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyextra;
+}
+
+/** Get the current line number.
+ * @param yyscanner The scanner object.
+ */
+int rcsget_lineno (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yylineno;
+}
+
+/** Get the current column number.
+ * @param yyscanner The scanner object.
+ */
+int rcsget_column (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ if (! YY_CURRENT_BUFFER)
+ return 0;
+
+ return yycolumn;
+}
+
+/** Get the input stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *rcsget_in (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyin;
+}
+
+/** Get the output stream.
+ * @param yyscanner The scanner object.
+ */
+FILE *rcsget_out (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyout;
+}
+
+/** Get the length of the current token.
+ * @param yyscanner The scanner object.
+ */
+int rcsget_leng (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yyleng;
+}
+
+/** Get the current token.
+ * @param yyscanner The scanner object.
+ */
+
+char *rcsget_text (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yytext;
+}
+
+/** Set the user-defined data. This data is never touched by the scanner.
+ * @param user_defined The data to be associated with this scanner.
+ * @param yyscanner The scanner object.
+ */
+void rcsset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyextra = user_defined ;
+}
+
+/** Set the current line number.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void rcsset_lineno (int line_number , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* lineno is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "rcsset_lineno called with no buffer" , yyscanner);
+
+ yylineno = line_number;
+}
+
+/** Set the current column.
+ * @param line_number
+ * @param yyscanner The scanner object.
+ */
+void rcsset_column (int column_no , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* column is only valid if an input buffer exists. */
+ if (! YY_CURRENT_BUFFER )
+ yy_fatal_error( "rcsset_column called with no buffer" , yyscanner);
+
+ yycolumn = column_no;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param in_str A readable stream.
+ * @param yyscanner The scanner object.
+ * @see rcs_switch_to_buffer
+ */
+void rcsset_in (FILE * in_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyin = in_str ;
+}
+
+void rcsset_out (FILE * out_str , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yyout = out_str ;
+}
+
+int rcsget_debug (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ return yy_flex_debug;
+}
+
+void rcsset_debug (int bdebug , yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ yy_flex_debug = bdebug ;
+}
+
+/* Accessor methods for yylval and yylloc */
+
+/* User-visible API */
+
+/* rcslex_init is special because it creates the scanner itself, so it is
+ * the ONLY reentrant function that doesn't take the scanner as the last argument.
+ * That's why we explicitly handle the declaration, instead of using our macros.
+ */
+
+int rcslex_init(yyscan_t* ptr_yy_globals)
+
+{
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) rcsalloc ( sizeof( struct yyguts_t ), NULL );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+/* rcslex_init_extra has the same functionality as rcslex_init, but follows the
+ * convention of taking the scanner as the last argument. Note however, that
+ * this is a *pointer* to a scanner, as it will be allocated by this call (and
+ * is the reason, too, why this function also must handle its own declaration).
+ * The user defined value in the first argument will be available to rcsalloc in
+ * the yyextra field.
+ */
+
+int rcslex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals )
+
+{
+ struct yyguts_t dummy_yyguts;
+
+ rcsset_extra (yy_user_defined, &dummy_yyguts);
+
+ if (ptr_yy_globals == NULL){
+ errno = EINVAL;
+ return 1;
+ }
+
+ *ptr_yy_globals = (yyscan_t) rcsalloc ( sizeof( struct yyguts_t ), &dummy_yyguts );
+
+ if (*ptr_yy_globals == NULL){
+ errno = ENOMEM;
+ return 1;
+ }
+
+ /* By setting to 0xAA, we expose bugs in
+ yy_init_globals. Leave at 0x00 for releases. */
+ memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t));
+
+ rcsset_extra (yy_user_defined, *ptr_yy_globals);
+
+ return yy_init_globals ( *ptr_yy_globals );
+}
+
+static int yy_init_globals (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+ /* Initialization is the same as for the non-reentrant scanner.
+ * This function is called from rcslex_destroy(), so don't allocate here.
+ */
+
+ yyg->yy_buffer_stack = 0;
+ yyg->yy_buffer_stack_top = 0;
+ yyg->yy_buffer_stack_max = 0;
+ yyg->yy_c_buf_p = (char *) 0;
+ yyg->yy_init = 0;
+ yyg->yy_start = 0;
+
+ yyg->yy_start_stack_ptr = 0;
+ yyg->yy_start_stack_depth = 0;
+ yyg->yy_start_stack = NULL;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+ yyin = stdin;
+ yyout = stdout;
+#else
+ yyin = (FILE *) 0;
+ yyout = (FILE *) 0;
+#endif
+
+ /* For future reference: Set errno on error, since we are called by
+ * rcslex_init()
+ */
+ return 0;
+}
+
+/* rcslex_destroy is for both reentrant and non-reentrant scanners. */
+int rcslex_destroy (yyscan_t yyscanner)
+{
+ struct yyguts_t * yyg = (struct yyguts_t*)yyscanner;
+
+ /* Pop the buffer stack, destroying each element. */
+ while(YY_CURRENT_BUFFER){
+ rcs_delete_buffer(YY_CURRENT_BUFFER ,yyscanner );
+ YY_CURRENT_BUFFER_LVALUE = NULL;
+ rcspop_buffer_state(yyscanner);
+ }
+
+ /* Destroy the stack itself. */
+ rcsfree(yyg->yy_buffer_stack ,yyscanner);
+ yyg->yy_buffer_stack = NULL;
+
+ /* Destroy the start condition stack. */
+ rcsfree(yyg->yy_start_stack ,yyscanner );
+ yyg->yy_start_stack = NULL;
+
+ /* Reset the globals. This is important in a non-reentrant scanner so the next time
+ * rcslex() is called, initialization will occur. */
+ yy_init_globals( yyscanner);
+
+ /* Destroy the main struct (reentrant only). */
+ rcsfree ( yyscanner , yyscanner );
+ yyscanner = NULL;
+ return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner)
+{
+ register int i;
+ for ( i = 0; i < n; ++i )
+ s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner)
+{
+ register int n;
+ for ( n = 0; s[n]; ++n )
+ ;
+
+ return n;
+}
+#endif
+
+void *rcsalloc (yy_size_t size , yyscan_t yyscanner)
+{
+ return (void *) malloc( size );
+}
+
+void *rcsrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner)
+{
+ /* The cast to (char *) in the following accommodates both
+ * implementations that use char* generic pointers, and those
+ * that use void* generic pointers. It works with the latter
+ * because both ANSI C and C++ allow castless assignment from
+ * any pointer type to void*, and deal with argument conversions
+ * as though doing an assignment.
+ */
+ return (void *) realloc( (char *) ptr, size );
+}
+
+void rcsfree (void * ptr , yyscan_t yyscanner)
+{
+ free( (char *) ptr ); /* see rcsrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+#line 73 "rcstokenizer.l"
+
+
+
diff --git a/contrib/csup/lister.c b/contrib/csup/lister.c
index 98a9c83..e969f6d 100644
--- a/contrib/csup/lister.c
+++ b/contrib/csup/lister.c
@@ -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)
diff --git a/contrib/csup/misc.c b/contrib/csup/misc.c
index 97a02ab..6a53b3c 100644
--- a/contrib/csup/misc.c
+++ b/contrib/csup/misc.c
@@ -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)
@@ -249,34 +249,87 @@ rcsdatetotime(const char *revdate)
}
/*
- * Returns a buffer allocated with malloc() containing the absolute
- * pathname to the checkout file made from the prefix and the path
- * of the corresponding RCS file relatively to the prefix. If the
- * filename is not an RCS filename, NULL will be returned.
+ * Checks if a file is an RCS file.
*/
-char *
-checkoutpath(const char *prefix, const char *file)
+int
+isrcs(const char *file, size_t *len)
{
const char *cp;
- char *path;
- size_t len;
if (file[0] == '/')
- return (NULL);
+ return (0);
cp = file;
while ((cp = strstr(cp, "..")) != NULL) {
if (cp == file || cp[2] == '\0' ||
(cp[-1] == '/' && cp[2] == '/'))
- return (NULL);
+ return (0);
cp += 2;
}
- len = strlen(file);
- if (len < 2 || file[len - 1] != 'v' || file[len - 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
+ * of the corresponding RCS file relatively to the prefix. If the
+ * filename is not an RCS filename, NULL will be returned.
+ */
+char *
+checkoutpath(const char *prefix, const char *file)
+{
+ char *path;
+ size_t len;
+
+ 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);
+}
diff --git a/contrib/csup/misc.h b/contrib/csup/misc.h
index f0d0352..1f6d8b2 100644
--- a/contrib/csup/misc.h
+++ b/contrib/csup/misc.h
@@ -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 *);
diff --git a/contrib/csup/mux.c b/contrib/csup/mux.c
index 2be1245..b344be1 100644
--- a/contrib/csup/mux.c
+++ b/contrib/csup/mux.c
@@ -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);
diff --git a/contrib/csup/proto.c b/contrib/csup/proto.c
index 0a89858..b8fb2bf 100644
--- a/contrib/csup/proto.c
+++ b/contrib/csup/proto.c
@@ -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);
diff --git a/contrib/csup/rcsfile.c b/contrib/csup/rcsfile.c
new file mode 100644
index 0000000..37eba83
--- /dev/null
+++ b/contrib/csup/rcsfile.c
@@ -0,0 +1,1381 @@
+/*-
+ * 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$
+ */
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <err.h>
+#include <errno.h>
+#include "diff.h"
+#include "misc.h"
+#include "keyword.h"
+#include "rcsfile.h"
+#include "rcsparse.h"
+#include "stream.h"
+#include "proto.h"
+#include "queue.h"
+
+/*
+ * RCS parser library. This is the part of the library that handles the
+ * importing, editing and exporting of RCS files. It currently supports only the
+ * part of the RCS file specification that is needed for csup (for instance,
+ * newphrases are not supported), and assumes that you can store the whole RCS
+ * file in memory.
+ */
+
+/*
+ * Linked list for string tokens.
+ */
+struct string {
+ char *str;
+ STAILQ_ENTRY(string) string_next;
+};
+
+/*
+ * Linked list of tags and revision numbers, in the RCS file header.
+ */
+struct tag {
+ char *tag;
+ char *revnum;
+ STAILQ_ENTRY(tag) tag_next;
+};
+
+/*
+ * A RCS delta. The delta is identified by a revision number, and contains the
+ * most important RCS attributes that is needed by csup. It also contains
+ * pointers to other nodes in the RCS file delta structure.
+ */
+struct delta {
+ char *revdate;
+ char *revnum;
+ char *author;
+ char *state;
+ struct buf *log;
+ struct buf *text;
+ int placeholder;
+ struct delta *diffbase;
+ struct delta *prev;
+
+ LIST_ENTRY(delta) delta_next;
+ STAILQ_ENTRY(delta) delta_prev;
+ LIST_ENTRY(delta) table_next;
+ STAILQ_ENTRY(delta) stack_next;
+ STAILQ_HEAD(, branch) branchlist;
+ LIST_ENTRY(delta) branch_next_date;
+};
+
+/*
+ * A branch data structure containing information about deltas in the branch as
+ * well as a base revision number.
+ */
+struct branch {
+ char *revnum;
+ LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */
+ STAILQ_ENTRY(branch) branch_next;
+};
+
+/*
+ * The rcsfile structure is the "main" structure of the RCS parser library. It
+ * contains administrative data as well as pointers to the deltas within the
+ * file.
+ */
+struct rcsfile {
+ char *name;
+ char *head;
+ char *branch; /* Default branch. */
+ char *cvsroot;
+ char *colltag;
+ STAILQ_HEAD(, string) accesslist;
+ STAILQ_HEAD(, tag) taglist;
+ int strictlock;
+ char *comment;
+ int expand;
+ struct branch *trunk; /* The tip delta. */
+
+ LIST_HEAD(, delta) deltatable;
+ LIST_HEAD(, delta) deltatable_dates;
+
+ char *desc;
+};
+
+static void rcsfile_freedelta(struct delta *);
+static void rcsfile_insertdelta(struct branch *, struct delta *,
+ int);
+static struct delta *rcsfile_createdelta(char *);
+static int rcsfile_write_deltatext(struct rcsfile *,
+ struct stream *);
+static int rcsfile_puttext(struct rcsfile *, struct stream *,
+ struct delta *, struct delta *);
+static struct branch *rcsfile_getbranch(struct rcsfile *, char *);
+static void rcsfile_insertsorteddelta(struct rcsfile *,
+ struct delta *);
+static struct stream *rcsfile_getdeltatext(struct rcsfile *, struct delta *,
+ struct buf **);
+
+
+/* Space formatting of RCS file. */
+const char *head_space = "\t";
+const char *branch_space = "\t";
+const char *tag_space = "\t";
+const char *date_space = "\t";
+const char *auth_space = "\t";
+const char *state_space = "\t";
+const char *next_space = "\t";
+const char *branches_space = "\t";
+const char *comment_space ="\t";
+
+void print_stream(struct stream *);
+
+/* Print the contents of a stream, for debugging. */
+void
+print_stream(struct stream *s)
+{
+ char *line;
+
+ line = stream_getln(s, NULL);
+ while (line != NULL) {
+ fprintf(stderr, "%s\n", line);
+ line = stream_getln(s, NULL);
+ }
+ fprintf(stderr, "\n");
+}
+
+/*
+ * Parse rcsfile from path and return a pointer to it.
+ */
+struct rcsfile *
+rcsfile_frompath(char *path, char *name, char *cvsroot, char *colltag)
+{
+ FILE *infp;
+ struct rcsfile *rf;
+ char one[10] = "1";
+ int error;
+
+ if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL)
+ return (NULL);
+
+ rf = xmalloc(sizeof(struct rcsfile));
+ rf->name = xstrdup(name);
+ rf->cvsroot = xstrdup(cvsroot);
+ rf->colltag = xstrdup(colltag);
+ /*fprintf(stderr, "Doing file %s\n", rf->name);*/
+
+ /* Initialize head branch. */
+ rf->trunk = xmalloc(sizeof(struct branch));
+ rf->trunk->revnum = xstrdup(one);
+ LIST_INIT(&rf->trunk->deltalist);
+ /* Initialize delta list. */
+ LIST_INIT(&rf->deltatable);
+ /* Initialize tag list. */
+ STAILQ_INIT(&rf->taglist);
+ /* Initialize accesslist. */
+ STAILQ_INIT(&rf->accesslist);
+
+ /* Initialize all fields. */
+ rf->head = NULL;
+ rf->branch = NULL;
+ rf->strictlock = 0;
+ rf->comment = NULL;
+ rf->expand = -1;
+ rf->desc = NULL;
+
+ infp = fopen(path, "r");
+ if (infp == NULL) {
+ lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno));
+ rcsfile_free(rf);
+ return (NULL);
+ }
+ error = rcsparse_run(rf, infp);
+ fclose(infp);
+ if (error) {
+ lprintf(-1, "Error parsing \"%s\"\n", name);
+ rcsfile_free(rf);
+ return (NULL);
+ }
+ return (rf);
+}
+
+/*
+ * Write content of rcsfile to server. Assumes we have a complete RCS file
+ * loaded.
+ */
+int
+rcsfile_send_details(struct rcsfile *rf, struct stream *wr)
+{
+ struct delta *d;
+ struct tag *t;
+ int error;
+
+ assert(rf != NULL);
+
+ error = proto_printf(wr, "V %s\n", rf->name);
+ if (error)
+ return(error);
+
+ /* Write default branch. */
+ if (rf->branch == NULL)
+ error = proto_printf(wr, "b\n");
+ else
+ error = proto_printf(wr, "B %s\n", rf->branch);
+ if (error)
+ return(error);
+
+ /* Write deltas to server. */
+ error = proto_printf(wr, "D\n");
+ if (error)
+ return(error);
+
+ LIST_FOREACH(d, &rf->deltatable, table_next) {
+ error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate);
+ if (error)
+ return(error);
+ }
+ error = proto_printf(wr, ".\n");
+
+ if (error)
+ return(error);
+ /* Write expand. */
+ if (rf->expand >= 0) {
+ error = proto_printf(wr, "E %s\n", keyword_encode_expand(rf->expand));
+ if (error)
+ return(error);
+ }
+
+ /* Write tags to server. */
+ error = proto_printf(wr, "T\n");
+ if (error)
+ return(error);
+ STAILQ_FOREACH(t, &rf->taglist, tag_next) {
+ error = proto_printf(wr, "%s %s\n", t->tag, t->revnum);
+ if (error)
+ return(error);
+ }
+ error = proto_printf(wr, ".\n");
+ if (error)
+ return(error);
+ error = proto_printf(wr, ".\n");
+ return (error);
+}
+
+/*
+ * Write a RCS file to disk represented by the destination stream. Keep track of
+ * deltas with a stack and an inverted stack.
+ */
+int
+rcsfile_write(struct rcsfile *rf, struct stream *dest)
+{
+ STAILQ_HEAD(, delta) deltastack;
+ STAILQ_HEAD(, delta) deltalist_inverted;
+ struct tag *t;
+ struct branch *b;
+ struct delta *d, *d_tmp, *d_next;
+ int error;
+
+ /* First write head. */
+ d = LIST_FIRST(&rf->trunk->deltalist);
+ stream_printf(dest, "head%s%s;\n", head_space, d->revnum);
+
+ /* Write branch, if we have. */
+ if (rf->branch != NULL)
+ stream_printf(dest, "branch%s%s;\n", branch_space, rf->branch);
+
+ /* Write access. */
+ stream_printf(dest, "access");
+#if 0
+ if (!STAILQ_EMPTY(&rf->accesslist)) {
+ /*
+ * XXX: Write out access. This doesn't seem to be necessary for
+ * the time being.
+ */
+ }
+#endif
+ stream_printf(dest, ";\n");
+
+ /* Write out taglist. */
+ stream_printf(dest, "symbols");
+ if (!STAILQ_EMPTY(&rf->taglist)) {
+ STAILQ_FOREACH(t, &rf->taglist, tag_next) {
+ stream_printf(dest, "\n%s%s:%s", tag_space, t->tag,
+ t->revnum);
+ }
+ }
+ stream_printf(dest, ";\n");
+
+ /* Write out locks and strict. */
+ stream_printf(dest, "locks;");
+ if (rf->strictlock)
+ stream_printf(dest, " strict;");
+ stream_printf(dest, "\n");
+
+ /* Write out the comment. */
+ if (rf->comment != NULL)
+ stream_printf(dest, "comment%s%s;\n", comment_space, rf->comment);
+
+ stream_printf(dest, "\n\n");
+
+ /*
+ * Write out deltas. We use a stack where we push the appropriate deltas
+ * that is to be written out during the loop.
+ */
+ STAILQ_INIT(&deltastack);
+ d = LIST_FIRST(&rf->trunk->deltalist);
+ STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
+ while (!STAILQ_EMPTY(&deltastack)) {
+ d = STAILQ_FIRST(&deltastack);
+ STAILQ_REMOVE_HEAD(&deltastack, stack_next);
+ /* Do not write out placeholders just to be safe. */
+ if (d->placeholder)
+ continue;
+ stream_printf(dest, "%s\n", d->revnum);
+ stream_printf(dest, "date%s%s;%sauthor %s;%sstate",
+ date_space, d->revdate, auth_space, d->author,
+ state_space);
+ if (d->state != NULL)
+ stream_printf(dest, " %s", d->state);
+ stream_printf(dest, ";\n");
+ stream_printf(dest, "branches");
+ /*
+ * Write out our branches. Add them to a reversed list for use
+ * later when we write out the text.
+ */
+ STAILQ_INIT(&deltalist_inverted);
+ STAILQ_FOREACH(b, &d->branchlist, branch_next) {
+ d_tmp = LIST_FIRST(&b->deltalist);
+ STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev);
+ STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
+ }
+
+ /* Push branch heads on stack. */
+ STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) {
+ if (d_tmp == NULL)
+ err(1, "empty branch!");
+ stream_printf(dest, "\n%s%s", branches_space,
+ d_tmp->revnum);
+ }
+ stream_printf(dest, ";\n");
+
+ stream_printf(dest, "next%s", next_space);
+ /* Push next delta on stack. */
+ d_next = LIST_NEXT(d, delta_next);
+ if (d_next != NULL) {
+ stream_printf(dest, "%s", d_next->revnum);
+ STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
+ }
+ stream_printf(dest, ";\n\n");
+ }
+ stream_printf(dest, "\n");
+ /* Write out desc. */
+ stream_printf(dest, "desc\n@@");
+ d = LIST_FIRST(&rf->trunk->deltalist);
+
+ /*
+ * XXX: We do not take as much care as cvsup to cope with hand-hacked
+ * RCS-files, and therefore we'll just let them be updated. If having
+ * them correct is important, it will be catched by the checksum anyway.
+ */
+
+ /* Write out deltatexts. */
+ error = rcsfile_write_deltatext(rf, dest);
+ stream_printf(dest, "\n");
+ return (error);
+}
+
+/*
+ * Write out deltatexts of a delta and it's subbranches recursively.
+ */
+int
+rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest)
+{
+ STAILQ_HEAD(, delta) deltastack;
+ LIST_HEAD(, delta) branchlist_datesorted;
+ struct stream *in;
+ struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3;
+ struct branch *b;
+ char *line;
+ size_t size;
+ int error;
+
+ error = 0;
+ STAILQ_INIT(&deltastack);
+ d = LIST_FIRST(&rf->trunk->deltalist);
+ d->prev = NULL;
+ STAILQ_INSERT_HEAD(&deltastack, d, stack_next);
+ while (!STAILQ_EMPTY(&deltastack)) {
+ d = STAILQ_FIRST(&deltastack);
+ STAILQ_REMOVE_HEAD(&deltastack, stack_next);
+ /* Do not write out placeholders just to be safe. */
+ if (d->placeholder)
+ return (0);
+ stream_printf(dest, "\n\n\n%s\n", d->revnum);
+ stream_printf(dest, "log\n@");
+ in = stream_open_buf(d->log);
+ line = stream_getln(in, &size);
+ while (line != NULL) {
+ stream_write(dest, line, size);
+ line = stream_getln(in, &size);
+ }
+ stream_close(in);
+ stream_printf(dest, "@\n");
+ stream_printf(dest, "text\n@");
+ error = rcsfile_puttext(rf, dest, d, d->prev);
+ if (error)
+ return (error);
+ stream_printf(dest, "@");
+
+ LIST_INIT(&branchlist_datesorted);
+ d_next = LIST_NEXT(d, delta_next);
+ if (d_next != NULL) {
+ d_next->prev = d;
+ /*
+ * If it's trunk, treat it like the oldest, if not treat
+ * it like a child.
+ */
+ if (rcsrev_istrunk(d_next->revnum))
+ STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next);
+ else
+ LIST_INSERT_HEAD(&branchlist_datesorted, d_next,
+ branch_next_date);
+ }
+
+ /*
+ * First, we need to sort our branches based on their date to
+ * take into account some self-hacked RCS files.
+ */
+ STAILQ_FOREACH(b, &d->branchlist, branch_next) {
+ d_tmp = LIST_FIRST(&b->deltalist);
+ if (LIST_EMPTY(&branchlist_datesorted)) {
+ LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp,
+ branch_next_date);
+ continue;
+ }
+
+ d_tmp2 = LIST_FIRST(&branchlist_datesorted);
+ if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) < 0) {
+ LIST_INSERT_BEFORE(d_tmp2, d_tmp, branch_next_date);
+ continue;
+ }
+ while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date))
+ != NULL) {
+ if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate)
+ < 0)
+ break;
+ d_tmp2 = d_tmp3;
+ }
+ LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date);
+ }
+ /*
+ * Invert the deltalist of a branch, since we're writing them
+ * the opposite way.
+ */
+ LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) {
+ d_tmp->prev = d;
+ STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next);
+ }
+ }
+ return (0);
+}
+
+/*
+ * Generates text given a delta and a diffbase.
+ */
+static int
+rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d,
+ struct delta *diffbase)
+{
+ struct stream *in, *rd, *orig;
+ struct keyword *k;
+ struct diffinfo dibuf, *di;
+ struct buf *b;
+ char *line;
+ int error;
+ size_t size;
+
+ di = &dibuf;
+ b = NULL;
+ error = 0;
+
+ /* Write if the diffbase is the previous */
+ if (d->diffbase == diffbase) {
+
+ /* Write out the text. */
+ in = stream_open_buf(d->text);
+ line = stream_getln(in, &size);
+ while (line != NULL) {
+ stream_write(dest, line, size);
+ line = stream_getln(in, &size);
+ }
+ stream_close(in);
+ /* We need to apply diff to produce text, this is probably HEAD. */
+ } else if (diffbase == NULL) {
+ /* Apply diff. */
+ orig = rcsfile_getdeltatext(rf, d, &b);
+ if (orig == NULL) {
+ error = -1;
+ goto cleanup;
+ }
+ line = stream_getln(orig, &size);
+ while (line != NULL) {
+ stream_write(dest, line, size);
+ line = stream_getln(orig, &size);
+ }
+ stream_close(orig);
+ /*
+ * A new head was probably added, and now the previous HEAD must be
+ * changed to include the diff instead.
+ */
+ } else if (diffbase->diffbase == d) {
+ /* Get reverse diff. */
+ orig = rcsfile_getdeltatext(rf, d, &b);
+ if (orig == NULL) {
+ error = -1;
+ goto cleanup;
+ }
+ rd = stream_open_buf(diffbase->text);
+ di->di_rcsfile = rf->name;
+ di->di_cvsroot = rf->cvsroot;
+ di->di_revnum = d->revnum;
+ di->di_revdate = d->revdate;
+ di->di_author = d->author;
+ di->di_tag = rf->colltag;
+ di->di_state = d->state;
+ di->di_expand = rf->expand;
+ k = keyword_new();
+
+ rd = stream_open_buf(diffbase->text);
+ error = diff_reverse(rd, orig, dest, k, di);
+ if (error) {
+ fprintf(stderr, "Error applying reverse diff: %d\n",
+ error);
+ goto cleanup;
+ }
+ keyword_free(k);
+ stream_close(rd);
+ stream_close(orig);
+ }
+cleanup:
+ if (b != NULL)
+ buf_free(b);
+ return (error);
+}
+
+/*
+ * Return a stream with an applied diff of a delta.
+ * XXX: extra overhead on the last apply. Could write directly to file, but
+ * makes things complicated though.
+ */
+static struct stream *
+rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest)
+{
+ struct diffinfo dibuf, *di;
+ struct stream *orig, *dest, *rd;
+ struct buf *buf_orig;
+ struct keyword *k;
+ int error;
+
+ buf_orig = NULL;
+ error = 0;
+
+ /* If diffbase is NULL or we are head (the old head), we have a normal complete deltatext. */
+ if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) {
+ orig = stream_open_buf(d->text);
+ return (orig);
+ }
+
+ di = &dibuf;
+ /* If not, we need to apply our diff to that of our diffbase. */
+ orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig);
+ if (orig == NULL)
+ return (NULL);
+
+ /*
+ * Now that we are sure we have a complete deltatext in ret, let's apply
+ * our diff to it.
+ */
+ *buf_dest = buf_new(128);
+ dest = stream_open_buf(*buf_dest);
+
+ di->di_rcsfile = rf->name;
+ di->di_cvsroot = rf->cvsroot;
+ di->di_revnum = d->revnum;
+ di->di_revdate = d->revdate;
+ di->di_author = d->author;
+ di->di_tag = rf->colltag;
+ di->di_state = d->state;
+ di->di_expand = rf->expand;
+ rd = stream_open_buf(d->text);
+ k = keyword_new();
+ error = diff_apply(rd, orig, dest, k, di, 0);
+ stream_flush(dest);
+ stream_close(rd);
+ stream_close(orig);
+ stream_close(dest);
+ keyword_free(k);
+ if (buf_orig != NULL)
+ buf_free(buf_orig);
+ if (error) {
+ lprintf(-1, "Error applying diff: %d\n", error);
+ return (NULL);
+ }
+
+ /* Now reopen the stream for the reading. */
+ dest = stream_open_buf(*buf_dest);
+ return (dest);
+}
+
+/* Print content of rcsfile. Useful for debugging. */
+void
+rcsfile_print(struct rcsfile *rf)
+{
+ struct delta *d;
+ struct tag *t;
+ struct string *s;
+ char *line;
+ struct stream *in;
+
+ printf("\n");
+ if (rf->name != NULL)
+ printf("name: '%s'\n", rf->name);
+ if (rf->head != NULL)
+ printf("head: '%s'\n", rf->head);
+ if (rf->branch != NULL)
+ printf("branch: '%s'\n", rf->branch);
+ printf("Access: ");
+ STAILQ_FOREACH(s, &rf->accesslist, string_next) {
+ printf("'%s' ", s->str);
+ }
+ printf("\n");
+
+ /* Print all tags. */
+ STAILQ_FOREACH(t, &rf->taglist, tag_next) {
+ printf("Tag: ");
+ if (t->tag != NULL)
+ printf("name: %s ", t->tag);
+ if (t->revnum != NULL)
+ printf("rev: %s", t->revnum);
+ printf("\n");
+ }
+
+ if (rf->strictlock)
+ printf("Strict!\n");
+ if (rf->comment != NULL)
+ printf("comment: '%s'\n", rf->comment);
+ if (rf->expand >= 0)
+ printf("expand: '%s'\n", keyword_encode_expand(rf->expand));
+
+ /* Print all deltas. */
+ LIST_FOREACH(d, &rf->deltatable, table_next) {
+ printf("Delta: ");
+ if (d->revdate != NULL)
+ printf("date: %s ", d->revdate);
+ if (d->revnum != NULL)
+ printf("rev: %s", d->revnum);
+ if (d->author != NULL)
+ printf("author: %s", d->author);
+ if (d->state != NULL)
+ printf("state: %s", d->state);
+
+ printf("Text:\n");
+ in = stream_open_buf(d->text);
+ line = stream_getln(in, NULL);
+ while (line != NULL) {
+ lprintf(1, "TEXT: %s\n", line);
+ line = stream_getln(in, NULL);
+ }
+ stream_close(in);
+ printf("branches: ");
+ printf("\n");
+ }
+
+ if (rf->desc != NULL)
+ printf("desc: '%s'\n", rf->desc);
+}
+
+/* Free all memory associated with a struct rcsfile. */
+void
+rcsfile_free(struct rcsfile *rf)
+{
+ struct delta *d;
+ struct tag *t;
+ struct string *s;
+
+ if (rf->name != NULL)
+ free(rf->name);
+ if (rf->head != NULL)
+ free(rf->head);
+ if (rf->branch != NULL)
+ free(rf->branch);
+ if (rf->cvsroot != NULL)
+ free(rf->cvsroot);
+ if (rf->colltag != NULL)
+ free(rf->colltag);
+
+ /* Free all access ids. */
+ while (!STAILQ_EMPTY(&rf->accesslist)) {
+ s = STAILQ_FIRST(&rf->accesslist);
+ STAILQ_REMOVE_HEAD(&rf->accesslist, string_next);
+ if (s->str != NULL)
+ free(s->str);
+ free(s);
+ }
+
+ /* Free all tags. */
+ while (!STAILQ_EMPTY(&rf->taglist)) {
+ t = STAILQ_FIRST(&rf->taglist);
+ STAILQ_REMOVE_HEAD(&rf->taglist, tag_next);
+ if (t->tag != NULL)
+ free(t->tag);
+ if (t->revnum != NULL)
+ free(t->revnum);
+ free(t);
+ }
+
+ if (rf->comment != NULL)
+ free(rf->comment);
+
+ /* Free all deltas in global list */
+ while (!LIST_EMPTY(&rf->deltatable)) {
+ d = LIST_FIRST(&rf->deltatable);
+ LIST_REMOVE(d, table_next);
+ rcsfile_freedelta(d);
+ }
+
+ /* Free global branch. */
+ if (rf->trunk->revnum != NULL)
+ free(rf->trunk->revnum);
+ free(rf->trunk);
+
+ if (rf->desc != NULL)
+ free(rf->desc);
+
+ free(rf);
+}
+
+/*
+ * Free a RCS delta.
+ */
+static void
+rcsfile_freedelta(struct delta *d)
+{
+ struct branch *b;
+
+ if (d->revdate != NULL)
+ free(d->revdate);
+ if (d->revnum != NULL)
+ free(d->revnum);
+ if (d->author != NULL)
+ free(d->author);
+ if (d->state != NULL)
+ free(d->state);
+ if (d->log != NULL)
+ buf_free(d->log);
+ if (d->text != NULL)
+ buf_free(d->text);
+
+ /* Free all subbranches of a delta. */
+ /* XXX: Is this ok? Since the branchpoint is removed, there is no good
+ * reason for the branch to exists, but we might still have deltas in
+ * these branches.
+ */
+ while (!STAILQ_EMPTY(&d->branchlist)) {
+ b = STAILQ_FIRST(&d->branchlist);
+ STAILQ_REMOVE_HEAD(&d->branchlist, branch_next);
+ free(b->revnum);
+ free(b);
+ }
+ free(d);
+}
+
+/*
+ * Functions for editing RCS deltas.
+ */
+
+/* Add a new entry to the access list. */
+void
+rcsfile_addaccess(struct rcsfile *rf, char *id)
+{
+ struct string *s;
+
+ s = xmalloc(sizeof(struct string));
+ s->str = xstrdup(id);
+ STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next);
+}
+
+/* Add a tag to a RCS file. */
+void
+rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum)
+{
+ struct tag *t;
+
+ t = xmalloc(sizeof(struct tag));
+ t->tag = xstrdup(tag);
+ t->revnum = xstrdup(revnum);
+
+ STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next);
+}
+
+/* Import a tag to a RCS file. */
+void
+rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum)
+{
+ struct tag *t;
+
+ t = xmalloc(sizeof(struct tag));
+ t->tag = xstrdup(tag);
+ t->revnum = xstrdup(revnum);
+
+ STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next);
+}
+
+/*
+ * Delete a revision from the global delta list and the branch it is in. Csup
+ * will tell us to delete the tags involved.
+ */
+void
+rcsfile_deleterev(struct rcsfile *rf, char *revname)
+{
+ struct delta *d;
+
+ d = rcsfile_getdelta(rf, revname);
+ LIST_REMOVE(d, delta_next);
+ LIST_REMOVE(d, table_next);
+ rcsfile_freedelta(d);
+}
+
+/* Delete a tag from the tag list. */
+void
+rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum)
+{
+ struct tag *t;
+
+ STAILQ_FOREACH(t, &rf->taglist, tag_next) {
+ if ((strcmp(tag, t->tag) == 0) &&
+ (strcmp(revnum, t->revnum) == 0)) {
+ STAILQ_REMOVE(&rf->taglist, t, tag, tag_next);
+ free(t->tag);
+ free(t->revnum);
+ free(t);
+ return;
+ }
+ }
+}
+
+/*
+ * Searches the global deltalist for a delta.
+ */
+struct delta *
+rcsfile_getdelta(struct rcsfile *rf, char *revnum)
+{
+ struct delta *d;
+
+ LIST_FOREACH(d, &rf->deltatable, table_next) {
+ if (strcmp(revnum, d->revnum) == 0)
+ return (d);
+ }
+ return (NULL);
+}
+
+/* Set rcsfile head. */
+void
+rcsfile_setval(struct rcsfile *rf, int field, char *val)
+{
+
+ switch (field) {
+ case RCSFILE_HEAD:
+ if (rf->head != NULL)
+ free(rf->head);
+ rf->head = xstrdup(val);
+ break;
+ case RCSFILE_BRANCH:
+ if (rf->branch != NULL)
+ free(rf->branch);
+ rf->branch = (val == NULL) ? NULL : xstrdup(val);
+ break;
+ case RCSFILE_STRICT:
+ if (val != NULL)
+ rf->strictlock = 1;
+ break;
+ case RCSFILE_COMMENT:
+ if (rf->comment != NULL)
+ free(rf->comment);
+ rf->comment = xstrdup(val);
+ break;
+ case RCSFILE_EXPAND:
+ rf->expand = keyword_decode_expand(val);
+ break;
+ case RCSFILE_DESC:
+ if (rf->desc != NULL)
+ free(rf->desc);
+ rf->desc = xstrdup(val);
+ break;
+ default:
+ lprintf(-1, "Setting invalid RCSfile value.\n");
+ break;
+ }
+}
+
+/* Create and initialize a delta. */
+static struct delta *
+rcsfile_createdelta(char *revnum)
+{
+ struct delta *d;
+
+ d = xmalloc(sizeof(struct delta));
+ d->revnum = xstrdup(revnum);
+ d->revdate = NULL;
+ d->state = NULL;
+ d->author = NULL;
+ /* XXX: default. */
+ d->log = buf_new(128);
+ d->text = buf_new(128);
+ d->diffbase = NULL;
+
+ STAILQ_INIT(&d->branchlist);
+ return (d);
+}
+
+/* Add a delta to a imported delta tree. Used by the updater. */
+struct delta *
+rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
+ char *diffbase)
+{
+ struct branch *b;
+ struct delta *d, *d_bp, *d_next;
+ char *brev, *bprev;
+ int trunk;
+
+ d_next = NULL;
+ d = rcsfile_getdelta(rf, revnum);
+ if (d != NULL) {
+ lprintf(-1, "Delta %s already exists!\n", revnum);
+ return (NULL);
+ }
+ d = rcsfile_createdelta(revnum);
+ d->placeholder = 0;
+ d->revdate = xstrdup(revdate);
+ d->author = xstrdup(author);
+ d->diffbase = rcsfile_getdelta(rf, diffbase);
+
+ /* If it's trunk, insert it in the head branch list. */
+ b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf,
+ d->revnum);
+
+ /*
+ * We didn't find a branch, check if we can find a branchpoint and
+ * create a branch there.
+ */
+ if (b == NULL) {
+ brev = rcsrev_prefix(d->revnum);
+ bprev = rcsrev_prefix(brev);
+
+ d_bp = rcsfile_getdelta(rf, bprev);
+ free(bprev);
+ if (d_bp == NULL) {
+ lprintf(-1, "No branch point for adding delta %s\n",
+ d->revnum);
+ return (NULL);
+ }
+
+ /* Create the branch and insert in delta. */
+ b = xmalloc(sizeof(struct branch));
+ b->revnum = brev;
+ LIST_INIT(&b->deltalist);
+ STAILQ_INSERT_HEAD(&d_bp->branchlist, b, branch_next);
+ }
+
+ /* Insert both into the tree, and into the lookup list. */
+ trunk = rcsrev_istrunk(d->revnum);
+ rcsfile_insertdelta(b, d, trunk);
+ rcsfile_insertsorteddelta(rf, d);
+ return (d);
+}
+
+/* Adds a delta to a rcsfile struct. Used by the parser. */
+void
+rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author,
+ char *state, char *next)
+{
+ struct branch *b;
+ struct delta *d, *d_bp, *d_next;
+ char *brev, *bprev;
+ int trunk;
+
+ d_next = NULL;
+ d = rcsfile_getdelta(rf, revnum);
+
+ if (d == NULL) {
+ /* If not, we'll just create a new entry. */
+ d = rcsfile_createdelta(revnum);
+ d->placeholder = 0;
+ } else {
+ if (d->placeholder == 0) {
+ lprintf(-1, "Trying to import already existing delta\n");
+ return;
+ }
+ }
+ /*
+ * If already exists, assume that only revnum is filled out, and set the
+ * rest of the fields. This should be an OK assumption given that we can
+ * be sure internally that the structure is sufficiently initialized so
+ * we won't have any unfreed memory.
+ */
+ d->revdate = xstrdup(revdate);
+ d->author = xstrdup(author);
+ if (state != NULL)
+ d->state = xstrdup(state);
+
+ /* If we have a next, create a placeholder for it. */
+ if (next != NULL) {
+ d_next = rcsfile_createdelta(next);
+ d_next->placeholder = 1;
+ /* Diffbase should be the previous. */
+ d_next->diffbase = d;
+ }
+
+ /* If it's trunk, insert it in the head branch list. */
+ b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf,
+ d->revnum);
+
+ /*
+ * We didn't find a branch, check if we can find a branchpoint and
+ * create a branch there.
+ */
+ if (b == NULL) {
+ brev = rcsrev_prefix(d->revnum);
+ bprev = rcsrev_prefix(brev);
+
+ d_bp = rcsfile_getdelta(rf, bprev);
+ free(bprev);
+ if (d_bp == NULL) {
+ lprintf(-1, "No branch point for adding delta %s\n",
+ d->revnum);
+ return;
+ }
+
+ /* Create the branch and insert in delta. */
+ b = xmalloc(sizeof(struct branch));
+ b->revnum = brev;
+ LIST_INIT(&b->deltalist);
+ STAILQ_INSERT_HEAD(&d_bp->branchlist, b, branch_next);
+ }
+
+ /* Insert if not a placeholder. */
+ if (!d->placeholder) {
+ /*fprintf(stderr, "Insert %s\n", d->revnum);*/
+ /* Insert both into the tree, and into the lookup list. */
+ if (rcsrev_istrunk(d->revnum))
+ rcsfile_insertdelta(b, d, 1);
+ else {
+ rcsfile_insertdelta(b, d, 0);
+ /*
+ * On import we need to set the diffbase to our
+ * branchpoint for writing out later.
+ * XXX: this could perhaps be done at a better time.
+ */
+ if (LIST_FIRST(&b->deltalist) == d) {
+ brev = rcsrev_prefix(d->revnum);
+ bprev = rcsrev_prefix(brev);
+ d_bp = rcsfile_getdelta(rf, bprev);
+ /* This should really not happen. */
+ assert(d_bp != NULL);
+ d->diffbase = d_bp;
+ free(brev);
+ free(bprev);
+ }
+ }
+ rcsfile_insertsorteddelta(rf, d);
+ } else /* Not a placeholder anymore. */ {
+ d->placeholder = 0;
+ /* Put it into the tree. */
+ trunk = rcsrev_istrunk(d->revnum);
+ rcsfile_insertdelta(b, d, trunk);
+ }
+
+ /* If we have a next, insert the placeholder into the lookup list. */
+ if (d_next != NULL)
+ rcsfile_insertsorteddelta(rf, d_next);
+}
+
+/*
+ * Find the branch of a revision number.
+ */
+static struct branch *
+rcsfile_getbranch(struct rcsfile *rf, char *revnum)
+{
+ struct branch *b;
+ struct delta *d;
+ char *branchrev;
+ char *bprev;
+
+ branchrev = rcsrev_prefix(revnum);
+ bprev = rcsrev_prefix(branchrev);
+ d = rcsfile_getdelta(rf, bprev);
+ free(bprev);
+ STAILQ_FOREACH(b, &d->branchlist, branch_next) {
+ if(rcsnum_cmp(b->revnum, branchrev) == 0) {
+ free(branchrev);
+ return (b);
+ }
+ }
+ free(branchrev);
+ return (NULL);
+}
+
+#if 0
+/* Add a new branch to a delta. */
+void
+rcsfile_addbranch(struct rcsfile *rf, char *branch)
+{
+ struct delta *d;
+ struct branch *b;
+ char *branchrev, *deltarev;
+ int trunk;
+
+ /*
+ * Branchrev is our branches revision, the delta actual delta will be
+ * taken care of later.
+ */
+ branchrev = rcsrev_prefix(branch);
+ deltarev = rcsrev_prefix(branchrev);
+
+ /* XXX: Could we refer to a delta that is not added yet? If we're
+ * refferring to branches without having been added before, this could
+ * happen in the head branch.
+ */
+ /*fprintf(stderr, "Add branch %s to delta %s\n", branchrev, deltarev);*/
+ d = rcsfile_getdelta(rf, deltarev);
+ if (d == NULL) {
+ /* We must create a placeholder for the delta holding the
+ * branch. */
+ d = rcsfile_createdelta(deltarev);
+ d->placeholder = 1;
+ /* XXX: Can we assume this branch exists? */
+ trunk = rcsrev_istrunk(d->revnum);
+ b = trunk ? rf->trunk : rcsfile_getbranch(rf, d->revnum);
+ rcsfile_insertdelta(b, d, trunk);
+ rcsfile_insertsorteddelta(rf, d);
+ }
+ b = xmalloc(sizeof(struct branch));
+ b->revnum = branchrev;
+ LIST_INIT(&b->deltalist);
+ STAILQ_INSERT_HEAD(&d->branchlist, b, branch_next);
+ /* Free only deltarev, branchrev is used by branch. */
+ free(deltarev);
+}
+#endif
+
+/*
+ * Insert a delta into the correct place in the table of the rcsfile. Sorted by
+ * date.
+ */
+static void
+rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d)
+{
+ struct delta *d2;
+
+ /* If it's empty, insert into head. */
+ if (LIST_EMPTY(&rf->deltatable)) {
+ LIST_INSERT_HEAD(&rf->deltatable, d, table_next);
+ return;
+ }
+
+ /* Just put it in before the revdate that is lower. */
+ LIST_FOREACH(d2, &rf->deltatable, table_next) {
+ if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) {
+ LIST_INSERT_BEFORE(d2, d, table_next);
+ return;
+ }
+ if (LIST_NEXT(d2, table_next) == NULL)
+ break;
+ }
+ /* Insert after last element. */
+ LIST_INSERT_AFTER(d2, d, table_next);
+}
+
+/*
+ * Insert a delta into the correct place in branch. A trunk branch will have
+ * different ordering scheme and be sorted by revision number, but a normal
+ * branch will be sorted by date to maintain compability with branches that is
+ * "hand-hacked".
+ */
+static void
+rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk)
+{
+ struct delta *d2;
+
+ /* If it's empty, insert into head. */
+ if (LIST_EMPTY(&b->deltalist)) {
+ LIST_INSERT_HEAD(&b->deltalist, d, delta_next);
+ return;
+ }
+
+ /*
+ * Just put it in before the revnum that is lower. Sort trunk branch by
+ * branchnum but the subbranches after deltadate.
+ */
+ LIST_FOREACH(d2, &b->deltalist, delta_next) {
+ if (trunk) {
+ /*fprintf(stderr, "Comparing %s and %s\n", d->revnum,
+ * d2->revnum);*/
+ if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) {
+ LIST_INSERT_BEFORE(d2, d, delta_next);
+ return;
+ }
+ } else {
+ /* XXX: here we depend on the date being set, but it
+ * should be before this is called anyway. */
+ if (rcsnum_cmp(d->revdate, d2->revdate) <= 0) {
+ LIST_INSERT_BEFORE(d2, d, delta_next);
+ return;
+ }
+ }
+ if (LIST_NEXT(d2, delta_next) == NULL)
+ break;
+ }
+ /* Insert after last element. */
+ LIST_INSERT_AFTER(d2, d, delta_next);
+}
+
+
+/* Add logtext to a delta. Assume the delta already exists. */
+int
+rcsdelta_addlog(struct delta *d, char *log)
+{
+ struct stream *dest;
+
+ assert(d != NULL);
+ log++;
+ log[strlen(log) - 1] = '\0';
+
+ dest = stream_open_buf(d->log);
+ stream_write(dest, log, strlen(log));
+ stream_close(dest);
+ return (0);
+}
+
+/* Add deltatext to a delta. Assume the delta already exists. */
+int
+rcsdelta_addtext(struct delta *d, char *text)
+{
+ struct stream *dest;
+
+ assert(d != NULL);
+ text++;
+ text[strlen(text) - 1] = '\0';
+
+ dest = stream_open_buf(d->text);
+ stream_write(dest, text, strlen(text));
+ stream_close(dest);
+ return (0);
+}
+
+/* Add a deltatext logline to a delta. */
+void
+rcsdelta_appendlog(struct delta *d, char *logline, size_t size)
+{
+ struct stream *dest;
+ char buf[3];
+ size_t i;
+ int count;
+
+ assert(d != NULL);
+ dest = stream_open_buf(d->log);
+ for (i = 0; i < size; i++) {
+ buf[0] = logline[i];
+ buf[1] = '\0';
+ count = 1;
+ /* Expand @'s */
+ if (buf[0] == '@') {
+ buf[1] = '@';
+ buf[2] = '\0';
+ count = 2;
+ }
+ stream_write(dest, buf, count);
+ }
+ stream_close(dest);
+}
+
+/* Add a deltatext textline to a delta. */
+void
+rcsdelta_appendtext(struct delta *d, char *textline, size_t size)
+{
+ struct stream *dest;
+ char buf[3];
+ size_t i;
+ int count;
+
+ assert(d != NULL);
+ dest = stream_open_buf(d->text);
+ /* XXX: code reuse. */
+ for (i = 0; i < size; i++) {
+ buf[0] = textline[i];
+ buf[1] = '\0';
+ count = 1;
+ /* Expand @'s */
+ if (buf[0] == '@') {
+ buf[1] = '@';
+ buf[2] = '\0';
+ count = 2;
+ }
+ stream_write(dest, buf, count);
+ }
+
+ stream_close(dest);
+}
+
+
+/* Set delta state. */
+void
+rcsdelta_setstate(struct delta *d, char *state)
+{
+
+ if (d->state != NULL)
+ free(state);
+ if (state != NULL) {
+ d->state = xstrdup(state);
+ return;
+ }
+ d->state = NULL;
+}
+
+/* Truncate the deltalog with a certain offset. */
+/* XXX: error values for these. */
+void
+rcsdelta_truncatelog(struct delta *d, off_t offset)
+{
+
+ stream_truncate_buf(d->log, offset);
+}
+
+/* Truncate the deltatext with a certain offset. */
+void
+rcsdelta_truncatetext(struct delta *d, off_t offset)
+{
+
+ stream_truncate_buf(d->text, offset);
+}
diff --git a/contrib/csup/rcsfile.h b/contrib/csup/rcsfile.h
new file mode 100644
index 0000000..f9a8dcc
--- /dev/null
+++ b/contrib/csup/rcsfile.h
@@ -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_ */
diff --git a/contrib/csup/rcsparse.c b/contrib/csup/rcsparse.c
new file mode 100644
index 0000000..88a3762
--- /dev/null
+++ b/contrib/csup/rcsparse.c
@@ -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);
+}
diff --git a/contrib/csup/rcsparse.h b/contrib/csup/rcsparse.h
new file mode 100644
index 0000000..9eacbc8
--- /dev/null
+++ b/contrib/csup/rcsparse.h
@@ -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
diff --git a/contrib/csup/rcstokenizer.h b/contrib/csup/rcstokenizer.h
new file mode 100644
index 0000000..66ea724
--- /dev/null
+++ b/contrib/csup/rcstokenizer.h
@@ -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 */
diff --git a/contrib/csup/rcstokenizer.l b/contrib/csup/rcstokenizer.l
new file mode 100644
index 0000000..583bcbe
--- /dev/null
+++ b/contrib/csup/rcstokenizer.l
@@ -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]+ ;
+%%
diff --git a/contrib/csup/status.c b/contrib/csup/status.c
index 32b6821..9e1a2c0 100644
--- a/contrib/csup/status.c
+++ b/contrib/csup/status.c
@@ -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);
diff --git a/contrib/csup/stream.c b/contrib/csup/stream.c
index 34aa71e..7ea6515 100644
--- a/contrib/csup/stream.c
+++ b/contrib/csup/stream.c
@@ -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);
+}
+
diff --git a/contrib/csup/stream.h b/contrib/csup/stream.h
index 062fd34..2128635 100644
--- a/contrib/csup/stream.h
+++ b/contrib/csup/stream.h
@@ -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_ */
diff --git a/contrib/csup/updater.c b/contrib/csup/updater.c
index df74d52..9d99ef7 100644
--- a/contrib/csup/updater.c
+++ b/contrib/csup/updater.c
@@ -30,6 +30,7 @@
#include <sys/stat.h>
#include <assert.h>
+#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <stddef.h>
@@ -47,6 +48,7 @@
#include "misc.h"
#include "mux.h"
#include "proto.h"
+#include "rcsfile.h"
#include "status.h"
#include "stream.h"
@@ -56,11 +58,14 @@
#define UPDATER_ERR_READ (-3) /* Error reading from server. */
#define UPDATER_ERR_DELETELIM (-4) /* File deletion limit exceeded. */
+#define BUFSIZE 1024
+
/* Everything needed to update a file. */
struct file_update {
struct statusrec srbuf;
char *destpath;
char *temppath;
+ char *origpath;
char *coname; /* Points somewhere in destpath. */
char *wantmd5;
struct coll *coll;
@@ -69,6 +74,7 @@ struct file_update {
char *author;
struct stream *orig;
struct stream *to;
+ int attic;
int expand;
};
@@ -80,7 +86,7 @@ struct updater {
};
static struct file_update *fup_new(struct coll *, struct status *);
-static int fup_prepare(struct file_update *, char *);
+static int fup_prepare(struct file_update *, char *, int);
static void fup_cleanup(struct file_update *);
static void fup_free(struct file_update *);
@@ -90,14 +96,25 @@ static int updater_docoll(struct updater *, struct file_update *, int);
static int updater_delete(struct updater *, struct file_update *);
static void updater_deletefile(const char *);
static int updater_checkout(struct updater *, struct file_update *, int);
+static int updater_addfile(struct updater *, struct file_update *,
+ char *);
+int updater_addelta(struct rcsfile *, struct stream *, char *);
static int updater_setattrs(struct updater *, struct file_update *,
char *, char *, char *, char *, char *, struct fattr *);
+static int updater_setdirattrs(struct updater *, struct coll *,
+ struct file_update *, char *, char *);
static int updater_updatefile(struct updater *, struct file_update *fup,
const char *, int);
+static int updater_updatenode(struct updater *, struct coll *,
+ struct file_update *, char *, char *);
static int updater_diff(struct updater *, struct file_update *);
static int updater_diff_batch(struct updater *, struct file_update *);
static int updater_diff_apply(struct updater *, struct file_update *,
char *);
+static int updater_rcsedit(struct updater *, struct file_update *, char *,
+ char *);
+int updater_append_file(struct updater *, struct file_update *,
+ off_t);
static struct file_update *
fup_new(struct coll *coll, struct status *st)
@@ -112,12 +129,27 @@ fup_new(struct coll *coll, struct status *st)
}
static int
-fup_prepare(struct file_update *fup, char *name)
+fup_prepare(struct file_update *fup, char *name, int attic)
{
struct coll *coll;
coll = fup->coll;
- fup->destpath = checkoutpath(coll->co_prefix, name);
+ fup->attic = 0;
+ fup->origpath = NULL;
+
+ if (coll->co_options & CO_CHECKOUTMODE)
+ fup->destpath = checkoutpath(coll->co_prefix, name);
+ else {
+ fup->destpath = cvspath(coll->co_prefix, name, attic);
+ fup->origpath = atticpath(coll->co_prefix, name);
+ /* If they're equal, we don't need special care. */
+ if (fup->origpath != NULL &&
+ strcmp(fup->origpath, fup->destpath) == 0) {
+ free(fup->origpath);
+ fup->origpath = NULL;
+ }
+ fup->attic = attic;
+ }
if (fup->destpath == NULL)
return (-1);
fup->coname = fup->destpath + coll->co_prefixlen + 1;
@@ -140,6 +172,10 @@ fup_cleanup(struct file_update *fup)
free(fup->temppath);
fup->temppath = NULL;
}
+ if (fup->origpath != NULL) {
+ free(fup->origpath);
+ fup->origpath = NULL;
+ }
fup->coname = NULL;
if (fup->author != NULL) {
free(fup->author);
@@ -312,7 +348,9 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
char *cmd, *line, *msg, *attr;
char *name, *tag, *date, *revdate;
char *expand, *wantmd5, *revnum;
+ char *optstr, *rcsopt, *pos;
time_t t;
+ off_t position;
int error, needfixupmsg;
error = 0;
@@ -347,7 +385,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
if (rcsattr == NULL)
return (UPDATER_ERR_PROTO);
- error = fup_prepare(fup, name);
+ error = fup_prepare(fup, name, 0);
if (error)
return (UPDATER_ERR_PROTO);
error = updater_setattrs(up, fup, name, tag, date,
@@ -365,12 +403,12 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
if (attr == NULL || line != NULL)
return (UPDATER_ERR_PROTO);
- error = fup_prepare(fup, name);
+ error = fup_prepare(fup, name, 0);
if (error)
return (UPDATER_ERR_PROTO);
/* Theoritically, the file does not exist on the client.
Just to make sure, we'll delete it here, if it
- exists. */
+ exists. */
if (access(fup->destpath, F_OK) == 0) {
error = updater_delete(up, fup);
if (error)
@@ -419,7 +457,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
fup->expand = keyword_decode_expand(expand);
if (fup->expand == -1)
return (UPDATER_ERR_PROTO);
- error = fup_prepare(fup, name);
+ error = fup_prepare(fup, name, 0);
if (error)
return (UPDATER_ERR_PROTO);
@@ -438,7 +476,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
if (attr == NULL || line != NULL)
return (UPDATER_ERR_PROTO);
- error = fup_prepare(fup, name);
+ error = fup_prepare(fup, name, 0);
if (error)
return (UPDATER_ERR_PROTO);
error = updater_delete(up, fup);
@@ -492,7 +530,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
fattr_override(sr->sr_clientattr, tmp, FA_MASK);
fattr_free(tmp);
fattr_mergedefault(sr->sr_clientattr);
- error = fup_prepare(fup, name);
+ error = fup_prepare(fup, name, 0);
if (error)
return (UPDATER_ERR_PROTO);
fup->temppath = tempname(fup->destpath);
@@ -508,7 +546,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
name = proto_get_ascii(&line);
if (name == NULL || line != NULL)
return (UPDATER_ERR_PROTO);
- error = fup_prepare(fup, name);
+ error = fup_prepare(fup, name, 0);
if (error)
return (UPDATER_ERR_PROTO);
error = updater_delete(up, fup);
@@ -520,6 +558,332 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
return (UPDATER_ERR_MSG);
}
break;
+ case 'a':
+ name = proto_get_ascii(&line);
+ attr = proto_get_ascii(&line);
+ if (name == NULL || attr == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 1);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ fup->temppath = tempname(fup->destpath);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILEDEAD;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ if (sr->sr_serverattr == NULL)
+ return (UPDATER_ERR_PROTO);
+ lprintf(1, " Create %s -> Attic\n", name);
+ error = updater_addfile(up, fup, attr);
+ if (error)
+ return (error);
+ break;
+
+ case 'A':
+ case 'R': // XXX
+ name = proto_get_ascii(&line);
+ attr = proto_get_ascii(&line);
+ if (name == NULL || attr == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+
+ fup->temppath = tempname(fup->destpath);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILELIVE;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ if (sr->sr_serverattr == NULL)
+ return (UPDATER_ERR_PROTO);
+ lprintf(1, " Create %s\n", name);
+ error = updater_addfile(up, fup, attr);
+ if (error)
+ return (error);
+ break;
+ case 'r': /* XXX: rsync support. */
+ break;
+
+ case 'I':
+ /*
+ * Create directory and add DirDown entry in status
+ * file.
+ */
+ name = proto_get_ascii(&line);
+ if (name == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_DIRDOWN;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = NULL;
+ sr->sr_clientattr = fattr_new(FT_DIRECTORY, -1);
+ fattr_mergedefault(sr->sr_clientattr);
+
+ error = mkdirhier(fup->destpath, coll->co_umask);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ if (access(fup->destpath, F_OK) != 0) {
+ lprintf(1, " Mkdir %s\n", name);
+ error = fattr_makenode(sr->sr_clientattr,
+ fup->destpath);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ }
+ error = status_put(fup->st, sr);
+ if (error) {
+ up->errmsg = status_errmsg(fup->st);
+ return (UPDATER_ERR_MSG);
+ }
+ break;
+ case 'i':
+ /* Remove DirDown entry in status file. */
+ name = proto_get_ascii(&line);
+ if (name == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ error = status_delete(fup->st, name, 0);
+ if (error) {
+ up->errmsg = status_errmsg(fup->st);
+ return (UPDATER_ERR_MSG);
+ }
+ break;
+ case 'J':
+ /*
+ * Set attributes of directory and update DirUp entry in
+ * status file.
+ */
+ name = proto_get_ascii(&line);
+ if (name == NULL)
+ return (UPDATER_ERR_PROTO);
+ attr = proto_get_ascii(&line);
+ if (attr == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ error = updater_setdirattrs(up, coll, fup, name, attr);
+ if (error)
+ return (error);
+ break;
+ case 'j':
+ /*
+ * Remove directory and delete its DirUp entry in status
+ * file.
+ */
+ name = proto_get_ascii(&line);
+ if (name == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ lprintf(1, " Rmdir %s\n", name);
+ updater_deletefile(fup->destpath);
+ error = status_delete(fup->st, name, 0);
+ if (error) {
+ up->errmsg = status_errmsg(fup->st);
+ return (UPDATER_ERR_MSG);
+ }
+ break;
+#if 0
+ case 'h':
+ /* XXX: SR_LINKFILEDEAD. */
+ case 'H':
+ lprintf(1, "Got 'H'\n");
+ break;
+#endif
+ case 'l':
+ name = proto_get_ascii(&line);
+ if (name == NULL)
+ return (UPDATER_ERR_PROTO);
+ attr = proto_get_ascii(&line);
+ if (attr == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILEDEAD;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ sr->sr_clientattr = fattr_decode(attr);
+ if (sr->sr_serverattr == NULL ||
+ sr->sr_clientattr == NULL)
+ return (UPDATER_ERR_PROTO);
+
+ /* Save space. Described in detail in updatefile. */
+ if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT)
+ || fattr_getlinkcount(sr->sr_clientattr) <= 1)
+ fattr_maskout(sr->sr_clientattr,
+ FA_DEV | FA_INODE);
+ fattr_maskout(sr->sr_clientattr, FA_FLAGS);
+ error = status_put(fup->st, sr);
+ if (error) {
+ up->errmsg = status_errmsg(fup->st);
+ return (UPDATER_ERR_MSG);
+ }
+ break;
+ case 'L':
+ name = proto_get_ascii(&line);
+ if (name == NULL)
+ return (UPDATER_ERR_PROTO);
+ attr = proto_get_ascii(&line);
+ if (attr == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILELIVE;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ sr->sr_clientattr = fattr_decode(attr);
+ if (sr->sr_serverattr == NULL ||
+ sr->sr_clientattr == NULL)
+ return (UPDATER_ERR_PROTO);
+ /* Save space. Described in detail in updatefile. */
+ if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT)
+ || fattr_getlinkcount(sr->sr_clientattr) <= 1)
+ fattr_maskout(sr->sr_clientattr,
+ FA_DEV | FA_INODE);
+ fattr_maskout(sr->sr_clientattr, FA_FLAGS);
+ error = status_put(fup->st, sr);
+ if (error) {
+ up->errmsg = status_errmsg(fup->st);
+ return (UPDATER_ERR_MSG);
+ }
+ break;
+ case 'n':
+ name = proto_get_ascii(&line);
+ attr = proto_get_ascii(&line);
+ if (name == NULL || attr == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 1);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILEDEAD;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ sr->sr_clientattr = fattr_new(FT_SYMLINK, -1);
+ fattr_mergedefault(sr->sr_clientattr);
+ error = updater_updatenode(up, coll, fup, name, attr);
+ if (error)
+ return (error);
+ break;
+ case 'N':
+ name = proto_get_ascii(&line);
+ attr = proto_get_ascii(&line);
+ if (name == NULL || attr == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILELIVE;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ sr->sr_clientattr = fattr_new(FT_SYMLINK, -1);
+ fattr_mergedefault(sr->sr_clientattr);
+ fattr_maskout(sr->sr_clientattr, FA_FLAGS);
+ error = updater_updatenode(up, coll, fup, name, attr);
+ if (error)
+ return (error);
+ break;
+ case 'v':
+ name = proto_get_ascii(&line);
+ attr = proto_get_ascii(&line);
+ optstr = proto_get_ascii(&line);
+ wantmd5 = proto_get_ascii(&line);
+ rcsopt = NULL; /*rcs_decode(optstr);*/
+ if (attr == NULL || line != NULL || wantmd5 == NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 1);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ fup->temppath = tempname(fup->destpath);
+ fup->wantmd5 = xstrdup(wantmd5);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILEDEAD;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ if (sr->sr_serverattr == NULL)
+ return (UPDATER_ERR_PROTO);
+
+ error = 0;
+ error = updater_rcsedit(up, fup, name, rcsopt);
+ if (error)
+ return (error);
+ break;
+ case 'V':
+ name = proto_get_ascii(&line);
+ attr = proto_get_ascii(&line);
+ optstr = proto_get_ascii(&line);
+ wantmd5 = proto_get_ascii(&line);
+ rcsopt = NULL; /*rcs_decode(optstr);*/
+ if (attr == NULL || line != NULL || wantmd5 == NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+ fup->temppath = tempname(fup->destpath);
+ fup->wantmd5 = xstrdup(wantmd5);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILELIVE;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ if (sr->sr_serverattr == NULL)
+ return (UPDATER_ERR_PROTO);
+
+ error = updater_rcsedit(up, fup, name, rcsopt);
+ if (error)
+ return (error);
+ break;
+/*
+ X <file> <attr>
+ Receive the live RCS file <file> in its entirety, as a fixup.
+ Set its attributes to <attr>. The data follows, in the same
+ format as for the "A" command. CVS mode only.
+
+ x <file> <attr>
+ Like "X", but put the file into the Attic. CVS mode only.
+*/
+#if 0
+ case 'X':
+ case 'x':
+ lprintf(1, "Got X\n");
+ break;
+#endif
+/*
+ Z <file> <attr> <pos>
+ Append some new data to the end of the existing file <file>,
+ and set its attributes to <attr>. The data should be written
+ to the file starting at file offset <pos>, which should be
+ exactly at the end of the file. Exactly n bytes of data follow,
+ where n is the size attribute minus <pos>. After the data
+ comes a terminating line, as in the "A" command. The number
+ of bytes n can be 0, in which case the file's attributes are
+ simply updated.
+*/
+ case 'Z':
+ name = proto_get_ascii(&line);
+ attr = proto_get_ascii(&line);
+ pos = proto_get_ascii(&line);
+ if (name == NULL || attr == NULL || pos == NULL || line != NULL)
+ return (UPDATER_ERR_PROTO);
+ error = fup_prepare(fup, name, 0);
+ fup->temppath = tempname(fup->destpath);
+ sr = &fup->srbuf;
+ sr->sr_type = SR_FILELIVE;
+ sr->sr_file = xstrdup(name);
+ sr->sr_serverattr = fattr_decode(attr);
+ if (sr->sr_serverattr == NULL)
+ return (UPDATER_ERR_PROTO);
+ position = strtol(pos, NULL, 10);
+ error = updater_append_file(up, fup, position);
+ if (error)
+ return (error);
+ break;
case '!':
/* Warning from server. */
msg = proto_get_rest(&line);
@@ -528,6 +892,7 @@ updater_docoll(struct updater *up, struct file_update *fup, int isfixups)
lprintf(-1, "Server warning: %s\n", msg);
break;
default:
+ lprintf(-1, "Unknown command: %s\n", cmd);
return (UPDATER_ERR_PROTO);
}
fup_cleanup(fup);
@@ -717,6 +1082,8 @@ updater_updatefile(struct updater *up, struct file_update *fup,
if (coll->co_options & CO_CHECKOUTMODE)
fattr_maskout(sr->sr_clientattr, FA_COIGNORE);
+ /* XXX: Mask out chflags for now. Cvsup doesn't record them. */
+ /*fattr_maskout(sr->sr_clientattr, FA_FLAGS);*/
error = status_put(st, sr);
if (error) {
up->errmsg = status_errmsg(st);
@@ -725,6 +1092,55 @@ updater_updatefile(struct updater *up, struct file_update *fup,
return (0);
}
+/*
+ * Update attributes of a directory.
+ */
+static int
+updater_setdirattrs(struct updater *up, struct coll *coll,
+ struct file_update *fup, char *name, char *attr)
+{
+ struct statusrec *sr;
+ struct fattr *fa;
+ int error, rv;
+
+ sr = &fup->srbuf;
+ sr->sr_type = SR_DIRUP;
+ sr->sr_file = xstrdup(name);
+ sr->sr_clientattr = fattr_decode(attr);
+ sr->sr_serverattr = fattr_decode(attr);
+ if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL)
+ return (UPDATER_ERR_PROTO);
+ fattr_mergedefault(sr->sr_clientattr);
+ fattr_umask(sr->sr_clientattr, coll->co_umask);
+ rv = fattr_install(sr->sr_clientattr, fup->destpath, NULL);
+ lprintf(1, " SetAttrs %s\n", name);
+ if (rv == -1) {
+ xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s",
+ fup->temppath, fup->destpath, strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ /*
+ * Now, make sure they were set and record what was set in the status
+ * file.
+ */
+ fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
+ if (fa == NULL) {
+ xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath,
+ strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ fattr_free(sr->sr_clientattr);
+ fattr_maskout(fa, FA_FLAGS);
+ sr->sr_clientattr = fa;
+ error = status_put(fup->st, sr);
+ if (error) {
+ up->errmsg = status_errmsg(fup->st);
+ return (UPDATER_ERR_MSG);
+ }
+
+ return (0);
+}
+
static int
updater_diff(struct updater *up, struct file_update *fup)
{
@@ -812,6 +1228,9 @@ updater_diff(struct updater *up, struct file_update *fup)
return (error);
}
+/*
+ * Edit a file and add delta.
+ */
static int
updater_diff_batch(struct updater *up, struct file_update *fup)
{
@@ -895,7 +1314,7 @@ updater_diff_apply(struct updater *up, struct file_update *fup, char *state)
di->di_state = state;
di->di_expand = fup->expand;
- error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di);
+ error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di, 1);
if (error) {
/* XXX Bad error message */
xasprintf(&up->errmsg, "Bad diff from server");
@@ -904,6 +1323,172 @@ updater_diff_apply(struct updater *up, struct file_update *fup, char *state)
return (0);
}
+/* Update or create a node. */
+static int
+updater_updatenode(struct updater *up, struct coll *coll, struct file_update *fup, char *name,
+ char *attr)
+{
+ struct fattr *fa, *fileattr;
+ struct status *st;
+ struct statusrec *sr;
+ int error, issymlink, rv;
+
+ sr = &fup->srbuf;
+ st = fup->st;
+ fa = fattr_decode(attr);
+
+ if (fattr_type(fa) == FT_SYMLINK) {
+ lprintf(1, " Symlink %s -> %s\n", name,
+ fattr_getlinktarget(fa));
+ issymlink = 1;
+ } else {
+ lprintf(1, " Mknod %s\n", name);
+ issymlink = 0;
+ }
+
+ /* Create directory. */
+ error = mkdirhier(fup->destpath, coll->co_umask);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+
+ /* If it exists, update attributes. */
+ if (access(fup->destpath, F_OK) != 0)
+ fattr_makenode(fa, fup->destpath);
+
+ /*
+ * Coming from attic? I don't think this is a problem since we have
+ * determined attic before we call this function (Look at UpdateNode in
+ * cvsup).
+ */
+ fattr_umask(fa, coll->co_umask);
+ rv = fattr_install(fa, fup->destpath, fup->temppath);
+ if (rv == -1) {
+ xasprintf(&up->errmsg, "Cannot update attributes on "
+ "\"%s\": %s", fup->destpath, strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ /* XXX Executes */
+ /*
+ * We weren't necessarily able to set all the file attributes to the
+ * desired values, and any executes may have altered the attributes.
+ * To make sure we record the actual attribute values, we fetch
+ * them from the file.
+ *
+ * However, we preserve the link count as received from the
+ * server. This is important for preserving hard links in mirror
+ * mode.
+ */
+ fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
+ if (fileattr == NULL) {
+ xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath,
+ strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT);
+ fattr_free(sr->sr_clientattr);
+ sr->sr_clientattr = fileattr;
+
+ /*
+ * To save space, don't write out the device and inode unless
+ * the link count is greater than 1. These attributes are used
+ * only for detecting hard links. If the link count is 1 then we
+ * know there aren't any hard links.
+ */
+ if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
+ fattr_getlinkcount(sr->sr_clientattr) <= 1)
+ fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
+
+ /* If it is a symlink, write only out it's path. */
+ if (fattr_type(fa) == FT_SYMLINK) {
+ fattr_maskout(sr->sr_clientattr, ~(FA_FILETYPE |
+ FA_LINKTARGET));
+ }
+ fattr_maskout(sr->sr_clientattr, FA_FLAGS);
+ error = status_put(st, sr);
+ if (error) {
+ up->errmsg = status_errmsg(st);
+ return (UPDATER_ERR_MSG);
+ }
+ fattr_free(fa);
+
+ return (0);
+}
+
+/*
+ * Fetches a new file in CVS mode.
+ */
+static int
+updater_addfile(struct updater *up, struct file_update *fup, char *attr)
+{
+ char md5[MD5_DIGEST_SIZE];
+ struct coll *coll;
+ struct stream *to;
+ struct statusrec *sr;
+ struct fattr *fa;
+ char *path, *line, *cmd;
+ int error;
+ off_t fsize;
+ char buf[BUFSIZE];
+ ssize_t nread;
+ off_t remains;
+
+ coll = fup->coll;
+ path = fup->destpath;
+ nread = 0;
+ sr = &fup->srbuf;
+ fa = fattr_decode(attr);
+ fsize = fattr_filesize(fa);
+
+ error = mkdirhier(path, coll->co_umask);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+
+ to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755);
+ if (to == NULL) {
+ xasprintf(&up->errmsg, "%s: Cannot create: %s",
+ fup->temppath, strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ stream_filter_start(to, STREAM_FILTER_MD5, md5);
+ remains = fsize;
+ do {
+ nread = stream_read(up->rd, buf, (BUFSIZE > remains ?
+ remains : BUFSIZE));
+ remains -= nread;
+ stream_write(to, buf, nread);
+ } while (remains > 0);
+ stream_flush(to);
+ stream_filter_stop(to);
+ stream_close(to);
+ line = stream_getln(up->rd, NULL);
+ if (line == NULL)
+ return (UPDATER_ERR_PROTO);
+ /* Check for EOF. */
+ if (!(*line == '.' || (strncmp(line, ".<", 2) != 0)))
+ return (UPDATER_ERR_PROTO);
+ line = stream_getln(up->rd, NULL);
+ if (line == NULL)
+ return (UPDATER_ERR_PROTO);
+
+ cmd = proto_get_ascii(&line);
+ fup->wantmd5 = proto_get_ascii(&line);
+ if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
+ return (UPDATER_ERR_PROTO);
+
+ /* UPDATE FILE. */
+ sr->sr_clientattr = fattr_frompath(fup->temppath, FATTR_NOFOLLOW);
+ if (sr->sr_clientattr == NULL)
+ return (UPDATER_ERR_PROTO);
+ fattr_override(sr->sr_clientattr, sr->sr_serverattr,
+ FA_MODTIME | FA_MASK);
+ error = updater_updatefile(up, fup, md5, 0);
+ fup->wantmd5 = NULL; /* So that it doesn't get freed. */
+ /* UPDATE IT. */
+ if (error)
+ return (error);
+ return (0);
+}
+
static int
updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
{
@@ -933,7 +1518,7 @@ updater_checkout(struct updater *up, struct file_update *fup, int isfixup)
}
to = stream_open_file(fup->temppath,
- O_WRONLY | O_CREAT | O_TRUNC, 0600);
+ O_WRONLY | O_CREAT | O_TRUNC, 0600); /*XXX: Change to correct perm*/
if (to == NULL) {
xasprintf(&up->errmsg, "%s: Cannot create: %s",
fup->temppath, strerror(errno));
@@ -1009,3 +1594,368 @@ updater_prunedirs(char *base, char *file)
return;
}
}
+
+/*
+ * Edit an RCS file.
+ */
+static int
+updater_rcsedit(struct updater *up, struct file_update *fup, char *name,
+ char *rcsopt)
+{
+ struct coll *coll;
+ struct stream *dest;
+ struct statusrec *sr;
+ struct status *st;
+ struct rcsfile *rf;
+ struct fattr *oldfattr;
+ char md5[MD5_DIGEST_SIZE];
+ char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath;
+ int error, changed;
+
+ rcsopt = NULL; /* XXX: just for now. */
+ coll = fup->coll;
+ sr = &fup->srbuf;
+ st = fup->st;
+ temppath = fup->temppath;
+ path = fup->origpath != NULL ? fup->origpath : fup->destpath;
+ changed = 0;
+ error = 0;
+
+ /* If the path is new, we must create the Attic dir if needed. */
+ if (fup->origpath != NULL) {
+ error = mkdirhier(fup->destpath, coll->co_umask);
+ if (error) {
+ xasprintf(&up->errmsg, "Unable to create Attic dir for "
+ "%s\n", fup->origpath);
+ return (UPDATER_ERR_MSG);
+ }
+ }
+ /*
+ * XXX: we could avoid parsing overhead if we're reading ahead before we
+ * parse the file.
+ */
+ rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag);
+ if (rf == NULL) {
+ xasprintf(&up->errmsg, "Error reading rcsfile %s\n", name);
+ return (UPDATER_ERR_MSG);
+ }
+ oldfattr = fattr_frompath(path, FATTR_NOFOLLOW);
+ if (oldfattr == NULL) {
+ xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path,
+ strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ fattr_merge(sr->sr_serverattr, oldfattr);
+
+ while ((line = stream_getln(up->rd, NULL)) != NULL) {
+ if (strcmp(line, ".") == 0)
+ break;
+ cmd = proto_get_ascii(&line);
+ if (cmd == NULL) {
+ fprintf(stderr, "Error when adding delta\n");
+ return (UPDATER_ERR_PROTO);
+ }
+ switch(cmd[0]) {
+ case 'B':
+ branch = proto_get_ascii(&line);
+ if (branch == NULL || line != NULL) {
+ fprintf(stderr, "problems with branch\n");
+ return (UPDATER_ERR_PROTO);
+ }
+ rcsfile_setval(rf, RCSFILE_BRANCH, branch);
+ changed = 1;
+ break;
+ case 'b':
+ rcsfile_setval(rf, RCSFILE_BRANCH, NULL);
+ changed = 1;
+ break;
+ case 'D':
+ error = updater_addelta(rf, up->rd, line);
+ changed = 1;
+ if (error)
+ return (error);
+ break;
+ case 'd':
+ revnum = proto_get_ascii(&line);
+ if (revnum == NULL || line != NULL) {
+ fprintf(stderr, "Problems with delta\n");
+ return (UPDATER_ERR_PROTO);
+ }
+ rcsfile_deleterev(rf, revnum);
+ changed = 1;
+ break;
+ case 'E':
+ expand = proto_get_ascii(&line);
+ if (expand == NULL || line != NULL) {
+ fprintf(stderr, "Expand\n");
+ return (UPDATER_ERR_PROTO);
+ }
+ rcsfile_setval(rf, RCSFILE_EXPAND, expand);
+ changed = 1;
+ break;
+ case 'T':
+ tag = proto_get_ascii(&line);
+ revnum = proto_get_ascii(&line);
+ if (tag == NULL || revnum == NULL ||
+ line != NULL) {
+ fprintf(stderr, "Add tag\n");
+ return (UPDATER_ERR_PROTO);
+ }
+ rcsfile_addtag(rf, tag, revnum);
+ changed = 1;
+ break;
+ case 't':
+ tag = proto_get_ascii(&line);
+ revnum = proto_get_ascii(&line);
+ if (tag == NULL || revnum == NULL ||
+ line != NULL) {
+ fprintf(stderr, "Delete tag\n");
+ return (UPDATER_ERR_PROTO);
+ }
+ rcsfile_deletetag(rf, tag, revnum);
+ changed = 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown %s\n", line);
+ return (UPDATER_ERR_PROTO);
+ break;
+ }
+ }
+
+ if (!changed) {
+ fattr_maskout(oldfattr, ~FA_MODTIME);
+ if (fattr_equal(oldfattr, sr->sr_serverattr) == 0)
+ lprintf(1, " SetAttrs %s", fup->coname);
+ else
+ lprintf(1, " Touch %s", fup->coname);
+ if (fup->attic)
+ lprintf(1, " -> Attic");
+ lprintf(1, "\n");
+ fattr_free(oldfattr);
+ goto finish;
+ }
+ lprintf(1, " Edit %s", fup->coname);
+ if (fup->attic)
+ lprintf(1, " -> Attic");
+ lprintf(1, "\n");
+
+ /* Write and rename temp file. */
+ dest = stream_open_file(fup->temppath,
+ O_RDWR | O_CREAT | O_TRUNC, 0600);
+ if (dest == NULL) {
+ xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n",
+ fup->temppath, strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5);
+ error = rcsfile_write(rf, dest);
+ stream_flush(dest);
+ stream_filter_stop(dest);
+ stream_close(dest);
+ rcsfile_free(rf);
+ if (error)
+ return (UPDATER_ERR_PROTO);
+
+finish:
+ sr->sr_clientattr = fattr_frompath(path, FATTR_NOFOLLOW);
+ if (sr->sr_clientattr == NULL) {
+ xasprintf(&up->errmsg, "%s: Cannot get attributes: %s",
+ fup->destpath, strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ fattr_override(sr->sr_clientattr, sr->sr_serverattr,
+ FA_MODTIME | FA_MASK);
+ if (changed) {
+ error = updater_updatefile(up, fup, md5, 0);
+ fup->wantmd5 = NULL; /* So that it doesn't get freed. */
+ if (error)
+ return (error);
+ } else {
+ /* Record its attributes since we touched it. */
+ if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) ||
+ fattr_getlinkcount(sr->sr_clientattr) <= 1)
+ fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE);
+ error = status_put(st, sr);
+ if (error) {
+ up->errmsg = status_errmsg(st);
+ return (UPDATER_ERR_MSG);
+ }
+ }
+
+ /* In this case, we need to remove the old file afterwards. */
+ /* XXX: Can we be sure that a file not edited is moved? I don't think
+ * this is a problem, since if a file is moved, it should be edited to
+ * show if it's dead or not.
+ */
+ if (fup->origpath != NULL) {
+ /*
+ * XXX: Should we track delete count or is this a "silent"
+ * delete?
+ */
+ updater_deletefile(fup->origpath);
+ }
+ return (0);
+}
+
+/*
+ * Add a delta to a RCS file.
+ */
+int
+updater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline)
+{
+ struct delta *d;
+ char *author, *cmd, *diffbase, *line, *logline, *revdate, *revnum, *state,
+ *textline;
+ size_t size;
+ int stop;
+
+ revnum = proto_get_ascii(&cmdline);
+ diffbase = proto_get_ascii(&cmdline); /* XXX: diffBase. */
+ revdate = proto_get_ascii(&cmdline);
+ author = proto_get_ascii(&cmdline);
+ size = 0;
+
+ if (revnum == NULL || revdate == NULL || author == NULL)
+ return (UPDATER_ERR_PROTO);
+
+ /* First add the delta so we have it. */
+ d = rcsfile_addelta(rf, revnum, revdate, author, diffbase);
+ if (d == NULL)
+ err(1, "Error adding delta %s\n", revnum);
+ while ((line = stream_getln(rd, NULL)) != NULL) {
+ if (strcmp(line, ".") == 0)
+ break;
+ cmd = proto_get_ascii(&line);
+ switch (cmd[0]) {
+ case 'L':
+ /* Do the same as in 'C' command. */
+ logline = stream_getln(rd, &size);
+ while (logline != NULL) {
+ if (size == 2 && *logline == '.')
+ break;
+ if (size == 3 &&
+ memcmp(logline, ".+", 2) == 0) {
+ rcsdelta_truncatelog(d, -1);
+ break;
+ }
+ if (size >= 3 &&
+ memcmp(logline, "..", 2) == 0) {
+ size--;
+ logline++;
+ }
+ rcsdelta_appendlog(d, logline, size);
+ logline = stream_getln(rd, &size);
+ }
+ break;
+ case 'N':
+ case 'n':
+ /* XXX: Not implemented. */
+ break;
+ case 'S':
+ state = proto_get_ascii(&line);
+ if (state == NULL)
+ return (UPDATER_ERR_PROTO);
+ rcsdelta_setstate(d, state);
+ break;
+ case 'T':
+ /* Do the same as in 'C' command. */
+ stop = 0;
+ textline = stream_getln(rd, &size);
+ while (textline != NULL) {
+ if (size == 2 && *textline == '.')
+ stop = 1;
+ if (size == 3 &&
+ memcmp(textline, ".+", 2) == 0) {
+ /* Truncate newline. */
+ stop = 1;
+ }
+ if (size >= 3 &&
+ memcmp(textline, "..", 2) == 0) {
+ size--;
+ textline++;
+ }
+ rcsdelta_appendtext(d, textline, size);
+ if (stop)
+ break;
+ textline = stream_getln(rd, &size);
+ }
+ break;
+ }
+ }
+
+ return (0);
+}
+
+int
+updater_append_file(struct updater *up, struct file_update *fup, off_t pos)
+{
+ struct fattr *fa;
+ struct stream *to;
+ struct statusrec *sr;
+ char buf[BUFSIZE], *line, *cmd;
+ char md5[MD5_DIGEST_SIZE];
+ int error, fd;
+ off_t bytes;
+ ssize_t nread;
+
+ sr = &fup->srbuf;
+ fa = sr->sr_serverattr;
+ to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC,
+ 0755);
+ if (to == NULL) {
+ xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath,
+ strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+ fd = open(fup->destpath, O_RDONLY);
+ if (fd < 0) {
+ xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath,
+ strerror(errno));
+ return (UPDATER_ERR_MSG);
+ }
+
+ stream_filter_start(to, STREAM_FILTER_MD5, md5);
+ /* First write the existing content. */
+ while ((nread = read(fd, buf, BUFSIZE)) > 0)
+ stream_write(to, buf, nread);
+ close(fd);
+
+ bytes = fattr_filesize(fa) - pos;
+ /* Append the new data. */
+ do {
+ nread = stream_read(up->rd, buf, (BUFSIZE > bytes) ? bytes : BUFSIZE);
+ bytes -= nread;
+ stream_write(to, buf, nread);
+ } while (bytes > 0);
+ stream_flush(to);
+ stream_filter_stop(to);
+ stream_close(to);
+
+ line = stream_getln(up->rd, NULL);
+ if (line == NULL)
+ return (UPDATER_ERR_PROTO);
+ /* Check for EOF. */
+ if (!(*line == '.' || (strncmp(line, ".<", 2) != 0)))
+ return (UPDATER_ERR_PROTO);
+ line = stream_getln(up->rd, NULL);
+ if (line == NULL)
+ return (UPDATER_ERR_PROTO);
+
+ cmd = proto_get_ascii(&line);
+ fup->wantmd5 = proto_get_ascii(&line);
+ if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0)
+ return (UPDATER_ERR_PROTO);
+
+ /* UPDATE FILE. */
+ sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW);
+ if (sr->sr_clientattr == NULL)
+ return (UPDATER_ERR_PROTO);
+ fattr_override(sr->sr_clientattr, sr->sr_serverattr,
+ FA_MODTIME | FA_MASK);
+ error = updater_updatefile(up, fup, md5, 0);
+ fup->wantmd5 = NULL; /* So that it doesn't get freed. */
+ /* UPDATE IT. */
+ if (error)
+ return (error);
+ return (0);
+}
OpenPOWER on IntegriCloud