Index: Makefile =================================================================== RCS file: /srv/ncvs/src/contrib/csup/Makefile,v retrieving revision 1.1.1.2 diff -u -u -r1.1.1.2 Makefile --- Makefile 14 Mar 2006 03:51:13 -0000 1.1.1.2 +++ Makefile 6 Mar 2008 12:13:43 -0000 @@ -9,10 +9,11 @@ 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... Index: config.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/config.c,v retrieving revision 1.1.1.2 diff -u -u -r1.1.1.2 config.c --- config.c 14 Mar 2006 03:51:13 -0000 1.1.1.2 +++ config.c 6 Mar 2008 12:13:43 -0000 @@ -444,10 +444,10 @@ "\"%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) { Index: detailer.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/detailer.c,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 detailer.c --- detailer.c 3 Mar 2006 04:11:26 -0000 1.1.1.1 +++ detailer.c 6 Mar 2008 12:13:43 -0000 @@ -30,6 +30,10 @@ #include #include #include +#include + +#include +#include #include "config.h" #include "detailer.h" @@ -37,6 +41,7 @@ #include "misc.h" #include "mux.h" #include "proto.h" +#include "rcsfile.h" #include "status.h" #include "stream.h" @@ -56,8 +61,13 @@ 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 *, struct status *, int); void * detailer(void *arg) @@ -186,8 +196,13 @@ } if (fixup->f_coll != coll) break; - error = proto_printf(wr, "Y %s %s %s\n", fixup->f_name, - coll->co_tag, coll->co_date); + if (coll->co_options & CO_CHECKOUTMODE) + error = proto_printf(wr, "Y %s %s %s\n", + fixup->f_name, coll->co_tag, coll->co_date); + else { + error = proto_printf(wr, "A %s\n", + fixup->f_name); + } if (error) return (DETAILER_ERR_WRITE); fixup = NULL; @@ -208,12 +223,14 @@ 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; + int error, attic; rd = d->rd; wr = d->wr; + attic = 0; line = stream_getln(rd, NULL); if (line == NULL) return (DETAILER_ERR_READ); @@ -231,12 +248,73 @@ if (error) return (DETAILER_ERR_WRITE); break; + case 'I': + case 'i': + case 'j': + /* Directory operations. */ + /* lprintf(1, "Line %s\n", line);*/ + file = proto_get_ascii(&line); + if (file == NULL || line != NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s\n", cmd, file); + if (error) + return (DETAILER_ERR_WRITE); + break; + case 'J': + /* Set directory attributes. */ + file = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (file == NULL || line != NULL || attr == NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s %s\n", cmd, file, attr); + if (error) + return (DETAILER_ERR_WRITE); + break; + case 'H': + case 'h': + /* Create a hard link. */ + file = proto_get_ascii(&line); + target = proto_get_ascii(&line); + if (file == NULL || target == NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s %s\n", cmd, file, + target); + break; + case 't': + /*lprintf(1, "ADD attic RCS: %s\n", line); */ + 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, + st, 1); + break; + + case 'T': +/* lprintf(1, "ADD RCS: %s\n", line); */ + 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, + st, 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 + error = DETAILER_ERR_PROTO; if (error) return (error); break; @@ -248,6 +326,7 @@ 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 +340,94 @@ 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); + } + proto_printf(wr, "R %s %u %s\n", name, st.st_size, md5); + return (0); +} + +/* + * Tell the server to update an RCS file that we have, or send it if we don't. + */ +static int +detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name, + char *path) +{ + struct stream *wr; + struct fattr *fa; + struct rcsfile *rf; + int error; + int dummy; + + wr = d->wr; + + 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); + return (0); + } + + /* + * XXX: Until we have working RCS stuff. + */ +/* if (1) { + error = proto_printf(wr, "A %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + return (0); + }*/ + + rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag); + if (rf == NULL) { + lprintf(-1, "Error parsing, resend file.\n"); + /* XXX: Perhaps another command?. */ + 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_print(rf);*/ +/* XXX: try to check how we're doing. + dest = stream_open_file("/home/lulf/testdetailerproto", O_WRONLY | + O_CREAT | O_TRUNC, 0755); + rcsfile_send_details(rf, dest); + stream_close(dest);*/ + rcsfile_send_details(rf, wr); + rcsfile_free(rf); + fattr_free(fa); + return (0); +} + static int -detailer_dofile(struct detailer *d, struct coll *coll, struct status *st, +detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st, char *file) { char md5[MD5_DIGEST_SIZE]; @@ -337,3 +502,62 @@ return (DETAILER_ERR_WRITE); return (0); } + +int +detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name, + struct fattr *server_attr, struct status *st, int attic) +{ + struct fattr *fa; + char *attr, *path; + size_t len; + int error; + int dummy; + + /* XXX */ + if (st != NULL) + dummy = 1; + /* XXX: Do as cvsup and be careful to check if we're in checkout mode?. + * */ +/* lprintf(1, "Attic %d and path %s coll_tag %s\n", attic, file, + coll->co_tag);*/ + path = cvspath(coll->co_prefix, name, attic); + if (path == NULL) + return (DETAILER_ERR_PROTO); + + fa = fattr_frompath(path, FATTR_NOFOLLOW); + if (fa != NULL && fattr_equal(fa, server_attr)) { + attr = fattr_encode(fa, NULL, 0); + if (attic) { + /* lprintf(1, "-> l %s %s\n", name, attr);*/ + error = proto_printf(d->wr, "l %s %s\n", name, attr); + } else { + /* lprintf(1, "-> L %s %s\n", name, attr);*/ + error = proto_printf(d->wr, "L %s %s\n", name, attr); + } + if (error) + return (DETAILER_ERR_WRITE); + /* XXX: Case where we don't have complete attr. */ + free(path); + free(attr); + fattr_free(fa); + return (0); + } + /* We don't have it, so tell the server to send it. */ +/* lprintf(1, "Tell to send %s\n", file);*/ + if (fa == NULL) { + lprintf(1, "-> A %s\n", name); + 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 + lprintf(1, "Nodes support not implemented\n"); + free(path); + fattr_free(fa); + return (0); +} Index: diff.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/diff.c,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 diff.c --- diff.c 3 Mar 2006 04:11:26 -0000 1.1.1.1 +++ diff.c 6 Mar 2008 12:13:43 -0000 @@ -31,11 +31,13 @@ #include #include #include +#include #include "diff.h" #include "keyword.h" #include "misc.h" #include "stream.h" +#include "queue.h" typedef long lineno_t; @@ -45,6 +47,9 @@ /* 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 +60,23 @@ 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 +117,7 @@ line = stream_getln(rd, &size); if (line == NULL) return (-1); - if (line[0] == '.') { + if (comode && line[0] == '.') { line++; size--; } @@ -143,6 +156,237 @@ 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; + /*fprintf(stderr, "Outputting d%ld %ld\n", firstoutputlinedeleted, + num_deleted);*/ + stream_printf(dest, "d%ld %ld\n", firstoutputlinedeleted, + num_deleted); + if (num_added <= 0) + continue; + } + if (num_added > 0) { + /*fprintf(stderr, "Outputtig a%ld %ld\n", ec->key, + * num_added);*/ + stream_printf(dest, "a%ld %ld\n", ec->key, num_added); + startline = ec->key - num_deleted + 1 + ec->offset; + endline = startline + num_added - 1; + + /*fprintf(stderr, "Getting from line %d to %d\n", startline, + endline);*/ + /* Copy lines from original file. First ignore some. */ + ec->editline = editline; + diff_ignoreln(ec, startline - 1); + /*fprintf(stderr, "Ignored lines\n");*/ + 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); + + /*fprintf(stderr, "Inserting edit key %d\n", ec->key);*/ + 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; +} + +/* + * Apply a diff of a delta to it's diffbase, and also apply the diffbase diff + * onto its diffbase if necessary. + */ +/* + * Write the reverse diff from the diff in rd, and original file into + * destination. + */ +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); + + /*fprintf(stderr, "Starting up...\n");*/ + + /* 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; + /*fprintf(stderr, "Add command key %d, count %d, \n", + * addec->key, addec->count);*/ + 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 { + /* XXX: Debug. */ + if (delec != NULL) { + diff_insert_edit(&ds, delec); + } + delec = NULL; + addec->offset = offset; + /*fprintf(stderr, "Add edit\n");*/ + diff_insert_edit(&ds, addec); + addec = NULL; + } + offset -= ec.count; + } else if (ec.cmd == EC_DEL) { + if (delec != NULL) { + /* Update offset to our next. */ + /* XXX: This will probably fail sometime. */ + diff_insert_edit(&ds, delec); + delec = NULL; + } + delec = xmalloc(sizeof(struct editcmd)); + malloccount++; + *delec = ec; + delec->key = delec->where - 1 - offset; + /*fprintf(stderr, "Delete command, key %d\n", + * delec->key);*/ + 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++; + /* XXX: find a correct solution. This will break on large files. The + * value should be the length of the file we're fetching lines from. + * Put a very large 32-bit value there for now. + */ + addec->key = 2147483640; + 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 +438,22 @@ 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) Index: diff.h =================================================================== RCS file: /srv/ncvs/src/contrib/csup/diff.h,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 diff.h --- diff.h 3 Mar 2006 04:11:26 -0000 1.1.1.1 +++ diff.h 6 Mar 2008 12:13:43 -0000 @@ -45,6 +45,8 @@ }; 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_ */ Index: fattr.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/fattr.c,v retrieving revision 1.1.1.2 diff -u -u -r1.1.1.2 fattr.c --- fattr.c 14 Mar 2006 03:51:12 -0000 1.1.1.2 +++ fattr.c 6 Mar 2008 12:13:43 -0000 @@ -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 @@ -534,6 +534,13 @@ 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 @@ -730,25 +737,41 @@ fattr_makenode(const struct fattr *fa, const char *path) { mode_t modemask, mode; + char *target, *temp; + const char *last; 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) { + last = pathlast(path); + temp = xstrdup(path); + temp[strlen(path) - strlen(last) - 1] = '\0'; + xasprintf(&target, "%s/%s", temp, fa->linktarget); + error = symlink(target, path); + free(temp); + } 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 +853,10 @@ error = rmdir(topath); else error = unlink(topath); - if (error) + if (error) { + printf("RMDING\n"); goto bad; + } } } @@ -842,8 +867,10 @@ tv[1].tv_sec = fa->modtime; /* Modification time. */ tv[1].tv_usec = 0; error = utimes(frompath, tv); - if (error) + if (error) { + printf("MODTIME ERR\n"); goto bad; + } } if (mask & FA_OWNER || mask & FA_GROUP) { uid = -1; @@ -853,8 +880,10 @@ if (mask & FA_GROUP) gid = fa->gid; error = chown(frompath, uid, gid); - if (error) + if (error) { + printf("BAD FROM HERE\n"); goto bad; + } } if (mask & FA_MODE) { newmode = fa->mode & modemask; @@ -936,3 +965,12 @@ 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); +} Index: fattr.h =================================================================== RCS file: /srv/ncvs/src/contrib/csup/fattr.h,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 fattr.h --- fattr.h 3 Mar 2006 04:11:26 -0000 1.1.1.1 +++ fattr.h 6 Mar 2008 12:13:43 -0000 @@ -101,6 +101,7 @@ 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_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_ */ Index: keyword.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/keyword.c,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 keyword.c --- keyword.c 3 Mar 2006 04:11:27 -0000 1.1.1.1 +++ keyword.c 6 Mar 2008 12:13:43 -0000 @@ -152,6 +152,29 @@ 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) { Index: keyword.h =================================================================== RCS file: /srv/ncvs/src/contrib/csup/keyword.h,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 keyword.h --- keyword.h 3 Mar 2006 04:11:28 -0000 1.1.1.1 +++ keyword.h 6 Mar 2008 12:13:43 -0000 @@ -42,6 +42,7 @@ 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 *); Index: lex.rcs.c =================================================================== RCS file: lex.rcs.c diff -N lex.rcs.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ lex.rcs.c 6 Mar 2008 12:13:43 -0000 @@ -0,0 +1,2099 @@ + +#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 34 +#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 +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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 ) + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int 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 + * 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 +#include "misc.h" +#include "rcsparse.h" + +#line 572 "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 +#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 796 "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 942 "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" + + + Index: misc.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/misc.c,v retrieving revision 1.1.1.3 diff -u -u -r1.1.1.3 misc.c --- misc.c 14 Mar 2006 03:51:11 -0000 1.1.1.3 +++ misc.c 6 Mar 2008 12:13:43 -0000 @@ -202,10 +202,10 @@ 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,70 @@ } /* - * 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; + size_t len; + + 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); +} + int mkdirhier(char *path, mode_t mask) { @@ -520,3 +556,76 @@ 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); +} Index: misc.h =================================================================== RCS file: /srv/ncvs/src/contrib/csup/misc.h,v retrieving revision 1.1.1.2 diff -u -u -r1.1.1.2 misc.h --- misc.h 14 Mar 2006 03:51:11 -0000 1.1.1.2 +++ misc.h 6 Mar 2008 12:13:43 -0000 @@ -107,14 +107,21 @@ 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 *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 *); Index: mux.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/mux.c,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 mux.c --- mux.c 3 Mar 2006 04:11:28 -0000 1.1.1.1 +++ mux.c 6 Mar 2008 12:13:43 -0000 @@ -23,7 +23,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $FreeBSD: src/contrib/csup/mux.c,v 1.1.1.1 2006/03/03 04:11:28 mux Exp $ + * $FreeBSD: src/contrib/csup/mux.c,v 1.2 2007/11/18 00:25:18 jb Exp $ */ #include @@ -782,9 +782,10 @@ struct buf *buf; uint32_t winsize; uint16_t hdrsize, size, len; - int error, id, iovcnt, what; + int error, id, iovcnt, what = 0; m = (struct mux *)arg; + what = 0; again: id = sender_waitforwork(m, &what); chan = chan_get(m, id); Index: queue.h =================================================================== RCS file: /srv/ncvs/src/contrib/csup/queue.h,v retrieving revision 1.1.1.3 diff -u -u -r1.1.1.3 queue.h --- queue.h 19 May 2007 13:55:01 -0000 1.1.1.3 +++ queue.h 6 Mar 2008 12:13:43 -0000 @@ -27,9 +27,9 @@ * SUCH DAMAGE. * * @(#)queue.h 8.5 (Berkeley) 8/20/94 - * $FreeBSD: src/contrib/csup/queue.h,v 1.1.1.3 2007/05/19 13:55:01 mux Exp $ + * $FreeBSD: src/contrib/csup/queue.h,v 1.1.1.2 2006/03/03 18:45:12 mux Exp $ * - * $FreeBSD: src/contrib/csup/queue.h,v 1.1.1.3 2007/05/19 13:55:01 mux Exp $ + * $FreeBSD: src/contrib/csup/queue.h,v 1.1.1.2 2006/03/03 18:45:12 mux Exp $ */ #ifndef _QUEUE_H_ Index: rcsfile.c =================================================================== RCS file: rcsfile.c diff -N rcsfile.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ rcsfile.c 6 Mar 2008 12:13:43 -0000 @@ -0,0 +1,1433 @@ +/*- + * Copyright (c) 2007-2008, Ulf Lilleengen + * 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 +#include +#include +#include +#include +#include +#include "diff.h" +#include "misc.h" +#include "keyword.h" +#include "rcsfile.h" +#include "rcsparse.h" +#include "stream.h" +#include "proto.h" +#include "queue.h" + +struct string { + char *str; + STAILQ_ENTRY(string) string_next; +}; + +struct tag { + char *tag; + char *revnum; + STAILQ_ENTRY(tag) tag_next; +}; + +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; +}; + +struct branch { + char *revnum; + LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */ + STAILQ_ENTRY(branch) branch_next; +}; + +struct rcsfile { + char *name; + char *head; /*XXX: Should use trunk->head instead. */ + char *branch; /* Default branch. */ + char *cvsroot; + char *colltag; + STAILQ_HEAD(, string) accesslist; + STAILQ_HEAD(, tag) taglist; + /* XXX: cvsup does not care about the lock list either, so don't do it. + */ + int strictlock; + char *comment; + int expand; + struct branch *trunk; /* The tip delta. */ + + LIST_HEAD(, delta) deltatable; + LIST_HEAD(, delta) deltatable_dates; + + STAILQ_HEAD(, string) branchstack; /* Used by parser. */ + char *desc; +}; + +static void rcsfile_freedelta(struct rcsfile *rf, struct delta *); +static void rcsfile_insertdelta(struct branch *, struct delta *, + int); +static void rcsfile_initdelta(struct rcsfile *, struct delta *, 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 *); +static void rcsfile_addbranch(struct rcsfile *, char *); + +/* XXX: need to support newphrases. */ + +/* 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); + STAILQ_INIT(&rf->branchstack); + + /* 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); + } + /*fprintf(stderr, "Parsing %s\n", path);*/ + error = rcsparse_run(rf, infp); +/* fprintf(stderr, "Done with %s\n", path);*/ + fclose(infp); + if (error) { + lprintf(-1, "Error parsing \"%s\"\n", name); + rcsfile_free(rf); + return (NULL); + } + return (rf); +} + +/* + * Write content of rcsfile to server. + */ +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) { + /* XXX: what is the policy on placeholders here? */ + 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"); + + error = proto_printf(wr, ".\n"); + if (error) + return(error); + return (0); +} + +/* + * Write a RCS file to disk. + */ +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); +/* printf("Writing out head: %s\n", d->revnum);*/ + 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 (!STAILQ_EMPTY(&rf->accesslist)) { + /* XXX: Write out access. */ + } + 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 iw 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); + /* Don't write out placeholders. */ + /* XXX: But perhaps write out the placeholders children? */ + 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. + */ + 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 need to remember how many newlines we read after desc to + * write this out correctly. + */ +/* if (!rcsnum_cmp(d->revnum, "1.1") && rf->branch != NULL) + stream_printf(dest, "\n");*/ + + /* 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; + STAILQ_HEAD(, delta) deltalist_inverted; + LIST_HEAD(, delta) branchlist_datesorted; + struct stream *in; + struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3; + struct branch *b, *b2, *b3; + char *line; + size_t size; + int found; + 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); + /* Don't write out placeholders. XXX but perhaps children? */ + 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. + */ +/*STAILQ_INIT(&deltalist_inverted);*/ + LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) { + d_tmp->prev = d; + STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); + /*STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev);*/ + } + + /* Push branch heads on stack. */ +/* STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) { + if (d_tmp == NULL) + err(1, "empty branch!"); + 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; + int i; + char *ptr; + size_t size; + + di = &dibuf; + b = NULL; + /* 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); + /* if (strstr(rf->name, "test_compat_zip.c") && + diffbase != NULL) { + ptr = line; + fprintf(stderr, "Write:'", rf->name); + for (i = 0; i < size; i++, ptr++) { + fprintf(stderr, "%c", *ptr); + } + fprintf(stderr, "'\n"); + }*/ +/* stream_printf(dest, "", line);*/ + 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. */ + /* XXX: extra overhead on the last apply. */ + orig = rcsfile_getdeltatext(rf, d, b); + /* XXX: make sure we have the buf. */ + 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); + rd = stream_open_buf(diffbase->text); + /* fprintf(stderr, "Diff: \n"); + print_stream(rd); + stream_close(rd);*/ + 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); + buf_free(b); + return (error); + } + keyword_free(k); + stream_close(rd); + stream_close(orig); + } + if (b != NULL) + buf_free(b); + return (0); +} + +/* + * Return a stream with an applied diff of a delta. + * XXX: extra overhead on the last apply. Could write directly to file. + */ +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)) { + /*fprintf(stderr, "Got diffbase NULL on rev %s\n", d->revnum);*/ + orig = stream_open_buf(d->text); + return (orig); + } + + di = &dibuf; + /*fprintf(stderr, "In process of applying diff of %s to %s\n", + d->revnum, d->diffbase->revnum);*/ + /* If not, we need to apply our diff to that of our diffbase. */ + orig = rcsfile_getdeltatext(rf, d->diffbase, buf_orig); + + /* + * 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); + /* First write a '.' to make diff_apply happy. */ + stream_printf(rd, ".\n"); + /* fprintf(stderr, "Apply diff:\n"); + print_stream(rd); + fprintf(stderr, "END\n");*/ + stream_close(rd); + rd = stream_open_buf(d->text); + k = keyword_new(); + error = diff_apply(rd, orig, dest, k, di, 0); + if (error) { + fprintf(stderr, "Error applying diff: %d\n", error); + if (buf_orig != NULL) + buf_free(buf_orig); + return (NULL); + } + stream_close(rd); + stream_close(orig); + stream_close(dest); + keyword_free(k); + if (buf_orig != NULL) + buf_free(buf_orig); + + /* Now reopen the stream for the reading. */ + dest = stream_open_buf(buf_dest); +/* fprintf(stderr, "Ok, we applied the diff and got: \n"); + print_stream(dest); + stream_close(dest); + 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; + + /* XXX: does not support newphrases. */ + 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(rf, d); + } + + /* Free global branch. */ + if (rf->trunk->revnum != NULL) + free(rf->trunk->revnum); + free(rf->trunk); + + if (rf->desc != NULL) + free(rf->desc); + + /*fprintf(stderr, "Did %d mallocs and %d frees with diff %d\n", + * rf->mallocs, rf->frees, rf->mallocs - rf->frees);*/ + free(rf); +} + +/* + * Free a RCS delta. + */ +static void +rcsfile_freedelta(struct rcsfile *rf, 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 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. + * XXX: Assume that we don't have to remove the associated tags. + */ +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(rf, 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; + + /*fprintf(stderr, "Found delta %s\n", revnum);*/ + 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; + +/* fprintf(stderr, "Found delta %s\n", revnum);*/ + d_next = NULL; + d = rcsfile_getdelta(rf, revnum); + + if (d == NULL) { +/* lprintf(1, "Need to create new...\n");*/ + /* 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) { + /*fprintf(stderr, "Create placeholder for %s\n", next);*/ + 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) { + /*fprintf(stderr, "Insert next: %s\n", d_next->revnum);*/ + /* trunk = rcsrev_istrunk(d_next->revnum);*/ +/* rcsfile_insertdelta(b, d_next, trunk);*/ + rcsfile_insertsorteddelta(rf, d_next); + } +/* fprintf(stderr, "Added %s\n", revnum);*/ +} + +/* + * 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); +} + +/* 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); +} + +/* + * 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. */ + 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); + /* XXX is this safe?. Get's rid of the @s */ + log++; + log[strlen(log) - 1] = '\0'; +/* fprintf(stderr, "%s text is now:\n%s\n", revnum, text);*/ + + 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); + /* XXX is this safe?. Get's rid of the @s */ + text++; + text[strlen(text) - 1] = '\0'; +/* fprintf(stderr, "%s text is now:\n%s\n", revnum, text);*/ + + 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]; + int i, count; + + assert(d != NULL); + dest = stream_open_buf(d->log); + /* XXX: more efficient please. */ + 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_printf(dest, "%s\n", logline);*/ + 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]; + int i, count; + + assert(d != NULL); + dest = stream_open_buf(d->text); + /* XXX: more efficient please. */ + /* 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_printf(dest, "%s\n", textline);*/ + 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); +} +#if 0 +/* Functions to add and initialize branchstack. */ +void +rcsfile_bstack_add(struct rcsfile *rf, char *branch) +{ + struct string *s; + + s = xmalloc(sizeof(struct string)); + s->str = xstrdup(branch); + STAILQ_INSERT_TAIL(&rf->branchstack, s, string_next); +} + +void +rcsfile_bstack_flush(struct rcsfile *rf) +{ + struct string *s; + + while (!STAILQ_EMPTY(&rf->branchstack)) { + s = STAILQ_FIRST(&rf->branchstack); + STAILQ_REMOVE_HEAD(&rf->branchstack, string_next); + rcsfile_addbranch(rf, s->str); + free(s->str); + free(s); + } + + /* Initialize for next user. */ + STAILQ_INIT(&rf->branchstack); +} +#endif Index: rcsfile.h =================================================================== RCS file: rcsfile.h diff -N rcsfile.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ rcsfile.h 6 Mar 2008 12:13:43 -0000 @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2007-2008, Ulf Lilleengen + * 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_ */ Index: rcsparse.c =================================================================== RCS file: rcsparse.c diff -N rcsparse.c --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ rcsparse.c 6 Mar 2008 12:13:43 -0000 @@ -0,0 +1,366 @@ +/*- + * Copyright (c) 2008, Ulf Lilleengen + * 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$ + * + */ + +/* + * An rcsparser using lex for tokenizing. + */ +#include +#include +#include "rcstokenizer.h" +#include "rcsparse.h" +#include "rcsfile.h" +#include "misc.h" +#include "queue.h" + +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); +} + +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); + /*fprintf(stderr, "Parsed head '%s'\n", 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; + struct string *s; + char *revnum, *revdate, *author, *state, *branch, *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); +/* fprintf(stderr, "Got revnum '%s'\n", revnum);*/ + /* 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) { +/* s = xmalloc(sizeof(struct string)); + s->str = duptext(sp);*/ +/* fprintf(stderr, "Adding branch %s\n", branch);*/ +/* STAILQ_INSERT_TAIL(&branchlist, s, next);*/ + 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); + /* fprintf(stderr, "Added delta %s %s %s %d %s\n", revnum, revdate, + author, state, next); + fprintf(stderr, "Finished delta %s\n", revnum);*/ + free(revnum); + free(revdate); + free(author); + if (state != NULL) + free(state); + if (next != NULL) + free(next); + /* Add the branches. */ +/* rcsfile_bstack_flush(rf);*/ + } 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); +} Index: rcsparse.h =================================================================== RCS file: rcsparse.h diff -N rcsparse.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ rcsparse.h 6 Mar 2008 12:13:43 -0000 @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2008, Ulf Lilleengen + * 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 Index: rcstokenizer.h =================================================================== RCS file: rcstokenizer.h diff -N rcstokenizer.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ rcstokenizer.h 6 Mar 2008 12:13:43 -0000 @@ -0,0 +1,338 @@ +#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 34 +#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 +#include +#include +#include + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have . 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 +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 + +/* The following is because we cannot portably get our hands on size_t + * (without autoconf's help, which isn't available because we want + * flex-generated scanners to compile on their own). + */ + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef unsigned int 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 +#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 337 "rcstokenizer.h" +#undef rcsIN_HEADER +#endif /* rcsHEADER_H */ Index: status.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/status.c,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 status.c --- status.c 3 Mar 2006 04:11:28 -0000 1.1.1.1 +++ status.c 6 Mar 2008 12:13:43 -0000 @@ -101,6 +101,15 @@ 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 @@ error = statusrec_cook(sr, line); if (error) { st->error = STATUS_ERR_PARSE; + printf("ERROR HERE\n"); return (NULL); } return (sr); @@ -197,6 +207,9 @@ } switch (cmd[0]) { + case 'A': + sr.sr_type = SR_FILELIVE; + break; case 'D': sr.sr_type = SR_DIRDOWN; st->depth++; @@ -215,6 +228,12 @@ } 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 @@ 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 @@ case SR_CHECKOUTDEAD: cmd = 'c'; break; + case SR_FILELIVE: + cmd = 'V'; + break; + case SR_FILEDEAD: + cmd = 'v'; + break; default: assert(0); return (-1); Index: stream.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/stream.c,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 stream.c --- stream.c 3 Mar 2006 04:11:28 -0000 1.1.1.1 +++ stream.c 6 Mar 2008 12:13:43 -0000 @@ -126,10 +126,8 @@ #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. */ @@ -200,7 +198,7 @@ /* Create a new buffer. */ -static struct buf * +struct buf * buf_new(size_t size) { struct buf *buf; @@ -211,6 +209,7 @@ * 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 +271,7 @@ } /* Free a buffer. */ -static void +void buf_free(struct buf *buf) { @@ -335,6 +334,28 @@ 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; + 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 +412,57 @@ 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 +518,28 @@ 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. * Index: stream.h =================================================================== RCS file: /srv/ncvs/src/contrib/csup/stream.h,v retrieving revision 1.1.1.1 diff -u -u -r1.1.1.1 stream.h --- stream.h 3 Mar 2006 04:11:28 -0000 1.1.1.1 +++ stream.h 6 Mar 2008 12:13:43 -0000 @@ -38,6 +38,7 @@ } 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 +49,20 @@ 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 +70,7 @@ 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 +78,6 @@ 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_ */ Index: updater.c =================================================================== RCS file: /srv/ncvs/src/contrib/csup/updater.c,v retrieving revision 1.1.1.2 diff -u -u -r1.1.1.2 updater.c --- updater.c 14 Mar 2006 03:51:10 -0000 1.1.1.2 +++ updater.c 6 Mar 2008 12:13:43 -0000 @@ -30,6 +30,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include "misc.h" #include "mux.h" #include "proto.h" +#include "rcsfile.h" #include "status.h" #include "stream.h" @@ -69,6 +71,7 @@ char *author; struct stream *orig; struct stream *to; + int attic; int expand; }; @@ -80,7 +83,7 @@ }; 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 +93,23 @@ 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 *); static struct file_update * fup_new(struct coll *coll, struct status *st) @@ -112,12 +124,18 @@ } 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; + if (coll->co_options & CO_CHECKOUTMODE) + fup->destpath = checkoutpath(coll->co_prefix, name); + else { + fup->destpath = cvspath(coll->co_prefix, name, attic); + fup->attic = attic; + } if (fup->destpath == NULL) return (-1); fup->coname = fup->destpath + coll->co_prefixlen + 1; @@ -312,6 +330,7 @@ char *cmd, *line, *msg, *attr; char *name, *tag, *date, *revdate; char *expand, *wantmd5, *revnum; + char *optstr, *rcsopt; time_t t; int error, needfixupmsg; @@ -347,7 +366,7 @@ 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 +384,12 @@ 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 +438,7 @@ 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 +457,7 @@ 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 +511,8 @@ fattr_override(sr->sr_clientattr, tmp, FA_MASK); fattr_free(tmp); fattr_mergedefault(sr->sr_clientattr); - error = fup_prepare(fup, name); + lprintf(1, "Doing from name %s\n", name); + error = fup_prepare(fup, name, 0); if (error) return (UPDATER_ERR_PROTO); fup->temppath = tempname(fup->destpath); @@ -508,7 +528,7 @@ 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 +540,299 @@ return (UPDATER_ERR_MSG); } break; + /* Add the CVSMODE only commands here. */ + 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); +/* xasprintf(&fup->destpath, "%s", coll->co_prefix)*/ + /* XXX: double-check this. */ + fup->temppath = tempname(fup->destpath); + /*xasprintf(&fup->temppath, "%s/%s", coll->co_prefix, + name);*/ + 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\n", name); + error = updater_addfile(up, fup, attr); + if (error) + return (error); + 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, 0); + if (error) + return (UPDATER_ERR_PROTO); +/* xasprintf(&fup->destpath, "%s", coll->co_prefix)*/ + + fup->temppath = tempname(fup->destpath); + /*xasprintf(&fup->temppath, "%s/%s", coll->co_prefix, + name);*/ + 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 -> Attic\n", name); + error = updater_addfile(up, fup, attr); + if (error) + return (error); + 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); + + lprintf(1, " Mkdir %s\n", fup->destpath); + error = mkdirhier(fup->destpath, coll->co_umask); + if (error) + return (UPDATER_ERR_PROTO); + if (access(fup->destpath, F_OK) != 0) { + error = fattr_makenode(sr->sr_clientattr, + fup->destpath); + if (error) { + lprintf(-1, "Hei her i 'I'\n"); + 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", fup->destpath); + /* XXX: Works, but misleading 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; + case 'h': + /* XXX: SR_LINKFILEDEAD. */ + case 'H': + lprintf(1, "Got 'H'\n"); + 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_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); + 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); + 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); + 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, 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); + 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); + 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); + error = updater_updatenode(up, coll, fup, name, attr); + if (error) + return (error); + break; + case 'v': + /*fprintf(stderr, "XXX: Edit RCSFILE\n");*/ + 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); +/* lprintf(1, "Got %s %s %s %s\n", name, attr, optstr, + wantmd5);*/ + 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': + /*fprintf(stderr, "XXX: Edit RCSFILE\n");*/ + 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); + /*lprintf(1, "Got %s %s %s %s\n", name, attr, optstr, + wantmd5);*/ + 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; case '!': /* Warning from server. */ msg = proto_get_rest(&line); @@ -528,6 +841,7 @@ lprintf(-1, "Server warning: %s\n", msg); break; default: + lprintf(1, "Got: %s %s\n", cmd, line); return (UPDATER_ERR_PROTO); } fup_cleanup(fup); @@ -725,6 +1039,43 @@ return (0); } +/* + * Update attributes of a directory. + * XXX: Should abstract this to include files and checkoutmode/cvsmode. + */ +static int +updater_setdirattrs(struct updater *up, struct coll *coll, + struct file_update *fup, char *name, char *attr) +{ + struct statusrec *sr; + int error, rv; + + sr = &fup->srbuf; + sr->sr_type = SR_DIRUP; + sr->sr_file = xstrdup(name); + sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_merge(sr->sr_clientattr, sr->sr_serverattr); + 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); + } + /* XXX: somehow not set like cvsup afterwards. */ + 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 +1163,9 @@ return (error); } +/* + * Edit a file and add delta. + */ static int updater_diff_batch(struct updater *up, struct file_update *fup) { @@ -895,7 +1249,7 @@ 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 +1258,190 @@ 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); + /* XXX: record in log? */ + + /* + * XXX: 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); + } + /*lprintf(1, "Attr linktarget1: %s\n", + * fattr_getlinktarget(sr->sr_clientattr));*/ + 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); + + 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. + * XXX: Let's see if we can use this for more stuff later. + */ +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, first, i; + size_t size; + off_t fsize, num_blocks, num_remaind; + int bufsize = 1024; + char buf[bufsize]; + ssize_t nbytes, nread, nwritten, cread, cwritten; + + coll = fup->coll; + path = fup->destpath; + size = 0; + nbytes = 0; + nread = 0; + nwritten = 0; + cwritten = 0; + cread = 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); + memset(buf, 0, bufsize); + num_blocks = fsize / bufsize; + num_remaind = fsize % bufsize; + for (i = 0; i < num_blocks; i++) { + cread = stream_read_blocking(up->rd, buf, bufsize); + cwritten = stream_write(to, buf, bufsize); + nread += cread; + nwritten += cwritten; + memset(buf, 0, bufsize); + } + if (num_remaind > 0) { + nread += stream_read_blocking(up->rd, buf, num_remaind); + nwritten += stream_write(to, buf, num_remaind); + memset(buf, 0, bufsize); + } + first = 1; + stream_close(to); + /*lprintf(1, "Read a total of %d bytes, and wrote %d\n", nread, + * nwritten);*/ + 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); + +/*bad: + xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, + strerror(errno)); + return (UPDATER_ERR_MSG);*/ +} + static int updater_checkout(struct updater *up, struct file_update *fup, int isfixup) { @@ -933,7 +1471,7 @@ } to = stream_open_file(fup->temppath, - O_WRONLY | O_CREAT | O_TRUNC, 0600); + O_WRONLY | O_CREAT | O_TRUNC, 0755); /*XXX: Change to correct perm*/ if (to == NULL) { xasprintf(&up->errmsg, "%s: Cannot create: %s", fup->temppath, strerror(errno)); @@ -1009,3 +1547,290 @@ 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 *fa; + char md5[MD5_DIGEST_SIZE]; + char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath; + int error, changed, errval; + + rcsopt = NULL; /* XXX: just for now. */ + coll = fup->coll; + sr = &fup->srbuf; + st = fup->st; + path = fup->destpath; + temppath = fup->temppath; + errval = 0; + + /* First read in the file. */ + changed = 0; + /* + * 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) { + lprintf(-1, "Error reading rcsfile %s\n", name); + return (UPDATER_ERR_MSG); + } + 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); + } + changed = 1; + 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); + break; + case 'b': + rcsfile_setval(rf, RCSFILE_BRANCH, NULL); + break; + case 'D': + error = updater_addelta(rf, up->rd, line); + 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); + 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); + 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); + 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); + break; + default: + fprintf(stderr, "Unknown %s\n", line); + return (UPDATER_ERR_PROTO); + break; + } + } + if (!changed) { + sr->sr_clientattr = fattr_frompath(fup->destpath, + FATTR_NOFOLLOW); + + goto finish; + } + + lprintf(1, " Edit %s\n", fup->coname); + /* Write and rename temp file. */ + dest = stream_open_file(fup->temppath, + O_RDWR | O_CREAT | O_TRUNC, 0600); +/* printf("Printing %s\n", name);*/ + /*rcsfile_print(rf);*/ + error = rcsfile_write(rf, dest); + stream_close(dest); + rcsfile_free(rf); + if (error) { + fprintf(stderr, "Error writing file\n"); + return (UPDATER_ERR_PROTO); + } +/* if (strcmp(md5, servermd5) != 0) { + lprintf(-1, "SERVERMD5 %s, CLIENTMD5 %s\n", servermd5, md5); + up->errmsg = xstrdup("Wrong md5sum"); + return (UPDATER_ERR_MSG); + }*/ + /*XXX: install attributes to the new file here? */ + sr->sr_clientattr = fattr_frompath(fup->temppath, FATTR_NOFOLLOW); +finish: + if (sr->sr_clientattr == NULL) { + fprintf(stderr, "No clientattr\n"); + return (UPDATER_ERR_PROTO); + } + fa = fattr_dup(sr->sr_clientattr); + fattr_maskout(fa, ~FA_MODTIME); + if (fattr_equal(sr->sr_clientattr, fa)) { + /* XXX: fix this. */ + if (fup->attic) + lprintf(1, " SetAttrs %s -> Attic\n", fup->coname); + else + lprintf(1, " SetAttrs %s\n", fup->coname); + } else { + if (fup->attic) + lprintf(1, " Touch %s -> Attic\n", fup->coname); + else + lprintf(1, " Touch %s\n", fup->coname); + } + free(fa); + + /* UPDATE FILE. */ + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + if (changed) { +/* if (MD5_File(fup->temppath, md5) == -1) { + xasprintf(&up->errmsg, + "Cannot calculate checksum for \"%s\": %s", + path, strerror(errno)); + return (UPDATER_ERR_MSG); + } + fprintf(stderr, "Md51: %s, md5server %s\n", md5, fup->wantmd5); + if (strcmp(fup->wantmd5, md5) != 0) { + fprintf(stderr, "File found in %s\n", fup->temppath); + return (UPDATER_ERR_PROTO); + }*/ + /* XXX: We need correct md5 of RCS implementation. Just skip it for now */ + errval = updater_updatefile(up, fup, fup->wantmd5, 0); + /*XXX:*/ + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + /* UPDATE IT. */ + return (errval); + } else { + 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); + } + } + return (0); +} + +/* + * Add a delta to a RCS file. + * XXX: should we pass cmdline here? + */ +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 i; + + 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) { + fprintf(stderr, "Whats wrong here: revnum %s, revdate %s, author %s\n", revnum, revdate, author); + return (UPDATER_ERR_PROTO); + } + +/* fprintf(stderr, "Adding delta %s %s from diffbase %s\n", revnum, + revdate, diffbase);*/ + /* 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. */ + textline = stream_getln(rd, &size); + while (textline != NULL) { + if (size == 2 && *textline == '.') + break; + if (size == 3 && + memcmp(textline, ".+", 2) == 0) { + /* Truncate newline. */ + rcsdelta_truncatetext(d, -1); + break; + } + if (size >= 3 && + memcmp(textline, "..", 2) == 0) { + size--; + textline++; + /* fprintf(stderr, "File %s textmod: '", rcsfile_getname(rf)); + for (i = 0; i < size; i++) { + fprintf(stderr, "%c", + textline[i]); + } + fprintf(stderr, "\n");*/ + } + rcsdelta_appendtext(d, textline, size); + textline = stream_getln(rd, &size); + } + break; + } + } + /* XXX: next should be the revision that is ahead of us. */ + /* XXX: Use diffbase to explore what branches should be?. */ + + return (0); +}