Index: contrib/csup/rsyncfile.c =================================================================== --- contrib/csup/rsyncfile.c (revision 0) +++ contrib/csup/rsyncfile.c (revision 0) @@ -0,0 +1,225 @@ +/*- + * 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$ + */ + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "misc.h" +#include "fattr.h" +#include "rsyncfile.h" + +#define MINBLOCKSIZE 1024 +#define MAXBLOCKSIZE (16 * 1024) +#define RECEIVEBUFFERSIZE (15 * 1024) +#define BLOCKINFOSIZE 26 +#define SEARCHREGION 10 +#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE) + +#define CHAR_OFFSET 3 +#define RSUM_SIZE 9 + +struct rsyncfile { + char *start; + char *buf; + char *end; + size_t blocksize; + size_t fsize; + struct fattr *fa; + int fd; + + char *blockptr; + int blocknum; + char blockmd5[MD5_DIGEST_SIZE]; + char rsumstr[RSUM_SIZE]; + uint32_t rsum; +}; + +static size_t rsync_chooseblocksize(size_t); +static uint32_t rsync_rollsum(char *, size_t); + +/* Open a file and initialize variable for rsync operation. */ +struct rsyncfile * +rsync_open(char *path, size_t blocksize, int rdonly) +{ + struct rsyncfile *rf; + struct stat st; + int error; + + rf = xmalloc(sizeof(*rf)); + error = stat(path, &st); + if (error) { + free(rf); + return (NULL); + } + rf->fsize = st.st_size; + rf->fa = fattr_fromstat(&st); + + rf->fd = open(path, rdonly ? O_RDONLY : O_RDWR); + if (rf->fd < 0) { + free(rf); + return (NULL); + } + rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0); + if (rf->buf == MAP_FAILED) { + free(rf); + return (NULL); + } + rf->start = rf->buf; + rf->end = rf->buf + rf->fsize; + rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) : + blocksize); + rf->blockptr = rf->buf; + rf->blocknum = 0; + return (rf); +} + +/* Close and free all resources related to an rsync file transfer. */ +int +rsync_close(struct rsyncfile *rf) +{ + int error; + + error = munmap(rf->buf, rf->fsize); + if (error) + return (error); + close(rf->fd); + free(rf); + return (0); +} + +/* + * Choose the most appropriate block size for an rsync transfer. Modeled + * algorithm after cvsup. + */ +static size_t +rsync_chooseblocksize(size_t fsize) +{ + size_t bestrem, blocksize, bs, hisearch, losearch, rem; + + blocksize = fsize / MAXBLOCKS; + losearch = blocksize - SEARCHREGION; + hisearch = blocksize + SEARCHREGION; + + if (losearch < MINBLOCKSIZE) { + losearch = MINBLOCKSIZE; + hisearch = losearch + (2 * SEARCHREGION); + } else if (hisearch > MAXBLOCKSIZE) { + hisearch = MAXBLOCKSIZE; + losearch = hisearch - (2 * SEARCHREGION); + } + + bestrem = MAXBLOCKSIZE; + for (bs = losearch; bs <= hisearch; bs++) { + rem = fsize % bs; + if (rem < bestrem) { + bestrem = rem; + blocksize = bs; + } + } + return (bestrem); +} + +/* Get the next rsync block of a file. */ +int +rsync_nextblock(struct rsyncfile *rf) +{ + MD5_CTX ctx; + size_t blocksize; + + if (rf->blockptr >= rf->end) + return (0); + blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize); + /* Calculate MD5 of the block. */ + MD5_Init(&ctx); + MD5_Update(&ctx, rf->blockptr, blocksize); + MD5_End(rf->blockmd5, &ctx); + + rf->rsum = rsync_rollsum(rf->blockptr, blocksize); + snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); + rf->blocknum++; + rf->blockptr += blocksize; + return (1); +} + +/* Get the rolling checksum of a file. */ +static uint32_t +rsync_rollsum(char *buf, size_t len) +{ + uint32_t a, b; + char *ptr, *limit; + + a = b = 0; + ptr = buf; + limit = buf + len; + + while (ptr < limit) { + a += *ptr + CHAR_OFFSET; + b += a; + ptr++; + } + return ((b << 16) | a); +} + +/* Get running sum so far. */ +char * +rsync_rsum(struct rsyncfile *rf) +{ + + return (rf->rsumstr); +} + +/* Get MD5 of current block. */ +char * +rsync_blockmd5(struct rsyncfile *rf) +{ + + return (rf->blockmd5); +} + +/* Accessor for blocksize. */ +size_t +rsync_blocksize(struct rsyncfile *rf) +{ + + return (rf->blocksize); +} + +/* Accessor for filesize. */ +size_t +rsync_filesize(struct rsyncfile *rf) +{ + + return (rf->fsize); +} Property changes on: contrib/csup/rsyncfile.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/misc.h =================================================================== --- contrib/csup/misc.h (revision 185155) +++ contrib/csup/misc.h (working copy) @@ -99,22 +99,30 @@ struct pattlist; struct tm; -int asciitoint(const char *, int *, int); -int lprintf(int, const char *, ...) __printflike(2, 3); -int MD5_File(char *, char *); -void MD5_End(char *, MD5_CTX *); -int rcsdatetotm(const char *, struct tm *); -time_t rcsdatetotime(const char *); -int pathcmp(const char *, const char *); -size_t commonpathlength(const char *, size_t, const char *, size_t); -char *pathlast(char *); -char *checkoutpath(const char *, const char *); -int mkdirhier(char *, mode_t); -char *tempname(const char *); -void *xmalloc(size_t); -void *xrealloc(void *, size_t); -char *xstrdup(const char *); -int xasprintf(char **, const char *, ...) __printflike(2, 3); +int asciitoint(const char *, int *, int); +int lprintf(int, const char *, ...) __printflike(2, 3); +int MD5_File(char *, char *); +void MD5_End(char *, MD5_CTX *); +int rcsdatetotm(const char *, struct tm *); +time_t rcsdatetotime(const char *); +int pathcmp(const char *, const char *); +size_t commonpathlength(const char *, size_t, const char *, size_t); +const char *pathlast(const char *); +int isrcs(const char *, size_t *); +char *checkoutpath(const char *, const char *); +char *cvspath(const char *, const char *, int); +char *atticpath(const char *, const char *); +char *path_prefix(char *); +char *path_first(char *); +int mkdirhier(char *, mode_t); +char *tempname(const char *); +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) __printflike(2, 3); +int rcsnum_cmp(char *, char *); +int rcsrev_istrunk(char *); +char *rcsrev_prefix(char *); struct pattlist *pattlist_new(void); void pattlist_add(struct pattlist *, const char *); Index: contrib/csup/proto.c =================================================================== --- contrib/csup/proto.c (revision 185155) +++ contrib/csup/proto.c (working copy) @@ -365,6 +365,8 @@ s = config->server; lprintf(2, "Exchanging collection information\n"); STAILQ_FOREACH(coll, &config->colls, co_next) { + if (coll->co_options & CO_SKIP) + continue; proto_printf(s, "COLL %s %s %o %d\n", coll->co_name, coll->co_release, coll->co_umask, coll->co_options); for (i = 0; i < pattlist_size(coll->co_accepts); i++) { @@ -768,6 +770,8 @@ va_list ap; char *cp, *s, *attr; ssize_t n; + size_t size; + off_t off; int rv, val, ignore; char c; @@ -801,6 +805,10 @@ val = va_arg(ap, int); rv = stream_printf(wr, "%o", val); break; + case 'O': + off = va_arg(ap, off_t); + rv = stream_printf(wr, "%llu", off); + break; case 'S': s = va_arg(ap, char *); assert(s != NULL); @@ -829,6 +837,11 @@ rv = proto_escape(wr, attr); free(attr); break; + case 'z': + size = va_arg(ap, size_t); + rv = stream_printf(wr, "%zu", size); + break; + case '%': n = stream_write(wr, "%", 1); if (n == -1) @@ -939,6 +952,26 @@ } /* + * Get a size_t token. + */ +int +proto_get_sizet(char **s, size_t *val, int base) +{ + unsigned long long tmp; + char *cp, *end; + + cp = proto_get_ascii(s); + if (cp == NULL) + return (-1); + errno = 0; + tmp = strtoll(cp, &end, base); + if (errno || *end != '\0') + return (-1); + *val = (size_t)tmp; + return (0); +} + +/* * Get a time_t token. * * Ideally, we would use an intmax_t and strtoimax() here, but strtoll() Index: contrib/csup/rsyncfile.h =================================================================== --- contrib/csup/rsyncfile.h (revision 0) +++ contrib/csup/rsyncfile.h (revision 0) @@ -0,0 +1,41 @@ +/*- + * 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 _RSYNCFILE_H_ +#define _RSYNCFILE_H_ + +struct rsyncfile; +struct rsyncfile *rsync_open(char *, size_t, int); +int rsync_nextblock(struct rsyncfile *); +char *rsync_rsum(struct rsyncfile *); +char *rsync_blockmd5(struct rsyncfile *); +int rsync_close(struct rsyncfile *); +size_t rsync_blocksize(struct rsyncfile *); +size_t rsync_filesize(struct rsyncfile *); + +#endif /* !_RSYNCFILE_H_ */ Property changes on: contrib/csup/rsyncfile.h ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/updater.c =================================================================== --- contrib/csup/updater.c (revision 185155) +++ contrib/csup/updater.c (working copy) @@ -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" @@ -56,11 +58,14 @@ #define UPDATER_ERR_READ (-3) /* Error reading from server. */ #define UPDATER_ERR_DELETELIM (-4) /* File deletion limit exceeded. */ +#define BUFSIZE 4096 + /* Everything needed to update a file. */ struct file_update { struct statusrec srbuf; char *destpath; char *temppath; + char *origpath; char *coname; /* Points somewhere in destpath. */ char *wantmd5; struct coll *coll; @@ -69,6 +74,7 @@ char *author; struct stream *orig; struct stream *to; + int attic; int expand; }; @@ -80,7 +86,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 +96,27 @@ 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); +int updater_addelta(struct rcsfile *, struct stream *, char *); static int updater_setattrs(struct updater *, struct file_update *, char *, char *, char *, char *, char *, struct fattr *); +static int updater_setdirattrs(struct updater *, struct coll *, + struct file_update *, char *, char *); static int updater_updatefile(struct updater *, struct file_update *fup, const char *, int); +static int updater_updatenode(struct updater *, struct coll *, + struct file_update *, char *, char *); static int updater_diff(struct updater *, struct file_update *); static int updater_diff_batch(struct updater *, struct file_update *); static int updater_diff_apply(struct updater *, struct file_update *, char *); +static int updater_rcsedit(struct updater *, struct file_update *, char *, + char *); +int updater_append_file(struct updater *, struct file_update *, + off_t); +static int updater_rsync(struct updater *, struct file_update *, size_t); +static int updater_read_checkout(struct stream *, struct stream *); static struct file_update * fup_new(struct coll *coll, struct status *st) @@ -112,12 +131,27 @@ } static int -fup_prepare(struct file_update *fup, char *name) +fup_prepare(struct file_update *fup, char *name, int attic) { struct coll *coll; coll = fup->coll; - fup->destpath = checkoutpath(coll->co_prefix, name); + fup->attic = 0; + fup->origpath = NULL; + + if (coll->co_options & CO_CHECKOUTMODE) + fup->destpath = checkoutpath(coll->co_prefix, name); + else { + fup->destpath = cvspath(coll->co_prefix, name, attic); + fup->origpath = atticpath(coll->co_prefix, name); + /* If they're equal, we don't need special care. */ + if (fup->origpath != NULL && + strcmp(fup->origpath, fup->destpath) == 0) { + free(fup->origpath); + fup->origpath = NULL; + } + fup->attic = attic; + } if (fup->destpath == NULL) return (-1); fup->coname = fup->destpath + coll->co_prefixlen + 1; @@ -140,6 +174,10 @@ free(fup->temppath); fup->temppath = NULL; } + if (fup->origpath != NULL) { + free(fup->origpath); + fup->origpath = NULL; + } fup->coname = NULL; if (fup->author != NULL) { free(fup->author); @@ -309,11 +347,13 @@ struct coll *coll; struct statusrec srbuf, *sr; struct fattr *rcsattr, *tmp; - char *cmd, *line, *msg, *attr; + char *attr, *cmd, *blocksize, *line, *msg; char *name, *tag, *date, *revdate; char *expand, *wantmd5, *revnum; + char *optstr, *rcsopt, *pos; time_t t; - int error, needfixupmsg; + off_t position; + int attic, error, needfixupmsg; error = 0; rd = up->rd; @@ -347,7 +387,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,7 +405,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); /* Theoritically, the file does not exist on the client. @@ -419,7 +459,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 +478,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 +532,7 @@ fattr_override(sr->sr_clientattr, tmp, FA_MASK); fattr_free(tmp); fattr_mergedefault(sr->sr_clientattr); - error = fup_prepare(fup, name); + error = fup_prepare(fup, name, 0); if (error) return (UPDATER_ERR_PROTO); fup->temppath = tempname(fup->destpath); @@ -508,7 +548,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 +560,263 @@ return (UPDATER_ERR_MSG); } break; + case 'A': + case 'a': + case 'R': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (name == NULL || attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'a'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + if (attic) + lprintf(1, " Create %s -> Attic\n", name); + else + lprintf(1, " Create %s\n", name); + error = updater_addfile(up, fup, attr, 0); + if (error) + return (error); + break; + case 'r': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + blocksize = proto_get_ascii(&line); + wantmd5 = proto_get_ascii(&line); + if (name == NULL || attr == NULL || blocksize == NULL || + wantmd5 == NULL) { + return (UPDATER_ERR_PROTO); + } + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + fup->wantmd5 = xstrdup(wantmd5); + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + sr->sr_type = SR_FILELIVE; + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + error = updater_rsync(up, fup, strtol(blocksize, NULL, + 10)); + 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); + + error = mkdirhier(fup->destpath, coll->co_umask); + if (error) + return (UPDATER_ERR_PROTO); + if (access(fup->destpath, F_OK) != 0) { + lprintf(1, " Mkdir %s\n", name); + error = fattr_makenode(sr->sr_clientattr, + fup->destpath); + if (error) + return (UPDATER_ERR_PROTO); + } + error = status_put(fup->st, sr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'i': + /* Remove DirDown entry in status file. */ + name = proto_get_ascii(&line); + if (name == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + error = status_delete(fup->st, name, 0); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'J': + /* + * Set attributes of directory and update DirUp entry in + * status file. + */ + name = proto_get_ascii(&line); + if (name == NULL) + return (UPDATER_ERR_PROTO); + attr = proto_get_ascii(&line); + if (attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + error = updater_setdirattrs(up, coll, fup, name, attr); + if (error) + return (error); + break; + case 'j': + /* + * Remove directory and delete its DirUp entry in status + * file. + */ + name = proto_get_ascii(&line); + if (name == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + lprintf(1, " Rmdir %s\n", name); + updater_deletefile(fup->destpath); + error = status_delete(fup->st, name, 0); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'L': + 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); + attic = (cmd[0] == 'l'); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + sr->sr_clientattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL || + sr->sr_clientattr == NULL) + return (UPDATER_ERR_PROTO); + + /* Save space. Described in detail in updatefile. */ + if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) + || fattr_getlinkcount(sr->sr_clientattr) <= 1) + fattr_maskout(sr->sr_clientattr, + FA_DEV | FA_INODE); + fattr_maskout(sr->sr_clientattr, FA_FLAGS); + error = status_put(fup->st, sr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'N': + case 'n': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (name == NULL || attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'n'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + sr = &fup->srbuf; + sr->sr_type = (attic ? SR_FILEDEAD : SR_FILELIVE); + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + sr->sr_clientattr = fattr_new(FT_SYMLINK, -1); + fattr_mergedefault(sr->sr_clientattr); + fattr_maskout(sr->sr_clientattr, FA_FLAGS); + error = updater_updatenode(up, coll, fup, name, attr); + if (error) + return (error); + break; + case 'V': + case 'v': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + optstr = proto_get_ascii(&line); + wantmd5 = proto_get_ascii(&line); + rcsopt = NULL; /* XXX: Not supported. */ + if (attr == NULL || line != NULL || wantmd5 == NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'v'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + fup->temppath = tempname(fup->destpath); + fup->wantmd5 = xstrdup(wantmd5); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + 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 'X': + case 'x': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (name == NULL || attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'x'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + lprintf(1, " Fixup %s\n", name); + error = updater_addfile(up, fup, attr, 1); + if (error) + return (error); + break; + case 'Z': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + pos = proto_get_ascii(&line); + if (name == NULL || attr == NULL || pos == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_type = SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + position = strtol(pos, NULL, 10); + error = updater_append_file(up, fup, position); + if (error) + return (error); + break; case '!': /* Warning from server. */ msg = proto_get_rest(&line); @@ -725,7 +1022,56 @@ return (0); } +/* + * Update attributes of a directory. + */ static int +updater_setdirattrs(struct updater *up, struct coll *coll, + struct file_update *fup, char *name, char *attr) +{ + struct statusrec *sr; + struct fattr *fa; + int error, rv; + + sr = &fup->srbuf; + sr->sr_type = SR_DIRUP; + sr->sr_file = xstrdup(name); + sr->sr_clientattr = fattr_decode(attr); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_mergedefault(sr->sr_clientattr); + fattr_umask(sr->sr_clientattr, coll->co_umask); + rv = fattr_install(sr->sr_clientattr, fup->destpath, NULL); + lprintf(1, " SetAttrs %s\n", name); + if (rv == -1) { + xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s", + fup->temppath, fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + /* + * Now, make sure they were set and record what was set in the status + * file. + */ + fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (fa == NULL) { + xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_free(sr->sr_clientattr); + fattr_maskout(fa, FA_FLAGS); + sr->sr_clientattr = fa; + error = status_put(fup->st, sr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + + return (0); +} + +static int updater_diff(struct updater *up, struct file_update *fup) { char md5[MD5_DIGEST_SIZE]; @@ -812,6 +1158,9 @@ return (error); } +/* + * Edit a file and add delta. + */ static int updater_diff_batch(struct updater *up, struct file_update *fup) { @@ -895,7 +1244,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,16 +1253,177 @@ 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, 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)); + } else { + lprintf(1, " Mknod %s\n", name); + } + + /* Create directory. */ + error = mkdirhier(fup->destpath, coll->co_umask); + if (error) + return (UPDATER_ERR_PROTO); + + /* If it does not exist, create it. */ + if (access(fup->destpath, F_OK) != 0) + fattr_makenode(fa, fup->destpath); + + /* + * Coming from attic? I don't think this is a problem since we have + * determined attic before we call this function (Look at UpdateNode in + * cvsup). + */ + fattr_umask(fa, coll->co_umask); + rv = fattr_install(fa, fup->destpath, fup->temppath); + if (rv == -1) { + xasprintf(&up->errmsg, "Cannot update attributes on " + "\"%s\": %s", fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + /* + * XXX: Executes not implemented. Have not encountered much use for it + * yet. + */ + /* + * We weren't necessarily able to set all the file attributes to the + * desired values, and any executes may have altered the attributes. + * To make sure we record the actual attribute values, we fetch + * them from the file. + * + * However, we preserve the link count as received from the + * server. This is important for preserving hard links in mirror + * mode. + */ + fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (fileattr == NULL) { + xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT); + fattr_free(sr->sr_clientattr); + sr->sr_clientattr = fileattr; + + /* + * To save space, don't write out the device and inode unless + * the link count is greater than 1. These attributes are used + * only for detecting hard links. If the link count is 1 then we + * know there aren't any hard links. + */ + if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || + fattr_getlinkcount(sr->sr_clientattr) <= 1) + fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); + + /* If it is a symlink, write only out it's path. */ + if (fattr_type(fa) == FT_SYMLINK) { + fattr_maskout(sr->sr_clientattr, ~(FA_FILETYPE | + FA_LINKTARGET)); + } + fattr_maskout(sr->sr_clientattr, FA_FLAGS); + error = status_put(st, sr); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + fattr_free(fa); + + return (0); +} + +/* + * Fetches a new file in CVS mode. + */ +static int +updater_addfile(struct updater *up, struct file_update *fup, char *attr, + int isfixup) +{ + struct coll *coll; + struct stream *to; + struct statusrec *sr; + struct fattr *fa; + char buf[BUFSIZE]; + char md5[MD5_DIGEST_SIZE]; + ssize_t nread; + off_t fsize, remains; + char *cmd, *line, *path; + int error; + + coll = fup->coll; + path = fup->destpath; + sr = &fup->srbuf; + fa = fattr_decode(attr); + fsize = fattr_filesize(fa); + + error = mkdirhier(path, coll->co_umask); + if (error) + return (UPDATER_ERR_PROTO); + to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755); + if (to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot create: %s", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + stream_filter_start(to, STREAM_FILTER_MD5, md5); + remains = fsize; + do { + nread = stream_read(up->rd, buf, (BUFSIZE > remains ? + remains : BUFSIZE)); + remains -= nread; + stream_write(to, buf, nread); + } while (remains > 0); + stream_close(to); + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + /* Check for EOF. */ + if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) + return (UPDATER_ERR_PROTO); + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + + cmd = proto_get_ascii(&line); + fup->wantmd5 = proto_get_ascii(&line); + if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) + return (UPDATER_ERR_PROTO); + + 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, isfixup); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + if (error) + return (error); + return (0); +} + +static int updater_checkout(struct updater *up, struct file_update *fup, int isfixup) { char md5[MD5_DIGEST_SIZE]; struct statusrec *sr; struct coll *coll; struct stream *to; + ssize_t nbytes; + size_t size; char *cmd, *path, *line; - size_t size; - ssize_t nbytes; int error, first; coll = fup->coll; @@ -1009,3 +1519,498 @@ return; } } + +/* + * Edit an RCS file. + */ +static int +updater_rcsedit(struct updater *up, struct file_update *fup, char *name, + char *rcsopt) +{ + struct coll *coll; + struct stream *dest; + struct statusrec *sr; + struct status *st; + struct rcsfile *rf; + struct fattr *oldfattr; + char md5[MD5_DIGEST_SIZE]; + char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath; + int error; + + coll = fup->coll; + sr = &fup->srbuf; + st = fup->st; + temppath = fup->temppath; + path = fup->origpath != NULL ? fup->origpath : fup->destpath; + error = 0; + + /* If the path is new, we must create the Attic dir if needed. */ + if (fup->origpath != NULL) { + error = mkdirhier(fup->destpath, coll->co_umask); + if (error) { + xasprintf(&up->errmsg, "Unable to create Attic dir for " + "%s\n", fup->origpath); + return (UPDATER_ERR_MSG); + } + } + /* + * XXX: we could avoid parsing overhead if we're reading ahead before we + * parse the file. + */ + oldfattr = fattr_frompath(path, FATTR_NOFOLLOW); + if (oldfattr == NULL) { + xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_merge(sr->sr_serverattr, oldfattr); + rf = NULL; + + /* Macro for making touching an RCS file faster. */ +#define UPDATER_OPENRCS(rf, up, path, name, cvsroot, tag) do { \ + if ((rf) == NULL) { \ + (rf) = rcsfile_frompath((path), (name), (cvsroot), \ + (tag)); \ + if ((rf) == NULL) { \ + xasprintf(&(up)->errmsg, \ + "Error reading rcsfile %s\n", (name)); \ + return (UPDATER_ERR_MSG); \ + } \ + } \ +} while (0) + + while ((line = stream_getln(up->rd, NULL)) != NULL) { + if (strcmp(line, ".") == 0) + break; + cmd = proto_get_ascii(&line); + if (cmd == NULL) { + lprintf(-1, "Error editing %s\n", name); + return (UPDATER_ERR_PROTO); + } + switch(cmd[0]) { + case 'B': + branch = proto_get_ascii(&line); + if (branch == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + break; + case 'b': + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_setval(rf, RCSFILE_BRANCH, NULL); + break; + case 'D': + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + error = updater_addelta(rf, up->rd, line); + if (error) + return (error); + break; + case 'd': + revnum = proto_get_ascii(&line); + if (revnum == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_deleterev(rf, revnum); + break; + case 'E': + expand = proto_get_ascii(&line); + if (expand == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + 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) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + 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) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_deletetag(rf, tag, revnum); + break; + default: + return (UPDATER_ERR_PROTO); + } + } + + if (rf == NULL) { + fattr_maskout(oldfattr, ~FA_MODTIME); + if (fattr_equal(oldfattr, sr->sr_serverattr) == 0) + lprintf(1, " SetAttrs %s", fup->coname); + else + lprintf(1, " Touch %s", fup->coname); + if (fup->attic) + lprintf(1, " -> Attic"); + lprintf(1, "\n"); + fattr_free(oldfattr); + goto finish; + } + lprintf(1, " Edit %s", fup->coname); + if (fup->attic) + lprintf(1, " -> Attic"); + lprintf(1, "\n"); + + /* Write and rename temp file. */ + dest = stream_open_file(fup->temppath, + O_RDWR | O_CREAT | O_TRUNC, 0600); + if (dest == NULL) { + xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5); + error = rcsfile_write(rf, dest); + stream_close(dest); + rcsfile_free(rf); + if (error) + return (UPDATER_ERR_PROTO); + +finish: + sr->sr_clientattr = fattr_frompath(path, FATTR_NOFOLLOW); + if (sr->sr_clientattr == NULL) { + xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", + fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + if (rf != NULL) { + error = updater_updatefile(up, fup, md5, 0); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + if (error) + return (error); + } else { + /* Record its attributes since we touched it. */ + if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || + fattr_getlinkcount(sr->sr_clientattr) <= 1) + fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); + error = status_put(st, sr); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + } + + /* In this case, we need to remove the old file afterwards. */ + /* XXX: Can we be sure that a file not edited is moved? I don't think + * this is a problem, since if a file is moved, it should be edited to + * show if it's dead or not. + */ + if (fup->origpath != NULL) + updater_deletefile(fup->origpath); + return (0); +} + +/* + * Add a delta to a RCS file. + */ +int +updater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline) +{ + struct delta *d; + size_t size; + int stop; + char *author, *cmd, *diffbase, *line, *logline; + char *revdate, *revnum, *state, *textline; + + revnum = proto_get_ascii(&cmdline); + diffbase = proto_get_ascii(&cmdline); + revdate = proto_get_ascii(&cmdline); + author = proto_get_ascii(&cmdline); + size = 0; + + if (revnum == NULL || revdate == NULL || author == NULL) + return (UPDATER_ERR_PROTO); + + /* First add the delta so we have it. */ + d = rcsfile_addelta(rf, revnum, revdate, author, diffbase); + if (d == NULL) { + lprintf(-1, "Error adding delta %s\n", revnum); + return (UPDATER_ERR_READ); + } + 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 supported. */ + break; + case 'S': + state = proto_get_ascii(&line); + if (state == NULL) + return (UPDATER_ERR_PROTO); + rcsdelta_setstate(d, state); + break; + case 'T': + /* Do the same as in 'C' command. */ + stop = 0; + textline = stream_getln(rd, &size); + while (textline != NULL) { + if (size == 2 && *textline == '.') + stop = 1; + if (size == 3 && + memcmp(textline, ".+", 2) == 0) { + /* Truncate newline. */ + stop = 1; + } + if (size >= 3 && + memcmp(textline, "..", 2) == 0) { + size--; + textline++; + } + rcsdelta_appendtext(d, textline, size); + if (stop) + break; + textline = stream_getln(rd, &size); + } + break; + } + } + + return (0); +} + +int +updater_append_file(struct updater *up, struct file_update *fup, off_t pos) +{ + struct fattr *fa; + struct stream *to; + struct statusrec *sr; + ssize_t nread; + off_t bytes; + char buf[BUFSIZE], md5[MD5_DIGEST_SIZE]; + char *line, *cmd; + int error, fd; + + sr = &fup->srbuf; + fa = sr->sr_serverattr; + to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, + 0755); + if (to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fd = open(fup->destpath, O_RDONLY); + if (fd < 0) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + + stream_filter_start(to, STREAM_FILTER_MD5, md5); + /* First write the existing content. */ + while ((nread = read(fd, buf, BUFSIZE)) > 0) + stream_write(to, buf, nread); + close(fd); + + bytes = fattr_filesize(fa) - pos; + /* Append the new data. */ + do { + nread = stream_read(up->rd, buf, + (BUFSIZE > bytes) ? bytes : BUFSIZE); + bytes -= nread; + stream_write(to, buf, nread); + } while (bytes > 0); + stream_close(to); + + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + /* Check for EOF. */ + if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) + return (UPDATER_ERR_PROTO); + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + + cmd = proto_get_ascii(&line); + fup->wantmd5 = proto_get_ascii(&line); + if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) + return (UPDATER_ERR_PROTO); + + sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (sr->sr_clientattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + error = updater_updatefile(up, fup, md5, 0); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + if (error) + return (error); + return (0); +} + +/* + * Read file data from stream of checkout commands, and write it to the + * destination. + */ +static int +updater_read_checkout(struct stream *src, struct stream *dest) +{ + ssize_t nbytes; + size_t size; + char *line; + int first; + + first = 1; + line = stream_getln(src, &size); + while (line != NULL) { + if (line[size - 1] == '\n') + size--; + if ((size == 1 && *line == '.') || + (size == 2 && strncmp(line, ".+", 2) == 0)) + break; + if (size >= 2 && strncmp(line, "..", 2) == 0) { + size--; + line++; + } + if (!first) { + nbytes = stream_write(dest, "\n", 1); + if (nbytes == -1) + return (UPDATER_ERR_MSG); + } + nbytes = stream_write(dest, line, size); + if (nbytes == -1) + return (UPDATER_ERR_MSG); + line = stream_getln(src, &size); + first = 0; + } + if (line == NULL) + return (UPDATER_ERR_READ); + if (size == 1 && *line == '.') { + nbytes = stream_write(dest, "\n", 1); + if (nbytes == -1) + return (UPDATER_ERR_MSG); + } + return (0); +} + +/* Update file using the rsync protocol. */ +static int +updater_rsync(struct updater *up, struct file_update *fup, size_t blocksize) +{ + struct statusrec *sr; + struct stream *to; + char md5[MD5_DIGEST_SIZE]; + ssize_t nbytes; + size_t blocknum, blockstart, blockcount; + char *buf, *line; + int error, orig; + + sr = &fup->srbuf; + + lprintf(1, " Rsync %s\n", fup->coname); + /* First open all files that we are going to work on. */ + to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, + 0600); + if (to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot create: %s", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + orig = open(fup->destpath, O_RDONLY); + if (orig < 0) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", + fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + stream_filter_start(to, STREAM_FILTER_MD5, md5); + + error = updater_read_checkout(up->rd, to); + if (error) { + xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, + strerror(errno)); + return (error); + } + + /* Buffer must contain blocksize bytes. */ + buf = xmalloc(blocksize); + /* Done with the initial text, read and write chunks. */ + line = stream_getln(up->rd, NULL); + while (line != NULL) { + if (strcmp(line, ".") == 0) + break; + error = UPDATER_ERR_PROTO; + if (proto_get_sizet(&line, &blockstart, 10) != 0) + goto bad; + if (proto_get_sizet(&line, &blockcount, 10) != 0) + goto bad; + /* Read blocks from original file. */ + lseek(orig, (blocksize * blockstart), SEEK_SET); + error = UPDATER_ERR_MSG; + for (blocknum = 0; blocknum < blockcount; blocknum++) { + nbytes = read(orig, buf, blocksize); + if (nbytes < 0) { + xasprintf(&up->errmsg, "%s: Cannot read: %s", + fup->destpath, strerror(errno)); + goto bad; + } + nbytes = stream_write(to, buf, nbytes); + if (nbytes == -1) { + xasprintf(&up->errmsg, "%s: Cannot write: %s", + fup->temppath, strerror(errno)); + goto bad; + } + } + /* Get the remaining text from the server. */ + error = updater_read_checkout(up->rd, to); + if (error) { + xasprintf(&up->errmsg, "%s: Cannot write: %s", + fup->temppath, strerror(errno)); + goto bad; + } + line = stream_getln(up->rd, NULL); + } + stream_close(to); + close(orig); + + sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (sr->sr_clientattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + + error = updater_updatefile(up, fup, md5, 0); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ +bad: + free(buf); + return (error); +} Index: contrib/csup/config.c =================================================================== --- contrib/csup/config.c (revision 185155) +++ contrib/csup/config.c (working copy) @@ -133,8 +133,6 @@ coll->co_options &= ~CO_CHECKRCS; /* In recent versions, we always try to set the file modes. */ coll->co_options |= CO_SETMODE; - /* XXX We don't support the rsync updating algorithm yet. */ - coll->co_options |= CO_NORSYNC; error = config_parse_refusefiles(coll); if (error) goto bad; @@ -444,10 +442,6 @@ "\"%s\"\n", cur_coll->co_name); exit(1); } - if (!(cur_coll->co_options & CO_CHECKOUTMODE)) { - lprintf(-1, "Client only supports checkout mode\n"); - exit(1); - } if (!STAILQ_EMPTY(&colls)) { coll = STAILQ_LAST(&colls, coll, co_next); if (strcmp(coll->co_host, cur_coll->co_host) != 0) { Index: contrib/csup/proto.h =================================================================== --- contrib/csup/proto.h (revision 185155) +++ contrib/csup/proto.h (working copy) @@ -44,6 +44,7 @@ char *proto_get_ascii(char **); char *proto_get_rest(char **); int proto_get_int(char **, int *, int); +int proto_get_sizet(char **, size_t *, int); int proto_get_time(char **, time_t *); #endif /* !_PROTO_H_ */ Index: contrib/csup/mux.c =================================================================== --- contrib/csup/mux.c (revision 185155) +++ contrib/csup/mux.c (working copy) @@ -785,6 +785,7 @@ int error, id, iovcnt, what = 0; m = (struct mux *)arg; + what = 0; again: id = sender_waitforwork(m, &what); chan = chan_get(m, id); Index: contrib/csup/lex.rcs.c =================================================================== --- contrib/csup/lex.rcs.c (revision 0) +++ contrib/csup/lex.rcs.c (revision 0) @@ -0,0 +1,2094 @@ + +#line 3 "lex.rcs.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#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 ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via rcsrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void rcsrestart (FILE *input_file ,yyscan_t yyscanner ); +void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void rcs_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcs_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcspush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void rcspop_buffer_state (yyscan_t yyscanner ); + +static void rcsensure_buffer_stack (yyscan_t yyscanner ); +static void rcs_load_buffer_state (yyscan_t yyscanner ); +static void rcs_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); + +#define YY_FLUSH_BUFFER rcs_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) + +YY_BUFFER_STATE rcs_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); + +void *rcsalloc (yy_size_t ,yyscan_t yyscanner ); +void *rcsrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void rcsfree (void * ,yyscan_t yyscanner ); + +#define yy_new_buffer rcs_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + rcsensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + rcsensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define rcswrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); +static int yy_get_next_buffer (yyscan_t yyscanner ); +static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 10 +#define YY_END_OF_BUFFER 11 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[89] = + { 0, + 0, 0, 11, 5, 9, 8, 10, 4, 4, 7, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 9, 5, 4, 4, 5, + 4, 4, 5, 0, 0, 5, 5, 3, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 3, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 1, 5, 5, 5, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 4, 1, 1, 1, 1, + 1, 1, 1, 4, 1, 5, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 7, 8, 1, + 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 10, 11, 12, 13, + + 14, 1, 15, 16, 17, 1, 18, 19, 20, 21, + 22, 23, 1, 24, 25, 26, 27, 1, 1, 28, + 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[30] = + { 0, + 1, 2, 2, 2, 1, 1, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[94] = + { 0, + 0, 0, 136, 25, 127, 297, 297, 27, 29, 297, + 297, 34, 39, 41, 47, 44, 50, 54, 57, 66, + 68, 70, 76, 80, 82, 91, 84, 86, 90, 93, + 95, 97, 102, 58, 61, 0, 0, 107, 109, 112, + 114, 117, 120, 122, 125, 129, 137, 127, 135, 145, + 148, 74, 152, 155, 157, 162, 167, 174, 164, 178, + 182, 184, 187, 189, 191, 193, 196, 200, 204, 206, + 214, 211, 218, 224, 228, 230, 236, 239, 241, 243, + 245, 248, 250, 254, 260, 265, 267, 297, 76, 56, + 47, 292, 294 + + } ; + +static yyconst flex_int16_t yy_def[94] = + { 0, + 88, 1, 88, 89, 88, 88, 88, 90, 91, 88, + 88, 92, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 88, 89, 90, 91, 89, + 91, 91, 92, 93, 93, 33, 33, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 0, 88, 88, + 88, 88, 88 + + } ; + +static yyconst flex_int16_t yy_nxt[327] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 4, 18, 4, 4, 19, 4, + 20, 4, 4, 4, 21, 22, 4, 4, 4, 24, + 25, 28, 29, 31, 32, 34, 35, 34, 36, 37, + 34, 34, 38, 24, 25, 24, 25, 30, 24, 25, + 39, 24, 25, 43, 24, 25, 27, 44, 24, 25, + 35, 24, 25, 35, 41, 40, 52, 46, 42, 52, + 24, 25, 24, 25, 24, 25, 23, 45, 47, 48, + 24, 25, 34, 51, 24, 25, 24, 25, 24, 25, + 28, 29, 26, 49, 31, 32, 50, 24, 25, 31, + + 32, 31, 32, 34, 35, 34, 36, 37, 34, 34, + 38, 24, 25, 24, 25, 33, 24, 25, 24, 25, + 53, 24, 25, 55, 24, 25, 24, 25, 26, 24, + 25, 24, 25, 24, 25, 88, 56, 54, 60, 24, + 25, 24, 25, 88, 64, 57, 58, 59, 61, 24, + 25, 62, 24, 25, 63, 88, 24, 25, 65, 24, + 25, 24, 25, 88, 66, 68, 24, 25, 24, 25, + 69, 24, 25, 72, 88, 67, 88, 70, 24, 25, + 62, 71, 24, 25, 88, 62, 24, 25, 24, 25, + 62, 24, 25, 24, 25, 24, 25, 24, 25, 73, + + 24, 25, 88, 76, 24, 25, 88, 75, 24, 25, + 24, 25, 62, 88, 74, 24, 25, 79, 24, 25, + 88, 62, 24, 25, 77, 78, 88, 80, 24, 25, + 88, 81, 24, 25, 24, 25, 88, 62, 88, 82, + 24, 25, 62, 24, 25, 24, 25, 24, 25, 24, + 25, 83, 24, 25, 24, 25, 84, 62, 24, 25, + 62, 88, 62, 85, 24, 25, 88, 87, 86, 24, + 25, 24, 25, 62, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 62, 88, 88, 88, 62, + 88, 62, 33, 33, 34, 34, 3, 88, 88, 88, + + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88 + } ; + +static yyconst flex_int16_t yy_chk[327] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 4, 8, 8, 9, 9, 12, 12, 12, 12, 12, + 12, 12, 12, 13, 13, 14, 14, 91, 16, 16, + 13, 15, 15, 16, 17, 17, 90, 16, 18, 18, + 34, 19, 19, 35, 14, 13, 34, 18, 15, 35, + 20, 20, 21, 21, 22, 22, 89, 17, 19, 20, + 23, 23, 52, 22, 24, 24, 25, 25, 27, 27, + 28, 28, 26, 21, 29, 29, 21, 30, 30, 31, + + 31, 32, 32, 33, 33, 33, 33, 33, 33, 33, + 33, 38, 38, 39, 39, 38, 40, 40, 41, 41, + 39, 42, 42, 41, 43, 43, 44, 44, 5, 45, + 45, 48, 48, 46, 46, 3, 42, 40, 46, 49, + 49, 47, 47, 0, 49, 43, 44, 45, 47, 50, + 50, 47, 51, 51, 48, 0, 53, 53, 49, 54, + 54, 55, 55, 0, 50, 53, 56, 56, 59, 59, + 54, 57, 57, 59, 0, 51, 0, 55, 58, 58, + 57, 56, 60, 60, 0, 58, 61, 61, 62, 62, + 60, 63, 63, 64, 64, 65, 65, 66, 66, 61, + + 67, 67, 0, 66, 68, 68, 0, 65, 69, 69, + 70, 70, 63, 0, 64, 72, 72, 70, 71, 71, + 0, 67, 73, 73, 68, 69, 0, 71, 74, 74, + 0, 72, 75, 75, 76, 76, 0, 74, 0, 75, + 77, 77, 73, 78, 78, 79, 79, 80, 80, 81, + 81, 76, 82, 82, 83, 83, 79, 81, 84, 84, + 77, 0, 78, 80, 85, 85, 0, 84, 83, 86, + 86, 87, 87, 82, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 85, 0, 0, 0, 86, + 0, 87, 92, 92, 93, 93, 88, 88, 88, 88, + + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "rcstokenizer.l" +/*- + * Copyright (c) 2007-2008, Ulf Lilleengen + * 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 567 "lex.rcs.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + }; /* end struct yyguts_t */ + +static int yy_init_globals (yyscan_t yyscanner ); + +int rcslex_init (yyscan_t* scanner); + +int rcslex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int rcslex_destroy (yyscan_t yyscanner ); + +int rcsget_debug (yyscan_t yyscanner ); + +void rcsset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner ); + +void rcsset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *rcsget_in (yyscan_t yyscanner ); + +void rcsset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *rcsget_out (yyscan_t yyscanner ); + +void rcsset_out (FILE * out_str ,yyscan_t yyscanner ); + +int rcsget_leng (yyscan_t yyscanner ); + +char *rcsget_text (yyscan_t yyscanner ); + +int rcsget_lineno (yyscan_t yyscanner ); + +void rcsset_lineno (int line_number ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int rcswrap (yyscan_t yyscanner ); +#else +extern int rcswrap (yyscan_t yyscanner ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (yyscan_t yyscanner ); +#else +static int input (yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int rcslex (yyscan_t yyscanner); + +#define YY_DECL int rcslex (yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +#line 51 "rcstokenizer.l" + + +#line 791 "lex.rcs.c" + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + rcsensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + rcs_load_buffer_state(yyscanner ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 89 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 297 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 53 "rcstokenizer.l" +{ + return (KEYWORD_TWO); +} + YY_BREAK +case 2: +YY_RULE_SETUP +#line 56 "rcstokenizer.l" +{ + return (KEYWORD); +} + YY_BREAK +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 59 "rcstokenizer.l" +{ + return (STRING); +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 62 "rcstokenizer.l" +{ + return (NUM); +} + YY_BREAK +case 5: +YY_RULE_SETUP +#line 65 "rcstokenizer.l" +{ +/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/ + return (ID); +} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 69 "rcstokenizer.l" +{ return (SEMIC); } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 70 "rcstokenizer.l" +{ return (COLON); } + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 71 "rcstokenizer.l" +; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 72 "rcstokenizer.l" +; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 73 "rcstokenizer.l" +ECHO; + YY_BREAK +#line 937 "lex.rcs.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * rcslex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( rcswrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of rcslex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + rcsrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + rcsrestart(yyin ,yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) rcsrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 89 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + register int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + register char *yy_cp = yyg->yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 89 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 88); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) +{ + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yyg->yy_n_chars + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + rcsrestart(yyin ,yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( rcswrap(yyscanner ) ) + return EOF; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void rcsrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + rcsensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + rcs_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + rcs_load_buffer_state(yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * rcspop_buffer_state(); + * rcspush_buffer_state(new_buffer); + */ + rcsensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + rcs_load_buffer_state(yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (rcswrap()) processing, but the only time this flag + * is looked at is after rcswrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void rcs_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE rcs_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) rcsalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) rcsalloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + rcs_init_buffer(b,file ,yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with rcs_create_buffer() + * @param yyscanner The scanner object. + */ + void rcs_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + rcsfree((void *) b->yy_ch_buf ,yyscanner ); + + rcsfree((void *) b ,yyscanner ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a rcsrestart() or at EOF. + */ + static void rcs_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + rcs_flush_buffer(b ,yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then rcs_init_buffer was _probably_ + * called from rcsrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void rcs_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + rcs_load_buffer_state(yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void rcspush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + rcsensure_buffer_stack(yyscanner); + + /* This block is copied from rcs_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from rcs_switch_to_buffer. */ + rcs_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void rcspop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + rcs_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + rcs_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void rcsensure_buffer_stack (yyscan_t yyscanner) +{ + int num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)rcsalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in rcsensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)rcsrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in rcsensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE rcs_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) rcsalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + rcs_switch_to_buffer(b ,yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to rcslex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * rcs_scan_bytes() instead. + */ +YY_BUFFER_STATE rcs_scan_string (yyconst char * yystr , yyscan_t yyscanner) +{ + + return rcs_scan_bytes(yystr,strlen(yystr) ,yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to rcslex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE rcs_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) rcsalloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = rcs_scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in rcs_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int rcsget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int rcsget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *rcsget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *rcsget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int rcsget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *rcsget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void rcsset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param line_number + * @param yyscanner The scanner object. + */ +void rcsset_lineno (int line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + yy_fatal_error( "rcsset_lineno called with no buffer" , yyscanner); + + yylineno = line_number; +} + +/** Set the current column. + * @param line_number + * @param yyscanner The scanner object. + */ +void rcsset_column (int column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + yy_fatal_error( "rcsset_column called with no buffer" , yyscanner); + + yycolumn = column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * @param yyscanner The scanner object. + * @see rcs_switch_to_buffer + */ +void rcsset_in (FILE * in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = in_str ; +} + +void rcsset_out (FILE * out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = out_str ; +} + +int rcsget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void rcsset_debug (int bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +/* User-visible API */ + +/* rcslex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ + +int rcslex_init(yyscan_t* ptr_yy_globals) + +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) rcsalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* rcslex_init_extra has the same functionality as rcslex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to rcsalloc in + * the yyextra field. + */ + +int rcslex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) + +{ + struct yyguts_t dummy_yyguts; + + rcsset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) rcsalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + rcsset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from rcslex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = 0; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = (char *) 0; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * rcslex_init() + */ + return 0; +} + +/* rcslex_destroy is for both reentrant and non-reentrant scanners. */ +int rcslex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + rcs_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + rcspop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + rcsfree(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + rcsfree(yyg->yy_start_stack ,yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * rcslex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + rcsfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *rcsalloc (yy_size_t size , yyscan_t yyscanner) +{ + return (void *) malloc( size ); +} + +void *rcsrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void rcsfree (void * ptr , yyscan_t yyscanner) +{ + free( (char *) ptr ); /* see rcsrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 73 "rcstokenizer.l" + + + Property changes on: contrib/csup/lex.rcs.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/fattr.c =================================================================== --- contrib/csup/fattr.c (revision 185155) +++ contrib/csup/fattr.c (working copy) @@ -44,7 +44,7 @@ /* * Include the appropriate definition for the file attributes we support. * There are two different files: fattr_bsd.h for BSD-like systems that - * support the extended file flags à la chflags() and fattr_posix.h for + * support the extended file flags a la chflags() and fattr_posix.h for * bare POSIX systems that don't. */ #ifdef HAVE_FFLAGS @@ -449,7 +449,7 @@ piece++; } if (mask & FA_DEV) { - vallen = snprintf(piece->val, sizeof(piece->val), "%lld", + vallen = snprintf(piece->val, sizeof(piece->val), "%llx", (long long)fa->dev); len += snprintf(piece->len, sizeof(piece->len), "%lld", (long long)vallen) + vallen + 1; @@ -534,6 +534,13 @@ return (fa->linkcount); } +char * +fattr_getlinktarget(const struct fattr *fa) +{ + + return (fa->linktarget); +} + /* * Eat the specified attribute and put it in the file attribute * structure. Returns NULL on error, or a pointer to the next @@ -732,18 +739,28 @@ mode_t modemask, mode; int error; + error = 0; + if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) modemask = FA_SETIDMASK | FA_PERMMASK; else modemask = FA_PERMMASK; /* We only implement fattr_makenode() for dirs for now. */ - assert(fa->type == FT_DIRECTORY); if (fa->mask & FA_MODE) mode = fa->mode & modemask; else mode = 0700; - error = mkdir(path, mode); + + if (fa->type == FT_DIRECTORY) + error = mkdir(path, mode); + else if (fa->type == FT_SYMLINK) { + error = symlink(fa->linktarget, path); + } else if (fa->type == FT_CDEV) { + lprintf(-1, "Character devices not supported!\n"); + } else if (fa->type == FT_BDEV) { + lprintf(-1, "Block devices not supported!\n"); + } return (error); } @@ -823,6 +840,19 @@ } #endif + /* + * If it is changed from a file to a symlink, remove the file + * and create the symlink. + */ + if (inplace && (fa->type == FT_SYMLINK) && + (old->type == FT_FILE)) { + error = unlink(topath); + if (error) + goto bad; + error = symlink(fa->linktarget, topath); + if (error) + goto bad; + } /* Determine whether we need to remove the target first. */ if (!inplace && (fa->type == FT_DIRECTORY) != (old->type == FT_DIRECTORY)) { @@ -853,8 +883,9 @@ if (mask & FA_GROUP) gid = fa->gid; error = chown(frompath, uid, gid); - if (error) + if (error) { goto bad; + } } if (mask & FA_MODE) { newmode = fa->mode & modemask; @@ -901,6 +932,9 @@ mask = fa1->mask & fa2->mask; if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN) return (0); + if (mask & FA_FILETYPE) + if (fa1->type != fa2->type) + return (0); if (mask & FA_MODTIME) if (fa1->modtime != fa2->modtime) return (0); @@ -936,3 +970,12 @@ 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: contrib/csup/rcsparse.c =================================================================== --- contrib/csup/rcsparse.c (revision 0) +++ contrib/csup/rcsparse.c (revision 0) @@ -0,0 +1,351 @@ +/*- + * 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$ + */ + +#include +#include +#include + +#include "misc.h" +#include "queue.h" +#include "rcsfile.h" +#include "rcsparse.h" +#include "rcstokenizer.h" + +/* + * This is an RCS-parser using lex for tokenizing and makes sure the RCS syntax + * is correct as it constructs an RCS file that is used by csup. + */ + +static void asserttoken(yyscan_t *, int); +static int parse_admin(struct rcsfile *, yyscan_t *); +static int parse_deltas(struct rcsfile *, yyscan_t *, int); +static int parse_deltatexts(struct rcsfile *, yyscan_t *, int); +static char *duptext(yyscan_t *); + +struct string { + char *str; + STAILQ_ENTRY(string) next; +}; + +static void +asserttoken(yyscan_t *sp, int token) +{ + int t; + + t = token; + t = rcslex(*sp); + assert(t == token); +} + +static char * +duptext(yyscan_t *sp) +{ + char *tmp, *val; + int len; + + tmp = rcsget_text(*sp); + len = rcsget_leng(*sp); + val = xmalloc(len + 2); + strlcpy(val, tmp, len + 1); + return (val); +} + +/* + * Start up parser, and use the rcsfile hook to add objects. + */ +int +rcsparse_run(struct rcsfile *rf, FILE *infp) +{ + yyscan_t scanner; + char *desc; + int error, tok; + + error = 0; + rcslex_init(&scanner); + rcsset_in(infp, scanner); + tok = parse_admin(rf, &scanner); + tok = parse_deltas(rf, &scanner, tok); + assert(tok == KEYWORD); + asserttoken(&scanner, STRING); + desc = duptext(&scanner); + rcsfile_setval(rf, RCSFILE_DESC, desc); + free(desc); + tok = rcslex(scanner); + error = parse_deltatexts(rf, &scanner, tok); + if (error) + return (error); + rcslex_destroy(scanner); + return (0); +} + +/* + * Parse the admin part of a RCS file. + */ +static int +parse_admin(struct rcsfile *rf, yyscan_t *sp) +{ + char *branch, *comment, *expand, *head, *id, *revnum, *tag, *tmp; + int strict, token; + + strict = 0; + branch = NULL; + + /* head {num}; */ + asserttoken(sp, KEYWORD); + asserttoken(sp, NUM); + head = duptext(sp); + rcsfile_setval(rf, RCSFILE_HEAD, head); + free(head); + asserttoken(sp, SEMIC); + + /* { branch {num}; } */ + token = rcslex(*sp); + if (token == KEYWORD_TWO) { + asserttoken(sp, NUM); + branch = duptext(sp); + rcsfile_setval(rf, RCSFILE_BRANCH, branch); + free(branch); + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + + /* access {id]*; */ + assert(token == KEYWORD); + token = rcslex(*sp); + while (token == ID) { + id = duptext(sp); + rcsfile_addaccess(rf, id); + free(id); + token = rcslex(*sp); + } + assert(token == SEMIC); + + /* symbols {sym : num}*; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + while (token == ID) { + tag = duptext(sp); + asserttoken(sp, COLON); + asserttoken(sp, NUM); + revnum = duptext(sp); + rcsfile_importtag(rf, tag, revnum); + free(tag); + free(revnum); + token = rcslex(*sp); + } + assert(token == SEMIC); + + /* locks {id : num}*; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + while (token == ID) { + /* XXX: locks field is skipped */ + asserttoken(sp, COLON); + asserttoken(sp, NUM); + token = rcslex(*sp); + } + assert(token == SEMIC); + token = rcslex(*sp); + while (token == KEYWORD) { + tmp = rcsget_text(*sp); + + /* {strict ;} */ + if (!strcmp(tmp, "strict")) { + rcsfile_setval(rf, RCSFILE_STRICT, tmp); + asserttoken(sp, SEMIC); + /* { comment {string}; } */ + } else if (!strcmp(tmp, "comment")) { + token = rcslex(*sp); + if (token == STRING) { + comment = duptext(sp); + 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: newphrases ignored */ + while (token == ID || token == NUM || token == STRING || + token == COLON) { + token = rcslex(*sp); + } + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + } + return (token); +} + +/* + * Parse RCS deltas. + */ +static int +parse_deltas(struct rcsfile *rf, yyscan_t *sp, int token) +{ + STAILQ_HEAD(, string) branchlist; + char *revnum, *revdate, *author, *state, *next; + + /* In case we don't have deltas. */ + if (token != NUM) + return (token); + do { + next = NULL; + state = NULL; + + /* num */ + assert(token == NUM); + revnum = duptext(sp); + /* date num; */ + asserttoken(sp, KEYWORD); + asserttoken(sp, NUM); + revdate = duptext(sp); + asserttoken(sp, SEMIC); + /* author id; */ + asserttoken(sp, KEYWORD); + asserttoken(sp, ID); + author = duptext(sp); + asserttoken(sp, SEMIC); + /* state {id}; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + if (token == ID) { + state = duptext(sp); + token = rcslex(*sp); + } + assert(token == SEMIC); + /* branches {num}*; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + STAILQ_INIT(&branchlist); + while (token == NUM) + token = rcslex(*sp); + assert(token == SEMIC); + /* next {num}; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + if (token == NUM) { + next = duptext(sp); + token = rcslex(*sp); + } + assert(token == SEMIC); + /* {newphrase }* */ + token = rcslex(*sp); + while (token == ID) { + token = rcslex(*sp); + /* XXX: newphrases ignored. */ + while (token == ID || token == NUM || token == STRING || + token == COLON) { + token = rcslex(*sp); + } + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + rcsfile_importdelta(rf, revnum, revdate, author, state, next); + free(revnum); + free(revdate); + free(author); + if (state != NULL) + free(state); + if (next != NULL) + free(next); + } while (token == NUM); + + return (token); +} + +/* + * Parse RCS deltatexts. + */ +static int +parse_deltatexts(struct rcsfile *rf, yyscan_t *sp, int token) +{ + struct delta *d; + char *log, *revnum, *text; + int error; + + error = 0; + /* In case we don't have deltatexts. */ + if (token != NUM) + 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: newphrases ignored. */ + while (token == ID || token == NUM || token == STRING || + token == COLON) { + token = rcslex(*sp); + } + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + /* text string */ + assert(token == KEYWORD); + asserttoken(sp, STRING); + text = duptext(sp); + 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); +} Property changes on: contrib/csup/rcsparse.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/status.c =================================================================== --- contrib/csup/status.c (revision 185155) +++ contrib/csup/status.c (working copy) @@ -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) @@ -197,6 +206,9 @@ } switch (cmd[0]) { + case 'A': + sr.sr_type = SR_FILELIVE; + break; case 'D': sr.sr_type = SR_DIRDOWN; st->depth++; @@ -215,6 +227,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 +308,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 +372,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: contrib/csup/keyword.c =================================================================== --- contrib/csup/keyword.c (revision 185155) +++ contrib/csup/keyword.c (working copy) @@ -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: contrib/csup/fattr.h =================================================================== --- contrib/csup/fattr.h (revision 185155) +++ contrib/csup/fattr.h (working copy) @@ -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: contrib/csup/rcstokenizer.h =================================================================== --- contrib/csup/rcstokenizer.h (revision 0) +++ contrib/csup/rcstokenizer.h (revision 0) @@ -0,0 +1,333 @@ +#ifndef rcsHEADER_H +#define rcsHEADER_H 1 +#define rcsIN_HEADER 1 + +#line 6 "rcstokenizer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include +#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 + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void rcsrestart (FILE *input_file ,yyscan_t yyscanner ); +void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void rcs_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcs_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcspush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void rcspop_buffer_state (yyscan_t yyscanner ); + +YY_BUFFER_STATE rcs_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); + +void *rcsalloc (yy_size_t ,yyscan_t yyscanner ); +void *rcsrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void rcsfree (void * ,yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define rcswrap(n) 1 +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int rcslex_init (yyscan_t* scanner); + +int rcslex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int rcslex_destroy (yyscan_t yyscanner ); + +int rcsget_debug (yyscan_t yyscanner ); + +void rcsset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner ); + +void rcsset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *rcsget_in (yyscan_t yyscanner ); + +void rcsset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *rcsget_out (yyscan_t yyscanner ); + +void rcsset_out (FILE * out_str ,yyscan_t yyscanner ); + +int rcsget_leng (yyscan_t yyscanner ); + +char *rcsget_text (yyscan_t yyscanner ); + +int rcsget_lineno (yyscan_t yyscanner ); + +void rcsset_lineno (int line_number ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int rcswrap (yyscan_t yyscanner ); +#else +extern int rcswrap (yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int rcslex (yyscan_t yyscanner); + +#define YY_DECL int rcslex (yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#line 73 "rcstokenizer.l" + + +#line 332 "rcstokenizer.h" +#undef rcsIN_HEADER +#endif /* rcsHEADER_H */ Property changes on: contrib/csup/rcstokenizer.h ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/rcsfile.c =================================================================== --- contrib/csup/rcsfile.c (revision 0) +++ contrib/csup/rcsfile.c (revision 0) @@ -0,0 +1,1328 @@ +/*- + * 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 "keyword.h" +#include "misc.h" +#include "proto.h" +#include "queue.h" +#include "rcsfile.h" +#include "rcsparse.h" +#include "stream.h" + +#define BUF_SIZE_DEFAULT 128 + +/* + * RCS parser library. This is the part of the library that handles the + * importing, editing and exporting of RCS files. It currently supports only the + * part of the RCS file specification that is needed for csup (for instance, + * newphrases are not supported), and assumes that you can store the whole RCS + * file in memory. + */ + +/* + * Linked list for string tokens. + */ +struct string { + char *str; + STAILQ_ENTRY(string) string_next; +}; + +/* + * Linked list of tags and revision numbers, in the RCS file header. + */ +struct tag { + char *tag; + char *revnum; + STAILQ_ENTRY(tag) tag_next; +}; + +/* + * A RCS delta. The delta is identified by a revision number, and contains the + * most important RCS attributes that is needed by csup. It also contains + * pointers to other nodes in the RCS file delta structure. + */ +struct delta { + char *revdate; + char *revnum; + char *author; + char *state; + struct buf *log; + struct buf *text; + int placeholder; + struct delta *diffbase; + struct delta *prev; + + LIST_ENTRY(delta) delta_next; + STAILQ_ENTRY(delta) delta_prev; + LIST_ENTRY(delta) table_next; + STAILQ_ENTRY(delta) stack_next; + STAILQ_HEAD(, branch) branchlist; + LIST_ENTRY(delta) branch_next_date; +}; + +/* + * A branch data structure containing information about deltas in the branch as + * well as a base revision number. + */ +struct branch { + char *revnum; + LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */ + STAILQ_ENTRY(branch) branch_next; +}; + +/* + * The rcsfile structure is the "main" structure of the RCS parser library. It + * contains administrative data as well as pointers to the deltas within the + * file. + */ +struct rcsfile { + char *name; + char *head; + char *branch; /* Default branch. */ + char *cvsroot; + char *colltag; + STAILQ_HEAD(, string) accesslist; + STAILQ_HEAD(, tag) taglist; + int strictlock; + char *comment; + int expand; + struct branch *trunk; /* The tip delta. */ + + LIST_HEAD(, delta) deltatable; + LIST_HEAD(, delta) deltatable_dates; + + char *desc; +}; + +static void rcsfile_freedelta(struct delta *); +static void rcsfile_insertdelta(struct branch *, struct delta *, + int); +static struct delta *rcsfile_createdelta(char *); +static int rcsfile_write_deltatext(struct rcsfile *, + struct stream *); +static int rcsfile_puttext(struct rcsfile *, struct stream *, + struct delta *, struct delta *); +static struct branch *rcsfile_getbranch(struct rcsfile *, char *); +static void rcsfile_insertsorteddelta(struct rcsfile *, + struct delta *); +static struct stream *rcsfile_getdeltatext(struct rcsfile *, struct delta *, + struct buf **); +static void rcsdelta_writestring(char *, size_t, struct stream *); + + +/* 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) { + lprintf(-1, "%s\n", line); + line = stream_getln(s, NULL); + } + lprintf(-1, "\n"); +} + +/* + * Parse rcsfile from path and return a pointer to it. + */ +struct rcsfile * +rcsfile_frompath(char *path, char *name, char *cvsroot, char *colltag) +{ + struct rcsfile *rf; + FILE *infp; + 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); + + /* Initialize head branch. */ + rf->trunk = xmalloc(sizeof(struct branch)); + rf->trunk->revnum = xstrdup("1"); + LIST_INIT(&rf->trunk->deltalist); + /* Initialize delta list. */ + LIST_INIT(&rf->deltatable); + /* Initialize tag list. */ + STAILQ_INIT(&rf->taglist); + /* Initialize accesslist. */ + STAILQ_INIT(&rf->accesslist); + + /* Initialize all fields. */ + rf->head = NULL; + rf->branch = NULL; + rf->strictlock = 0; + rf->comment = NULL; + rf->expand = -1; + rf->desc = NULL; + + infp = fopen(path, "r"); + if (infp == NULL) { + lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno)); + rcsfile_free(rf); + return (NULL); + } + error = rcsparse_run(rf, infp); + fclose(infp); + if (error) { + lprintf(-1, "Error parsing \"%s\"\n", name); + rcsfile_free(rf); + return (NULL); + } + return (rf); +} + +/* + * Write content of rcsfile to server. Assumes we have a complete RCS file + * loaded. + */ +int +rcsfile_send_details(struct rcsfile *rf, struct stream *wr) +{ + struct delta *d; + struct tag *t; + int error; + + assert(rf != NULL); + + error = proto_printf(wr, "V %s\n", rf->name); + if (error) + return(error); + + /* Write default branch. */ + if (rf->branch == NULL) + error = proto_printf(wr, "b\n"); + else + error = proto_printf(wr, "B %s\n", rf->branch); + if (error) + return(error); + + /* Write deltas to server. */ + error = proto_printf(wr, "D\n"); + if (error) + return(error); + + LIST_FOREACH(d, &rf->deltatable, table_next) { + error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate); + if (error) + return(error); + } + error = proto_printf(wr, ".\n"); + + if (error) + return(error); + /* Write expand. */ + if (rf->expand >= 0) { + error = proto_printf(wr, "E %s\n", + keyword_encode_expand(rf->expand)); + if (error) + return(error); + } + + /* Write tags to server. */ + error = proto_printf(wr, "T\n"); + if (error) + return(error); + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + error = proto_printf(wr, "%s %s\n", t->tag, t->revnum); + if (error) + return(error); + } + error = proto_printf(wr, ".\n"); + if (error) + return(error); + error = proto_printf(wr, ".\n"); + return (error); +} + +/* + * Write a RCS file to disk represented by the destination stream. Keep track of + * deltas with a stack and an inverted stack. + */ +int +rcsfile_write(struct rcsfile *rf, struct stream *dest) +{ + STAILQ_HEAD(, delta) deltastack; + STAILQ_HEAD(, delta) deltalist_inverted; + struct tag *t; + struct branch *b; + struct delta *d, *d_tmp, *d_next; + int error; + + /* First write head. */ + d = LIST_FIRST(&rf->trunk->deltalist); + stream_printf(dest, "head%s%s;\n", head_space, d->revnum); + + /* Write branch, if we have. */ + if (rf->branch != NULL) + stream_printf(dest, "branch%s%s;\n", branch_space, rf->branch); + + /* Write access. */ + stream_printf(dest, "access"); +#if 0 + if (!STAILQ_EMPTY(&rf->accesslist)) { + /* + * XXX: Write out access. This doesn't seem to be necessary for + * the time being. + */ + } +#endif + stream_printf(dest, ";\n"); + + /* Write out taglist. */ + stream_printf(dest, "symbols"); + if (!STAILQ_EMPTY(&rf->taglist)) { + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + stream_printf(dest, "\n%s%s:%s", tag_space, t->tag, + t->revnum); + } + } + stream_printf(dest, ";\n"); + + /* Write out locks and strict. */ + stream_printf(dest, "locks;"); + if (rf->strictlock) + stream_printf(dest, " strict;"); + stream_printf(dest, "\n"); + + /* Write out the comment. */ + if (rf->comment != NULL) + stream_printf(dest, "comment%s%s;\n", comment_space, rf->comment); + + stream_printf(dest, "\n\n"); + + /* + * Write out deltas. We use a stack where we push the appropriate deltas + * that is to be written out during the loop. + */ + STAILQ_INIT(&deltastack); + d = LIST_FIRST(&rf->trunk->deltalist); + STAILQ_INSERT_HEAD(&deltastack, d, stack_next); + while (!STAILQ_EMPTY(&deltastack)) { + d = STAILQ_FIRST(&deltastack); + STAILQ_REMOVE_HEAD(&deltastack, stack_next); + /* Do not write out placeholders just to be safe. */ + if (d->placeholder) + continue; + stream_printf(dest, "%s\n", d->revnum); + stream_printf(dest, "date%s%s;%sauthor %s;%sstate", + date_space, d->revdate, auth_space, d->author, + state_space); + if (d->state != NULL) + stream_printf(dest, " %s", d->state); + stream_printf(dest, ";\n"); + stream_printf(dest, "branches"); + /* + * Write out our branches. Add them to a reversed list for use + * later when we write out the text. + */ + STAILQ_INIT(&deltalist_inverted); + STAILQ_FOREACH(b, &d->branchlist, branch_next) { + d_tmp = LIST_FIRST(&b->deltalist); + STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev); + STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); + } + + /* Push branch heads on stack. */ + STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) { + if (d_tmp == NULL) + err(1, "empty branch!"); + stream_printf(dest, "\n%s%s", branches_space, + d_tmp->revnum); + } + stream_printf(dest, ";\n"); + + stream_printf(dest, "next%s", next_space); + /* Push next delta on stack. */ + d_next = LIST_NEXT(d, delta_next); + if (d_next != NULL) { + stream_printf(dest, "%s", d_next->revnum); + STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next); + } + stream_printf(dest, ";\n\n"); + } + stream_printf(dest, "\n"); + /* Write out desc. */ + stream_printf(dest, "desc\n@@"); + d = LIST_FIRST(&rf->trunk->deltalist); + + /* + * XXX: We do not take as much care as cvsup to cope with hand-hacked + * RCS-files, and therefore we'll just let them be updated. If having + * them correct is important, it will be catched by the checksum anyway. + */ + + /* Write out deltatexts. */ + error = rcsfile_write_deltatext(rf, dest); + stream_printf(dest, "\n"); + return (error); +} + +/* + * Write out deltatexts of a delta and it's subbranches recursively. + */ +int +rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest) +{ + STAILQ_HEAD(, delta) deltastack; + LIST_HEAD(, delta) branchlist_datesorted; + struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3; + struct stream *in; + struct branch *b; + size_t size; + char *line; + int error; + + error = 0; + STAILQ_INIT(&deltastack); + d = LIST_FIRST(&rf->trunk->deltalist); + d->prev = NULL; + STAILQ_INSERT_HEAD(&deltastack, d, stack_next); + while (!STAILQ_EMPTY(&deltastack)) { + d = STAILQ_FIRST(&deltastack); + STAILQ_REMOVE_HEAD(&deltastack, stack_next); + /* Do not write out placeholders just to be safe. */ + if (d->placeholder) + return (0); + stream_printf(dest, "\n\n\n%s\n", d->revnum); + stream_printf(dest, "log\n@"); + in = stream_open_buf(d->log); + line = stream_getln(in, &size); + while (line != NULL) { + stream_write(dest, line, size); + line = stream_getln(in, &size); + } + stream_close(in); + stream_printf(dest, "@\n"); + stream_printf(dest, "text\n@"); + error = rcsfile_puttext(rf, dest, d, d->prev); + if (error) + return (error); + stream_printf(dest, "@"); + + LIST_INIT(&branchlist_datesorted); + d_next = LIST_NEXT(d, delta_next); + if (d_next != NULL) { + d_next->prev = d; + /* + * If it's trunk, treat it like the oldest, if not treat + * it like a child. + */ + if (rcsrev_istrunk(d_next->revnum)) + STAILQ_INSERT_HEAD(&deltastack, d_next, + stack_next); + else + LIST_INSERT_HEAD(&branchlist_datesorted, d_next, + branch_next_date); + } + + /* + * First, we need to sort our branches based on their date to + * take into account some self-hacked RCS files. + */ + STAILQ_FOREACH(b, &d->branchlist, branch_next) { + d_tmp = LIST_FIRST(&b->deltalist); + if (LIST_EMPTY(&branchlist_datesorted)) { + LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp, + branch_next_date); + continue; + } + + d_tmp2 = LIST_FIRST(&branchlist_datesorted); + if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) < 0) { + LIST_INSERT_BEFORE(d_tmp2, d_tmp, + branch_next_date); + continue; + } + while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date)) + != NULL) { + if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate) + < 0) + break; + d_tmp2 = d_tmp3; + } + LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date); + } + /* + * Invert the deltalist of a branch, since we're writing them + * the opposite way. + */ + LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) { + d_tmp->prev = d; + STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); + } + } + return (0); +} + +/* + * Generates text given a delta and a diffbase. + */ +static int +rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d, + struct delta *diffbase) +{ + struct stream *in, *rd, *orig; + struct keyword *k; + struct diffinfo dibuf, *di; + struct buf *b; + size_t size; + char *line; + int error; + + di = &dibuf; + b = NULL; + error = 0; + + /* Write if the diffbase is the previous */ + if (d->diffbase == diffbase) { + + /* Write out the text. */ + in = stream_open_buf(d->text); + line = stream_getln(in, &size); + while (line != NULL) { + stream_write(dest, line, size); + line = stream_getln(in, &size); + } + stream_close(in); + /* We need to apply diff to produce text, this is probably HEAD. */ + } else if (diffbase == NULL) { + /* Apply diff. */ + orig = rcsfile_getdeltatext(rf, d, &b); + if (orig == NULL) { + error = -1; + goto cleanup; + } + line = stream_getln(orig, &size); + while (line != NULL) { + stream_write(dest, line, size); + line = stream_getln(orig, &size); + } + stream_close(orig); + /* + * A new head was probably added, and now the previous HEAD must be + * changed to include the diff instead. + */ + } else if (diffbase->diffbase == d) { + /* Get reverse diff. */ + orig = rcsfile_getdeltatext(rf, d, &b); + if (orig == NULL) { + error = -1; + goto cleanup; + } + rd = stream_open_buf(diffbase->text); + di->di_rcsfile = rf->name; + di->di_cvsroot = rf->cvsroot; + di->di_revnum = d->revnum; + di->di_revdate = d->revdate; + di->di_author = d->author; + di->di_tag = rf->colltag; + di->di_state = d->state; + di->di_expand = rf->expand; + k = keyword_new(); + + rd = stream_open_buf(diffbase->text); + error = diff_reverse(rd, orig, dest, k, di); + if (error) { + lprintf(-1, "Error applying reverse diff: %d\n", error); + goto cleanup; + } + keyword_free(k); + stream_close(rd); + stream_close(orig); + } +cleanup: + if (b != NULL) + buf_free(b); + return (error); +} + +/* + * Return a stream with an applied diff of a delta. + * XXX: extra overhead on the last apply. Could write directly to file, but + * makes things complicated though. + */ +static struct stream * +rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest) +{ + struct diffinfo dibuf, *di; + struct stream *orig, *dest, *rd; + struct buf *buf_orig; + struct keyword *k; + int error; + + buf_orig = NULL; + error = 0; + + /* + * If diffbase is NULL or we are head (the old head), we have a normal + * complete deltatext. + */ + if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) { + orig = stream_open_buf(d->text); + return (orig); + } + + di = &dibuf; + /* If not, we need to apply our diff to that of our diffbase. */ + orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig); + if (orig == NULL) + return (NULL); + + /* + * Now that we are sure we have a complete deltatext in ret, let's apply + * our diff to it. + */ + *buf_dest = buf_new(BUF_SIZE_DEFAULT); + dest = stream_open_buf(*buf_dest); + + di->di_rcsfile = rf->name; + di->di_cvsroot = rf->cvsroot; + di->di_revnum = d->revnum; + di->di_revdate = d->revdate; + di->di_author = d->author; + di->di_tag = rf->colltag; + di->di_state = d->state; + di->di_expand = rf->expand; + rd = stream_open_buf(d->text); + k = keyword_new(); + error = diff_apply(rd, orig, dest, k, di, 0); + stream_flush(dest); + stream_close(rd); + stream_close(orig); + stream_close(dest); + keyword_free(k); + if (buf_orig != NULL) + buf_free(buf_orig); + if (error) { + lprintf(-1, "Error applying diff: %d\n", error); + return (NULL); + } + + /* Now reopen the stream for the reading. */ + dest = stream_open_buf(*buf_dest); + return (dest); +} + +/* Print content of rcsfile. Useful for debugging. */ +void +rcsfile_print(struct rcsfile *rf) +{ + struct delta *d; + struct tag *t; + struct string *s; + struct stream *in; + char *line; + + lprintf(1, "\n"); + if (rf->name != NULL) + lprintf(1, "name: '%s'\n", rf->name); + if (rf->head != NULL) + lprintf(1, "head: '%s'\n", rf->head); + if (rf->branch != NULL) + lprintf(1, "branch: '%s'\n", rf->branch); + lprintf(1, "Access: "); + STAILQ_FOREACH(s, &rf->accesslist, string_next) + lprintf(1, "'%s' ", s->str); + lprintf(1, "\n"); + + /* Print all tags. */ + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + lprintf(1, "Tag: "); + if (t->tag != NULL) + lprintf(1, "name: %s ", t->tag); + if (t->revnum != NULL) + lprintf(1, "rev: %s", t->revnum); + lprintf(1, "\n"); + } + + if (rf->strictlock) + lprintf(1, "Strict!\n"); + if (rf->comment != NULL) + lprintf(1, "comment: '%s'\n", rf->comment); + if (rf->expand >= 0) + lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand)); + + /* Print all deltas. */ + LIST_FOREACH(d, &rf->deltatable, table_next) { + lprintf(1, "Delta: "); + if (d->revdate != NULL) + lprintf(1, "date: %s ", d->revdate); + if (d->revnum != NULL) + lprintf(1, "rev: %s", d->revnum); + if (d->author != NULL) + lprintf(1, "author: %s", d->author); + if (d->state != NULL) + lprintf(1, "state: %s", d->state); + + lprintf(1, "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); + lprintf(1, "\n"); + } + + if (rf->desc != NULL) + lprintf(1, "desc: '%s'\n", rf->desc); +} + +/* Free all memory associated with a struct rcsfile. */ +void +rcsfile_free(struct rcsfile *rf) +{ + struct delta *d; + struct tag *t; + struct string *s; + + if (rf->name != NULL) + free(rf->name); + if (rf->head != NULL) + free(rf->head); + if (rf->branch != NULL) + free(rf->branch); + if (rf->cvsroot != NULL) + free(rf->cvsroot); + if (rf->colltag != NULL) + free(rf->colltag); + + /* Free all access ids. */ + while (!STAILQ_EMPTY(&rf->accesslist)) { + s = STAILQ_FIRST(&rf->accesslist); + STAILQ_REMOVE_HEAD(&rf->accesslist, string_next); + if (s->str != NULL) + free(s->str); + free(s); + } + + /* Free all tags. */ + while (!STAILQ_EMPTY(&rf->taglist)) { + t = STAILQ_FIRST(&rf->taglist); + STAILQ_REMOVE_HEAD(&rf->taglist, tag_next); + if (t->tag != NULL) + free(t->tag); + if (t->revnum != NULL) + free(t->revnum); + free(t); + } + + if (rf->comment != NULL) + free(rf->comment); + + /* Free all deltas in global list */ + while (!LIST_EMPTY(&rf->deltatable)) { + d = LIST_FIRST(&rf->deltatable); + LIST_REMOVE(d, delta_next); + LIST_REMOVE(d, table_next); + rcsfile_freedelta(d); + } + + /* Free global branch. */ + if (rf->trunk->revnum != NULL) + free(rf->trunk->revnum); + free(rf->trunk); + + if (rf->desc != NULL) + free(rf->desc); + + free(rf); +} + +/* + * Free a RCS delta. + */ +static void +rcsfile_freedelta(struct delta *d) +{ + struct branch *b; + + if (d->revdate != NULL) + free(d->revdate); + if (d->revnum != NULL) + free(d->revnum); + if (d->author != NULL) + free(d->author); + if (d->state != NULL) + free(d->state); + if (d->log != NULL) + buf_free(d->log); + if (d->text != NULL) + buf_free(d->text); + + /* Free all subbranches of a delta. */ + /* XXX: Is this ok? Since the branchpoint is removed, there is no good + * reason for the branch to exists, but we might still have deltas in + * these branches. + */ + while (!STAILQ_EMPTY(&d->branchlist)) { + b = STAILQ_FIRST(&d->branchlist); + STAILQ_REMOVE_HEAD(&d->branchlist, branch_next); + free(b->revnum); + free(b); + } + free(d); +} + +/* + * Functions for editing RCS deltas. + */ + +/* Add a new entry to the access list. */ +void +rcsfile_addaccess(struct rcsfile *rf, char *id) +{ + struct string *s; + + s = xmalloc(sizeof(struct string)); + s->str = xstrdup(id); + STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next); +} + +/* Add a tag to a RCS file. */ +void +rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum) +{ + struct tag *t; + + t = xmalloc(sizeof(struct tag)); + t->tag = xstrdup(tag); + t->revnum = xstrdup(revnum); + + STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next); +} + +/* Import a tag to a RCS file. */ +void +rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum) +{ + struct tag *t; + + t = xmalloc(sizeof(struct tag)); + t->tag = xstrdup(tag); + t->revnum = xstrdup(revnum); + + STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next); +} + +/* + * Delete a revision from the global delta list and the branch it is in. Csup + * will tell us to delete the tags involved. + */ +void +rcsfile_deleterev(struct rcsfile *rf, char *revname) +{ + struct delta *d; + + d = rcsfile_getdelta(rf, revname); + LIST_REMOVE(d, delta_next); + LIST_REMOVE(d, table_next); + rcsfile_freedelta(d); +} + +/* Delete a tag from the tag list. */ +void +rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum) +{ + struct tag *t; + + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + if ((strcmp(tag, t->tag) == 0) && + (strcmp(revnum, t->revnum) == 0)) { + STAILQ_REMOVE(&rf->taglist, t, tag, tag_next); + free(t->tag); + free(t->revnum); + free(t); + return; + } + } +} + +/* + * Searches the global deltalist for a delta. + */ +struct delta * +rcsfile_getdelta(struct rcsfile *rf, char *revnum) +{ + struct delta *d; + + LIST_FOREACH(d, &rf->deltatable, table_next) { + if (strcmp(revnum, d->revnum) == 0) + return (d); + } + return (NULL); +} + +/* Set rcsfile head. */ +void +rcsfile_setval(struct rcsfile *rf, int field, char *val) +{ + + switch (field) { + case RCSFILE_HEAD: + if (rf->head != NULL) + free(rf->head); + rf->head = xstrdup(val); + break; + case RCSFILE_BRANCH: + if (rf->branch != NULL) + free(rf->branch); + rf->branch = (val == NULL) ? NULL : xstrdup(val); + break; + case RCSFILE_STRICT: + if (val != NULL) + rf->strictlock = 1; + break; + case RCSFILE_COMMENT: + if (rf->comment != NULL) + free(rf->comment); + rf->comment = xstrdup(val); + break; + case RCSFILE_EXPAND: + rf->expand = keyword_decode_expand(val); + break; + case RCSFILE_DESC: + if (rf->desc != NULL) + free(rf->desc); + rf->desc = xstrdup(val); + break; + default: + lprintf(-1, "Setting invalid RCSfile value.\n"); + break; + } +} + +/* Create and initialize a delta. */ +static struct delta * +rcsfile_createdelta(char *revnum) +{ + struct delta *d; + + d = xmalloc(sizeof(struct delta)); + d->revnum = xstrdup(revnum); + d->revdate = NULL; + d->state = NULL; + d->author = NULL; + d->log = buf_new(BUF_SIZE_DEFAULT); + d->text = buf_new(BUF_SIZE_DEFAULT); + d->diffbase = NULL; + + STAILQ_INIT(&d->branchlist); + return (d); +} + +/* Add a delta to a imported delta tree. Used by the updater. */ +struct delta * +rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, + char *diffbase) +{ + struct branch *b; + struct delta *d, *d_bp, *d_next; + char *brev, *bprev; + int trunk; + + d_next = NULL; + d = rcsfile_getdelta(rf, revnum); + if (d != NULL) { + lprintf(-1, "Delta %s already exists!\n", revnum); + return (NULL); + } + d = rcsfile_createdelta(revnum); + d->placeholder = 0; + d->revdate = xstrdup(revdate); + d->author = xstrdup(author); + d->diffbase = rcsfile_getdelta(rf, diffbase); + + /* If it's trunk, insert it in the head branch list. */ + b = rcsrev_istrunk(d->revnum) ? rf->trunk : + rcsfile_getbranch(rf, d->revnum); + + /* + * We didn't find a branch, check if we can find a branchpoint and + * create a branch there. + */ + if (b == NULL) { + brev = rcsrev_prefix(d->revnum); + bprev = rcsrev_prefix(brev); + + d_bp = rcsfile_getdelta(rf, bprev); + free(bprev); + if (d_bp == NULL) { + lprintf(-1, "No branch point for adding delta %s\n", + d->revnum); + return (NULL); + } + + /* Create the branch and insert in delta. */ + b = xmalloc(sizeof(struct branch)); + b->revnum = brev; + LIST_INIT(&b->deltalist); + STAILQ_INSERT_HEAD(&d_bp->branchlist, b, branch_next); + } + + /* Insert both into the tree, and into the lookup list. */ + trunk = rcsrev_istrunk(d->revnum); + rcsfile_insertdelta(b, d, trunk); + rcsfile_insertsorteddelta(rf, d); + return (d); +} + +/* Adds a delta to a rcsfile struct. Used by the parser. */ +void +rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, + char *state, char *next) +{ + struct branch *b; + struct delta *d, *d_bp, *d_next; + char *brev, *bprev; + int trunk; + + d_next = NULL; + d = rcsfile_getdelta(rf, revnum); + + if (d == NULL) { + /* If not, we'll just create a new entry. */ + d = rcsfile_createdelta(revnum); + d->placeholder = 0; + } else { + if (d->placeholder == 0) { + lprintf(-1, "Trying to import already existing delta\n"); + return; + } + } + /* + * If already exists, assume that only revnum is filled out, and set the + * rest of the fields. This should be an OK assumption given that we can + * be sure internally that the structure is sufficiently initialized so + * we won't have any unfreed memory. + */ + d->revdate = xstrdup(revdate); + d->author = xstrdup(author); + if (state != NULL) + d->state = xstrdup(state); + + /* If we have a next, create a placeholder for it. */ + if (next != NULL) { + d_next = rcsfile_createdelta(next); + d_next->placeholder = 1; + /* Diffbase should be the previous. */ + d_next->diffbase = d; + } + + /* If it's trunk, insert it in the head branch list. */ + b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf, + d->revnum); + + /* + * We didn't find a branch, check if we can find a branchpoint and + * create a branch there. + */ + if (b == NULL) { + brev = rcsrev_prefix(d->revnum); + bprev = rcsrev_prefix(brev); + + d_bp = rcsfile_getdelta(rf, bprev); + free(bprev); + if (d_bp == NULL) { + lprintf(-1, "No branch point for adding delta %s\n", + d->revnum); + return; + } + + /* Create the branch and insert in delta. */ + b = xmalloc(sizeof(struct branch)); + b->revnum = brev; + LIST_INIT(&b->deltalist); + STAILQ_INSERT_HEAD(&d_bp->branchlist, b, branch_next); + } + + /* Insert if not a placeholder. */ + if (!d->placeholder) { + /* Insert both into the tree, and into the lookup list. */ + if (rcsrev_istrunk(d->revnum)) + rcsfile_insertdelta(b, d, 1); + else { + rcsfile_insertdelta(b, d, 0); + /* + * On import we need to set the diffbase to our + * branchpoint for writing out later. + * XXX: this could perhaps be done at a better time. + */ + if (LIST_FIRST(&b->deltalist) == d) { + brev = rcsrev_prefix(d->revnum); + bprev = rcsrev_prefix(brev); + d_bp = rcsfile_getdelta(rf, bprev); + /* This should really not happen. */ + assert(d_bp != NULL); + d->diffbase = d_bp; + free(brev); + free(bprev); + } + } + rcsfile_insertsorteddelta(rf, d); + } else /* Not a placeholder anymore. */ { + d->placeholder = 0; + /* Put it into the tree. */ + trunk = rcsrev_istrunk(d->revnum); + rcsfile_insertdelta(b, d, trunk); + } + + /* If we have a next, insert the placeholder into the lookup list. */ + if (d_next != NULL) + rcsfile_insertsorteddelta(rf, d_next); +} + +/* + * Find the branch of a revision number. + */ +static struct branch * +rcsfile_getbranch(struct rcsfile *rf, char *revnum) +{ + struct branch *b; + struct delta *d; + char *branchrev, *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); +} + +/* + * 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) { + if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) { + LIST_INSERT_BEFORE(d2, d, delta_next); + return; + } + } else { + /* XXX: here we depend on the date being set, but it + * should be before this is called anyway. */ + if (rcsnum_cmp(d->revdate, d2->revdate) <= 0) { + LIST_INSERT_BEFORE(d2, d, delta_next); + return; + } + } + if (LIST_NEXT(d2, delta_next) == NULL) + break; + } + /* Insert after last element. */ + LIST_INSERT_AFTER(d2, d, delta_next); +} + + +/* Add logtext to a delta. Assume the delta already exists. */ +int +rcsdelta_addlog(struct delta *d, char *log) +{ + struct stream *dest; + + assert(d != NULL); + log++; + log[strlen(log) - 1] = '\0'; + + dest = stream_open_buf(d->log); + stream_write(dest, log, strlen(log)); + stream_close(dest); + return (0); +} + +/* Add deltatext to a delta. Assume the delta already exists. */ +int +rcsdelta_addtext(struct delta *d, char *text) +{ + struct stream *dest; + + assert(d != NULL); + text++; + text[strlen(text) - 1] = '\0'; + + dest = stream_open_buf(d->text); + stream_write(dest, text, strlen(text)); + stream_close(dest); + return (0); +} + +/* Add a deltatext logline to a delta. */ +void +rcsdelta_appendlog(struct delta *d, char *logline, size_t size) +{ + struct stream *dest; + + assert(d != NULL); + dest = stream_open_buf(d->log); + rcsdelta_writestring(logline, size, dest); + stream_close(dest); +} + +/* Add a deltatext textline to a delta. */ +void +rcsdelta_appendtext(struct delta *d, char *textline, size_t size) +{ + struct stream *dest; + + assert(d != NULL); + dest = stream_open_buf(d->text); + rcsdelta_writestring(textline, size, dest); + stream_close(dest); +} + +static void +rcsdelta_writestring(char *textline, size_t size, struct stream *dest) +{ + char buf[3]; + size_t i; + int count; + + 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); + } +} + +/* 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. */ +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); +} Property changes on: contrib/csup/rcsfile.c ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/rcsparse.h =================================================================== --- contrib/csup/rcsparse.h (revision 0) +++ contrib/csup/rcsparse.h (revision 0) @@ -0,0 +1,41 @@ +/*- + * 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 /* !_RCSPARSE_H_ */ Property changes on: contrib/csup/rcsparse.h ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/keyword.h =================================================================== --- contrib/csup/keyword.h (revision 185155) +++ contrib/csup/keyword.h (working copy) @@ -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: contrib/csup/detailer.c =================================================================== --- contrib/csup/detailer.c (revision 185155) +++ contrib/csup/detailer.c (working copy) @@ -30,13 +30,21 @@ #include #include #include +#include +#include +#include +#include + #include "config.h" #include "detailer.h" #include "fixups.h" +#include "globtree.h" #include "misc.h" #include "mux.h" #include "proto.h" +#include "rcsfile.h" +#include "rsyncfile.h" #include "status.h" #include "stream.h" @@ -56,8 +64,16 @@ static int detailer_batch(struct detailer *); static int detailer_coll(struct detailer *, struct coll *, struct status *); -static int detailer_dofile(struct detailer *, struct coll *, +static int detailer_dofile_co(struct detailer *, struct coll *, struct status *, char *); +static int detailer_dofile_rcs(struct detailer *, struct coll *, + char *, char *); +static int detailer_dofile_regular(struct detailer *, char *, char *); +static int detailer_dofile_rsync(struct detailer *, char *, char *); +static int detailer_checkrcsattr(struct detailer *, struct coll *, char *, + struct fattr *, int); +int detailer_send_details(struct detailer *, struct coll *, char *, + char *, struct fattr *); void * detailer(void *arg) @@ -186,8 +202,13 @@ } if (fixup->f_coll != coll) break; - error = proto_printf(wr, "Y %s %s %s\n", fixup->f_name, - coll->co_tag, coll->co_date); + if (coll->co_options & CO_CHECKOUTMODE) + error = proto_printf(wr, "Y %s %s %s\n", + fixup->f_name, coll->co_tag, coll->co_date); + else { + error = proto_printf(wr, "A %s\n", + fixup->f_name); + } if (error) return (DETAILER_ERR_WRITE); fixup = NULL; @@ -208,12 +229,14 @@ static int detailer_coll(struct detailer *d, struct coll *coll, struct status *st) { + struct fattr *rcsattr; struct stream *rd, *wr; - char *cmd, *file, *line, *msg; - int error; + char *attr, *cmd, *file, *line, *msg, *path, *target; + int error, attic; rd = d->rd; wr = d->wr; + attic = 0; line = stream_getln(rd, NULL); if (line == NULL) return (DETAILER_ERR_READ); @@ -226,17 +249,84 @@ /* Delete file. */ file = proto_get_ascii(&line); if (file == NULL || line != NULL) - return (DETAILER_ERR_PROTO); + return (DETAILER_ERR_PROTO); error = proto_printf(wr, "D %s\n", file); if (error) return (DETAILER_ERR_WRITE); break; + case 'I': + case 'i': + case 'j': + /* Directory operations. */ + file = proto_get_ascii(&line); + if (file == NULL || line != NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s\n", cmd, file); + if (error) + return (DETAILER_ERR_WRITE); + break; + case 'J': + /* Set directory attributes. */ + file = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (file == NULL || line != NULL || attr == NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s %s\n", cmd, file, attr); + if (error) + return (DETAILER_ERR_WRITE); + break; + case 'H': + case 'h': + /* Create a hard link. */ + file = proto_get_ascii(&line); + target = proto_get_ascii(&line); + if (file == NULL || target == NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s %s\n", cmd, file, + target); + break; + case 't': + file = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (file == NULL || attr == NULL || line != NULL) { + return (DETAILER_ERR_PROTO); + } + rcsattr = fattr_decode(attr); + if (rcsattr == NULL) { + return (DETAILER_ERR_PROTO); + } + error = detailer_checkrcsattr(d, coll, file, rcsattr, + 1); + break; + + case 'T': + file = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (file == NULL || attr == NULL || line != NULL) + return (DETAILER_ERR_PROTO); + rcsattr = fattr_decode(attr); + if (rcsattr == NULL) + return (DETAILER_ERR_PROTO); + error = detailer_checkrcsattr(d, coll, file, rcsattr, + 0); + break; + case 'U': /* Add or update file. */ file = proto_get_ascii(&line); if (file == NULL || line != NULL) return (DETAILER_ERR_PROTO); - error = detailer_dofile(d, coll, st, file); + if (coll->co_options & CO_CHECKOUTMODE) { + error = detailer_dofile_co(d, coll, st, file); + } else { + path = cvspath(coll->co_prefix, file, 0); + rcsattr = fattr_frompath(path, FATTR_NOFOLLOW); + error = detailer_send_details(d, coll, file, + path, rcsattr); + if (rcsattr != NULL) + fattr_free(rcsattr); + free(path); + } if (error) return (error); break; @@ -261,14 +351,110 @@ return (0); } +/* + * Tell the server to update a regular file. + */ static int -detailer_dofile(struct detailer *d, struct coll *coll, struct status *st, - char *file) +detailer_dofile_regular(struct detailer *d, char *name, char *path) { + struct stream *wr; + struct stat st; char md5[MD5_DIGEST_SIZE]; + int error; + + wr = d->wr; + error = stat(path, &st); + /* If we don't have it or it's unaccessible, we want it again. */ + if (error) { + proto_printf(wr, "A %s\n", name); + return (0); + } + + /* If not, we want the file to be updated. */ + error = MD5_File(path, md5); + if (error) { + lprintf(-1, "Error reading \"%s\"\n", name); + return (error); + } + error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5); + if (error) + return (DETAILER_ERR_WRITE); + return (0); +} + +/* + * Tell the server to update a file with the rsync algorithm. + */ +static int +detailer_dofile_rsync(struct detailer *d, char *name, char *path) +{ struct stream *wr; + struct rsyncfile *rf; + + wr = d->wr; + rf = rsync_open(path, 0, 1); + if (rf == NULL) { + /* Fallback if we fail in opening it. */ + proto_printf(wr, "A %s\n", name); + return (0); + } + proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf), + rsync_blocksize(rf)); + /* Detail the blocks. */ + while (rsync_nextblock(rf) != 0) + proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf)); + proto_printf(wr, ".\n"); + rsync_close(rf); + return (0); +} + +/* + * Tell the server to update an RCS file that we have, or send it if we don't. + */ +static int +detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name, + char *path) +{ + struct stream *wr; struct fattr *fa; + struct rcsfile *rf; + int error; + + wr = d->wr; + path = atticpath(coll->co_prefix, name); + fa = fattr_frompath(path, FATTR_NOFOLLOW); + if (fa == NULL) { + /* We don't have it, so send request to get it. */ + error = proto_printf(wr, "A %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + free(path); + return (0); + } + + rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag); + free(path); + if (rf == NULL) { + error = proto_printf(wr, "A %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + return (0); + } + /* Tell to update the RCS file. The client version details follow. */ + rcsfile_send_details(rf, wr); + rcsfile_free(rf); + fattr_free(fa); + return (0); +} + +static int +detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st, + char *file) +{ + struct stream *wr; + struct fattr *fa; struct statusrec *sr; + char md5[MD5_DIGEST_SIZE]; char *path; int error, ret; @@ -337,3 +523,81 @@ return (DETAILER_ERR_WRITE); return (0); } + +int +detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name, + struct fattr *server_attr, int attic) +{ + struct fattr *client_attr; + char *attr, *path; + int error; + + /* + * I don't think we can use the status file, since it only records file + * attributes in cvsmode. + */ + client_attr = NULL; + path = cvspath(coll->co_prefix, name, attic); + if (path == NULL) { + return (DETAILER_ERR_PROTO); + } + + if (access(path, F_OK) == 0 && + ((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) && + fattr_equal(client_attr, server_attr)) { + attr = fattr_encode(client_attr, NULL, 0); + if (attic) { + error = proto_printf(d->wr, "l %s %s\n", name, attr); + } else { + error = proto_printf(d->wr, "L %s %s\n", name, attr); + } + free(attr); + free(path); + fattr_free(client_attr); + if (error) + return (DETAILER_ERR_WRITE); + return (0); + } + /* We don't have it, so tell the server to send it. */ + error = detailer_send_details(d, coll, name, path, client_attr); + fattr_free(client_attr); + free(path); + return (error); +} + +int +detailer_send_details(struct detailer *d, struct coll *coll, char *name, + char *path, struct fattr *fa) +{ + int error; + size_t len; + + /* + * Try to check if the file exists either live or dead to see if we can + * edit it and put it live or dead, rather than receiving the entire + * file. + */ + if (fa == NULL) { + path = atticpath(coll->co_prefix, name); + fa = fattr_frompath(path, FATTR_NOFOLLOW); + } + if (fa == NULL) { + error = proto_printf(d->wr, "A %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + } else if (fattr_type(fa) == FT_FILE) { + if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) { + detailer_dofile_rcs(d, coll, name, path); + } else if (!(coll->co_options & CO_NORSYNC) && + !globtree_test(coll->co_norsync, name)) { + detailer_dofile_rsync(d, name, path); + } else { + detailer_dofile_regular(d, name, path); + } + } else { + error = proto_printf(d->wr, "N %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + } + return (0); +} Index: contrib/csup/stream.c =================================================================== --- contrib/csup/stream.c (revision 185155) +++ contrib/csup/stream.c (working copy) @@ -97,6 +97,7 @@ struct stream { void *cookie; int fd; + int buf; struct buf *rdbuf; struct buf *wrbuf; stream_readfn_t *readfn; @@ -126,10 +127,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. */ @@ -165,6 +164,12 @@ struct md5filter { MD5_CTX ctx; char *md5; + char lastc; +#define PRINT 1 +#define WS 2 +#define STRING 3 +#define SEEN 4 + int state; }; static int md5filter_init(struct stream *, void *); @@ -172,6 +177,8 @@ static ssize_t md5filter_fill(struct stream *, struct buf *); static int md5filter_flush(struct stream *, struct buf *, stream_flush_t); +static int md5rcsfilter_flush(struct stream *, struct buf *, + stream_flush_t); /* The available stream filters. */ struct stream_filter stream_filters[] = { @@ -195,12 +202,20 @@ md5filter_fini, md5filter_fill, md5filter_flush + }, + { + STREAM_FILTER_MD5RCS, + md5filter_init, + md5filter_fini, + md5filter_fill, + md5rcsfilter_flush } + }; /* Create a new buffer. */ -static struct buf * +struct buf * buf_new(size_t size) { struct buf *buf; @@ -211,6 +226,7 @@ * there in case the stream doesn't have an ending newline. */ buf->buf = xmalloc(size + 1); + memset(buf->buf, 0, size + 1); buf->size = size; buf->in = 0; buf->off = 0; @@ -272,7 +288,7 @@ } /* Free a buffer. */ -static void +void buf_free(struct buf *buf) { @@ -301,6 +317,7 @@ stream->wrbuf = NULL; stream->cookie = NULL; stream->fd = -1; + stream->buf = 0; stream->readfn = readfn; stream->writefn = writefn; stream->closefn = closefn; @@ -335,6 +352,29 @@ return (stream); } +/* Associate a buf with a stream. */ +struct stream * +stream_open_buf(struct buf *b) +{ + struct stream *stream; + + stream = stream_new(stream_read_buf, stream_append_buf, stream_close_buf); + stream->cookie = b; + stream->buf = 1; + b->in = 0; + return (stream); +} + +/* + * Truncate a buffer, just decrease offset pointer. + * XXX: this can be dangerous if not used correctly. + */ +void +stream_truncate_buf(struct buf *b, off_t off) +{ + b->off += off; +} + /* Like open() but returns a stream. */ struct stream * stream_open_file(const char *path, int flags, ...) @@ -391,6 +431,57 @@ return (stream->fd); } +/* Convenience read function for character buffers. */ +ssize_t +stream_read_buf(void *cookie, void *buf, size_t size) +{ + struct buf *b; + size_t avail; + + /* Use in to be read offset. */ + b = (struct buf *)cookie; + /* Just return what we have if the request is to large. */ + avail = b->off - b->in; + if (avail < size) { + memcpy(buf, (b->buf + b->in), avail); + b->in += avail; + return (avail); + } + memcpy(buf, (b->buf + b->in), size); + b->in += size; + return (size); +} + +/* Convenience write function for appending character buffers. */ +ssize_t +stream_append_buf(void *cookie, const void *buf, size_t size) +{ + struct buf *b; + size_t avail; + + /* Use off to be write offset. */ + b = (struct buf *)cookie; + + avail = b->size - b->off; + if (size > avail) + buf_grow(b, b->size + size); + memcpy((b->buf + b->off), buf, size); + b->off += size; + b->buf[b->off] = '\0'; + return (size); +} + +/* Convenience close function for freeing character buffers. */ +int +stream_close_buf(void *cookie) +{ + void *data; + + data = cookie; + /* Basically a NOP. */ + return (0); +} + /* Convenience read function for file descriptors. */ ssize_t stream_read_fd(void *cookie, void *buf, size_t size) @@ -446,6 +537,28 @@ return (n); } +/* A blocking stream_read call. */ +ssize_t +stream_read_blocking(struct stream *stream, void *buf, size_t size) +{ + struct buf *rdbuf; + ssize_t ret; + size_t n; + + rdbuf = stream->rdbuf; + while (buf_count(rdbuf) <= size) { + ret = stream_fill(stream); + if (ret <= 0) + return (-1); + } + /* XXX: Should be at least size bytes in the buffer, right? */ + /* Just do this to make sure. */ + n = min(size, buf_count(rdbuf)); + memcpy(buf, rdbuf->buf + rdbuf->off, n); + buf_less(rdbuf, n); + return (n); +} + /* * Read a line from the stream and return a pointer to it. * @@ -638,6 +751,10 @@ struct stat sb; int error; + if (stream->buf) { + stream_truncate_buf(stream->cookie, off); + return (0); + } if (stream->fd == -1) { errno = EINVAL; return (-1); @@ -1043,6 +1160,8 @@ mf = xmalloc(sizeof(struct md5filter)); MD5_Init(&mf->ctx); mf->md5 = data; + mf->lastc = ';'; + mf->state = PRINT; stream->fdata = mf; return (0); } @@ -1078,3 +1197,107 @@ error = stream_flush_default(stream, buf, how); return (error); } + +/* MD5 flush for RCS, where whitespaces are omitted. */ +static int +md5rcsfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how) +{ + struct md5filter *mf; + char *ptr, *end; + char *start; + char space[2]; + int error; + + mf = stream->fdata; + space[0] = ' '; + space[1] = '\0'; + ptr = buf->buf + buf->off; + end = buf->buf + buf->off + buf->in; + +#define IS_WS(var) ((var) == ' ' || (var) == '\n' || (var) == '\t' || \ + (var) == '\010' || (var) == '\013' || (var) == '\f' || \ + (var) == '\r') + +#define IS_SPECIAL(var) ((var) == '$' || (var) == ',' || (var) == ':' || \ + (var) == ';' || (var) == '@') + +#define IS_PRINT(var) (!IS_WS(var) && (var) != '@') + + /* XXX: We can do better than this state machine. */ + while (ptr < end) { + switch (mf->state) { + /* Outside RCS statements. */ + case PRINT: + start = ptr; + while (ptr < end && IS_PRINT(*ptr)) { + mf->lastc = *ptr; + ptr++; + } + MD5_Update(&mf->ctx, start, (ptr - start)); + if (ptr < end) { + if (*ptr == '@') { + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = STRING; + } else { + mf->state = WS; + } + } + break; + case WS: + while (ptr < end && IS_WS(*ptr)) { + ptr++; + } + if (ptr < end) { + if (*ptr == '@') { + if (mf->lastc == '@') { + MD5_Update(&mf->ctx, + space, 1); + } + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = STRING; + } else { + if (!IS_SPECIAL(*ptr) && + !IS_SPECIAL(mf->lastc)) { + MD5_Update(&mf->ctx, + space, 1); + } + mf->state = PRINT; + } + } + break; + case STRING: + start = ptr; + while (ptr < end && *ptr != '@') { + ptr++; + } + MD5_Update(&mf->ctx, start, (ptr - start)); + if (ptr < end) { + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = SEEN; + } + break; + case SEEN: + if (*ptr == '@') { + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = STRING; + } else if(IS_WS(*ptr)) { + mf->lastc = '@'; + mf->state = WS; + } else { + mf->state = PRINT; + } + break; + default: + err(1, "Invalid state"); + break; + } + } + + error = stream_flush_default(stream, buf, how); + return (error); +} + Index: contrib/csup/rcstokenizer.l =================================================================== --- contrib/csup/rcstokenizer.l (revision 0) +++ contrib/csup/rcstokenizer.l (revision 0) @@ -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$ + */ + +/* + * This tokenizer must be generated by a lexxer with support for reentrancy. + */ +%{ +#include + +#include "misc.h" +#include "rcsparse.h" + +%} +%option reentrant noyywrap +%option header-file="rcstokenizer.h" + +everything (.|\n)* +num [0-9\.]+ +whitespace [\t\n ] +digit [0-9] +idchar [^$,.:;\t\n ] +string @([^@]|\n|"@@")*@ +keyword head|access|symbols|locks|comment|expand|strict|date|author|state|branches|next|desc|log|text +keyword2 branch +newline \n +%% + +{keyword2} { + return (KEYWORD_TWO); +} +{keyword} { + return (KEYWORD); +} +{string} { + return (STRING); +} +{num} { + return (NUM); +} +{num}?{idchar}({idchar}|{num})* { +/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/ + return (ID); +} +; { return (SEMIC); } +: { return (COLON); } +\n ; +[ \t]+ ; +%% Property changes on: contrib/csup/rcstokenizer.l ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/Makefile =================================================================== --- contrib/csup/Makefile (revision 185155) +++ contrib/csup/Makefile (working copy) @@ -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 rsyncfile.c CFLAGS+= -I. -I${.CURDIR} -g -pthread -DHAVE_FFLAGS -DNDEBUG -WARNS?= 6 +WARNS?= 1 # A bit of tweaking is needed to get this Makefile working # with the bsd.prog.mk of all the *BSD OSes... Index: contrib/csup/rcsfile.h =================================================================== --- contrib/csup/rcsfile.h (revision 0) +++ contrib/csup/rcsfile.h (revision 0) @@ -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_ */ Property changes on: contrib/csup/rcsfile.h ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:keywords + FreeBSD=%H Added: svn:eol-style + native Index: contrib/csup/stream.h =================================================================== --- contrib/csup/stream.h (revision 185155) +++ contrib/csup/stream.h (working copy) @@ -34,10 +34,12 @@ typedef enum { STREAM_FILTER_NULL, STREAM_FILTER_ZLIB, - STREAM_FILTER_MD5 + STREAM_FILTER_MD5, + STREAM_FILTER_MD5RCS } stream_filter_t; struct stream; +struct buf; typedef ssize_t stream_readfn_t(void *, void *, size_t); typedef ssize_t stream_writefn_t(void *, const void *, size_t); @@ -48,13 +50,20 @@ stream_writefn_t stream_write_fd; stream_closefn_t stream_close_fd; +/* Convenience functions for handling character buffers. */ +stream_readfn_t stream_read_buf; +stream_writefn_t stream_append_buf; +stream_closefn_t stream_close_buf; + struct stream *stream_open(void *, stream_readfn_t *, stream_writefn_t *, stream_closefn_t *); struct stream *stream_open_fd(int, stream_readfn_t *, stream_writefn_t *, stream_closefn_t *); +struct stream *stream_open_buf(struct buf *); struct stream *stream_open_file(const char *, int, ...); int stream_fileno(struct stream *); ssize_t stream_read(struct stream *, void *, size_t); +ssize_t stream_read_blocking(struct stream *, void *, size_t); ssize_t stream_write(struct stream *, const void *, size_t); char *stream_getln(struct stream *, size_t *); int stream_printf(struct stream *, const char *, ...) @@ -62,6 +71,7 @@ int stream_flush(struct stream *); int stream_sync(struct stream *); int stream_truncate(struct stream *, off_t); +void stream_truncate_buf(struct buf *, off_t); int stream_truncate_rel(struct stream *, off_t); int stream_rewind(struct stream *); int stream_eof(struct stream *); @@ -69,4 +79,6 @@ int stream_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: contrib/csup/lister.c =================================================================== --- contrib/csup/lister.c (revision 185155) +++ contrib/csup/lister.c (working copy) @@ -64,6 +64,10 @@ struct statusrec *); static int lister_dodead(struct lister *, struct coll *, struct statusrec *); +static int lister_dorcsfile(struct lister *, struct coll *, + struct statusrec *); +static int lister_dorcsdead(struct lister *, struct coll *, + struct statusrec *); void * lister(void *arg) @@ -189,6 +193,24 @@ goto bad; } break; + case SR_FILEDEAD: + if (depth < prunedepth) { + if (!(coll->co_options & CO_CHECKOUTMODE)) { + error = lister_dorcsdead(l, coll, sr); + if (error) + goto bad; + } + } + break; + case SR_FILELIVE: + if (depth < prunedepth) { + if (!(coll->co_options & CO_CHECKOUTMODE)) { + error = lister_dorcsfile(l, coll, sr); + if (error) + goto bad; + } + } + break; } } if (ret == -1) { @@ -383,6 +405,60 @@ return (0); } +/* Handle a rcs file live entry found in the status file. */ +static int +lister_dorcsfile(struct lister *l, struct coll *coll, struct statusrec *sr) +{ + struct config *config; + struct stream *wr; + const struct fattr *sendattr; + struct fattr *fa; + char *path, *spath; + size_t len; + int error; + + if (!globtree_test(coll->co_filefilter, sr->sr_file)) + return (0); + config = l->config; + wr = l->wr; + if (!coll->co_options & CO_TRUSTSTATUSFILE) { + path = cvspath(coll->co_prefix, sr->sr_file, 0); + if (path == NULL) { + spath = coll_statuspath(coll); + xasprintf(&l->errmsg, "Error in \"%s\": " + "Invalid filename \"%s\"", spath, sr->sr_file); + free(spath); + return (LISTER_ERR_STATUS); + } + fa = fattr_frompath(path, FATTR_NOFOLLOW); + free(path); + } else + fa = sr->sr_clientattr; + if (fattr_equal(fa, sr->sr_clientattr)) { + /* + * If the file is an RCS file, we use "loose" equality, so sizes + * may disagress because of differences in whitespace. + */ + if (isrcs(sr->sr_file, &len) && + !(coll->co_options & CO_NORCS) && + !(coll->co_options & CO_STRICTCHECKRCS)) { + fattr_maskout(fa, FA_SIZE); + } + sendattr = fa; + } else { + /* + * If different, the user may have changed it, so we report + * bogus attributes to force a full comparison. + */ + sendattr = fattr_bogus; + } + error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr, + config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} + /* Handle a checkout dead entry found in the status file. */ static int lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr) @@ -437,3 +513,57 @@ return (LISTER_ERR_WRITE); return (0); } + +/* Handle a rcs file dead entry found in the status file. */ +static int +lister_dorcsdead(struct lister *l, struct coll *coll, struct statusrec *sr) +{ + struct config *config; + struct stream *wr; + const struct fattr *sendattr; + struct fattr *fa; + char *path, *spath; + size_t len; + int error; + + if (!globtree_test(coll->co_filefilter, sr->sr_file)) + return (0); + config = l->config; + wr = l->wr; + if (!coll->co_options & CO_TRUSTSTATUSFILE) { + path = cvspath(coll->co_prefix, sr->sr_file, 1); + if (path == NULL) { + spath = coll_statuspath(coll); + xasprintf(&l->errmsg, "Error in \"%s\": " + "Invalid filename \"%s\"", spath, sr->sr_file); + free(spath); + return (LISTER_ERR_STATUS); + } + fa = fattr_frompath(path, FATTR_NOFOLLOW); + free(path); + } else + fa = sr->sr_clientattr; + if (fattr_equal(fa, sr->sr_clientattr)) { + /* + * If the file is an RCS file, we use "loose" equality, so sizes + * may disagress because of differences in whitespace. + */ + if (isrcs(sr->sr_file, &len) && + !(coll->co_options & CO_NORCS) && + !(coll->co_options & CO_STRICTCHECKRCS)) { + fattr_maskout(fa, FA_SIZE); + } + sendattr = fa; + } else { + /* + * If different, the user may have changed it, so we report + * bogus attributes to force a full comparison. + */ + sendattr = fattr_bogus; + } + error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr, + config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} Index: contrib/csup/GNUmakefile =================================================================== --- contrib/csup/GNUmakefile (revision 185155) +++ contrib/csup/GNUmakefile (working copy) @@ -12,8 +12,9 @@ UNAME= $(shell uname -s) SRCS= attrstack.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \ - globtree.c idcache.c keyword.c lister.c main.c misc.c mux.c pathcomp.c \ - parse.c proto.c status.c stream.c threads.c token.c updater.c + globtree.c idcache.c keyword.c lex.rcs.c lister.c main.c misc.c mux.c \ + pathcomp.c parse.c proto.c rcsfile.c rcsparse.c rsyncfile.c status.c \ + stream.c threads.c token.c updater.c OBJS= $(SRCS:.c=.o) WARNS= -Wall -W -Wno-unused-parameter -Wmissing-prototypes -Wpointer-arith \ Index: contrib/csup/diff.c =================================================================== --- contrib/csup/diff.c (revision 185155) +++ contrib/csup/diff.c (working copy) @@ -26,9 +26,12 @@ * $FreeBSD$ */ +#include + #include #include #include +#include #include #include @@ -36,15 +39,20 @@ #include "keyword.h" #include "misc.h" #include "stream.h" +#include "queue.h" typedef long lineno_t; #define EC_ADD 0 #define EC_DEL 1 +#define MAXKEY LONG_MAX /* Editing command and state. */ struct editcmd { int cmd; + long key; + int havetext; + int offset; lineno_t where; lineno_t count; lineno_t lasta; @@ -55,20 +63,28 @@ struct diffinfo *di; struct stream *orig; struct stream *dest; + LIST_ENTRY(editcmd) next; }; +struct diffstart { + LIST_HEAD(, editcmd) dhead; +}; + static int diff_geteditcmd(struct editcmd *, char *); static int diff_copyln(struct editcmd *, lineno_t); +static int diff_ignoreln(struct editcmd *, lineno_t); static void diff_write(struct editcmd *, void *, size_t); +static int diff_insert_edit(struct diffstart *, struct editcmd *); +static void diff_free(struct diffstart *); int diff_apply(struct stream *rd, struct stream *orig, struct stream *dest, - struct keyword *keyword, struct diffinfo *di) + struct keyword *keyword, struct diffinfo *di, int comode) { struct editcmd ec; lineno_t i; + size_t size; char *line; - size_t size; int empty, error, noeol; memset(&ec, 0, sizeof(ec)); @@ -104,7 +120,7 @@ line = stream_getln(rd, &size); if (line == NULL) return (-1); - if (line[0] == '.') { + if (comode && line[0] == '.') { line++; size--; } @@ -143,6 +159,198 @@ return (0); } +/* + * Reverse a diff using the same algorithm as in cvsup. + */ +static int +diff_write_reverse(struct stream *dest, struct diffstart *ds) +{ + struct editcmd *ec, *nextec; + long editline, endline, firstoutputlinedeleted; + long num_added, num_deleted, startline; + int num; + + nextec = LIST_FIRST(&ds->dhead); + editline = 0; + num = 0; + while (nextec != NULL) { + ec = nextec; + nextec = LIST_NEXT(nextec, next); + if (nextec == NULL) + break; + num++; + num_deleted = 0; + if (ec->havetext) + num_deleted = ec->count; + num_added = num_deleted + nextec->offset - ec->offset; + if (num_deleted > 0) { + firstoutputlinedeleted = ec->key - num_deleted + 1; + stream_printf(dest, "d%ld %ld\n", firstoutputlinedeleted, + num_deleted); + if (num_added <= 0) + continue; + } + if (num_added > 0) { + stream_printf(dest, "a%ld %ld\n", ec->key, num_added); + startline = ec->key - num_deleted + 1 + ec->offset; + endline = startline + num_added - 1; + + /* Copy lines from original file. First ignore some. */ + ec->editline = editline; + diff_ignoreln(ec, startline - 1); + diff_copyln(ec, endline); + editline = ec->editline; + } + } + return (0); +} + +/* + * Insert a diff into the list sorted on key. Should perhaps use quicker + * algorithms than insertion sort, but do this for now. + */ +static int +diff_insert_edit(struct diffstart *ds, struct editcmd *ec) +{ + struct editcmd *curec; + + if (ec == NULL) + return (0); + + if (LIST_EMPTY(&ds->dhead)) { + LIST_INSERT_HEAD(&ds->dhead, ec, next); + return (0); + } + + /* Insertion sort based on key. */ + LIST_FOREACH(curec, &ds->dhead, next) { + if (ec->key < curec->key) { + LIST_INSERT_BEFORE(curec, ec, next); + return (0); + } + if (LIST_NEXT(curec, next) == NULL) + break; + } + /* Just insert it after. */ + LIST_INSERT_AFTER(curec, ec, next); + return (0); +} + +static void +diff_free(struct diffstart *ds) +{ + struct editcmd *ec; + + while(!LIST_EMPTY(&ds->dhead)) { + ec = LIST_FIRST(&ds->dhead); + LIST_REMOVE(ec, next); + free(ec); + } +} + +/* + * Write the reverse diff from the diff in rd, and original file into + * destination. This algorithm is the same as used in cvsup. + */ +int +diff_reverse(struct stream *rd, struct stream *orig, struct stream *dest, + struct keyword *keyword, struct diffinfo *di) +{ + struct diffstart ds; + struct editcmd ec, *addec, *delec; + lineno_t i; + char *line; + int error, offset; + + memset(&ec, 0, sizeof(ec)); + ec.orig = orig; + ec.dest = dest; + ec.keyword = keyword; + ec.di = di; + addec = NULL; + delec = NULL; + ec.havetext = 0; + offset = 0; + LIST_INIT(&ds.dhead); + + /* Start with next since we need it. */ + line = stream_getln(rd, NULL); + /* First we build up the list of diffs from input. */ + while (line != NULL) { + error = diff_geteditcmd(&ec, line); + if (error) + break; + if (ec.cmd == EC_ADD) { + addec = xmalloc(sizeof(struct editcmd)); + *addec = ec; + addec->havetext = 1; + /* Ignore the lines we was supposed to add. */ + for (i = 0; i < ec.count; i++) { + line = stream_getln(rd, NULL); + if (line == NULL) + return (-1); + } + + /* Get the next diff command if we have one. */ + addec->key = addec->where + addec->count - offset; + if (delec != NULL && + delec->key == addec->key - addec->count) { + delec->key = addec->key; + delec->havetext = addec->havetext; + delec->count = addec->count; + diff_insert_edit(&ds, delec); + free(addec); + delec = NULL; + addec = NULL; + } else { + if (delec != NULL) { + diff_insert_edit(&ds, delec); + } + delec = NULL; + addec->offset = offset; + diff_insert_edit(&ds, addec); + addec = NULL; + } + offset -= ec.count; + } else if (ec.cmd == EC_DEL) { + if (delec != NULL) { + /* Update offset to our next. */ + diff_insert_edit(&ds, delec); + delec = NULL; + } + delec = xmalloc(sizeof(struct editcmd)); + *delec = ec; + delec->key = delec->where - 1 - offset; + delec->offset = offset; + delec->count = 0; + delec->havetext = 0; + /* Important to use the count we had before reset.*/ + offset += ec.count; + } + line = stream_getln(rd, NULL); + } + + while (line != NULL) + line = stream_getln(rd, NULL); + if (delec != NULL) { + diff_insert_edit(&ds, delec); + delec = NULL; + } + + addec = xmalloc(sizeof(struct editcmd)); + /* Should be filesize, but we set it to max value. */ + addec->key = MAXKEY; + addec->offset = offset; + addec->havetext = 0; + addec->count = 0; + diff_insert_edit(&ds, addec); + addec = NULL; + diff_write_reverse(dest, &ds); + diff_free(&ds); + stream_flush(dest); + return (0); +} + /* Get an editing command from the diff. */ static int diff_geteditcmd(struct editcmd *ec, char *line) @@ -181,8 +389,8 @@ static int diff_copyln(struct editcmd *ec, lineno_t to) { + size_t size; char *line; - size_t size; while (ec->editline < to) { line = stream_getln(ec->orig, &size); @@ -194,12 +402,28 @@ return (0); } +/* Ignore lines from the original version of the file up to line "to". */ +static int +diff_ignoreln(struct editcmd *ec, lineno_t to) +{ + size_t size; + char *line; + + while (ec->editline < to) { + line = stream_getln(ec->orig, &size); + if (line == NULL) + return (-1); + ec->editline++; + } + return (0); +} + /* Write a new line to the file, expanding RCS keywords appropriately. */ static void diff_write(struct editcmd *ec, void *buf, size_t size) { + size_t newsize; char *line, *newline; - size_t newsize; int ret; line = buf; Index: contrib/csup/diff.h =================================================================== --- contrib/csup/diff.h (revision 185155) +++ contrib/csup/diff.h (working copy) @@ -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: contrib/csup/TODO =================================================================== --- contrib/csup/TODO (revision 185155) +++ contrib/csup/TODO (working copy) @@ -28,4 +28,3 @@ checkout files (files in CVS/ subdirectores), a command line override to only update a specific collection and a third verbosity level to display commit log messages. -- Add support for CVS mode (maybe?). Index: contrib/csup/misc.c =================================================================== --- contrib/csup/misc.c (revision 185155) +++ contrib/csup/misc.c (working copy) @@ -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,6 +249,31 @@ } /* + * Checks if a file is an RCS file. + */ +int +isrcs(const char *file, size_t *len) +{ + const char *cp; + + if (file[0] == '/') + return (0); + cp = file; + while ((cp = strstr(cp, "..")) != NULL) { + if (cp == file || cp[2] == '\0' || + (cp[-1] == '/' && cp[2] == '/')) + return (0); + cp += 2; + } + *len = strlen(file); + if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') { + return (0); + } + + return (1); +} + +/* * Returns a buffer allocated with malloc() containing the absolute * pathname to the checkout file made from the prefix and the path * of the corresponding RCS file relatively to the prefix. If the @@ -257,26 +282,54 @@ char * checkoutpath(const char *prefix, const char *file) { - const char *cp; char *path; size_t len; - if (file[0] == '/') + if (!isrcs(file, &len)) return (NULL); - cp = file; - while ((cp = strstr(cp, "..")) != NULL) { - if (cp == file || cp[2] == '\0' || - (cp[-1] == '/' && cp[2] == '/')) - return (NULL); - cp += 2; - } - len = strlen(file); - if (len < 2 || file[len - 1] != 'v' || file[len - 2] != ',') - return (NULL); xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file); return (path); } +/* + * Returns a cvs path allocated with malloc() containing absolute pathname to a + * file in cvs mode which can reside in the attic. XXX: filename has really no + * restrictions. + */ +char * +cvspath(const char *prefix, const char *file, int attic) +{ + const char *last; + char *path; + + last = pathlast(file); + if (attic) + xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file), + file, last); + else + xasprintf(&path, "%s/%s", prefix, file); + + return (path); +} + +/* + * Regular or attic path if regular fails. + * XXX: This should perhaps also check if the Attic file exists too, and return + * NULL if not. + */ +char * +atticpath(const char *prefix, const char *file) +{ + char *path; + + path = cvspath(prefix, file, 0); + if (access(path, F_OK) != 0) { + free(path); + path = cvspath(prefix, file, 1); + } + return (path); +} + int mkdirhier(char *path, mode_t mask) { @@ -520,3 +573,73 @@ free(bt); } + +/* Compare two revisions. */ +int +rcsnum_cmp(char *revision1, char *revision2) +{ + char *ptr1, *ptr2, *dot1, *dot2; + int num1len, num2len, ret; + + ptr1 = revision1; + ptr2 = revision2; + while (*ptr1 != '\0' && *ptr2 != '\0') { + dot1 = strchr(ptr1, '.'); + dot2 = strchr(ptr2, '.'); + if (dot1 == NULL) + dot1 = strchr(ptr1, '\0'); + if (dot2 == NULL) + dot2 = strchr(ptr2, '\0'); + + num1len = dot1 - ptr1; + num2len = dot2 - ptr2; + /* Check the distance between each, showing how many digits */ + if (num1len > num2len) + return (1); + else if (num1len < num2len) + return (-1); + + /* Equal distance means we must check each character. */ + ret = strncmp(ptr1, ptr2, num1len); + if (ret != 0) + return (ret); + ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1; + ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2; + } + + if (*ptr1 != '\0' && *ptr2 == '\0') + return (1); + if (*ptr1 == '\0' && *ptr2 != '\0') + return (-1); + return (0); + +} + +/* Returns 0 if a rcsrev is not a trunk revision number. */ +int +rcsrev_istrunk(char *revnum) +{ + char *tmp; + + tmp = strchr(revnum, '.'); + tmp++; + if (strchr(tmp, '.') != NULL) + return (0); + return (1); +} + +/* Return prefix of rcsfile. */ +char * +rcsrev_prefix(char *revnum) +{ + char *modrev, *pos; + + modrev = xstrdup(revnum); + pos = strrchr(modrev, '.'); + if (pos == NULL) { + free(modrev); + return (NULL); + } + *pos = '\0'; + return (modrev); +} Index: usr.bin/csup/Makefile =================================================================== --- usr.bin/csup/Makefile (revision 185155) +++ usr.bin/csup/Makefile (working copy) @@ -13,6 +13,7 @@ globtree.c \ idcache.c \ keyword.c \ + lex.rcs.c \ lister.c \ main.c \ misc.c \ @@ -20,6 +21,9 @@ parse.y \ pathcomp.c \ proto.c \ + rcsfile.c \ + rcsparse.c \ + rsyncfile.c \ status.c \ stream.c \ threads.c \ @@ -28,7 +32,7 @@ CFLAGS+= -I. -I${.CURDIR}/../../contrib/csup CFLAGS+= -DHAVE_FFLAGS -DNDEBUG -WARNS?= 6 +WARNS?= 1 DPADD= ${LIBCRYPTO} ${LIBZ} ${LIBPTHREAD} LDADD= -lcrypto -lz -lpthread