Initial import of csup.

This commit is contained in:
Maxime Henrion 2006-03-03 04:11:29 +00:00
commit bb215397ee
49 changed files with 12927 additions and 0 deletions

63
contrib/csup/GNUmakefile Normal file
View File

@ -0,0 +1,63 @@
# A simple gmake Makefile, to be used on Linux and Darwin. It shouldn't
# be used elsewhere because it assumes that the target system doesn't
# support BSD extended file flags.
#
# $FreeBSD$
#
PREFIX?=/usr/local
OWNER?= 0
GROUP?= 0
UNAME= $(shell uname -s)
SRCS= attrstack.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \
globtree.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
OBJS= $(SRCS:.c=.o)
WARNS= -Wall -W -Wno-unused-parameter -Wmissing-prototypes -Wpointer-arith \
-Wreturn-type -Wcast-qual -Wwrite-strings -Wswitch -Wshadow \
-Wcast-align -Wunused-parameter -Wchar-subscripts -Winline \
-Wnested-externs -Wredundant-decls -Wno-format-y2k
CFLAGS+= -g -O -pipe -DNDEBUG -I$(PREFIX)/include
ifeq ($(UNAME), "Linux")
CFLAGS+= -D_XOPEN_SOURCE -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64
endif
ifeq ($(UNAME), "Darwin")
CFLAGS+= -DHAVE_FFLAGS
endif
CFLAGS+= $(WARNS)
LDFLAGS= -L$(PREFIX)/lib -lcrypto -lz -lpthread
.PHONY: all clean install
all: csup csup.1.gz
csup: $(OBJS)
$(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
config.c: parse.h
token.c: token.l
parse.c: parse.y
parse.h: parse.c
clean:
rm -f csup $(OBJS) parse.c parse.h token.c csup.1.gz
%.o: %.c
$(CC) $(CFLAGS) -c -o $@ $<
%.c: %.y
$(YACC) -d -o $@ $<
csup.1.gz: csup.1
gzip -cn $< > $@
install: csup csup.1.gz
install -s -o $(OWNER) -g $(GROUP) csup $(PREFIX)/bin
install -s -o $(OWNER) -g $(GROUP) csup.1.gz $(PREFIX)/share/man/man1

62
contrib/csup/Makefile Normal file
View File

@ -0,0 +1,62 @@
# $FreeBSD$
PREFIX?= /usr/local
BINDIR?= ${PREFIX}/bin
MANDIR?= ${PREFIX}/man/man
UNAME!= /usr/bin/uname -s
PROG= csup
SRCS= attrstack.c attrstack.h \
config.c config.h \
detailer.c detailer.h \
diff.c diff.h \
fattr.c fattr.h fattr_bsd.h \
fixups.c fixups.h \
fnmatch.c fnmatch.h \
globtree.c globtree.h \
keyword.c keyword.h \
lister.c lister.h \
main.c main.h \
misc.c misc.h \
mux.c mux.h \
parse.h parse.y \
pathcomp.c pathcomp.h \
proto.c proto.h \
status.c status.h \
stream.c stream.h \
threads.c threads.h \
token.h token.l \
updater.c updater.h
CFLAGS+= -I. -I${.CURDIR} -g -pthread -DHAVE_FFLAGS -DNDEBUG
WARNS?= 6
# A bit of tweaking is needed to get this Makefile working
# with the bsd.prog.mk of all the *BSD OSes...
.if (${UNAME} == "NetBSD")
LDFLAGS+= -pthread
YHEADER= yes
.elif (${UNAME} == "OpenBSD")
# I bet there's a better way to do this with the OpenBSD mk
# framework but well, this works and I got bored.
LDFLAGS+= -pthread
YFLAGS= -d
CLEANFILES+= parse.c parse.h y.tab.h
config.c: parse.h
token.l: parse.h
y.tab.h: parse.c
parse.h: y.tab.h
cp ${.ALLSRC} ${.TARGET}
.endif
DPADD= ${LIBCRYPTO} ${LIBZ}
LDADD= -lcrypto -lz
.include <bsd.prog.mk>

39
contrib/csup/README Normal file
View File

@ -0,0 +1,39 @@
$FreeBSD$
Authors
-------
CVSup was originally written in Modula-3 by
John Polstra <jdp@polstra.com>.
Csup is a rewrite of CVSup in C. It has been mostly written by
Maxime Henrion <mux@FreeBSD.org>.
A few contributors have helped him in his task and they are listed here in
alphabetical order :
Olivier Houchard <cognet@FreeBSD.org>
Ulf Lilleengen <lulf@kerneled.org>
Christoph Mathys <cmathys@bluewin.ch> (Google SoC Project)
Etienne Vidal <etienne.vidal@gmail.com>
Building & Installing
---------------------
Csup should build and run fine under any *BSD OS (that includes FreeBSD,
NetBSD, OpenBSD and DragonFlyBSD), as well as Linux and Darwin. If you
have a problem building from source, drop me a mail!
There is one Makefile specifically tailored for *BSD systems named
Makefile and another one that is gmake-specific for Darwin and Linux
users named GNUmakefile. You don't really need to worry about that
since whatever your "make" command is, it should pick up the correct
Makefile.
As usual, to build the source code, just run "make". Once this is done,
just run "make install" to install the binary and manual page.
Be warned however that if the packaging system of your OS knows about
csup, it is certainly better to install it from there rather than by
hand, so that it can then be properly deinstalled.

34
contrib/csup/TODO Normal file
View File

@ -0,0 +1,34 @@
$FreeBSD$
BUGS:
- Fix every XXX in the code :-).
- The stream API needs some polishing. It needs proper error numbers
and a stream_error() function similar to the ferror() function.
- The yacc/lex code to parse the configuration file is sub-optimal. It
has global variables because of yacc, but I think it should be possible
to do it better by using YYFUNC_PROTOTYPE or something. I think it
should also be possible to completely get rid of the lex file.
- Some code uses getpwnam() while it should use the thread-safe variant,
getpwnam_r(). Same for getpwuid() and getgrgid(). We probably need a
UID/GID lookup cache here.
- The $Log$ CVS keyword is not supported.
- Add missing support for supfile keywords and add sanity checks for
some of them. Also, we're not supposed to choke on unknown keywords
to stay in line with CVSup, which just ignores them in order to
maintain compatibility with sup configuration files.
MISSING FEATURES:
- Add support for authentication.
- Add support for shell commands sent by the server.
- Add missing support for various CVSup options : -k, -d, -D,
-a (requires authentication support), -e and -E (requires shell
commands support) and the destDir parameter.
- For now, this code should build fine on FreeBSD, NetBSD, OpenBSD,
Linux and Darwin. Solaris support would also be nice at some point.
- Implement some new useful options : the ability to generate CVS
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?).

90
contrib/csup/attrstack.c Normal file
View File

@ -0,0 +1,90 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
#include <assert.h>
#include <stdlib.h>
#include "attrstack.h"
#include "fattr.h"
#include "misc.h"
#define ATTRSTACK_DEFSIZE 16 /* Initial size of the stack. */
struct attrstack {
struct fattr **stack;
size_t cur;
size_t size;
};
struct attrstack *
attrstack_new(void)
{
struct attrstack *as;
as = xmalloc(sizeof(struct attrstack));
as->stack = xmalloc(sizeof(struct fattr *) * ATTRSTACK_DEFSIZE);
as->size = ATTRSTACK_DEFSIZE;
as->cur = 0;
return (as);
}
struct fattr *
attrstack_pop(struct attrstack *as)
{
assert(as->cur > 0);
return (as->stack[--as->cur]);
}
void
attrstack_push(struct attrstack *as, struct fattr *fa)
{
if (as->cur >= as->size) {
as->size *= 2;
as->stack = xrealloc(as->stack,
sizeof(struct fattr *) * as->size);
}
as->stack[as->cur++] = fa;
}
size_t
attrstack_size(struct attrstack *as)
{
return (as->cur);
}
void
attrstack_free(struct attrstack *as)
{
assert(as->cur == 0);
free(as->stack);
free(as);
}

40
contrib/csup/attrstack.h Normal file
View File

@ -0,0 +1,40 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
#ifndef _ATTRSTACK_H_
#define _ATTRSTACK_H_
struct fattr;
struct attrstack;
struct attrstack *attrstack_new(void);
void attrstack_push(struct attrstack *, struct fattr *);
struct fattr *attrstack_pop(struct attrstack *);
size_t attrstack_size(struct attrstack *);
void attrstack_free(struct attrstack *);
#endif /* !_ATTRSTACK_H_ */

561
contrib/csup/config.c Normal file
View File

@ -0,0 +1,561 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "globtree.h"
#include "keyword.h"
#include "misc.h"
#include "parse.h"
#include "stream.h"
#include "token.h"
static int config_parse_refusefiles(struct coll *);
static int config_parse_refusefile(struct coll *, char *);
extern FILE *yyin;
/* These are globals because I can't think of a better way with yacc. */
static STAILQ_HEAD(, coll) colls;
static struct coll *cur_coll;
static struct coll *defaults;
static const char *cfgfile;
/*
* Extract all the configuration information from the config
* file and some command line parameters.
*/
struct config *
config_init(const char *file, struct coll *override, int overridemask)
{
struct config *config;
struct coll *coll;
size_t slen;
char *prefix;
int error;
mode_t mask;
config = xmalloc(sizeof(struct config));
memset(config, 0, sizeof(struct config));
STAILQ_INIT(&colls);
defaults = coll_new(NULL);
/* Set the default umask. */
mask = umask(0);
umask(mask);
defaults->co_umask = mask;
/* Extract a list of collections from the configuration file. */
cur_coll = coll_new(defaults);
yyin = fopen(file, "r");
if (yyin == NULL) {
lprintf(-1, "Cannot open \"%s\": %s\n", file,
strerror(errno));
goto bad;
}
cfgfile = file;
error = yyparse();
fclose(yyin);
if (error)
goto bad;
memcpy(&config->colls, &colls, sizeof(colls));
if (STAILQ_EMPTY(&config->colls)) {
lprintf(-1, "Empty supfile\n");
goto bad;
}
/* Fixup the list of collections. */
STAILQ_FOREACH(coll, &config->colls, co_next) {
coll_override(coll, override, overridemask);
if (coll->co_base == NULL)
coll->co_base = xstrdup("/usr/local/etc/cvsup");
if (coll->co_colldir == NULL)
coll->co_colldir = "sup";
if (coll->co_prefix == NULL) {
coll->co_prefix = xstrdup(coll->co_base);
/*
* If prefix is not an absolute pathname, it is
* interpreted relative to base.
*/
} else if (coll->co_prefix[0] != '/') {
slen = strlen(coll->co_base);
if (slen > 0 && coll->co_base[slen - 1] != '/')
xasprintf(&prefix, "%s/%s", coll->co_base,
coll->co_prefix);
else
xasprintf(&prefix, "%s%s", coll->co_base,
coll->co_prefix);
free(coll->co_prefix);
coll->co_prefix = prefix;
}
coll->co_prefixlen = strlen(coll->co_prefix);
/* Determine whether to checksum RCS files or not. */
if (coll->co_options & CO_EXACTRCS)
coll->co_options |= CO_CHECKRCS;
else
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;
}
coll_free(cur_coll);
coll_free(defaults);
config->host = STAILQ_FIRST(&config->colls)->co_host;
return (config);
bad:
coll_free(cur_coll);
coll_free(defaults);
config_free(config);
return (NULL);
}
int
config_checkcolls(struct config *config)
{
char linkname[4];
struct stat sb;
struct coll *coll;
int error, numvalid, ret;
numvalid = 0;
STAILQ_FOREACH(coll, &config->colls, co_next) {
error = stat(coll->co_prefix, &sb);
if (error || !S_ISDIR(sb.st_mode)) {
/* Skip this collection, and warn about it unless its
prefix is a symbolic link pointing to "SKIP". */
coll->co_options |= CO_SKIP;
ret = readlink(coll->co_prefix, linkname,
sizeof(linkname));
if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) {
lprintf(-1,"Nonexistent prefix \"%s\" for "
"%s/%s\n", coll->co_prefix, coll->co_name,
coll->co_release);
}
continue;
}
numvalid++;
}
return (numvalid);
}
static int
config_parse_refusefiles(struct coll *coll)
{
char *collstem, *suffix, *supdir, *path;
int error;
if (coll->co_colldir[0] == '/')
supdir = xstrdup(coll->co_colldir);
else
xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir);
/* First, the global refuse file that applies to all collections. */
xasprintf(&path, "%s/refuse", supdir);
error = config_parse_refusefile(coll, path);
free(path);
if (error) {
free(supdir);
return (error);
}
/* Next the per-collection refuse files that applies to all release/tag
combinations. */
xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name);
free(supdir);
error = config_parse_refusefile(coll, collstem);
if (error) {
free(collstem);
return (error);
}
/* Finally, the per-release and per-tag refuse file. */
suffix = coll_statussuffix(coll);
if (suffix != NULL) {
xasprintf(&path, "%s%s", collstem, suffix);
free(suffix);
error = config_parse_refusefile(coll, path);
free(path);
}
free(collstem);
return (error);
}
/*
* Parses a "refuse" file, and records the relevant information in
* coll->co_refusals. If the file does not exist, it is silently
* ignored.
*/
static int
config_parse_refusefile(struct coll *coll, char *path)
{
struct stream *rd;
char *line;
rd = stream_open_file(path, O_RDONLY);
if (rd == NULL)
return (0);
while ((line = stream_getln(rd, NULL)) != NULL)
pattlist_add(coll->co_refusals, line);
if (!stream_eof(rd)) {
stream_close(rd);
lprintf(-1, "Read failure from \"%s\": %s\n", path,
strerror(errno));
return (-1);
}
stream_close(rd);
return (0);
}
void
config_free(struct config *config)
{
struct coll *coll;
while (!STAILQ_EMPTY(&config->colls)) {
coll = STAILQ_FIRST(&config->colls);
STAILQ_REMOVE_HEAD(&config->colls, co_next);
coll_free(coll);
}
if (config->server != NULL)
stream_close(config->server);
if (config->laddr != NULL)
free(config->laddr);
free(config);
}
/* Create a new collection, inheriting options from the default collection. */
struct coll *
coll_new(struct coll *def)
{
struct coll *new;
new = xmalloc(sizeof(struct coll));
memset(new, 0, sizeof(struct coll));
if (def != NULL) {
new->co_options = def->co_options;
new->co_umask = def->co_umask;
if (def->co_host != NULL)
new->co_host = xstrdup(def->co_host);
if (def->co_base != NULL)
new->co_base = xstrdup(def->co_base);
if (def->co_date != NULL)
new->co_date = xstrdup(def->co_date);
if (def->co_prefix != NULL)
new->co_prefix = xstrdup(def->co_prefix);
if (def->co_release != NULL)
new->co_release = xstrdup(def->co_release);
if (def->co_tag != NULL)
new->co_tag = xstrdup(def->co_tag);
if (def->co_listsuffix != NULL)
new->co_listsuffix = xstrdup(def->co_listsuffix);
} else {
new->co_tag = xstrdup(".");
new->co_date = xstrdup(".");
}
new->co_keyword = keyword_new();
new->co_accepts = pattlist_new();
new->co_refusals = pattlist_new();
new->co_attrignore = FA_DEV | FA_INODE;
return (new);
}
void
coll_override(struct coll *coll, struct coll *from, int mask)
{
size_t i;
int newoptions, oldoptions;
newoptions = from->co_options & mask;
oldoptions = coll->co_options & (CO_MASK & ~mask);
if (from->co_release != NULL) {
if (coll->co_release != NULL)
free(coll->co_release);
coll->co_release = xstrdup(from->co_release);
}
if (from->co_host != NULL) {
if (coll->co_host != NULL)
free(coll->co_host);
coll->co_host = xstrdup(from->co_host);
}
if (from->co_base != NULL) {
if (coll->co_base != NULL)
free(coll->co_base);
coll->co_base = xstrdup(from->co_base);
}
if (from->co_colldir != NULL)
coll->co_colldir = from->co_colldir;
if (from->co_prefix != NULL) {
if (coll->co_prefix != NULL)
free(coll->co_prefix);
coll->co_prefix = xstrdup(from->co_prefix);
}
if (newoptions & CO_CHECKOUTMODE) {
if (from->co_tag != NULL) {
if (coll->co_tag != NULL)
free(coll->co_tag);
coll->co_tag = xstrdup(from->co_tag);
}
if (from->co_date != NULL) {
if (coll->co_date != NULL)
free(coll->co_date);
coll->co_date = xstrdup(from->co_date);
}
}
if (from->co_listsuffix != NULL) {
if (coll->co_listsuffix != NULL)
free(coll->co_listsuffix);
coll->co_listsuffix = xstrdup(from->co_listsuffix);
}
for (i = 0; i < pattlist_size(from->co_accepts); i++) {
pattlist_add(coll->co_accepts,
pattlist_get(from->co_accepts, i));
}
for (i = 0; i < pattlist_size(from->co_refusals); i++) {
pattlist_add(coll->co_refusals,
pattlist_get(from->co_refusals, i));
}
coll->co_options = oldoptions | newoptions;
}
char *
coll_statussuffix(struct coll *coll)
{
const char *tag;
char *suffix;
if (coll->co_listsuffix != NULL) {
xasprintf(&suffix, ".%s", coll->co_listsuffix);
} else if (coll->co_options & CO_USERELSUFFIX) {
if (coll->co_tag == NULL)
tag = ".";
else
tag = coll->co_tag;
if (coll->co_release != NULL) {
if (coll->co_options & CO_CHECKOUTMODE) {
xasprintf(&suffix, ".%s:%s",
coll->co_release, tag);
} else {
xasprintf(&suffix, ".%s", coll->co_release);
}
} else if (coll->co_options & CO_CHECKOUTMODE) {
xasprintf(&suffix, ":%s", tag);
}
} else
suffix = NULL;
return (suffix);
}
char *
coll_statuspath(struct coll *coll)
{
char *path, *suffix;
suffix = coll_statussuffix(coll);
if (suffix != NULL) {
if (coll->co_colldir[0] == '/')
xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir,
coll->co_name, suffix);
else
xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base,
coll->co_colldir, coll->co_name, suffix);
} else {
if (coll->co_colldir[0] == '/')
xasprintf(&path, "%s/%s/checkouts", coll->co_colldir,
coll->co_name);
else
xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base,
coll->co_colldir, coll->co_name);
}
free(suffix);
return (path);
}
void
coll_add(char *name)
{
struct coll *coll;
cur_coll->co_name = name;
if (cur_coll->co_release == NULL) {
lprintf(-1, "Release not specified for collection "
"\"%s\"\n", cur_coll->co_name);
exit(1);
}
if (cur_coll->co_host == NULL) {
lprintf(-1, "Host not specified for collection "
"\"%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) {
lprintf(-1, "All \"host\" fields in the supfile "
"must be the same\n");
exit(1);
}
}
STAILQ_INSERT_TAIL(&colls, cur_coll, co_next);
cur_coll = coll_new(defaults);
}
void
coll_free(struct coll *coll)
{
if (coll == NULL)
return;
if (coll->co_host != NULL)
free(coll->co_host);
if (coll->co_base != NULL)
free(coll->co_base);
if (coll->co_date != NULL)
free(coll->co_date);
if (coll->co_prefix != NULL)
free(coll->co_prefix);
if (coll->co_release != NULL)
free(coll->co_release);
if (coll->co_tag != NULL)
free(coll->co_tag);
if (coll->co_cvsroot != NULL)
free(coll->co_cvsroot);
if (coll->co_name != NULL)
free(coll->co_name);
if (coll->co_listsuffix != NULL)
free(coll->co_listsuffix);
keyword_free(coll->co_keyword);
if (coll->co_dirfilter != NULL)
globtree_free(coll->co_dirfilter);
if (coll->co_dirfilter != NULL)
globtree_free(coll->co_filefilter);
if (coll->co_accepts != NULL)
pattlist_free(coll->co_accepts);
if (coll->co_refusals != NULL)
pattlist_free(coll->co_refusals);
free(coll);
}
void
coll_setopt(int opt, char *value)
{
struct coll *coll;
coll = cur_coll;
switch (opt) {
case PT_HOST:
if (coll->co_host != NULL)
free(coll->co_host);
coll->co_host = value;
break;
case PT_BASE:
if (coll->co_base != NULL)
free(coll->co_base);
coll->co_base = value;
break;
case PT_DATE:
if (coll->co_date != NULL)
free(coll->co_date);
coll->co_date = value;
coll->co_options |= CO_CHECKOUTMODE;
break;
case PT_PREFIX:
if (coll->co_prefix != NULL)
free(coll->co_prefix);
coll->co_prefix = value;
break;
case PT_RELEASE:
if (coll->co_release != NULL)
free(coll->co_release);
coll->co_release = value;
break;
case PT_TAG:
if (coll->co_tag != NULL)
free(coll->co_tag);
coll->co_tag = value;
coll->co_options |= CO_CHECKOUTMODE;
break;
case PT_LIST:
if (strchr(value, '/') != NULL) {
lprintf(-1, "Parse error in \"%s\": \"list\" suffix "
"must not contain slashes\n", cfgfile);
exit(1);
}
if (coll->co_listsuffix != NULL)
free(coll->co_listsuffix);
coll->co_listsuffix = value;
break;
case PT_UMASK:
errno = 0;
coll->co_umask = strtol(value, NULL, 8);
free(value);
if (errno) {
lprintf(-1, "Parse error in \"%s\": Invalid "
"umask value\n", cfgfile);
exit(1);
}
break;
case PT_USE_REL_SUFFIX:
coll->co_options |= CO_USERELSUFFIX;
break;
case PT_DELETE:
coll->co_options |= CO_DELETE | CO_EXACTRCS;
break;
case PT_COMPRESS:
coll->co_options |= CO_COMPRESS;
break;
}
}
/* Set "coll" as being the default collection. */
void
coll_setdef(void)
{
coll_free(defaults);
defaults = cur_coll;
cur_coll = coll_new(defaults);
}

124
contrib/csup/config.h Normal file
View File

@ -0,0 +1,124 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _CONFIG_H_
#define _CONFIG_H_
#include <sys/types.h>
#include <sys/socket.h>
#include <time.h>
#include "fattr.h"
#include "queue.h"
#include "misc.h"
/*
* Collection options.
*/
#define CO_BACKUP 0x00000001
#define CO_DELETE 0x00000002
#define CO_KEEP 0x00000004
#define CO_OLD 0x00000008
#define CO_UNLINKBUSY 0x00000010
#define CO_NOUPDATE 0x00000020
#define CO_COMPRESS 0x00000040
#define CO_USERELSUFFIX 0x00000080
#define CO_EXACTRCS 0x00000100
#define CO_CHECKRCS 0x00000200
#define CO_SKIP 0x00000400
#define CO_CHECKOUTMODE 0x00000800
#define CO_NORSYNC 0x00001000
#define CO_KEEPBADFILES 0x00002000
#define CO_EXECUTE 0x00004000
#define CO_SETOWNER 0x00008000
#define CO_SETMODE 0x00010000
#define CO_SETFLAGS 0x00020000
#define CO_NORCS 0x00040000
#define CO_STRICTCHECKRCS 0x00080000
#define CO_TRUSTSTATUSFILE 0x00100000
#define CO_DODELETESONLY 0x00200000
#define CO_DETAILALLRCSFILES 0x00400000
#define CO_MASK 0x007fffff
/* Options that the server is allowed to set. */
#define CO_SERVMAYSET (CO_SKIP | CO_NORSYNC | CO_NORCS)
/* Options that the server is allowed to clear. */
#define CO_SERVMAYCLEAR CO_CHECKRCS
struct coll {
char *co_name;
char *co_host;
char *co_base;
char *co_date;
char *co_prefix;
size_t co_prefixlen;
char *co_release;
char *co_tag;
char *co_cvsroot;
int co_attrignore;
struct pattlist *co_accepts;
struct pattlist *co_refusals;
struct globtree *co_dirfilter;
struct globtree *co_filefilter;
const char *co_colldir;
char *co_listsuffix;
time_t co_scantime; /* Set by the detailer thread. */
int co_options;
mode_t co_umask;
struct keyword *co_keyword;
STAILQ_ENTRY(coll) co_next;
};
struct config {
STAILQ_HEAD(, coll) colls;
struct fixups *fixups;
char *host;
struct sockaddr *laddr;
socklen_t laddrlen;
int socket;
struct chan *chan0;
struct chan *chan1;
struct stream *server;
fattr_support_t fasupport;
};
struct config *config_init(const char *, struct coll *, int);
int config_checkcolls(struct config *);
void config_free(struct config *);
struct coll *coll_new(struct coll *);
void coll_override(struct coll *, struct coll *, int);
char *coll_statuspath(struct coll *);
char *coll_statussuffix(struct coll *);
void coll_add(char *);
void coll_free(struct coll *);
void coll_setdef(void);
void coll_setopt(int, char *);
#endif /* !_CONFIG_H_ */

880
contrib/csup/csup.1 Normal file
View File

@ -0,0 +1,880 @@
.\" Copyright 1996-2003 John D. Polstra.
.\" 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 ``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 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.
.\"
.\" $Id: cvsup.1,v 1.70 2003/03/04 18:23:46 jdp Exp $
.\" $FreeBSD$
.\"
.Dd February 1, 2006
.Os FreeBSD
.Dt CSUP 1
.Sh NAME
.Nm csup
.Nd network distribution package for CVS repositories
.Sh SYNOPSIS
.Nm
.Op Fl 146svzZ
.Op Fl A Ar addr
.Op Fl b Ar base
.Op Fl c Ar collDir
.Op Fl h Ar host
.Op Fl i Ar pattern
.Op Fl l Ar lockfile
.Op Fl L Ar verbosity
.Op Fl p Ar port
.Op Fl r Ar maxRetries
.Ar supfile
.Sh DESCRIPTION
.Nm
is a software package for updating collections of files across a network.
It is a rewrite of the
.Nm CVSup
software in C.
This manual page describes the usage of the
.Nm
client program.
.Pp
Unlike more traditional network distribution packages, such as
.Nm rdist
and
.Nm sup ,
.Nm
has specific optimizations for distributing CVS repositories.
.Nm
takes advantage of the properties of CVS repositories and the files they
contain (in particular, RCS files), enabling it to perform updates much
faster than traditional systems.
.Pp
.Nm
is a general-purpose network file updating package.
It is extremely fast,
even for collections of files which have nothing to do with CVS or
RCS.
.Sh OPTIONS
The client program
.Nm
requires at least a single argument,
.Ar supfile .
It names a file describing one or more collections of files to be
transferred and/or updated from the server.
The
.Ar supfile
has a format similar to the corresponding file used by
.Nm sup .
In most cases,
.Nm
can use existing
.Nm sup Ar supfiles .
.Pp
The following options are supported by
.Nm :
.Bl -tag -width Fl
.It Fl 1
Disables automatic retries when transient failures occur.
Without this option, a transient failure such as a dropped network
connection causes
.Nm
to retry repeatedly, using randomized exponential backoff to space the
retries.
This option is equivalent to
.Fl r Cm 0 .
.It Fl 4
Forces
.Nm
to use IPv4 addresses only.
.It Fl 6
Forces
.Nm
to use IPv6 addresses only.
.It Fl A Ar addr
Specifies a local address to bind to when connecting to the server.
The local address might be a hostname or a numeric host address string
consisting of a dotted decimal IPv4 address or an IPv6 address.
This may be useful on hosts which have multiple IP addresses.
.It Fl b Ar base
Specifies the base directory under which
.Nm
will maintain its bookkeeping files, overriding any
.Cm base
specifications in the
.Ar supfile .
.It Fl c Ar collDir
Specifies the subdirectory of
.Ar base
where the information about the collections is maintained.
The default is
.Pa sup .
.It Fl h Ar host
Specifies the server host to contact, overriding any
.Cm host
specifications in the
.Ar supfile .
.It Fl i Ar pattern
Causes
.Nm
to include only files and directories matching
.Ar pattern
in the update. If a directory matches the pattern, then the entire
subtree rooted at the directory is included. If this option is
specified multiple times, the patterns are combined using the
.Ql or
operation. If no
.Fl i
options are given, the default is to update all files in each
collection.
.Pp
The
.Ar pattern
is a standard file name pattern.
It is interpreted relative to the collection's prefix directory.
Slash characters are matched only by explicit slashes in the pattern.
Leading periods in file name are not treated specially.
.It Fl l Ar lockfile
Creates and locks the
.Ar lockfile
while the update is in progress.
If
.Ar lockfile
is already locked,
.Nm
fails without performing automatic retries.
This option is useful when
.Nm
is executed periodically from
.Nm cron .
It prevents a job from interfering with an earlier job that is perhaps
taking extra long because of network problems.
.Pp
The process-ID is written to the lock file in text form when the lock
is successfully acquired.
Upon termination of the update, the lock file is removed.
.It Fl L Ar verbosity
Sets the verbosity level for output.
A level of 0 causes
.Nm
to be completely silent unless errors occur.
A level of 1 (the default) causes each updated file to be listed.
A level of 2 provides more detailed information about the updates
performed on each file.
All messages are directed to the standard output.
.It Fl p Ar port
Sets the TCP port to which
.Nm
attempts to connect on the server host.
The default port is 5999.
.It Fl r Ar maxRetries
Limits the number of automatic retries that will be attempted when
transient errors such as lost network connections are encountered.
By default,
.Nm
will retry indefinitely until an update is successfully completed.
The retries are spaced using randomized exponential backoff.
Note that
.Fl r Cm 0
is equivalent to the
.Fl 1
option.
.It Fl s
Suppresses the check of each client file's status against what is
recorded in the list file. Instead, the list file is assumed to be
accurate. This option greatly reduces the amount of disk activity and
results in faster updates with less load on the client host. However
it should only be used if client's files are never modified locally in
any way. Mirror sites may find this option beneficial to reduce the
disk load on their systems. For safety, even mirror sites should run
.Nm
occasionally (perhaps once a day) without the
.Fl s
option.
.Pp
Without the
.Fl s
option,
.Nm
performs a
.Xr stat 2
call on each file and verifies that its attributes match those
recorded in the list file. This ensures that any file changes made
outside of
.Nm
are detected and corrected.
.Pp
If the
.Fl s
option is used when one or more files have been modified locally, the
results are undefined. Local file damage may remain uncorrected,
updates may be missed, or
.Nm
may abort prematurely.
.It Fl v
Prints the version number and exits, without contacting the server.
.It Fl z
Enables compression for all collections, as if the
.Cm compress
keyword were added to every collection in the
.Ar supfile .
.It Fl Z
Disables compression for all collections, as if the
.Cm compress
keyword were removed from every collection in the
.Ar supfile .
.El
.Pp
The
.Ar supfile
is a text file which specifies the file collections to be updated.
Comments begin with
.Ql #
and extend to the end of the line. Lines that are empty except for
comments and white space are ignored. Each remaining line begins
with the name of a server-defined collection of files. Following the
collection name on the line are zero or more keywords or keyword=value
pairs.
.Pp
Default settings may be specified in lines whose collection name is
.Cm *default .
Such defaults will apply to subsequent lines in the
.Ar supfile .
Multiple
.Cm *default
lines may be present.
New values augment or override any defaults specified earlier in the
.Ar supfile .
Values specified explicitly for a collection override any default
values.
.Pp
The most commonly used keywords are:
.Bl -tag -width Fl
.It Cm release= Ns Ar releaseName
This specifies the release of the files within a collection.
Like collection names, release names are defined by the server
configuration files. Usually there is only one release in each
collection, but there may be any number. Collections which come from
a CVS repository often use
.Cm release=cvs
by convention. Non-CVS collections conventionally use
.Cm release=current .
.It Cm base= Ns Ar base
This specifies a directory under which
.Nm
will maintain its bookkeeping files, describing the state of each
collection on the client machine.
The
.Ar base
directory must already exist;
.Nm
will not create it.
The default
.Ar base
directory is
.Pa /usr/local/etc/csup .
.It Cm prefix= Ns Ar prefix
This is the directory under which updated files will be placed.
By default, it is the same as
.Ar base .
If it is not an absolute pathname, it is interpreted relative to
.Ar base .
The
.Ar prefix
directory must already exist;
.Nm
will not create it.
.Pp
As a special case, if
.Ar prefix
is a symbolic link pointing to a nonexistent file named
.Ql SKIP ,
then
.Nm
will skip the collection.
The parameters associated with the collection are still checked for
validity, but none of its files will be updated.
This feature allows a site to use a standard
.Ar supfile
on several machines, yet control which collections get updated on a
per-machine basis.
.It Cm host= Ns Ar hostname
This specifies the server machine from which all files will be taken.
.Nm
requires that all collections in a single run come from the same host.
If you wish to update collections from several different hosts, you must
run
.Nm
several times.
.It Cm delete
The presence of this keyword gives
.Nm
permission to delete files.
If it is missing, no files will be deleted.
.Pp
The presence of the
.Cm delete
keyword puts
.Nm
into so-called
.Em exact
mode. In exact mode,
.Nm
does its best to make the client's files correspond to those on the server.
This includes deleting individual deltas and symbolic tags from RCS
files, as well as deleting entire files.
In exact mode,
.Nm
verifies every edited file with a checksum, to ensure that the edits
have produced a file identical to the master copy on the server.
If the checksum test fails for a file, then
.Nm
falls back upon transferring the entire file.
.Pp
In general,
.Nm
deletes only files which are known to the server.
Extra files present in the client's tree are left alone, even in exact
mode.
More precisely,
.Nm
is willing to delete two classes of files:
.Bl -bullet -compact
.It
Files that were previously created or updated by
.Nm
itself.
.It
Checked-out versions of files which are marked as dead on the server.
.El
.It Cm use-rel-suffix
Causes
.Nm
to append a suffix constructed from the release and tag to the name of
each list file that it maintains.
See
.Sx THE LIST FILE
for details.
.It Cm compress
This enables compression of all data sent across the network.
Compression is quite effective, normally eliminating 65% to 75% of the
bytes that would otherwise need to be transferred.
However, it is costly in terms of CPU time on both the client and the
server.
On local area networks, compression is generally counter-productive; it
actually slows down file updates.
On links with speeds of 56K bits/second or less, compression is almost
always beneficial.
For network links with speeds between these two extremes, let
experimentation be your guide.
.Pp
The
.Fl z
command line option enables the
.Cm compress
keyword for all collections, regardless of what is specified in the supfile.
Likewise, the
.Fl Z
command line option disables the
.Cm compress
option for all collections.
.Nm
uses a looser checksum for RCS files, which ignores harmless
differences in white space. Different versions of CVS and RCS produce
a variety of differences in white space for the same RCS files. Thus
the strict checksum can report spurious mismatches for files which are
logically identical. This can lead to numerous unneeded
.Dq fixups ,
and thus to slow updates.
.It Cm umask= Ns Ar n
Causes
.Nm
to use a umask value of
.Ar n
(an octal number) when updating the files in the collection.
This option is ignored if
.Cm preserve
is specified.
.El
.Pp
Some additional, more specialized keywords are described below.
Unrecognized keywords are silently ignored for backward compatibility
with
.Nm sup .
.Sh CVS MODE
.Nm CVSup
supports two primary modes of operation.
They are called
.Em CVS
mode and
.Em checkout
mode.
.Nm
only supports the checkout mode for now.
.Pp
In CVS mode, the client receives copies of the actual RCS files making
up the master CVS repository. CVS mode is the default mode of operation.
It is appropriate when the user wishes to maintain a full copy of the
CVS repository on the client machine.
.Pp
CVS mode is also appropriate for file collections which are not
based upon a CVS repository. The files are simply transferred
verbatim, without interpretation.
.Sh CHECKOUT MODE
In checkout mode, the client receives specific revisions of files,
checked out directly from the server's CVS repository.
Checkout mode allows the client to receive any version from the
repository, without requiring any extra disk space on the server for
storing multiple versions in checked-out form.
Checkout mode provides much flexibility beyond that basic functionality,
however.
The client can specify any CVS symbolic tag, or any date, or both, and
.Nm
will provide the corresponding checked-out versions of the files in the
repository.
.Pp
Checkout mode is selected on a per-collection basis, by the presence of
one or both of the following keywords in the
.Ar supfile :
.Bl -tag -width Fl
.It Cm tag= Ns Ar tagname
This specifies a symbolic tag that should be used to select the
revisions that are checked out from the CVS repository.
The tag may refer to either a branch or a specific revision.
It must be symbolic; numeric revision numbers are not supported.
.Pp
For the FreeBSD source repository, the most commonly used tags will be:
.Bl -tag -width RELENG_6
.It Li RELENG_6
The
.Ql stable
branch.
.It Li \&.
The main branch (the
.Ql current
release).
This is the default, if only the
.Cm date
keyword is given.
.El
.Sm off
.It Xo Cm date=
.Op Ar cc
.Ar yy.mm.dd.hh.mm.ss
.Xc
.Sm on
This specifies a date that should be used to select the revisions that
are checked out from the CVS repository.
The client will receive the revisions that were in effect at the
specified date and time.
.Pp
At present, the date format is inflexible. All 17 or 19 characters must
be specified, exactly as shown.
For the years 2000 and beyond, specify the century
.Ar cc .
For earlier years, specify only the last two digits
.Ar yy .
Dates and times are considered to
be GMT.
The default date is
.Ql \&. ,
which means
.Dq as late as possible .
.El
.Pp
To enable checkout mode, you must specify at least one of these keywords.
If both are missing,
.Nm
defaults to CVS mode.
.Pp
If both a branch tag and a date are specified, then the revisions on the
given branch, as of the given date, will be checked out. It is
permitted, but not particularly useful, to specify a date with a
specific release tag.
.Pp
In checkout mode, the tag and/or date may be changed between updates.
For example, suppose that a collection has been transferred using the
specification
.Ql tag=. .
The user could later change the specification to
.Ql tag=RELENG_3 .
This would cause
.Nm
to edit the checked-out files in such a way as to transform them from the
.Ql current
versions to the
.Ql stable
versions.
In general,
.Nm
is willing to transform any tag/date combination into any other tag/date
combination, by applying the intervening RCS deltas to the existing files.
.Pp
When transforming a collection of checked-out files from one tag to
another, it is important to specify the
.Cm list
keyword in the
.Ar supfile ,
to ensure that the same list file is used both before and after the
transformation.
The list file is described in
.Sx THE LIST FILE ,
below.
.Sh THE LIST FILE
For efficiency,
.Nm
maintains a bookkeeping file for each collection, called the list file.
The list file contains information about which files and revisions the client
currently possesses.
It also contains information used for verifying that the list file
is consistent with the actual files in the client's tree.
.Pp
The list file is not strictly necessary. If it is deleted, or becomes
inconsistent with the actual client files,
.Nm
falls back upon a less efficient method of identifying the client's
files and performing its updates.
Depending on
.Nm csup Ns No 's
mode of operation, the fallback method employs time stamps, checksums, or
analysis of RCS files.
.Pp
Because the list file is not essential,
.Nm
is able to
.Dq adopt
an existing file tree acquired by FTP or from a CD-ROM.
.Nm
identifies the client's versions of the files, updates them as
necessary, and creates a list file for future use.
Adopting a foreign file tree is not as fast as performing a normal
update.
It also produces a heavier load on the server.
.Pp
The list file is stored in a collection-specific directory; see
.Sx FILES
for details.
Its name always begins with
.Ql checkouts .
If the keyword
.Cm use-rel-suffix
is specified in the
.Ar supfile ,
a suffix, formed from the release and tag, is appended to the name.
The default suffix can be overridden by specifying an explicit suffix in
the
.Ar supfile :
.Bl -tag -width Fl
.It Cm list= Ns Ar suffix
This specifies a suffix for the name of the list file. A leading dot is
provided automatically.
For example,
.Ql list=stable
would produce a list file named
.Pa checkouts.stable ,
regardless of the release, tag, or
.Cm use-rel-suffix
keyword.
.El
.Sh REFUSE FILES
The user can specify sets of files that he does not wish to receive.
The files are specified as file name patterns in so-called
.Em refuse
files.
The patterns are separated by whitespace, and multiple patterns are
permitted on each line.
Files and directories matching the patterns are neither updated nor
deleted; they are simply ignored.
.Pp
There is currently no provision for comments in refuse files.
.Pp
The patterns are similar to those of
.Xr sh 1 ,
except that there is no special treatment for slashes or for
filenames that begin with a period.
For example, the pattern
.Ql *.c
will match any file name ending with
.Ql \&.c
including those in subdirectories, such as
.Ql foo/bar/lam.c .
All patterns are interpreted relative to the collection's prefix
directory.
.Pp
If the files are coming from a CVS repository, as is usually
the case, then they will be RCS files. These have a
.Ql \&,v
suffix which must be taken into account in the patterns. For
example, the FreeBSD documentation files are in a sub-directory of
.Ar base
called
.Ql doc .
If
.Ql Makefile
from that directory is not required then the line
.Pp
.Bl -item -compact -offset indent
.It
.Pa doc/Makefile
.El
.Pp
will not work because the file on the server is called
.Ql Makefile,v.
A better solution would be
.Pp
.Bl -item -compact -offset indent
.It
.Pa doc/Makefile*
.El
.Pp
which will match whether
.Ql Makefile
is an RCS file or not.
.Pp
As another example, to receive the FreeBSD documentation files without
the Japanese, Russian, and Chinese translations, create a refuse file
containing the following lines:
.Pp
.Bl -item -compact -offset indent
.It
.Pa doc/ja*
.It
.Pa doc/ru*
.It
.Pa doc/zh*
.El
.Pp
As many as three refuse files are examined for each
.Ar supfile
line.
There can be a global refuse file named
.Sm off
.Ar base / Ar collDir Pa /refuse
.Sm on
which applies to all collections and releases.
There can be a per-collection refuse file named
.Sm off
.Xo Ar base / Ar collDir / Ar collection
.Pa /refuse
.Xc
.Sm on
which applies to a specific collection.
Finally, there can be a per-release and tag refuse file which applies only
to a given release/tag combination within a collection.
The name of the latter is formed by suffixing the name of the
per-collection refuse file in the same manner as described above for the
list file.
None of the refuse files are required to exist.
.Pp
.Nm
has a built-in default value of
.Ar /usr/local/etc/cvsup
for
.Ar base
and
.Ar sup
for
.Ar collDir
but it is possible to override both of these. The value of
.Ar base
can be changed using the
.Fl b
option or a
.Ar base=pathname
entry in the
.Ar supfile .
(If both are used the
.Fl b
option will override the
.Ar supfile
entry.) The value of
.Ar collDir
can only be changed with the
.Fl c
option; there is no
.Ar supfile
command to change it.
.Pp
As an example, suppose that the
.Ar base
and
.Ar collDir
both have their default values, and that the collection and release are
.Ql src-all
and
.Ql cvs ,
respectively.
Assume further that checkout mode is being used with
.Ql tag=RELENG_3 .
The three possible refuse files would then be named:
.Pp
.Bl -item -compact -offset indent
.It
.Pa /usr/local/etc/cvsup/sup/refuse
.It
.Pa /usr/local/etc/cvsup/sup/src-all/refuse
.It
.Pa /usr/local/etc/cvsup/sup/src-all/refuse.cvs:RELENG_3
.El
.Pp
If the
.Ar supfile
includes the command
.Ar base=/foo
the refuse files would be:
.Pp
.Bl -item -compact -offset indent
.It
.Pa /foo/sup/refuse
.It
.Pa /foo/sup/src-all/refuse
.It
.Pa /foo/sup/src-all/refuse.cvs:RELENG_3
.El
.Pp
If
.Fl b
.Ar /bar
is used (even with
.Ar base=/foo
in the
.Ar supfile ) :
.Pp
.Bl -item -compact -offset indent
.It
.Pa /bar/sup/refuse
.It
.Pa /bar/sup/src-all/refuse
.It
.Pa /bar/sup/src-all/refuse.cvs:RELENG_3
.El
.Pp
and with
.Fl c
.Ar stool
as well:
.Pp
.Bl -item -compact -offset indent
.It
.Pa /bar/stool/refuse
.It
.Pa /bar/stool/src-all/refuse
.It
.Pa /bar/stool/src-all/refuse.cvs:RELENG_3
.El
.Sh csup AND FIREWALLS
In its default mode,
.Nm
will work through any firewall which permits outbound connections to
port 5999 of the server host.
.Sh USING csup WITH SOCKS
.Nm
can be used through a SOCKS proxy server with the standard
.Nm runsocks
command.
Your
.Nm
executable needs to be dynamically-linked with the system
libraries for
.Nm runsocks
to work properly.
.Sh USING ssh PORT FORWARDING
As an alternative to SOCKS, a user behind a firewall can penetrate it
with the TCP port forwarding provided by the Secure Shell package
.Nm ssh .
The user must have a login account on the
.Nm CVSup
server host in order to do this.
The procedure is as follows:
.Bl -enum
.It
Establish a connection to the server host with
.Nm ssh ,
like this:
.Bd -literal
ssh -f -x -L 5999:localhost:5999 serverhost sleep 60
.Ed
.Pp
Replace
.Ar serverhost
with the hostname of the CVSup server, but type
.Ql localhost
literally.
This sets up the required port forwarding.
You must start
.Nm
before the 60-second
.Nm sleep
finishes.
Once the update has begun,
.Nm ssh
will keep the forwarded channels open as long as they are needed.
.It
Run
.Nm
on the local host, including the arguments
.Ql -h localhost
on the command line.
.El
.Sh FILES
.Bl -tag -width base/sup/collection/checkouts*xx -compact
.It Pa /usr/local/etc/cvsup
Default
.Ar base
directory.
.It Pa sup
Default
.Ar collDir
subdirectory.
.Sm off
.It Xo Ar base / Ar collDir / Ar collection
.Pa /checkouts*
.Xc
.Sm on
List files.
.El
.Sh SEE ALSO
.Xr cvs 1 ,
.Xr rcsintro 1 ,
.Xr ssh 1 .
.Pp
.Bd -literal
http://mu.org/~mux/csup.html
.Ed
.Sh AUTHORS
.An -nosplit
.An Maxime Henrion Aq mux@FreeBSD.org
is the author of
.Nm ,
the rewrite of
.Nm CVSup
in C.
.An John Polstra Aq jdp@polstra.com
is the author of
.Nm CVSup .
.Sh LEGALITIES
CVSup is a registered trademark of John D. Polstra.
.Pp
.Nm
is released under a 2-clauses BSD license.
.Sh BUGS
An RCS file is not recognized as such unless its name ends with
.Ql \&,v .
.Pp
Any directory named
.Ql Attic
is assumed to be a CVS Attic, and is treated specially.

339
contrib/csup/detailer.c Normal file
View File

@ -0,0 +1,339 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "config.h"
#include "detailer.h"
#include "fixups.h"
#include "misc.h"
#include "mux.h"
#include "proto.h"
#include "status.h"
#include "stream.h"
/* Internal error codes. */
#define DETAILER_ERR_PROTO (-1) /* Protocol error. */
#define DETAILER_ERR_MSG (-2) /* Error is in detailer->errmsg. */
#define DETAILER_ERR_READ (-3) /* Error reading from server. */
#define DETAILER_ERR_WRITE (-4) /* Error writing to server. */
struct detailer {
struct config *config;
struct stream *rd;
struct stream *wr;
char *errmsg;
};
static int detailer_batch(struct detailer *);
static int detailer_coll(struct detailer *, struct coll *,
struct status *);
static int detailer_dofile(struct detailer *, struct coll *,
struct status *, char *);
void *
detailer(void *arg)
{
struct thread_args *args;
struct detailer dbuf, *d;
int error;
args = arg;
d = &dbuf;
d->config = args->config;
d->rd = args->rd;
d->wr = args->wr;
d->errmsg = NULL;
error = detailer_batch(d);
switch (error) {
case DETAILER_ERR_PROTO:
xasprintf(&args->errmsg, "Detailer failed: Protocol error");
args->status = STATUS_FAILURE;
break;
case DETAILER_ERR_MSG:
xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg);
free(d->errmsg);
args->status = STATUS_FAILURE;
break;
case DETAILER_ERR_READ:
if (stream_eof(d->rd)) {
xasprintf(&args->errmsg, "Detailer failed: "
"Premature EOF from server");
} else {
xasprintf(&args->errmsg, "Detailer failed: "
"Network read failure: %s", strerror(errno));
}
args->status = STATUS_TRANSIENTFAILURE;
break;
case DETAILER_ERR_WRITE:
xasprintf(&args->errmsg, "Detailer failed: "
"Network write failure: %s", strerror(errno));
args->status = STATUS_TRANSIENTFAILURE;
break;
default:
assert(error == 0);
args->status = STATUS_SUCCESS;
}
return (NULL);
}
static int
detailer_batch(struct detailer *d)
{
struct config *config;
struct stream *rd, *wr;
struct coll *coll;
struct status *st;
struct fixup *fixup;
char *cmd, *collname, *line, *release;
int error, fixupseof;
config = d->config;
rd = d->rd;
wr = d->wr;
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
line = stream_getln(rd, NULL);
cmd = proto_get_ascii(&line);
collname = proto_get_ascii(&line);
release = proto_get_ascii(&line);
error = proto_get_time(&line, &coll->co_scantime);
if (error || line != NULL || strcmp(cmd, "COLL") != 0 ||
strcmp(collname, coll->co_name) != 0 ||
strcmp(release, coll->co_release) != 0)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
coll->co_release);
if (error)
return (DETAILER_ERR_WRITE);
stream_flush(wr);
if (coll->co_options & CO_COMPRESS) {
stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL);
stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
}
st = status_open(coll, -1, &d->errmsg);
if (st == NULL)
return (DETAILER_ERR_MSG);
error = detailer_coll(d, coll, st);
status_close(st, NULL);
if (error)
return (error);
if (coll->co_options & CO_COMPRESS) {
stream_filter_stop(rd);
stream_filter_stop(wr);
}
stream_flush(wr);
}
line = stream_getln(rd, NULL);
if (line == NULL)
return (DETAILER_ERR_READ);
if (strcmp(line, ".") != 0)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
stream_flush(wr);
/* Now send fixups if needed. */
fixup = NULL;
fixupseof = 0;
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
coll->co_release);
if (error)
return (DETAILER_ERR_WRITE);
if (coll->co_options & CO_COMPRESS)
stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
while (!fixupseof) {
if (fixup == NULL)
fixup = fixups_get(config->fixups);
if (fixup == NULL) {
fixupseof = 1;
break;
}
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 (error)
return (DETAILER_ERR_WRITE);
fixup = NULL;
}
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
if (coll->co_options & CO_COMPRESS)
stream_filter_stop(wr);
stream_flush(wr);
}
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
static int
detailer_coll(struct detailer *d, struct coll *coll, struct status *st)
{
struct stream *rd, *wr;
char *cmd, *file, *line, *msg;
int error;
rd = d->rd;
wr = d->wr;
line = stream_getln(rd, NULL);
if (line == NULL)
return (DETAILER_ERR_READ);
while (strcmp(line, ".") != 0) {
cmd = proto_get_ascii(&line);
if (cmd == NULL || strlen(cmd) != 1)
return (DETAILER_ERR_PROTO);
switch (cmd[0]) {
case 'D':
/* Delete file. */
file = proto_get_ascii(&line);
if (file == NULL || line != NULL)
return (DETAILER_ERR_PROTO);
error = proto_printf(wr, "D %s\n", file);
if (error)
return (DETAILER_ERR_WRITE);
break;
case '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 (error)
return (error);
break;
case '!':
/* Warning from server. */
msg = proto_get_rest(&line);
if (msg == NULL)
return (DETAILER_ERR_PROTO);
lprintf(-1, "Server warning: %s\n", msg);
break;
default:
return (DETAILER_ERR_PROTO);
}
stream_flush(wr);
line = stream_getln(rd, NULL);
if (line == NULL)
return (DETAILER_ERR_READ);
}
error = proto_printf(wr, ".\n");
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
static int
detailer_dofile(struct detailer *d, struct coll *coll, struct status *st,
char *file)
{
char md5[MD5_DIGEST_SIZE];
struct stream *wr;
struct fattr *fa;
struct statusrec *sr;
char *path;
int error, ret;
wr = d->wr;
path = checkoutpath(coll->co_prefix, file);
if (path == NULL)
return (DETAILER_ERR_PROTO);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
/* We don't have the file, so the only option at this
point is to tell the server to send it. The server
may figure out that the file is dead, in which case
it will tell us. */
error = proto_printf(wr, "C %s %s %s\n",
file, coll->co_tag, coll->co_date);
free(path);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
ret = status_get(st, file, 0, 0, &sr);
if (ret == -1) {
d->errmsg = status_errmsg(st);
free(path);
return (DETAILER_ERR_MSG);
}
if (ret == 0)
sr = NULL;
/* If our recorded information doesn't match the file that the
client has, then ignore the recorded information. */
if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE ||
!fattr_equal(sr->sr_clientattr, fa)))
sr = NULL;
fattr_free(fa);
if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) {
error = proto_printf(wr, "U %s %s %s %s %s\n", file,
coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate);
free(path);
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}
/*
* We don't have complete and/or accurate recorded information
* about what version of the file we have. Compute the file's
* checksum as an aid toward identifying which version it is.
*/
error = MD5_File(path, md5);
if (error) {
xasprintf(&d->errmsg,
"Cannot calculate checksum for \"%s\": %s", path,
strerror(errno));
return (DETAILER_ERR_MSG);
}
free(path);
if (sr == NULL) {
error = proto_printf(wr, "S %s %s %s %s\n", file,
coll->co_tag, coll->co_date, md5);
} else {
error = proto_printf(wr, "s %s %s %s %s %s\n", file,
coll->co_tag, coll->co_date, sr->sr_revnum, md5);
}
if (error)
return (DETAILER_ERR_WRITE);
return (0);
}

33
contrib/csup/detailer.h Normal file
View File

@ -0,0 +1,33 @@
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _DETAILER_H_
#define _DETAILER_H_
void *detailer(void *);
#endif /* !_DETAILER_H_ */

214
contrib/csup/diff.c Normal file
View File

@ -0,0 +1,214 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "diff.h"
#include "keyword.h"
#include "misc.h"
#include "stream.h"
typedef long lineno_t;
#define EC_ADD 0
#define EC_DEL 1
/* Editing command and state. */
struct editcmd {
int cmd;
lineno_t where;
lineno_t count;
lineno_t lasta;
lineno_t lastd;
lineno_t editline;
/* For convenience. */
struct keyword *keyword;
struct diffinfo *di;
struct stream *orig;
struct stream *dest;
};
static int diff_geteditcmd(struct editcmd *, char *);
static int diff_copyln(struct editcmd *, lineno_t);
static void diff_write(struct editcmd *, void *, size_t);
int
diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
struct keyword *keyword, struct diffinfo *di)
{
struct editcmd ec;
lineno_t i;
char *line;
size_t size;
int empty, error, noeol;
memset(&ec, 0, sizeof(ec));
empty = 0;
noeol = 0;
ec.di = di;
ec.keyword = keyword;
ec.orig = orig;
ec.dest = dest;
line = stream_getln(rd, NULL);
while (line != NULL && strcmp(line, ".") != 0 &&
strcmp(line, ".+") != 0) {
/*
* The server sends an empty line and then terminates
* with .+ for forced (and thus empty) commits.
*/
if (*line == '\0') {
if (empty)
return (-1);
empty = 1;
line = stream_getln(rd, NULL);
continue;
}
error = diff_geteditcmd(&ec, line);
if (error)
return (-1);
if (ec.cmd == EC_ADD) {
error = diff_copyln(&ec, ec.where);
if (error)
return (-1);
for (i = 0; i < ec.count; i++) {
line = stream_getln(rd, &size);
if (line == NULL)
return (-1);
if (line[0] == '.') {
line++;
size--;
}
diff_write(&ec, line, size);
}
} else {
assert(ec.cmd == EC_DEL);
error = diff_copyln(&ec, ec.where - 1);
if (error)
return (-1);
for (i = 0; i < ec.count; i++) {
line = stream_getln(orig, NULL);
if (line == NULL)
return (-1);
ec.editline++;
}
}
line = stream_getln(rd, NULL);
}
if (line == NULL)
return (-1);
/* If we got ".+", there's no ending newline. */
if (strcmp(line, ".+") == 0 && !empty)
noeol = 1;
ec.where = 0;
while ((line = stream_getln(orig, &size)) != NULL)
diff_write(&ec, line, size);
stream_flush(dest);
if (noeol) {
error = stream_truncate_rel(dest, -1);
if (error) {
warn("stream_truncate_rel");
return (-1);
}
}
return (0);
}
/* Get an editing command from the diff. */
static int
diff_geteditcmd(struct editcmd *ec, char *line)
{
char *end;
if (line[0] == 'a')
ec->cmd = EC_ADD;
else if (line[0] == 'd')
ec->cmd = EC_DEL;
else
return (-1);
errno = 0;
ec->where = strtol(line + 1, &end, 10);
if (errno || ec->where < 0 || *end != ' ')
return (-1);
line = end + 1;
errno = 0;
ec->count = strtol(line, &end, 10);
if (errno || ec->count <= 0 || *end != '\0')
return (-1);
if (ec->cmd == EC_ADD) {
if (ec->where < ec->lasta)
return (-1);
ec->lasta = ec->where + 1;
} else {
if (ec->where < ec->lasta || ec->where < ec->lastd)
return (-1);
ec->lasta = ec->where;
ec->lastd = ec->where + ec->count;
}
return (0);
}
/* Copy lines from the original version of the file up to line "to". */
static int
diff_copyln(struct editcmd *ec, lineno_t to)
{
char *line;
size_t size;
while (ec->editline < to) {
line = stream_getln(ec->orig, &size);
if (line == NULL)
return (-1);
ec->editline++;
diff_write(ec, line, size);
}
return (0);
}
/* Write a new line to the file, expanding RCS keywords appropriately. */
static void
diff_write(struct editcmd *ec, void *buf, size_t size)
{
char *line, *newline;
size_t newsize;
int ret;
line = buf;
ret = keyword_expand(ec->keyword, ec->di, line, size,
&newline, &newsize);
if (ret) {
stream_write(ec->dest, newline, newsize);
free(newline);
} else {
stream_write(ec->dest, buf, size);
}
}

50
contrib/csup/diff.h Normal file
View File

@ -0,0 +1,50 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _DIFF_H_
#define _DIFF_H_
struct stream;
struct keyword;
struct file_update;
/* Description of an RCS delta. */
struct diffinfo {
char *di_rcsfile; /* RCS filename */
char *di_cvsroot; /* CVS root prefix */
char *di_revnum; /* Revision number */
char *di_revdate; /* Revision date */
char *di_author; /* Author of the delta */
char *di_tag; /* CVS tag, if any */
char *di_state; /* State of the branch */
int di_expand; /* CVS expansion mode */
};
int diff_apply(struct stream *, struct stream *, struct stream *,
struct keyword *, struct diffinfo *);
#endif /* !_DIFF_H_ */

946
contrib/csup/fattr.c Normal file
View File

@ -0,0 +1,946 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#include <errno.h>
#include <grp.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "fattr.h"
#include "misc.h"
/*
* 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
* bare POSIX systems that don't.
*/
#ifdef HAVE_FFLAGS
#include "fattr_bsd.h"
#else
#include "fattr_posix.h"
#endif
#ifdef __FreeBSD__
#include <osreldate.h>
#endif
/* Define fflags_t if we're on a system that doesn't have it. */
#if !defined(__FreeBSD_version) || __FreeBSD_version < 500030
typedef uint32_t fflags_t;
#endif
#define FA_MASKRADIX 16
#define FA_FILETYPERADIX 10
#define FA_MODTIMERADIX 10
#define FA_SIZERADIX 10
#define FA_RDEVRADIX 16
#define FA_MODERADIX 8
#define FA_FLAGSRADIX 16
#define FA_LINKCOUNTRADIX 10
#define FA_DEVRADIX 16
#define FA_INODERADIX 10
#define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO)
#define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX)
struct fattr {
int mask;
int type;
time_t modtime;
off_t size;
char *linktarget;
dev_t rdev;
uid_t uid;
gid_t gid;
mode_t mode;
fflags_t flags;
nlink_t linkcount;
dev_t dev;
ino_t inode;
};
static const struct fattr bogus = {
FA_MODTIME | FA_SIZE | FA_MODE,
FT_UNKNOWN,
1,
0,
NULL,
0,
0,
0,
0,
0,
0,
0,
0
};
static struct fattr *defaults[FT_NUMBER];
void
fattr_init(void)
{
struct fattr *fa;
int i;
for (i = 0; i < FT_NUMBER; i++) {
fa = fattr_new(i, -1);
if (i == FT_DIRECTORY)
fa->mode = 0777;
else
fa->mode = 0666;
fa->mask |= FA_MODE;
defaults[i] = fa;
}
}
void
fattr_fini(void)
{
int i;
for (i = 0; i < FT_NUMBER; i++)
fattr_free(defaults[i]);
}
const struct fattr *fattr_bogus = &bogus;
static char *fattr_scanattr(struct fattr *, int, const char *);
int
fattr_supported(int type)
{
return (fattr_support[type]);
}
struct fattr *
fattr_new(int type, time_t modtime)
{
struct fattr *new;
new = xmalloc(sizeof(struct fattr));
memset(new, 0, sizeof(struct fattr));
new->type = type;
if (type != FT_UNKNOWN)
new->mask |= FA_FILETYPE;
if (modtime != -1) {
new->modtime = modtime;
new->mask |= FA_MODTIME;
}
if (fattr_supported(new->type) & FA_LINKCOUNT) {
new->mask |= FA_LINKCOUNT;
new->linkcount = 1;
}
return (new);
}
/* Returns a new file attribute structure based on a stat structure. */
struct fattr *
fattr_fromstat(struct stat *sb)
{
struct fattr *fa;
fa = fattr_new(FT_UNKNOWN, -1);
if (S_ISREG(sb->st_mode))
fa->type = FT_FILE;
else if (S_ISDIR(sb->st_mode))
fa->type = FT_DIRECTORY;
else if (S_ISCHR(sb->st_mode))
fa->type = FT_CDEV;
else if (S_ISBLK(sb->st_mode))
fa->type = FT_BDEV;
else if (S_ISLNK(sb->st_mode))
fa->type = FT_SYMLINK;
else
fa->type = FT_UNKNOWN;
fa->mask = FA_FILETYPE | fattr_supported(fa->type);
if (fa->mask & FA_MODTIME)
fa->modtime = sb->st_mtime;
if (fa->mask & FA_SIZE)
fa->size = sb->st_size;
if (fa->mask & FA_RDEV)
fa->rdev = sb->st_rdev;
if (fa->mask & FA_OWNER)
fa->uid = sb->st_uid;
if (fa->mask & FA_GROUP)
fa->gid = sb->st_gid;
if (fa->mask & FA_MODE)
fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK);
#ifdef HAVE_FFLAGS
if (fa->mask & FA_FLAGS)
fa->flags = sb->st_flags;
#endif
if (fa->mask & FA_LINKCOUNT)
fa->linkcount = sb->st_nlink;
if (fa->mask & FA_DEV)
fa->dev = sb->st_dev;
if (fa->mask & FA_INODE)
fa->inode = sb->st_ino;
return (fa);
}
struct fattr *
fattr_frompath(const char *path, int nofollow)
{
struct fattr *fa;
struct stat sb;
int error, len;
if (nofollow)
error = lstat(path, &sb);
else
error = stat(path, &sb);
if (error)
return (NULL);
fa = fattr_fromstat(&sb);
if (fa->mask & FA_LINKTARGET) {
char buf[1024];
len = readlink(path, buf, sizeof(buf));
if (len == -1) {
fattr_free(fa);
return (NULL);
}
if ((unsigned)len > sizeof(buf) - 1) {
fattr_free(fa);
errno = ENAMETOOLONG;
return (NULL);
}
buf[len] = '\0';
fa->linktarget = xstrdup(buf);
}
return (fa);
}
struct fattr *
fattr_fromfd(int fd)
{
struct fattr *fa;
struct stat sb;
int error;
error = fstat(fd, &sb);
if (error)
return (NULL);
fa = fattr_fromstat(&sb);
return (fa);
}
int
fattr_type(const struct fattr *fa)
{
return (fa->type);
}
/* Returns a new file attribute structure from its encoded text form. */
struct fattr *
fattr_decode(char *attr)
{
struct fattr *fa;
char *next;
fa = fattr_new(FT_UNKNOWN, -1);
next = fattr_scanattr(fa, FA_MASK, attr);
if (next == NULL || (fa->mask & ~FA_MASK) > 0)
goto bad;
if (fa->mask & FA_FILETYPE) {
next = fattr_scanattr(fa, FA_FILETYPE, next);
if (next == NULL)
goto bad;
if (fa->type < 0 || fa->type > FT_MAX)
fa->type = FT_UNKNOWN;
} else {
/* The filetype attribute is always valid. */
fa->mask |= FA_FILETYPE;
fa->type = FT_UNKNOWN;
}
fa->mask = fa->mask & fattr_supported(fa->type);
if (fa->mask & FA_MODTIME)
next = fattr_scanattr(fa, FA_MODTIME, next);
if (fa->mask & FA_SIZE)
next = fattr_scanattr(fa, FA_SIZE, next);
if (fa->mask & FA_LINKTARGET)
next = fattr_scanattr(fa, FA_LINKTARGET, next);
if (fa->mask & FA_RDEV)
next = fattr_scanattr(fa, FA_RDEV, next);
if (fa->mask & FA_OWNER)
next = fattr_scanattr(fa, FA_OWNER, next);
if (fa->mask & FA_GROUP)
next = fattr_scanattr(fa, FA_GROUP, next);
if (fa->mask & FA_MODE)
next = fattr_scanattr(fa, FA_MODE, next);
if (fa->mask & FA_FLAGS)
next = fattr_scanattr(fa, FA_FLAGS, next);
if (fa->mask & FA_LINKCOUNT) {
next = fattr_scanattr(fa, FA_LINKCOUNT, next);
} else if (fattr_supported(fa->type) & FA_LINKCOUNT) {
/* If the link count is missing but supported, fake it as 1. */
fa->mask |= FA_LINKCOUNT;
fa->linkcount = 1;
}
if (fa->mask & FA_DEV)
next = fattr_scanattr(fa, FA_DEV, next);
if (fa->mask & FA_INODE)
next = fattr_scanattr(fa, FA_INODE, next);
if (next == NULL)
goto bad;
return (fa);
bad:
fattr_free(fa);
return (NULL);
}
char *
fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore)
{
struct {
char val[32];
char len[4];
int extval;
char *ext;
} pieces[FA_NUMBER], *piece;
struct passwd *pw;
struct group *gr;
char *cp, *s;
size_t len, vallen;
mode_t mode, modemask;
int mask, n, i;
pw = NULL;
gr = NULL;
if (support == NULL)
mask = fa->mask;
else
mask = fa->mask & support[fa->type];
mask &= ~ignore;
/* XXX - Use getpwuid_r() and getgrgid_r(). */
if (fa->mask & FA_OWNER) {
pw = getpwuid(fa->uid);
if (pw == NULL)
mask &= ~FA_OWNER;
}
if (fa->mask & FA_GROUP) {
gr = getgrgid(fa->gid);
if (gr == NULL)
mask &= ~FA_GROUP;
}
if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1)
mask &= ~FA_LINKCOUNT;
memset(pieces, 0, FA_NUMBER * sizeof(*pieces));
len = 0;
piece = pieces;
vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
if (mask & FA_FILETYPE) {
vallen = snprintf(piece->val, sizeof(piece->val),
"%d", fa->type);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_MODTIME) {
vallen = snprintf(piece->val, sizeof(piece->val),
"%lld", (long long)fa->modtime);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_SIZE) {
vallen = snprintf(piece->val, sizeof(piece->val),
"%lld", (long long)fa->size);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_LINKTARGET) {
vallen = strlen(fa->linktarget);
piece->extval = 1;
piece->ext = fa->linktarget;
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_RDEV) {
vallen = snprintf(piece->val, sizeof(piece->val),
"%lld", (long long)fa->rdev);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_OWNER) {
vallen = strlen(pw->pw_name);
piece->extval = 1;
piece->ext = pw->pw_name;
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_GROUP) {
vallen = strlen(gr->gr_name);
piece->extval = 1;
piece->ext = gr->gr_name;
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_MODE) {
if (mask & FA_OWNER && mask & FA_GROUP)
modemask = FA_SETIDMASK | FA_PERMMASK;
else
modemask = FA_PERMMASK;
mode = fa->mode & modemask;
vallen = snprintf(piece->val, sizeof(piece->val),
"%o", mode);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_FLAGS) {
vallen = snprintf(piece->val, sizeof(piece->val), "%llx",
(long long)fa->flags);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_LINKCOUNT) {
vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
(long long)fa->linkcount);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_DEV) {
vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
(long long)fa->dev);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
if (mask & FA_INODE) {
vallen = snprintf(piece->val, sizeof(piece->val), "%lld",
(long long)fa->inode);
len += snprintf(piece->len, sizeof(piece->len), "%lld",
(long long)vallen) + vallen + 1;
piece++;
}
s = xmalloc(len + 1);
n = piece - pieces;
piece = pieces;
cp = s;
for (i = 0; i < n; i++) {
if (piece->extval)
len = sprintf(cp, "%s#%s", piece->len, piece->ext);
else
len = sprintf(cp, "%s#%s", piece->len, piece->val);
cp += len;
piece++;
}
return (s);
}
struct fattr *
fattr_dup(const struct fattr *from)
{
struct fattr *fa;
fa = fattr_new(FT_UNKNOWN, -1);
fattr_override(fa, from, FA_MASK);
return (fa);
}
void
fattr_free(struct fattr *fa)
{
if (fa == NULL)
return;
if (fa->linktarget != NULL)
free(fa->linktarget);
free(fa);
}
void
fattr_umask(struct fattr *fa, mode_t newumask)
{
if (fa->mask & FA_MODE)
fa->mode = fa->mode & ~newumask;
}
void
fattr_maskout(struct fattr *fa, int mask)
{
/* Don't forget to free() the linktarget attribute if we remove it. */
if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) {
free(fa->linktarget);
fa->linktarget = NULL;
}
fa->mask &= ~mask;
}
int
fattr_getmask(const struct fattr *fa)
{
return (fa->mask);
}
nlink_t
fattr_getlinkcount(const struct fattr *fa)
{
return (fa->linkcount);
}
/*
* Eat the specified attribute and put it in the file attribute
* structure. Returns NULL on error, or a pointer to the next
* attribute to parse.
*
* This would be much prettier if we had strntol() so that we're
* not forced to write '\0' to the string before calling strtol()
* and then put back the old value...
*
* We need to use (unsigned) long long types here because some
* of the opaque types we're parsing (off_t, time_t...) may need
* 64bits to fit.
*/
static char *
fattr_scanattr(struct fattr *fa, int type, const char *attr)
{
struct passwd *pw;
struct group *gr;
char *attrend, *attrstart, *end;
size_t len;
unsigned long attrlen;
mode_t modemask;
char tmp;
if (attr == NULL)
return (NULL);
errno = 0;
attrlen = strtoul(attr, &end, 10);
if (errno || *end != '#')
return (NULL);
len = strlen(attr);
attrstart = end + 1;
attrend = attrstart + attrlen;
tmp = *attrend;
*attrend = '\0';
switch (type) {
/* Using FA_MASK here is a bit bogus semantically. */
case FA_MASK:
errno = 0;
fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_FILETYPE:
errno = 0;
fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_MODTIME:
errno = 0;
fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_SIZE:
errno = 0;
fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_LINKTARGET:
fa->linktarget = xstrdup(attrstart);
break;
case FA_RDEV:
errno = 0;
fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_OWNER:
/*
* XXX - We need to use getpwnam_r() since getpwnam()
* is not thread-safe, and we also need to use a cache.
*/
pw = getpwnam(attrstart);
if (pw != NULL)
fa->uid = pw->pw_uid;
else
fa->mask &= ~FA_OWNER;
break;
case FA_GROUP:
gr = getgrnam(attrstart);
if (gr != NULL)
fa->gid = gr->gr_gid;
else
fa->mask &= ~FA_GROUP;
break;
case FA_MODE:
errno = 0;
fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX);
if (errno || end != attrend)
goto bad;
if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
modemask = FA_SETIDMASK | FA_PERMMASK;
else
modemask = FA_PERMMASK;
fa->mode &= modemask;
break;
case FA_FLAGS:
errno = 0;
fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_LINKCOUNT:
errno = 0;
fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_DEV:
errno = 0;
fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX);
if (errno || end != attrend)
goto bad;
break;
case FA_INODE:
errno = 0;
fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX);
if (errno || end != attrend)
goto bad;
break;
}
*attrend = tmp;
return (attrend);
bad:
*attrend = tmp;
return (NULL);
}
/* Return a file attribute structure built from the RCS file attributes. */
struct fattr *
fattr_forcheckout(const struct fattr *rcsattr, mode_t mask)
{
struct fattr *fa;
fa = fattr_new(FT_FILE, -1);
if (rcsattr->mask & FA_MODE) {
if ((rcsattr->mode & 0111) > 0)
fa->mode = 0777;
else
fa->mode = 0666;
fa->mode &= ~mask;
fa->mask |= FA_MODE;
}
return (fa);
}
/* Merge attributes from "from" that aren't present in "fa". */
void
fattr_merge(struct fattr *fa, const struct fattr *from)
{
fattr_override(fa, from, from->mask & ~fa->mask);
}
/* Merge default attributes. */
void
fattr_mergedefault(struct fattr *fa)
{
fattr_merge(fa, defaults[fa->type]);
}
/* Override selected attributes of "fa" with values from "from". */
void
fattr_override(struct fattr *fa, const struct fattr *from, int mask)
{
mask &= from->mask;
if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET)
free(fa->linktarget);
fa->mask |= mask;
if (mask & FA_FILETYPE)
fa->type = from->type;
if (mask & FA_MODTIME)
fa->modtime = from->modtime;
if (mask & FA_SIZE)
fa->size = from->size;
if (mask & FA_LINKTARGET)
fa->linktarget = xstrdup(from->linktarget);
if (mask & FA_RDEV)
fa->rdev = from->rdev;
if (mask & FA_OWNER)
fa->uid = from->uid;
if (mask & FA_GROUP)
fa->gid = from->gid;
if (mask & FA_MODE)
fa->mode = from->mode;
if (mask & FA_FLAGS)
fa->flags = from->flags;
if (mask & FA_LINKCOUNT)
fa->linkcount = from->linkcount;
if (mask & FA_DEV)
fa->dev = from->dev;
if (mask & FA_INODE)
fa->inode = from->inode;
}
/* Create a node. */
int
fattr_makenode(const struct fattr *fa, const char *path)
{
mode_t modemask, mode;
int error;
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);
return (error);
}
int
fattr_delete(const char *path)
{
struct fattr *fa;
int error;
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
if (errno == ENOENT)
return (0);
return (-1);
}
/* Clear flags. */
if (fa->mask & FA_FLAGS && fa->flags != 0) {
fa->flags = 0;
(void)chflags(path, fa->flags);
}
if (fa->type == FT_DIRECTORY)
error = rmdir(path);
else
error = unlink(path);
fattr_free(fa);
return (error);
}
/*
* Changes those attributes we can change. Returns -1 on error,
* 0 if no update was needed, and 1 if an update was needed and
* it has been applied successfully.
*/
int
fattr_install(struct fattr *fa, const char *topath, const char *frompath)
{
struct timeval tv[2];
struct fattr *old;
int error, inplace, mask;
mode_t modemask, newmode;
uid_t uid;
gid_t gid;
mask = fa->mask & fattr_supported(fa->type);
if (mask & FA_OWNER && mask & FA_GROUP)
modemask = FA_SETIDMASK | FA_PERMMASK;
else
modemask = FA_PERMMASK;
inplace = 0;
if (frompath == NULL) {
/* Changing attributes in place. */
frompath = topath;
inplace = 1;
}
old = fattr_frompath(topath, FATTR_NOFOLLOW);
if (old == NULL)
return (-1);
if (inplace && fattr_equal(fa, old)) {
fattr_free(old);
return (0);
}
#ifdef HAVE_FFLAGS
/*
* Determine whether we need to clear the flags of the target.
* This is bogus in that it assumes a value of 0 is safe and
* that non-zero is unsafe. I'm not really worried by that
* since as far as I know that's the way things are.
*/
if ((old->mask & FA_FLAGS) && old->flags > 0) {
(void)chflags(topath, 0);
old->flags = 0;
}
#endif
/* Determine whether we need to remove the target first. */
if (!inplace && (fa->type == FT_DIRECTORY) !=
(old->type == FT_DIRECTORY)) {
if (old->type == FT_DIRECTORY)
error = rmdir(topath);
else
error = unlink(topath);
if (error)
goto bad;
}
/* Change those attributes that we can before moving the file
* into place. That makes installation atomic in most cases. */
if (mask & FA_MODTIME) {
gettimeofday(tv, NULL); /* Access time. */
tv[1].tv_sec = fa->modtime; /* Modification time. */
tv[1].tv_usec = 0;
error = utimes(frompath, tv);
if (error)
goto bad;
}
if (mask & FA_OWNER || mask & FA_GROUP) {
uid = -1;
gid = -1;
if (mask & FA_OWNER)
uid = fa->uid;
if (mask & FA_GROUP)
gid = fa->gid;
error = chown(frompath, uid, gid);
if (error)
goto bad;
}
if (mask & FA_MODE) {
newmode = fa->mode & modemask;
/* Merge in set*id bits from the old attribute. XXX - Why? */
if (old->mask & FA_MODE) {
newmode |= (old->mode & ~modemask);
newmode &= (FA_SETIDMASK | FA_PERMMASK);
}
error = chmod(frompath, newmode);
if (error)
goto bad;
}
if (!inplace) {
error = rename(frompath, topath);
if (error)
goto bad;
}
#ifdef HAVE_FFLAGS
/* Set the flags. */
if (mask & FA_FLAGS)
(void)chflags(topath, fa->flags);
#endif
fattr_free(old);
return (1);
bad:
fattr_free(old);
return (-1);
}
/*
* Returns 1 if both attributes are equal, 0 otherwise.
*
* This function only compares attributes that are valid in both
* files. A file of unknown type ("FT_UNKNOWN") is unequal to
* anything, including itself.
*/
int
fattr_equal(const struct fattr *fa1, const struct fattr *fa2)
{
int mask;
mask = fa1->mask & fa2->mask;
if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN)
return (0);
if (mask & FA_MODTIME)
if (fa1->modtime != fa2->modtime)
return (0);
if (mask & FA_SIZE)
if (fa1->size != fa2->size)
return (0);
if (mask & FA_LINKTARGET)
if (strcmp(fa1->linktarget, fa2->linktarget) != 0)
return (0);
if (mask & FA_RDEV)
if (fa1->rdev != fa2->rdev)
return (0);
if (mask & FA_OWNER)
if (fa1->uid != fa2->uid)
return (0);
if (mask & FA_GROUP)
if (fa1->gid != fa2->gid)
return (0);
if (mask & FA_MODE)
if (fa1->mode != fa2->mode)
return (0);
if (mask & FA_FLAGS)
if (fa1->flags != fa2->flags)
return (0);
if (mask & FA_LINKCOUNT)
if (fa1->linkcount != fa2->linkcount)
return (0);
if (mask & FA_DEV)
if (fa1->dev != fa2->dev)
return (0);
if (mask & FA_INODE)
if (fa1->inode != fa2->inode)
return (0);
return (1);
}

115
contrib/csup/fattr.h Normal file
View File

@ -0,0 +1,115 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _FATTR_H_
#define _FATTR_H_
#include <sys/types.h>
#include <fcntl.h>
#include <time.h>
/*
* File types.
*/
#define FT_UNKNOWN 0 /* Unknown file type. */
#define FT_FILE 1 /* Regular file. */
#define FT_DIRECTORY 2 /* Directory. */
#define FT_CDEV 3 /* Character device. */
#define FT_BDEV 4 /* Block device. */
#define FT_SYMLINK 5 /* Symbolic link. */
#define FT_MAX FT_SYMLINK /* Maximum file type number. */
#define FT_NUMBER (FT_MAX + 1) /* Number of file types. */
/*
* File attributes.
*/
#define FA_FILETYPE 0x0001 /* True for all supported file types. */
#define FA_MODTIME 0x0002 /* Last file modification time. */
#define FA_SIZE 0x0004 /* Size of the file. */
#define FA_LINKTARGET 0x0008 /* Target of a symbolic link. */
#define FA_RDEV 0x0010 /* Device for a device node. */
#define FA_OWNER 0x0020 /* Owner of the file. */
#define FA_GROUP 0x0040 /* Group of the file. */
#define FA_MODE 0x0080 /* File permissions. */
#define FA_FLAGS 0x0100 /* 4.4BSD flags, a la chflags(2). */
#define FA_LINKCOUNT 0x0200 /* Hard link count. */
#define FA_DEV 0x0400 /* Device holding the inode. */
#define FA_INODE 0x0800 /* Inode number. */
#define FA_MASK 0x0fff
#define FA_NUMBER 12 /* Number of file attributes. */
/* Attributes that we might be able to change. */
#define FA_CHANGEABLE (FA_MODTIME | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS)
/*
* Attributes that we don't want to save in the "checkouts" file
* when in checkout mode.
*/
#define FA_COIGNORE (FA_MASK & ~(FA_FILETYPE|FA_MODTIME|FA_SIZE|FA_MODE))
/* These are for fattr_frompath(). */
#define FATTR_FOLLOW 0
#define FATTR_NOFOLLOW 1
struct stat;
struct fattr;
typedef int fattr_support_t[FT_NUMBER];
extern const struct fattr *fattr_bogus;
void fattr_init(void);
void fattr_fini(void);
struct fattr *fattr_new(int, time_t);
struct fattr *fattr_default(int);
struct fattr *fattr_fromstat(struct stat *);
struct fattr *fattr_frompath(const char *, int);
struct fattr *fattr_fromfd(int);
struct fattr *fattr_decode(char *);
struct fattr *fattr_forcheckout(const struct fattr *, mode_t);
struct fattr *fattr_dup(const struct fattr *);
char *fattr_encode(const struct fattr *, fattr_support_t, int);
int fattr_type(const struct fattr *);
void fattr_maskout(struct fattr *, int);
int fattr_getmask(const struct fattr *);
nlink_t fattr_getlinkcount(const struct fattr *);
void fattr_umask(struct fattr *, mode_t);
void fattr_merge(struct fattr *, const struct fattr *);
void fattr_mergedefault(struct fattr *);
void fattr_override(struct fattr *, const struct fattr *, int);
int fattr_makenode(const struct fattr *, const char *);
int fattr_delete(const char *path);
int fattr_install(struct fattr *, const char *, const char *);
int fattr_equal(const struct fattr *, const struct fattr *);
void fattr_free(struct fattr *);
int fattr_supported(int);
#endif /* !_FATTR_H_ */

52
contrib/csup/fattr_bsd.h Normal file
View File

@ -0,0 +1,52 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
/*
* The file attributes we support in a BSD environment.
*
* This is similar to fattr_posix.h, except that we support the FA_FLAGS
* attribute when it makes sense. The FA_FLAGS attribute is for the
* extended BSD file flags, see chflags(2).
*/
fattr_support_t fattr_support = {
/* FT_UNKNOWN */
0,
/* FT_FILE */
FA_FILETYPE | FA_MODTIME | FA_SIZE | FA_OWNER | FA_GROUP | FA_MODE |
FA_FLAGS | FA_LINKCOUNT | FA_INODE | FA_DEV,
/* FT_DIRECTORY */
FA_FILETYPE | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS,
/* FT_CDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS |
FA_LINKCOUNT | FA_DEV | FA_INODE,
/* FT_BDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS |
FA_LINKCOUNT | FA_DEV | FA_INODE,
/* FT_SYMLINK */
FA_FILETYPE | FA_LINKTARGET
};

View File

@ -0,0 +1,48 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
/*
* The file attributes we support in a POSIX environment.
*/
fattr_support_t fattr_support = {
/* FT_UNKNOWN */
0,
/* FT_FILE */
FA_FILETYPE | FA_MODTIME | FA_SIZE | FA_OWNER | FA_GROUP | FA_MODE |
FA_LINKCOUNT | FA_INODE | FA_DEV,
/* FT_DIRECTORY */
FA_FILETYPE | FA_OWNER | FA_GROUP | FA_MODE,
/* FT_CDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_LINKCOUNT |
FA_DEV | FA_INODE,
/* FT_BDEV */
FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_LINKCOUNT |
FA_DEV | FA_INODE,
/* FT_SYMLINK */
FA_FILETYPE | FA_LINKTARGET
};

198
contrib/csup/fixups.c Normal file
View File

@ -0,0 +1,198 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <pthread.h>
#include <stdlib.h>
#include <string.h>
#include "fixups.h"
#include "misc.h"
#include "queue.h"
/*
* A synchronized queue to implement fixups. The updater thread adds
* fixup requests to the queue with fixups_put() when a checksum
* mismatch error occured. It then calls fixups_close() when he's
* done requesting fixups. The detailer thread gets the fixups with
* fixups_get() and then send the requests to the server.
*
* The queue is synchronized with a mutex and a condition variable.
*/
struct fixups {
pthread_mutex_t lock;
pthread_cond_t cond;
STAILQ_HEAD(, fixup) fixupq;
struct fixup *cur;
size_t size;
int closed;
};
static void fixups_lock(struct fixups *);
static void fixups_unlock(struct fixups *);
static struct fixup *fixup_new(struct coll *, const char *);
static void fixup_free(struct fixup *);
static void
fixups_lock(struct fixups *f)
{
int error;
error = pthread_mutex_lock(&f->lock);
assert(!error);
}
static void
fixups_unlock(struct fixups *f)
{
int error;
error = pthread_mutex_unlock(&f->lock);
assert(!error);
}
static struct fixup *
fixup_new(struct coll *coll, const char *name)
{
struct fixup *fixup;
fixup = xmalloc(sizeof(struct fixup));
fixup->f_name = xstrdup(name);
fixup->f_coll = coll;
return (fixup);
}
static void
fixup_free(struct fixup *fixup)
{
free(fixup->f_name);
free(fixup);
}
/* Create a new fixup queue. */
struct fixups *
fixups_new(void)
{
struct fixups *f;
f = xmalloc(sizeof(struct fixups));
f->size = 0;
f->closed = 0;
f->cur = NULL;
STAILQ_INIT(&f->fixupq);
pthread_mutex_init(&f->lock, NULL);
pthread_cond_init(&f->cond, NULL);
return (f);
}
/* Add a fixup request to the queue. */
void
fixups_put(struct fixups *f, struct coll *coll, const char *name)
{
struct fixup *fixup;
int dosignal;
dosignal = 0;
fixup = fixup_new(coll, name);
fixups_lock(f);
assert(!f->closed);
STAILQ_INSERT_TAIL(&f->fixupq, fixup, f_link);
if (f->size++ == 0)
dosignal = 1;
fixups_unlock(f);
if (dosignal)
pthread_cond_signal(&f->cond);
}
/* Get a fixup request from the queue. */
struct fixup *
fixups_get(struct fixups *f)
{
struct fixup *fixup, *tofree;
fixups_lock(f);
while (f->size == 0 && !f->closed)
pthread_cond_wait(&f->cond, &f->lock);
if (f->closed) {
fixups_unlock(f);
return (NULL);
}
assert(f->size > 0);
fixup = STAILQ_FIRST(&f->fixupq);
tofree = f->cur;
f->cur = fixup;
STAILQ_REMOVE_HEAD(&f->fixupq, f_link);
f->size--;
fixups_unlock(f);
if (tofree != NULL)
fixup_free(tofree);
return (fixup);
}
/* Close the writing end of the queue. */
void
fixups_close(struct fixups *f)
{
int dosignal;
dosignal = 0;
fixups_lock(f);
if (f->size == 0 && !f->closed)
dosignal = 1;
f->closed = 1;
fixups_unlock(f);
if (dosignal)
pthread_cond_signal(&f->cond);
}
/* Free a fixups queue. */
void
fixups_free(struct fixups *f)
{
struct fixup *fixup, *fixup2;
assert(f->closed);
/*
* Free any fixup that has been left on the queue.
* This can happen if we have been aborted prematurely.
*/
fixup = STAILQ_FIRST(&f->fixupq);
while (fixup != NULL) {
fixup2 = STAILQ_NEXT(fixup, f_link);
fixup_free(fixup);
fixup = fixup2;
}
if (f->cur != NULL)
fixup_free(f->cur);
pthread_cond_destroy(&f->cond);
pthread_mutex_destroy(&f->lock);
free(f);
}

48
contrib/csup/fixups.h Normal file
View File

@ -0,0 +1,48 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _FIXUPS_H_
#define _FIXUPS_H_
#include "queue.h"
struct coll;
struct fixups;
struct fixup {
struct coll *f_coll;
char *f_name;
STAILQ_ENTRY(fixup) f_link; /* Not for consumers. */
};
struct fixups *fixups_new(void);
void fixups_put(struct fixups *, struct coll *, const char *);
struct fixup *fixups_get(struct fixups *);
void fixups_close(struct fixups *);
void fixups_free(struct fixups *);
#endif /* !_FIXUPS_H_ */

199
contrib/csup/fnmatch.c Normal file
View File

@ -0,0 +1,199 @@
/*
* Copyright (c) 1989, 1993, 1994
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Guido van Rossum.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* From FreeBSD fnmatch.c 1.11
* $Id: fnmatch.c,v 1.3 1997/08/19 02:34:30 jdp Exp $
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94";
#endif /* LIBC_SCCS and not lint */
/*
* Function fnmatch() as specified in POSIX 1003.2-1992, section B.6.
* Compares a filename or pathname to a pattern.
*/
#include <ctype.h>
#include <string.h>
#include <stdio.h>
#include "fnmatch.h"
#define EOS '\0'
static const char *rangematch(const char *, char, int);
int
fnmatch(const char *pattern, const char *string, int flags)
{
const char *stringstart;
char c, test;
for (stringstart = string;;)
switch (c = *pattern++) {
case EOS:
if ((flags & FNM_LEADING_DIR) && *string == '/')
return (0);
return (*string == EOS ? 0 : FNM_NOMATCH);
case '?':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && (flags & FNM_PATHNAME))
return (FNM_NOMATCH);
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
++string;
break;
case '*':
c = *pattern;
/* Collapse multiple stars. */
while (c == '*')
c = *++pattern;
if (*string == '.' && (flags & FNM_PERIOD) &&
(string == stringstart ||
((flags & FNM_PATHNAME) && *(string - 1) == '/')))
return (FNM_NOMATCH);
/* Optimize for pattern with * at end or before /. */
if (c == EOS)
if (flags & FNM_PATHNAME)
return ((flags & FNM_LEADING_DIR) ||
strchr(string, '/') == NULL ?
0 : FNM_NOMATCH);
else
return (0);
else if (c == '/' && flags & FNM_PATHNAME) {
if ((string = strchr(string, '/')) == NULL)
return (FNM_NOMATCH);
break;
}
/* General case, use recursion. */
while ((test = *string) != EOS) {
if (!fnmatch(pattern, string, flags & ~FNM_PERIOD))
return (0);
if (test == '/' && flags & FNM_PATHNAME)
break;
++string;
}
return (FNM_NOMATCH);
case '[':
if (*string == EOS)
return (FNM_NOMATCH);
if (*string == '/' && flags & FNM_PATHNAME)
return (FNM_NOMATCH);
if ((pattern =
rangematch(pattern, *string, flags)) == NULL)
return (FNM_NOMATCH);
++string;
break;
case '\\':
if (!(flags & FNM_NOESCAPE)) {
if ((c = *pattern++) == EOS) {
c = '\\';
--pattern;
}
}
/* FALLTHROUGH */
default:
if (c == *string)
;
else if ((flags & FNM_CASEFOLD) &&
(tolower((unsigned char)c) ==
tolower((unsigned char)*string)))
;
else if ((flags & FNM_PREFIX_DIRS) && *string == EOS &&
((c == '/' && string != stringstart) ||
(string == stringstart+1 && *stringstart == '/')))
return (0);
else
return (FNM_NOMATCH);
string++;
break;
}
/* NOTREACHED */
}
static const char *
rangematch(const char *pattern, char test, int flags)
{
int negate, ok;
char c, c2;
/*
* A bracket expression starting with an unquoted circumflex
* character produces unspecified results (IEEE 1003.2-1992,
* 3.13.2). This implementation treats it like '!', for
* consistency with the regular expression syntax.
* J.T. Conklin (conklin@ngai.kaleida.com)
*/
if ( (negate = (*pattern == '!' || *pattern == '^')) )
++pattern;
if (flags & FNM_CASEFOLD)
test = tolower((unsigned char)test);
for (ok = 0; (c = *pattern++) != ']';) {
if (c == '\\' && !(flags & FNM_NOESCAPE))
c = *pattern++;
if (c == EOS)
return (NULL);
if (flags & FNM_CASEFOLD)
c = tolower((unsigned char)c);
if (*pattern == '-'
&& (c2 = *(pattern+1)) != EOS && c2 != ']') {
pattern += 2;
if (c2 == '\\' && !(flags & FNM_NOESCAPE))
c2 = *pattern++;
if (c2 == EOS)
return (NULL);
if (flags & FNM_CASEFOLD)
c2 = tolower((unsigned char)c2);
if ((unsigned char)c <= (unsigned char)test &&
(unsigned char)test <= (unsigned char)c2)
ok = 1;
} else if (c == test)
ok = 1;
}
return (ok == negate ? NULL : pattern);
}

58
contrib/csup/fnmatch.h Normal file
View File

@ -0,0 +1,58 @@
/*-
* Copyright (c) 1992, 1993
* The Regents of the University of California. 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)fnmatch.h 8.1 (Berkeley) 6/2/93
*
* From FreeBSD fnmatch.h 1.7
* $Id: fnmatch.h,v 1.4 2001/10/04 02:46:21 jdp Exp $
*/
#ifndef _FNMATCH_H_
#define _FNMATCH_H_
#define FNM_NOMATCH 1 /* Match failed. */
#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */
#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */
#define FNM_PERIOD 0x04 /* Period must be matched by period. */
#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */
#define FNM_CASEFOLD 0x10 /* Case insensitive search. */
#define FNM_PREFIX_DIRS 0x20 /* Directory prefixes of pattern match too. */
/* Make this compile successfully with "gcc -traditional" */
#ifndef __STDC__
#define const /* empty */
#endif
int fnmatch(const char *, const char *, int);
#endif /* !_FNMATCH_H_ */

393
contrib/csup/globtree.c Normal file
View File

@ -0,0 +1,393 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <assert.h>
#include <fnmatch.h>
#include <regex.h>
#include <stdlib.h>
#include "globtree.h"
#include "misc.h"
/*
* The "GlobTree" interface allows one to construct arbitrarily complex
* boolean expressions for evaluating whether to accept or reject a
* filename. The globtree_test() function returns true or false
* according to whether the name is accepted or rejected by the
* expression.
*
* Expressions are trees constructed from nodes representing either
* primitive matching operations (primaries) or operators that are
* applied to their subexpressions. The simplest primitives are
* globtree_false(), which matches nothing, and globtree_true(), which
* matches everything.
*
* A more useful primitive is the matching operation, constructed with
* globtree_match(). It will call fnmatch() with the suppliedi
* shell-style pattern to determine if the filename matches.
*
* Expressions can be combined with the boolean operators AND, OR, and
* NOT, to form more complex expressions.
*/
/* Node types. */
#define GLOBTREE_NOT 0
#define GLOBTREE_AND 1
#define GLOBTREE_OR 2
#define GLOBTREE_MATCH 3
#define GLOBTREE_REGEX 4
#define GLOBTREE_TRUE 5
#define GLOBTREE_FALSE 6
/* A node. */
struct globtree {
int type;
struct globtree *left;
struct globtree *right;
/* The "data" field points to the text pattern for GLOBTREE_MATCH
nodes, and to the regex_t for GLOBTREE_REGEX nodes. For any
other node, it is set to NULL. */
void *data;
/* The "flags" field contains the flags to pass to fnmatch() for
GLOBTREE_MATCH nodes. */
int flags;
};
static struct globtree *globtree_new(int);
static int globtree_eval(struct globtree *, const char *);
static struct globtree *
globtree_new(int type)
{
struct globtree *gt;
gt = xmalloc(sizeof(struct globtree));
gt->type = type;
gt->data = NULL;
gt->flags = 0;
gt->left = NULL;
gt->right = NULL;
return (gt);
}
struct globtree *
globtree_true(void)
{
struct globtree *gt;
gt = globtree_new(GLOBTREE_TRUE);
return (gt);
}
struct globtree *
globtree_false(void)
{
struct globtree *gt;
gt = globtree_new(GLOBTREE_FALSE);
return (gt);
}
struct globtree *
globtree_match(const char *pattern, int flags)
{
struct globtree *gt;
gt = globtree_new(GLOBTREE_MATCH);
gt->data = xstrdup(pattern);
gt->flags = flags;
return (gt);
}
struct globtree *
globtree_regex(const char *pattern)
{
struct globtree *gt;
int error;
gt = globtree_new(GLOBTREE_REGEX);
gt->data = xmalloc(sizeof(regex_t));
error = regcomp(gt->data, pattern, REG_NOSUB);
assert(!error);
return (gt);
}
struct globtree *
globtree_and(struct globtree *left, struct globtree *right)
{
struct globtree *gt;
if (left->type == GLOBTREE_FALSE || right->type == GLOBTREE_FALSE) {
globtree_free(left);
globtree_free(right);
gt = globtree_false();
return (gt);
}
if (left->type == GLOBTREE_TRUE) {
globtree_free(left);
return (right);
}
if (right->type == GLOBTREE_TRUE) {
globtree_free(right);
return (left);
}
gt = globtree_new(GLOBTREE_AND);
gt->left = left;
gt->right = right;
return (gt);
}
struct globtree *
globtree_or(struct globtree *left, struct globtree *right)
{
struct globtree *gt;
if (left->type == GLOBTREE_TRUE || right->type == GLOBTREE_TRUE) {
globtree_free(left);
globtree_free(right);
gt = globtree_true();
return (gt);
}
if (left->type == GLOBTREE_FALSE) {
globtree_free(left);
return (right);
}
if (right->type == GLOBTREE_FALSE) {
globtree_free(right);
return (left);
}
gt = globtree_new(GLOBTREE_OR);
gt->left = left;
gt->right = right;
return (gt);
}
struct globtree *
globtree_not(struct globtree *child)
{
struct globtree *gt;
if (child->type == GLOBTREE_TRUE) {
globtree_free(child);
gt = globtree_new(GLOBTREE_FALSE);
return (gt);
}
if (child->type == GLOBTREE_FALSE) {
globtree_free(child);
gt = globtree_new(GLOBTREE_TRUE);
return (gt);
}
gt = globtree_new(GLOBTREE_NOT);
gt->left = child;
return (gt);
}
/* Evaluate one node (must be a leaf node). */
static int
globtree_eval(struct globtree *gt, const char *path)
{
int rv;
switch (gt->type) {
case GLOBTREE_TRUE:
return (1);
case GLOBTREE_FALSE:
return (0);
case GLOBTREE_MATCH:
assert(gt->data != NULL);
rv = fnmatch(gt->data, path, gt->flags);
if (rv == 0)
return (1);
assert(rv == FNM_NOMATCH);
return (0);
case GLOBTREE_REGEX:
assert(gt->data != NULL);
rv = regexec(gt->data, path, 0, NULL, 0);
if (rv == 0)
return (1);
assert(rv == REG_NOMATCH);
return (0);
}
assert(0);
return (-1);
}
/* Small stack API to walk the tree iteratively. */
typedef enum {
STATE_DOINGLEFT,
STATE_DOINGRIGHT
} walkstate_t;
struct stack {
struct stackelem *stack;
size_t size;
size_t in;
};
struct stackelem {
struct globtree *node;
walkstate_t state;
};
static void
stack_init(struct stack *stack)
{
stack->in = 0;
stack->size = 8; /* Initial size. */
stack->stack = xmalloc(sizeof(struct stackelem) * stack->size);
}
static size_t
stack_size(struct stack *stack)
{
return (stack->in);
}
static void
stack_push(struct stack *stack, struct globtree *node, walkstate_t state)
{
struct stackelem *e;
if (stack->in == stack->size) {
stack->size *= 2;
stack->stack = xrealloc(stack->stack,
sizeof(struct stackelem) * stack->size);
}
e = stack->stack + stack->in++;
e->node = node;
e->state = state;
}
static void
stack_pop(struct stack *stack, struct globtree **node, walkstate_t *state)
{
struct stackelem *e;
assert(stack->in > 0);
e = stack->stack + --stack->in;
*node = e->node;
*state = e->state;
}
static void
stack_free(struct stack *s)
{
free(s->stack);
}
/* Tests if the supplied filename matches. */
int
globtree_test(struct globtree *gt, const char *path)
{
struct stack stack;
walkstate_t state;
int val;
stack_init(&stack);
for (;;) {
doleft:
/* Descend to the left until we hit bottom. */
while (gt->left != NULL) {
stack_push(&stack, gt, STATE_DOINGLEFT);
gt = gt->left;
}
/* Now we're at a leaf node. Evaluate it. */
val = globtree_eval(gt, path);
/* Ascend, propagating the value through operator nodes. */
for (;;) {
if (stack_size(&stack) == 0) {
stack_free(&stack);
return (val);
}
stack_pop(&stack, &gt, &state);
switch (gt->type) {
case GLOBTREE_NOT:
val = !val;
break;
case GLOBTREE_AND:
/* If we haven't yet evaluated the right subtree
and the partial result is true, descend to
the right. Otherwise the result is already
determined to be val. */
if (state == STATE_DOINGLEFT && val) {
stack_push(&stack, gt,
STATE_DOINGRIGHT);
gt = gt->right;
goto doleft;
}
break;
case GLOBTREE_OR:
/* If we haven't yet evaluated the right subtree
and the partial result is false, descend to
the right. Otherwise the result is already
determined to be val. */
if (state == STATE_DOINGLEFT && !val) {
stack_push(&stack, gt,
STATE_DOINGRIGHT);
gt = gt->right;
goto doleft;
}
break;
default:
/* We only push nodes that have children. */
assert(0);
return (-1);
}
}
}
}
/*
* We could de-recursify this function using a stack, but it would be
* overkill since it is never called from a thread context with a
* limited stack size nor used in a critical path, so I think we can
* afford keeping it recursive.
*/
void
globtree_free(struct globtree *gt)
{
if (gt->data != NULL) {
if (gt->type == GLOBTREE_REGEX)
regfree(gt->data);
free(gt->data);
}
if (gt->left != NULL)
globtree_free(gt->left);
if (gt->right != NULL)
globtree_free(gt->right);
free(gt);
}

45
contrib/csup/globtree.h Normal file
View File

@ -0,0 +1,45 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _GLOBTREE_H_
#define _GLOBTREE_H_
#include "fnmatch.h"
struct globtree;
struct globtree *globtree_true(void);
struct globtree *globtree_false(void);
struct globtree *globtree_match(const char *, int);
struct globtree *globtree_regex(const char *);
struct globtree *globtree_and(struct globtree *, struct globtree *);
struct globtree *globtree_or(struct globtree *, struct globtree *);
struct globtree *globtree_not(struct globtree *);
int globtree_test(struct globtree *, const char *);
void globtree_free(struct globtree *);
#endif /* !_GLOBTREE_H_ */

502
contrib/csup/keyword.c Normal file
View File

@ -0,0 +1,502 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "diff.h"
#include "keyword.h"
#include "misc.h"
#include "queue.h"
#include "stream.h"
/*
* The keyword API is used to expand the CVS/RCS keywords in files,
* such as $Id$, $Revision$, etc. The server does it for us when it
* sends us entire files, but we need to handle the expansion when
* applying a diff update.
*/
enum rcskey {
RCSKEY_AUTHOR,
RCSKEY_CVSHEADER,
RCSKEY_DATE,
RCSKEY_HEADER,
RCSKEY_ID,
RCSKEY_LOCKER,
RCSKEY_LOG,
RCSKEY_NAME,
RCSKEY_RCSFILE,
RCSKEY_REVISION,
RCSKEY_SOURCE,
RCSKEY_STATE
};
typedef enum rcskey rcskey_t;
struct tag {
char *ident;
rcskey_t key;
int enabled;
STAILQ_ENTRY(tag) next;
};
static struct tag *tag_new(const char *, rcskey_t);
static char *tag_expand(struct tag *, struct diffinfo *);
static void tag_free(struct tag *);
struct keyword {
STAILQ_HEAD(, tag) keywords; /* Enabled keywords. */
size_t minkeylen;
size_t maxkeylen;
};
/* Default CVS keywords. */
static struct {
const char *ident;
rcskey_t key;
} tag_defaults[] = {
{ "Author", RCSKEY_AUTHOR },
{ "CVSHeader", RCSKEY_CVSHEADER },
{ "Date", RCSKEY_DATE },
{ "Header", RCSKEY_HEADER },
{ "Id", RCSKEY_ID },
{ "Locker", RCSKEY_LOCKER },
{ "Log", RCSKEY_LOG },
{ "Name", RCSKEY_NAME },
{ "RCSfile", RCSKEY_RCSFILE },
{ "Revision", RCSKEY_REVISION },
{ "Source", RCSKEY_SOURCE },
{ "State", RCSKEY_STATE },
{ NULL, 0, }
};
struct keyword *
keyword_new(void)
{
struct keyword *new;
struct tag *tag;
size_t len;
int i;
new = xmalloc(sizeof(struct keyword));
STAILQ_INIT(&new->keywords);
new->minkeylen = ~0;
new->maxkeylen = 0;
for (i = 0; tag_defaults[i].ident != NULL; i++) {
tag = tag_new(tag_defaults[i].ident, tag_defaults[i].key);
STAILQ_INSERT_TAIL(&new->keywords, tag, next);
len = strlen(tag->ident);
/*
* These values are only computed here and not updated when
* adding an alias. This is a bug, but CVSup has it and we
* need to be bug-to-bug compatible since the server will
* expect us to do the same, and we will fail with an MD5
* checksum mismatch if we don't.
*/
new->minkeylen = min(new->minkeylen, len);
new->maxkeylen = max(new->maxkeylen, len);
}
return (new);
}
int
keyword_decode_expand(const char *expand)
{
if (strcmp(expand, ".") == 0)
return (EXPAND_DEFAULT);
else if (strcmp(expand, "kv") == 0)
return (EXPAND_KEYVALUE);
else if (strcmp(expand, "kvl") == 0)
return (EXPAND_KEYVALUELOCKER);
else if (strcmp(expand, "k") == 0)
return (EXPAND_KEY);
else if (strcmp(expand, "o") == 0)
return (EXPAND_OLD);
else if (strcmp(expand, "b") == 0)
return (EXPAND_BINARY);
else if (strcmp(expand, "v") == 0)
return (EXPAND_VALUE);
else
return (-1);
}
void
keyword_free(struct keyword *keyword)
{
struct tag *tag;
if (keyword == NULL)
return;
while (!STAILQ_EMPTY(&keyword->keywords)) {
tag = STAILQ_FIRST(&keyword->keywords);
STAILQ_REMOVE_HEAD(&keyword->keywords, next);
tag_free(tag);
}
free(keyword);
}
int
keyword_alias(struct keyword *keyword, const char *ident, const char *rcskey)
{
struct tag *new, *tag;
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (strcmp(tag->ident, rcskey) == 0) {
new = tag_new(ident, tag->key);
STAILQ_INSERT_HEAD(&keyword->keywords, new, next);
return (0);
}
}
errno = ENOENT;
return (-1);
}
int
keyword_enable(struct keyword *keyword, const char *ident)
{
struct tag *tag;
int all;
all = 0;
if (strcmp(ident, ".") == 0)
all = 1;
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (!all && strcmp(tag->ident, ident) != 0)
continue;
tag->enabled = 1;
if (!all)
return (0);
}
if (!all) {
errno = ENOENT;
return (-1);
}
return (0);
}
int
keyword_disable(struct keyword *keyword, const char *ident)
{
struct tag *tag;
int all;
all = 0;
if (strcmp(ident, ".") == 0)
all = 1;
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (!all && strcmp(tag->ident, ident) != 0)
continue;
tag->enabled = 0;
if (!all)
return (0);
}
if (!all) {
errno = ENOENT;
return (-1);
}
return (0);
}
void
keyword_prepare(struct keyword *keyword)
{
struct tag *tag, *temp;
STAILQ_FOREACH_SAFE(tag, &keyword->keywords, next, temp) {
if (!tag->enabled) {
STAILQ_REMOVE(&keyword->keywords, tag, tag, next);
tag_free(tag);
continue;
}
}
}
/*
* Expand appropriate RCS keywords. If there's no tag to expand,
* keyword_expand() returns 0, otherwise it returns 1 and writes a
* pointer to the new line in *buf and the new len in *len. The
* new line is allocated with malloc() and needs to be freed by the
* caller after use.
*/
int
keyword_expand(struct keyword *keyword, struct diffinfo *di, char *line,
size_t size, char **buf, size_t *len)
{
struct tag *tag;
char *dollar, *keystart, *valstart, *vallim, *next;
char *linestart, *newline, *newval, *cp, *tmp;
size_t left, newsize, vallen;
if (di->di_expand == EXPAND_OLD || di->di_expand == EXPAND_BINARY)
return (0);
newline = NULL;
newsize = 0;
left = size;
linestart = cp = line;
again:
dollar = memchr(cp, '$', left);
if (dollar == NULL) {
if (newline != NULL) {
*buf = newline;
*len = newsize;
return (1);
}
return (0);
}
keystart = dollar + 1;
left -= keystart - cp;
vallim = memchr(keystart, '$', left);
if (vallim == NULL) {
if (newline != NULL) {
*buf = newline;
*len = newsize;
return (1);
}
return (0);
}
if (vallim == keystart) {
cp = keystart;
goto again;
}
valstart = memchr(keystart, ':', left);
if (valstart == keystart) {
cp = vallim;
left -= vallim - keystart;
goto again;
}
if (valstart == NULL || valstart > vallim)
valstart = vallim;
if (valstart < keystart + keyword->minkeylen ||
valstart > keystart + keyword->maxkeylen) {
cp = vallim;
left -= vallim -keystart;
goto again;
}
STAILQ_FOREACH(tag, &keyword->keywords, next) {
if (strncmp(tag->ident, keystart, valstart - keystart) == 0 &&
tag->ident[valstart - keystart] == '\0') {
if (newline != NULL)
tmp = newline;
else
tmp = NULL;
newval = NULL;
if (di->di_expand == EXPAND_KEY) {
newsize = dollar - linestart + 1 +
valstart - keystart + 1 +
size - (vallim + 1 - linestart);
newline = xmalloc(newsize);
cp = newline;
memcpy(cp, linestart, dollar - linestart);
cp += dollar - linestart;
*cp++ = '$';
memcpy(cp, keystart, valstart - keystart);
cp += valstart - keystart;
*cp++ = '$';
next = cp;
memcpy(cp, vallim + 1,
size - (vallim + 1 - linestart));
} else if (di->di_expand == EXPAND_VALUE) {
newval = tag_expand(tag, di);
if (newval == NULL)
vallen = 0;
else
vallen = strlen(newval);
newsize = dollar - linestart +
vallen +
size - (vallim + 1 - linestart);
newline = xmalloc(newsize);
cp = newline;
memcpy(cp, linestart, dollar - linestart);
cp += dollar - linestart;
if (newval != NULL) {
memcpy(cp, newval, vallen);
cp += vallen;
}
next = cp;
memcpy(cp, vallim + 1,
size - (vallim + 1 - linestart));
} else {
assert(di->di_expand == EXPAND_DEFAULT ||
di->di_expand == EXPAND_KEYVALUE ||
di->di_expand == EXPAND_KEYVALUELOCKER);
newval = tag_expand(tag, di);
if (newval == NULL)
vallen = 0;
else
vallen = strlen(newval);
newsize = dollar - linestart + 1 +
valstart - keystart + 2 +
vallen + 2 +
size - (vallim + 1 - linestart);
newline = xmalloc(newsize);
cp = newline;
memcpy(cp, linestart, dollar - linestart);
cp += dollar - linestart;
*cp++ = '$';
memcpy(cp, keystart, valstart - keystart);
cp += valstart - keystart;
*cp++ = ':';
*cp++ = ' ';
if (newval != NULL) {
memcpy(cp, newval, vallen);
cp += vallen;
}
*cp++ = ' ';
*cp++ = '$';
next = cp;
memcpy(cp, vallim + 1,
size - (vallim + 1 - linestart));
}
if (newval != NULL)
free(newval);
if (tmp != NULL)
free(tmp);
/*
* Continue looking for tags in the rest of the line.
*/
cp = next;
size = newsize;
left = size - (cp - newline);
linestart = newline;
goto again;
}
}
cp = vallim;
left = size - (cp - linestart);
goto again;
}
static struct tag *
tag_new(const char *ident, rcskey_t key)
{
struct tag *new;
new = xmalloc(sizeof(struct tag));
new->ident = xstrdup(ident);
new->key = key;
new->enabled = 1;
return (new);
}
static void
tag_free(struct tag *tag)
{
free(tag->ident);
free(tag);
}
/*
* Expand a specific tag and return the new value. If NULL
* is returned, the tag is empty.
*/
static char *
tag_expand(struct tag *tag, struct diffinfo *di)
{
/*
* CVS formats dates as "XXXX/XX/XX XX:XX:XX". 32 bytes
* is big enough until year 10,000,000,000,000,000 :-).
*/
char cvsdate[32];
struct tm tm;
char *filename, *val;
int error;
error = rcsdatetotm(di->di_revdate, &tm);
if (error)
err(1, "strptime");
if (strftime(cvsdate, sizeof(cvsdate), "%Y/%m/%d %H:%M:%S", &tm) == 0)
err(1, "strftime");
filename = strrchr(di->di_rcsfile, '/');
if (filename == NULL)
filename = di->di_rcsfile;
else
filename++;
switch (tag->key) {
case RCSKEY_AUTHOR:
xasprintf(&val, "%s", di->di_author);
break;
case RCSKEY_CVSHEADER:
xasprintf(&val, "%s %s %s %s %s", di->di_rcsfile,
di->di_revnum, cvsdate, di->di_author, di->di_state);
break;
case RCSKEY_DATE:
xasprintf(&val, "%s", cvsdate);
break;
case RCSKEY_HEADER:
xasprintf(&val, "%s/%s %s %s %s %s", di->di_cvsroot,
di->di_rcsfile, di->di_revnum, cvsdate, di->di_author,
di->di_state);
break;
case RCSKEY_ID:
xasprintf(&val, "%s %s %s %s %s", filename, di->di_revnum,
cvsdate, di->di_author, di->di_state);
break;
case RCSKEY_LOCKER:
/*
* Unimplemented even in CVSup sources. It seems we don't
* even have this information sent by the server.
*/
return (NULL);
case RCSKEY_LOG:
/* XXX */
printf("%s: Implement Log keyword expansion\n", __func__);
return (NULL);
case RCSKEY_NAME:
if (di->di_tag != NULL)
xasprintf(&val, "%s", di->di_tag);
else
return (NULL);
break;
case RCSKEY_RCSFILE:
xasprintf(&val, "%s", filename);
break;
case RCSKEY_REVISION:
xasprintf(&val, "%s", di->di_revnum);
break;
case RCSKEY_SOURCE:
xasprintf(&val, "%s/%s", di->di_cvsroot, di->di_rcsfile);
break;
case RCSKEY_STATE:
xasprintf(&val, "%s", di->di_state);
break;
}
return (val);
}

53
contrib/csup/keyword.h Normal file
View File

@ -0,0 +1,53 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _KEYWORD_H_
#define _KEYWORD_H_
/* CVS expansion modes. */
#define EXPAND_DEFAULT 0
#define EXPAND_KEYVALUE 1
#define EXPAND_KEYVALUELOCKER 2
#define EXPAND_KEY 3
#define EXPAND_OLD 4
#define EXPAND_BINARY 5
#define EXPAND_VALUE 6
struct diffinfo;
struct keyword;
struct keyword *keyword_new(void);
int keyword_decode_expand(const char *);
int keyword_alias(struct keyword *, const char *, const char *);
int keyword_enable(struct keyword *, const char *);
int keyword_disable(struct keyword *, const char *);
void keyword_prepare(struct keyword *);
int keyword_expand(struct keyword *, struct diffinfo *, char *,
size_t, char **, size_t *);
void keyword_free(struct keyword *);
#endif /* !_KEYWORD_H_ */

438
contrib/csup/lister.c Normal file
View File

@ -0,0 +1,438 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attrstack.h"
#include "config.h"
#include "fattr.h"
#include "globtree.h"
#include "lister.h"
#include "misc.h"
#include "mux.h"
#include "proto.h"
#include "status.h"
#include "stream.h"
/* Internal error codes. */
#define LISTER_ERR_WRITE (-1) /* Error writing to server. */
#define LISTER_ERR_STATUS (-2) /* Status file error in lstr->errmsg. */
struct lister {
struct config *config;
struct stream *wr;
char *errmsg;
};
static int lister_batch(struct lister *);
static int lister_coll(struct lister *, struct coll *, struct status *);
static int lister_dodirdown(struct lister *, struct coll *,
struct statusrec *, struct attrstack *as);
static int lister_dodirup(struct lister *, struct coll *,
struct statusrec *, struct attrstack *as);
static int lister_dofile(struct lister *, struct coll *,
struct statusrec *);
static int lister_dodead(struct lister *, struct coll *,
struct statusrec *);
void *
lister(void *arg)
{
struct thread_args *args;
struct lister lbuf, *l;
int error;
args = arg;
l = &lbuf;
l->config = args->config;
l->wr = args->wr;
l->errmsg = NULL;
error = lister_batch(l);
switch (error) {
case LISTER_ERR_WRITE:
xasprintf(&args->errmsg,
"TreeList failed: Network write failure: %s",
strerror(errno));
args->status = STATUS_TRANSIENTFAILURE;
break;
case LISTER_ERR_STATUS:
xasprintf(&args->errmsg,
"TreeList failed: %s. Delete it and try again.",
l->errmsg);
free(l->errmsg);
args->status = STATUS_FAILURE;
break;
default:
assert(error == 0);
args->status = STATUS_SUCCESS;
};
return (NULL);
}
static int
lister_batch(struct lister *l)
{
struct config *config;
struct stream *wr;
struct status *st;
struct coll *coll;
int error;
config = l->config;
wr = l->wr;
STAILQ_FOREACH(coll, &config->colls, co_next) {
if (coll->co_options & CO_SKIP)
continue;
st = status_open(coll, -1, &l->errmsg);
if (st == NULL)
return (LISTER_ERR_STATUS);
error = proto_printf(wr, "COLL %s %s\n", coll->co_name,
coll->co_release);
if (error)
return (LISTER_ERR_WRITE);
stream_flush(wr);
if (coll->co_options & CO_COMPRESS)
stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL);
error = lister_coll(l, coll, st);
status_close(st, NULL);
if (error)
return (error);
if (coll->co_options & CO_COMPRESS)
stream_filter_stop(wr);
stream_flush(wr);
}
error = proto_printf(wr, ".\n");
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* List a single collection based on the status file. */
static int
lister_coll(struct lister *l, struct coll *coll, struct status *st)
{
struct stream *wr;
struct attrstack *as;
struct statusrec *sr;
struct fattr *fa;
int depth, error, ret, prunedepth;
wr = l->wr;
depth = 0;
prunedepth = INT_MAX;
as = attrstack_new();
while ((ret = status_get(st, NULL, 0, 0, &sr)) == 1) {
switch (sr->sr_type) {
case SR_DIRDOWN:
depth++;
if (depth < prunedepth) {
error = lister_dodirdown(l, coll, sr, as);
if (error < 0)
goto bad;
if (error)
prunedepth = depth;
}
break;
case SR_DIRUP:
if (depth < prunedepth) {
error = lister_dodirup(l, coll, sr, as);
if (error)
goto bad;
} else if (depth == prunedepth) {
/* Finished pruning. */
prunedepth = INT_MAX;
}
depth--;
continue;
case SR_CHECKOUTLIVE:
if (depth < prunedepth) {
error = lister_dofile(l, coll, sr);
if (error)
goto bad;
}
break;
case SR_CHECKOUTDEAD:
if (depth < prunedepth) {
error = lister_dodead(l, coll, sr);
if (error)
goto bad;
}
break;
}
}
if (ret == -1) {
l->errmsg = status_errmsg(st);
error = LISTER_ERR_STATUS;
goto bad;
}
assert(status_eof(st));
assert(depth == 0);
error = proto_printf(wr, ".\n");
attrstack_free(as);
if (error)
return (LISTER_ERR_WRITE);
return (0);
bad:
while (depth-- > 0) {
fa = attrstack_pop(as);
fattr_free(fa);
}
attrstack_free(as);
return (error);
}
/* Handle a directory up entry found in the status file. */
static int
lister_dodirdown(struct lister *l, struct coll *coll, struct statusrec *sr,
struct attrstack *as)
{
struct config *config;
struct stream *wr;
struct fattr *fa, *fa2;
char *path;
int error;
config = l->config;
wr = l->wr;
if (!globtree_test(coll->co_dirfilter, sr->sr_file))
return (1);
if (coll->co_options & CO_TRUSTSTATUSFILE) {
fa = fattr_new(FT_DIRECTORY, -1);
} else {
xasprintf(&path, "%s/%s", coll->co_prefix, sr->sr_file);
fa = fattr_frompath(path, FATTR_NOFOLLOW);
if (fa == NULL) {
/* The directory doesn't exist, prune
* everything below it. */
free(path);
return (1);
}
if (fattr_type(fa) == FT_SYMLINK) {
fa2 = fattr_frompath(path, FATTR_FOLLOW);
if (fa2 != NULL && fattr_type(fa2) == FT_DIRECTORY) {
/* XXX - When not in checkout mode, CVSup warns
* here about the file being a symlink to a
* directory instead of a directory. */
fattr_free(fa);
fa = fa2;
} else {
fattr_free(fa2);
}
}
free(path);
}
if (fattr_type(fa) != FT_DIRECTORY) {
fattr_free(fa);
/* Report it as something bogus so
* that it will be replaced. */
error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file),
fattr_bogus, config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (1);
}
/* It really is a directory. */
attrstack_push(as, fa);
error = proto_printf(wr, "D %s\n", pathlast(sr->sr_file));
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
/* Handle a directory up entry found in the status file. */
static int
lister_dodirup(struct lister *l, struct coll *coll, struct statusrec *sr,
struct attrstack *as)
{
struct config *config;
const struct fattr *sendattr;
struct stream *wr;
struct fattr *fa, *fa2;
int error;
config = l->config;
wr = l->wr;
fa = attrstack_pop(as);
if (coll->co_options & CO_TRUSTSTATUSFILE) {
fattr_free(fa);
fa = sr->sr_clientattr;
}
fa2 = sr->sr_clientattr;
if (fattr_equal(fa, fa2))
sendattr = fa;
else
sendattr = fattr_bogus;
error = proto_printf(wr, "U %F\n", sendattr, config->fasupport,
coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
if (!(coll->co_options & CO_TRUSTSTATUSFILE))
fattr_free(fa);
/* XXX CVSup flushes here for some reason with a comment saying
"Be smarter". We don't flush when listing other file types. */
stream_flush(wr);
return (0);
}
/* Handle a checkout live entry found in the status file. */
static int
lister_dofile(struct lister *l, struct coll *coll, struct statusrec *sr)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr, *fa;
struct fattr *fa2, *rfa;
char *path, *spath;
int error;
if (!globtree_test(coll->co_filefilter, sr->sr_file))
return (0);
config = l->config;
wr = l->wr;
rfa = NULL;
sendattr = NULL;
error = 0;
if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
path = checkoutpath(coll->co_prefix, sr->sr_file);
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);
}
rfa = fattr_frompath(path, FATTR_NOFOLLOW);
free(path);
if (rfa == NULL) {
/*
* According to the checkouts file we should have
* this file but we don't. Maybe the user deleted
* the file, or maybe the checkouts file is wrong.
* List the file with bogus attributes to cause the
* server to get things back in sync again.
*/
sendattr = fattr_bogus;
goto send;
}
fa = rfa;
} else {
fa = sr->sr_clientattr;
}
fa2 = fattr_forcheckout(sr->sr_serverattr, coll->co_umask);
if (!fattr_equal(fa, sr->sr_clientattr) || !fattr_equal(fa, fa2) ||
strcmp(coll->co_tag, sr->sr_tag) != 0 ||
strcmp(coll->co_date, sr->sr_date) != 0) {
/*
* The file corresponds to the information we have
* recorded about it, and its moded is correct for
* the requested umask setting.
*/
sendattr = fattr_bogus;
} else {
/*
* Either the file has been touched, or we are asking
* for a different revision than the one we recorded
* information about, or its mode isn't right (because
* it was last updated using a version of CVSup that
* wasn't so strict about modes).
*/
sendattr = sr->sr_serverattr;
}
fattr_free(fa2);
if (rfa != NULL)
fattr_free(rfa);
send:
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)
{
struct config *config;
struct stream *wr;
const struct fattr *sendattr;
struct fattr *fa;
char *path, *spath;
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 = checkoutpath(coll->co_prefix, sr->sr_file);
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);
if (fa != NULL && fattr_type(fa) != FT_DIRECTORY) {
/*
* We shouldn't have this file but we do. Report
* it to the server, which will either send a
* deletion request, of (if the file has come alive)
* sent the correct version.
*/
fattr_free(fa);
error = proto_printf(wr, "F %s %F\n",
pathlast(sr->sr_file), fattr_bogus,
config->fasupport, coll->co_attrignore);
if (error)
return (LISTER_ERR_WRITE);
return (0);
}
fattr_free(fa);
}
if (strcmp(coll->co_tag, sr->sr_tag) != 0 ||
strcmp(coll->co_date, sr->sr_date) != 0)
sendattr = fattr_bogus;
else
sendattr = sr->sr_serverattr;
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);
}

33
contrib/csup/lister.h Normal file
View File

@ -0,0 +1,33 @@
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _LISTER_H_
#define _LISTER_H_
void *lister(void *);
#endif /* !_LISTER_H_ */

315
contrib/csup/main.c Normal file
View File

@ -0,0 +1,315 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/file.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "fattr.h"
#include "misc.h"
#include "proto.h"
#include "stream.h"
#define USAGE_OPTFMT " %-12s %s\n"
#define USAGE_OPTFMTSUB " %-14s %s\n", ""
int verbose = 1;
static void
usage(char *argv0)
{
lprintf(-1, "Usage: %s [options] supfile\n", basename(argv0));
lprintf(-1, " Options:\n");
lprintf(-1, USAGE_OPTFMT, "-1", "Don't retry automatically on failure "
"(same as \"-r 0\")");
lprintf(-1, USAGE_OPTFMT, "-4", "Force usage of IPv4 addresses");
lprintf(-1, USAGE_OPTFMT, "-6", "Force usage of IPv6 addresses");
lprintf(-1, USAGE_OPTFMT, "-A addr",
"Bind local socket to a specific address");
lprintf(-1, USAGE_OPTFMT, "-b base",
"Override supfile's \"base\" directory");
lprintf(-1, USAGE_OPTFMT, "-c collDir",
"Subdirectory of \"base\" for collections (default \"sup\")");
lprintf(-1, USAGE_OPTFMT, "-h host",
"Override supfile's \"host\" name");
lprintf(-1, USAGE_OPTFMT, "-i pattern",
"Include only files/directories matching pattern.");
lprintf(-1, USAGE_OPTFMTSUB,
"May be repeated for an OR operation. Default is");
lprintf(-1, USAGE_OPTFMTSUB, "to include each entire collection.");
lprintf(-1, USAGE_OPTFMT, "-l lockfile",
"Lock file during update; fail if already locked");
lprintf(-1, USAGE_OPTFMT, "-L n",
"Verbosity level (0..2, default 1)");
lprintf(-1, USAGE_OPTFMT, "-p port",
"Alternate server port (default 5999)");
lprintf(-1, USAGE_OPTFMT, "-r n",
"Maximum retries on transient errors (default unlimited)");
lprintf(-1, USAGE_OPTFMT, "-s",
"Don't stat client files; trust the checkouts file");
lprintf(-1, USAGE_OPTFMT, "-v", "Print version and exit");
lprintf(-1, USAGE_OPTFMT, "-z", "Enable compression for all "
"collections");
lprintf(-1, USAGE_OPTFMT, "-Z", "Disable compression for all "
"collections");
}
int
main(int argc, char *argv[])
{
struct tm tm;
struct backoff_timer *timer;
struct config *config;
struct coll *override;
struct addrinfo *res;
struct sockaddr *laddr;
socklen_t laddrlen;
struct stream *lock;
char *argv0, *file, *lockfile;
uint16_t port;
int family, error, lockfd, lflag, overridemask;
int c, i, retries, status;
time_t nexttry;
error = 0;
family = PF_UNSPEC;
port = 0;
lflag = 0;
lockfd = 0;
nexttry = 0;
retries = -1;
argv0 = argv[0];
laddr = NULL;
laddrlen = 0;
lockfile = NULL;
override = coll_new(NULL);
overridemask = 0;
while ((c = getopt(argc, argv, "146A:b:c:gh:i:l:L:p:P:r:svzZ")) != -1) {
switch (c) {
case '1':
retries = 0;
break;
case '4':
family = AF_INET;
break;
case '6':
family = AF_INET6;
break;
case 'A':
error = getaddrinfo(optarg, NULL, NULL, &res);
if (error) {
lprintf(-1, "%s: %s\n", optarg,
gai_strerror(error));
return (1);
}
laddrlen = res->ai_addrlen;
laddr = xmalloc(laddrlen);
memcpy(laddr, res->ai_addr, laddrlen);
freeaddrinfo(res);
break;
case 'b':
if (override->co_base != NULL)
free(override->co_base);
override->co_base = xstrdup(optarg);
break;
case 'c':
override->co_colldir = optarg;
break;
case 'g':
/* For compatibility. */
break;
case 'h':
if (override->co_host != NULL)
free(override->co_host);
override->co_host = xstrdup(optarg);
break;
case 'i':
pattlist_add(override->co_accepts, optarg);
break;
case 'l':
lockfile = optarg;
lflag = 1;
lockfd = open(lockfile,
O_CREAT | O_WRONLY | O_TRUNC, 0700);
if (lockfd != -1) {
error = flock(lockfd, LOCK_EX | LOCK_NB);
if (error == -1 && errno == EWOULDBLOCK) {
if (lockfd != -1)
close(lockfd);
lprintf(-1, "\"%s\" is already locked "
"by another process\n", lockfile);
return (1);
}
}
if (lockfd == -1 || error == -1) {
if (lockfd != -1)
close(lockfd);
lprintf(-1, "Error locking \"%s\": %s\n",
lockfile, strerror(errno));
return (1);
}
lock = stream_open_fd(lockfd,
NULL, stream_write_fd, NULL);
(void)stream_printf(lock, "%10ld\n", (long)getpid());
stream_close(lock);
break;
case 'L':
errno = 0;
verbose = strtol(optarg, NULL, 0);
if (errno == EINVAL) {
lprintf(-1, "Invalid verbosity\n");
usage(argv0);
return (1);
}
break;
case 'p':
/* Use specified server port. */
errno = 0;
port = strtol(optarg, NULL, 0);
if (errno == EINVAL) {
lprintf(-1, "Invalid server port\n");
usage(argv0);
return (1);
}
break;
case 'P':
/* For compatibility. */
if (strcmp(optarg, "m") != 0) {
lprintf(-1,
"Client only supports multiplexed mode\n");
return (1);
}
break;
case 'r':
errno = 0;
retries = strtol(optarg, NULL, 0);
if (errno == EINVAL || retries < 0) {
lprintf(-1, "Invalid retry limit\n");
usage(argv0);
return (1);
}
break;
case 's':
override->co_options |= CO_TRUSTSTATUSFILE;
overridemask |= CO_TRUSTSTATUSFILE;
break;
case 'v':
lprintf(0, "CVSup client written in C\n");
lprintf(0, "Software version: %s\n", PROTO_SWVER);
lprintf(0, "Protocol version: %d.%d\n",
PROTO_MAJ, PROTO_MIN);
lprintf(0, "http://mu.org/~mux/csup.html\n");
return (0);
break;
case 'z':
/* Force compression on all collections. */
override->co_options |= CO_COMPRESS;
overridemask |= CO_COMPRESS;
break;
case 'Z':
/* Disables compression on all collections. */
override->co_options &= ~CO_COMPRESS;
overridemask &= ~CO_COMPRESS;
break;
case '?':
default:
usage(argv0);
return (1);
}
}
argc -= optind;
argv += optind;
if (argc < 1) {
usage(argv0);
return (1);
}
file = argv[0];
lprintf(2, "Parsing supfile \"%s\"\n", file);
config = config_init(file, override, overridemask);
coll_free(override);
if (config == NULL)
return (1);
if (laddr != NULL) {
config->laddr = laddr;
config->laddrlen = laddrlen;
}
if (config_checkcolls(config) == 0) {
lprintf(-1, "No collections selected\n");
return (1);
}
lprintf(2, "Connecting to %s\n", config->host);
i = 0;
fattr_init(); /* Initialize the fattr API. */
timer = bt_new(300, 7200, 2.0, 0.1);
for (;;) {
status = proto_connect(config, family, port);
if (status == STATUS_SUCCESS) {
status = proto_run(config);
if (status != STATUS_TRANSIENTFAILURE)
break;
}
if (retries >= 0 && i >= retries)
break;
nexttry = time(0) + bt_get(timer);
localtime_r(&nexttry, &tm);
lprintf(1, "Will retry at %02d:%02d:%02d\n",
tm.tm_hour, tm.tm_min, tm.tm_sec);
bt_pause(timer);
lprintf(1, "Retrying\n");
i++;
}
bt_free(timer);
fattr_fini();
if (lflag) {
unlink(lockfile);
flock(lockfd, LOCK_UN);
close(lockfd);
}
config_free(config);
if (status != STATUS_SUCCESS)
return (1);
return (0);
}

29
contrib/csup/main.h Normal file
View File

@ -0,0 +1,29 @@
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $Id$
*/
extern int verbose;

503
contrib/csup/misc.c Normal file
View File

@ -0,0 +1,503 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <openssl/md5.h>
#include <assert.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "fattr.h"
#include "main.h"
#include "misc.h"
struct pattlist {
char **patterns;
size_t size;
size_t in;
};
struct backoff_timer {
time_t min;
time_t max;
time_t interval;
float backoff;
float jitter;
};
static void bt_update(struct backoff_timer *);
static void bt_addjitter(struct backoff_timer *);
int
lprintf(int level, const char *fmt, ...)
{
FILE *to;
va_list ap;
int ret;
if (level > verbose)
return (0);
if (level == -1)
to = stderr;
else
to = stdout;
va_start(ap, fmt);
ret = vfprintf(to, fmt, ap);
va_end(ap);
fflush(to);
return (ret);
}
/*
* Compute the MD5 checksum of a file. The md parameter must
* point to a buffer containing at least MD5_DIGEST_SIZE bytes.
*
* Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own
* MD5_DIGEST_SIZE macro.
*/
int
MD5_File(char *path, char *md)
{
char buf[1024];
MD5_CTX ctx;
ssize_t n;
int fd;
fd = open(path, O_RDONLY);
if (fd == -1)
return (-1);
MD5_Init(&ctx);
while ((n = read(fd, buf, sizeof(buf))) > 0)
MD5_Update(&ctx, buf, n);
close(fd);
if (n == -1)
return (-1);
MD5_End(md, &ctx);
return (0);
}
/*
* Wrapper around MD5_Final() that converts the 128 bits MD5 hash
* to an ASCII string representing this value in hexadecimal.
*/
void
MD5_End(char *md, MD5_CTX *c)
{
unsigned char md5[MD5_DIGEST_LENGTH];
const char hex[] = "0123456789abcdef";
int i, j;
MD5_Final(md5, c);
j = 0;
for (i = 0; i < MD5_DIGEST_LENGTH; i++) {
md[j++] = hex[md5[i] >> 4];
md[j++] = hex[md5[i] & 0xf];
}
md[j] = '\0';
}
int
pathcmp(const char *s1, const char *s2)
{
char c1, c2;
do {
c1 = *s1++;
if (c1 == '/')
c1 = 1;
c2 = *s2++;
if (c2 == '/')
c2 = 1;
} while (c1 == c2 && c1 != '\0');
return (c1 - c2);
}
size_t
commonpathlength(const char *a, size_t alen, const char *b, size_t blen)
{
size_t i, minlen, lastslash;
minlen = min(alen, blen);
lastslash = 0;
for (i = 0; i < minlen; i++) {
if (a[i] != b[i])
return (lastslash);
if (a[i] == '/') {
if (i == 0) /* Include the leading slash. */
lastslash = 1;
else
lastslash = i;
}
}
/* One path is a prefix of the other/ */
if (alen > minlen) { /* Path "b" is a prefix of "a". */
if (a[minlen] == '/')
return (minlen);
else
return (lastslash);
} else if (blen > minlen) { /* Path "a" is a prefix of "b". */
if (b[minlen] == '/')
return (minlen);
else
return (lastslash);
}
/* The paths are identical. */
return (minlen);
}
char *
pathlast(char *path)
{
char *s;
s = strrchr(path, '/');
if (s == NULL)
return (path);
return (++s);
}
int
rcsdatetotm(const char *revdate, struct tm *tm)
{
char *cp;
size_t len;
cp = strchr(revdate, '.');
if (cp == NULL)
return (-1);
len = cp - revdate;
if (len == 4)
cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm);
else if (len == 2)
cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm);
else
return (-1);
if (cp == NULL || *cp != '\0')
return (-1);
return (0);
}
time_t
rcsdatetotime(const char *revdate)
{
struct tm tm;
time_t t;
int error;
error = rcsdatetotm(revdate, &tm);
if (error)
return (error);
t = timegm(&tm);
return (t);
}
/*
* Returns a buffer allocated with malloc() containing the absolute
* pathname to the checkout file made from the prefix and the path
* of the corresponding RCS file relatively to the prefix. If the
* filename is not an RCS filename, NULL will be returned.
*/
char *
checkoutpath(const char *prefix, const char *file)
{
const char *cp;
char *path;
size_t len;
if (file[0] == '/')
return (NULL);
cp = file;
while ((cp = strstr(cp, "..")) != NULL) {
if (cp == file || cp[2] == '\0' ||
(cp[-1] == '/' && cp[2] == '/'))
return (NULL);
cp += 2;
}
len = strlen(file);
if (len < 2 || file[len - 1] != 'v' || file[len - 2] != ',')
return (NULL);
xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
return (path);
}
int
mkdirhier(char *path, mode_t mask)
{
struct fattr *fa;
size_t i, last, len;
int error, finish, rv;
finish = 0;
last = 0;
len = strlen(path);
for (i = len - 1; i > 0; i--) {
if (path[i] == '/') {
path[i] = '\0';
if (access(path, F_OK) == 0) {
path[i] = '/';
break;
}
if (errno != ENOENT) {
path[i] = '/';
if (last == 0)
return (-1);
finish = 1;
break;
}
last = i;
}
}
if (last == 0)
return (0);
i = strlen(path);
fa = fattr_new(FT_DIRECTORY, -1);
fattr_mergedefault(fa);
fattr_umask(fa, mask);
while (i < len) {
if (!finish) {
rv = 0;
error = fattr_makenode(fa, path);
if (!error)
rv = fattr_install(fa, path, NULL);
if (error || rv == -1)
finish = 1;
}
path[i] = '/';
i += strlen(path + i);
}
assert(i == len);
if (finish)
return (-1);
return (0);
}
/*
* Compute temporary pathnames.
* This can look a bit like overkill but we mimic CVSup's behaviour.
*/
#define TEMPNAME_PREFIX "#cvs.csup"
static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER;
static pid_t tempname_pid = -1;
static int tempname_count;
char *
tempname(const char *path)
{
char *cp, *temp;
int count, error;
error = pthread_mutex_lock(&tempname_mtx);
assert(!error);
if (tempname_pid == -1) {
tempname_pid = getpid();
tempname_count = 0;
}
count = tempname_count++;
error = pthread_mutex_unlock(&tempname_mtx);
assert(!error);
cp = strrchr(path, '/');
if (cp == NULL)
xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX,
(long)tempname_pid, count);
else
xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path,
TEMPNAME_PREFIX, (long)tempname_pid, count);
return (temp);
}
void *
xmalloc(size_t size)
{
void *buf;
buf = malloc(size);
if (buf == NULL)
err(1, "malloc");
return (buf);
}
void *
xrealloc(void *buf, size_t size)
{
buf = realloc(buf, size);
if (buf == NULL)
err(1, "realloc");
return (buf);
}
char *
xstrdup(const char *str)
{
char *buf;
buf = strdup(str);
if (buf == NULL)
err(1, "strdup");
return (buf);
}
int
xasprintf(char **ret, const char *format, ...)
{
va_list ap;
int rv;
va_start(ap, format);
rv = vasprintf(ret, format, ap);
va_end(ap);
if (*ret == NULL)
err(1, "asprintf");
return (rv);
}
struct pattlist *
pattlist_new(void)
{
struct pattlist *p;
p = xmalloc(sizeof(struct pattlist));
p->size = 4; /* Initial size. */
p->patterns = xmalloc(p->size * sizeof(char *));
p->in = 0;
return (p);
}
void
pattlist_add(struct pattlist *p, const char *pattern)
{
if (p->in == p->size) {
p->size *= 2;
p->patterns = xrealloc(p->patterns, p->size * sizeof(char *));
}
assert(p->in < p->size);
p->patterns[p->in++] = xstrdup(pattern);
}
char *
pattlist_get(struct pattlist *p, size_t i)
{
assert(i < p->in);
return (p->patterns[i]);
}
size_t
pattlist_size(struct pattlist *p)
{
return (p->in);
}
void
pattlist_free(struct pattlist *p)
{
size_t i;
for (i = 0; i < p->in; i++)
free(p->patterns[i]);
free(p->patterns);
free(p);
}
/* Creates a backoff timer. */
struct backoff_timer *
bt_new(time_t min, time_t max, float backoff, float jitter)
{
struct backoff_timer *bt;
bt = xmalloc(sizeof(struct backoff_timer));
bt->min = min;
bt->max = max;
bt->backoff = backoff;
bt->jitter = jitter;
bt->interval = min;
bt_addjitter(bt);
srandom(time(0));
return (bt);
}
/* Updates the backoff timer. */
static void
bt_update(struct backoff_timer *bt)
{
bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max);
bt_addjitter(bt);
}
/* Adds some jitter. */
static void
bt_addjitter(struct backoff_timer *bt)
{
long mag;
mag = (long)(bt->jitter * bt->interval);
/* We want a random number between -mag and mag. */
bt->interval += (time_t)(random() % (2 * mag) - mag);
}
/* Returns the current timer value. */
time_t
bt_get(struct backoff_timer *bt)
{
return (bt->interval);
}
/* Times out for bt->interval seconds. */
void
bt_pause(struct backoff_timer *bt)
{
sleep(bt->interval);
bt_update(bt);
}
void
bt_free(struct backoff_timer *bt)
{
free(bt);
}

128
contrib/csup/misc.h Normal file
View File

@ -0,0 +1,128 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _MISC_H_
#define _MISC_H_
#include <openssl/md5.h>
#include <sys/types.h>
/* If we're not compiling in a C99 environment, define the C99 types. */
#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901
#ifdef uint32_t
#undef uint32_t
#endif
#define uint32_t u_int32_t
#ifdef uint16_t
#undef uint16_t
#endif
#define uint16_t u_int16_t
#ifdef uint8_t
#undef uint8_t
#endif
#define uint8_t u_int8_t
#else
#include <stdint.h>
#endif
/* This is a GCC-specific keyword but some other compilers (namely icc)
understand it, and the code won't work if we can't disable padding
anyways. */
#undef __packed
#define __packed __attribute__((__packed__))
/* We explicitely don't define this with icc because it defines __GNUC__
but doesn't support it. */
#undef __printflike
#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && \
(__GNUC__ > 2 || __GNUC__ == 2 && __GNUC__MINOR__ >= 7)
#define __printflike(fmtarg, firstvararg) \
__attribute__((__format__ (__printf__, fmtarg, firstvararg)))
#else
#define __printflike(fmtarg, firstvararg)
#endif
/* Exit codes. */
#define STATUS_SUCCESS 0
#define STATUS_FAILURE 1
#define STATUS_TRANSIENTFAILURE 2
#define STATUS_INTERRUPTED 3
struct config;
struct stream;
/* Thread parameters. */
struct thread_args {
struct config *config;
struct stream *rd;
struct stream *wr;
int status;
char *errmsg;
};
/* Minimum size for MD5_File() and MD5_End() buffers. */
#define MD5_DIGEST_SIZE 33
#define min(a, b) ((a) > (b) ? (b) : (a))
#define max(a, b) ((a) < (b) ? (b) : (a))
struct backoff_timer;
struct pattlist;
struct tm;
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);
struct pattlist *pattlist_new(void);
void pattlist_add(struct pattlist *, const char *);
char *pattlist_get(struct pattlist *, size_t);
size_t pattlist_size(struct pattlist *);
void pattlist_free(struct pattlist *);
struct backoff_timer *bt_new(time_t, time_t, float, float);
time_t bt_get(struct backoff_timer *);
void bt_pause(struct backoff_timer *);
void bt_free(struct backoff_timer *);
#endif /* !_MISC_H_ */

1201
contrib/csup/mux.c Normal file

File diff suppressed because it is too large Load Diff

45
contrib/csup/mux.h Normal file
View File

@ -0,0 +1,45 @@
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _MUX_H_
#define _MUX_H_
struct mux;
struct chan;
struct mux *mux_open(int, struct chan **);
void mux_shutdown(struct mux *, const char *, int);
int mux_close(struct mux *);
void chan_wait(struct chan *);
int chan_listen(struct mux *);
struct chan *chan_accept(struct mux *, int);
ssize_t chan_read(struct chan *, void *, size_t);
ssize_t chan_write(struct chan *, const void *, size_t);
int chan_close(struct chan *);
#endif /* !_MUX_H_ */

91
contrib/csup/parse.y Normal file
View File

@ -0,0 +1,91 @@
%{
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include "config.h"
#include "token.h"
%}
%union {
char *str;
int i;
}
%token DEFAULT
%token <i> NAME
%token <i> BOOLEAN
%token EQUAL
%token <str> STRING
%%
config_file
: config_list
|
;
config_list
: config
| config_list config
;
config
: default_line
| collection
;
default_line
: DEFAULT options
{ coll_setdef(); }
;
collection
: STRING options
{ coll_add($1); }
;
options
:
| options option
;
option
: BOOLEAN
{ coll_setopt($1, NULL); }
| value
;
value
: NAME EQUAL STRING
{ coll_setopt($1, $3); }
;
%%

182
contrib/csup/pathcomp.c Normal file
View File

@ -0,0 +1,182 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include "misc.h"
#include "pathcomp.h"
struct pathcomp {
char *target;
size_t targetlen;
char *trashed;
char *prev;
size_t prevlen;
size_t goal;
size_t curlen;
};
struct pathcomp *
pathcomp_new(void)
{
struct pathcomp *pc;
pc = xmalloc(sizeof(struct pathcomp));
pc->curlen = 0;
pc->target = NULL;
pc->targetlen = 0;
pc->trashed = NULL;
pc->prev = NULL;
pc->prevlen = 0;
return (pc);
}
int
pathcomp_put(struct pathcomp *pc, int type, char *path)
{
char *cp;
assert(pc->target == NULL);
if (*path == '/')
return (-1);
switch (type) {
case PC_DIRDOWN:
pc->target = path;
pc->targetlen = strlen(path);
break;
case PC_FILE:
case PC_DIRUP:
cp = strrchr(path, '/');
pc->target = path;
if (cp != NULL)
pc->targetlen = cp - path;
else
pc->targetlen = 0;
break;
}
if (pc->prev != NULL)
pc->goal = commonpathlength(pc->prev, pc->prevlen, pc->target,
pc->targetlen);
else
pc->goal = 0;
if (pc->curlen == pc->goal) /* No need to go up. */
pc->goal = pc->targetlen;
return (0);
}
int
pathcomp_get(struct pathcomp *pc, int *type, char **name)
{
char *cp;
size_t slashpos, start;
if (pc->curlen > pc->goal) { /* Going up. */
assert(pc->prev != NULL);
pc->prev[pc->curlen] = '\0';
cp = pc->prev + pc->curlen - 1;
while (cp >= pc->prev) {
if (*cp == '/')
break;
cp--;
}
if (cp >= pc->prev)
slashpos = cp - pc->prev;
else
slashpos = 0;
pc->curlen = slashpos;
if (pc->curlen <= pc->goal) { /* Done going up. */
assert(pc->curlen == pc->goal);
pc->goal = pc->targetlen;
}
*type = PC_DIRUP;
*name = pc->prev;
return (1);
} else if (pc->curlen < pc->goal) { /* Going down. */
/* Restore the previously overwritten '/' character. */
if (pc->trashed != NULL) {
*pc->trashed = '/';
pc->trashed = NULL;
}
if (pc->curlen == 0)
start = pc->curlen;
else
start = pc->curlen + 1;
slashpos = start;
while (slashpos < pc->goal) {
if (pc->target[slashpos] == '/')
break;
slashpos++;
}
if (pc->target[slashpos] != '\0') {
assert(pc->target[slashpos] == '/');
pc->trashed = pc->target + slashpos;
pc->target[slashpos] = '\0';
}
pc->curlen = slashpos;
*type = PC_DIRDOWN;
*name = pc->target;
return (1);
} else { /* Done. */
if (pc->target != NULL) {
if (pc->trashed != NULL) {
*pc->trashed = '/';
pc->trashed = NULL;
}
if (pc->prev != NULL)
free(pc->prev);
pc->prev = xmalloc(pc->targetlen + 1);
memcpy(pc->prev, pc->target, pc->targetlen);
pc->prev[pc->targetlen] = '\0';
pc->prevlen = pc->targetlen;
pc->target = NULL;
pc->targetlen = 0;
}
return (0);
}
}
void
pathcomp_finish(struct pathcomp *pc)
{
pc->target = NULL;
pc->targetlen = 0;
pc->goal = 0;
}
void
pathcomp_free(struct pathcomp *pc)
{
if (pc->prev != NULL)
free(pc->prev);
free(pc);
}

44
contrib/csup/pathcomp.h Normal file
View File

@ -0,0 +1,44 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _PATHCOMP_H
#define _PATHCOMP_H
/* File types */
#define PC_DIRDOWN 0
#define PC_FILE 1
#define PC_DIRUP 2
struct pathcomp;
struct pathcomp *pathcomp_new(void);
int pathcomp_put(struct pathcomp *, int, char *);
int pathcomp_get(struct pathcomp *, int *, char **);
void pathcomp_finish(struct pathcomp *);
void pathcomp_free(struct pathcomp *);
#endif /* !_PATHCOMP_H */

1004
contrib/csup/proto.c Normal file

File diff suppressed because it is too large Load Diff

49
contrib/csup/proto.h Normal file
View File

@ -0,0 +1,49 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _PROTO_H_
#define _PROTO_H_
#include <time.h>
#include "misc.h"
#define PROTO_MAJ 17
#define PROTO_MIN 0
#define PROTO_SWVER "CSUP_1_0"
struct stream;
int proto_connect(struct config *, int, uint16_t);
int proto_run(struct config *);
int proto_printf(struct stream *, const char *, ...);
char *proto_get_ascii(char **);
char *proto_get_rest(char **);
int proto_get_int(char **, int *, int);
int proto_get_time(char **, time_t *);
#endif /* !_PROTO_H_ */

227
contrib/csup/queue.h Normal file
View File

@ -0,0 +1,227 @@
/*-
* Copyright (c) 1991, 1993
* The Regents of the University of California. 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.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
* $FreeBSD$
*
* $FreeBSD$
*/
#ifndef _QUEUE_H_
#define _QUEUE_H_
#undef __ofsetof
#define __offsetof(type, field) ((size_t)(&((type *)0)->field))
/*
* Singly-linked Tail queue declarations.
*/
#undef STAILQ_HEAD
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first;/* first element */ \
struct type **stqh_last;/* addr of last next element */ \
}
#undef STAILQ_HEAD_INITIALIZER
#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).stqh_first }
#undef STAILQ_ENTRY
#define STAILQ_ENTRY(type) \
struct { \
struct type *stqe_next; /* next element */ \
}
#undef STAILQ_EMPTY
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
#undef STAILQ_FIRST
#define STAILQ_FIRST(head) ((head)->stqh_first)
#undef STAILQ_FOREACH
#define STAILQ_FOREACH(var, head, field) \
for((var) = STAILQ_FIRST((head)); \
(var); \
(var) = STAILQ_NEXT((var), field))
#undef STAILQ_FOREACH_SAFE
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST((head)); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#undef STAILQ_INIT
#define STAILQ_INIT(head) do { \
STAILQ_FIRST((head)) = NULL; \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#undef STAILQ_INSERT_AFTER
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_NEXT((tqelm), field) = (elm); \
} while (0)
#undef STAILQ_INSERT_HEAD
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_FIRST((head)) = (elm); \
} while (0)
#undef STAILQ_INSERT_TAIL
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
STAILQ_NEXT((elm), field) = NULL; \
*(head)->stqh_last = (elm); \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#undef STAILQ_LAST
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY((head)) ? \
NULL : \
((struct type *) \
((char *)((head)->stqh_last) - __offsetof(struct type, field))))
#undef STAILQ_NEXT
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#undef STAILQ_REMOVE
#define STAILQ_REMOVE(head, elm, type, field) do { \
if (STAILQ_FIRST((head)) == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} \
else { \
struct type *curelm = STAILQ_FIRST((head)); \
while (STAILQ_NEXT(curelm, field) != (elm)) \
curelm = STAILQ_NEXT(curelm, field); \
if ((STAILQ_NEXT(curelm, field) = \
STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
(head)->stqh_last = &STAILQ_NEXT((curelm), field);\
} \
} while (0)
#undef STAILQ_REMOVE_HEAD
#define STAILQ_REMOVE_HEAD(head, field) do { \
if ((STAILQ_FIRST((head)) = \
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#undef STAILQ_REMOVE_HEAD_UNTIL
#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \
if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
/*
* List declarations.
*/
#undef LIST_HEAD
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#undef LIST_HEAD_INITIALIZER
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#undef LIST_ENTRY
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#undef LIST_EMPTY
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
#undef LIST_FIRST
#define LIST_FIRST(head) ((head)->lh_first)
#undef LIST_FOREACH
#define LIST_FOREACH(var, head, field) \
for ((var) = LIST_FIRST((head)); \
(var); \
(var) = LIST_NEXT((var), field))
#undef LIST_FOREACH_SAFE
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST((head)); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#undef LIST_INIT
#define LIST_INIT(head) do { \
LIST_FIRST((head)) = NULL; \
} while (0)
#undef LIST_INSERT_AFTER
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
LIST_NEXT((listelm), field)->field.le_prev = \
&LIST_NEXT((elm), field); \
LIST_NEXT((listelm), field) = (elm); \
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
} while (0)
#undef LIST_INSERT_BEFORE
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
(elm)->field.le_prev = (listelm)->field.le_prev; \
LIST_NEXT((elm), field) = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
} while (0)
#undef LIST_INSERT_HEAD
#define LIST_INSERT_HEAD(head, elm, field) do { \
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
LIST_FIRST((head)) = (elm); \
(elm)->field.le_prev = &LIST_FIRST((head)); \
} while (0)
#undef LIST_NEXT
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#undef LIST_REMOVE
#define LIST_REMOVE(elm, field) do { \
if (LIST_NEXT((elm), field) != NULL) \
LIST_NEXT((elm), field)->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
} while (0)
#endif /* !_QUEUE_H_ */

842
contrib/csup/status.c Normal file
View File

@ -0,0 +1,842 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "config.h"
#include "fattr.h"
#include "misc.h"
#include "pathcomp.h"
#include "proto.h"
#include "queue.h"
#include "status.h"
#include "stream.h"
#define STATUS_VERSION 5
/* Internal error codes. */
#define STATUS_ERR_READ (-1)
#define STATUS_ERR_WRITE (-2)
#define STATUS_ERR_PARSE (-3)
#define STATUS_ERR_UNSORTED (-4)
#define STATUS_ERR_TRUNC (-5)
#define STATUS_ERR_BOGUS_DIRUP (-6)
#define STATUS_ERR_BAD_TYPE (-7)
#define STATUS_ERR_RENAME (-8)
static struct status *status_new(char *, time_t, struct stream *);
static struct statusrec *status_rd(struct status *);
static struct statusrec *status_rdraw(struct status *, char **);
static int status_wr(struct status *, struct statusrec *);
static int status_wrraw(struct status *, struct statusrec *,
char *);
static struct status *status_fromrd(char *, struct stream *);
static struct status *status_fromnull(char *);
static void status_free(struct status *);
static void statusrec_init(struct statusrec *);
static void statusrec_fini(struct statusrec *);
static int statusrec_cook(struct statusrec *, char *);
static int statusrec_cmp(struct statusrec *, struct statusrec *);
struct status {
char *path;
char *tempfile;
int error;
int suberror;
struct pathcomp *pc;
struct statusrec buf;
struct statusrec *previous;
struct statusrec *current;
struct stream *rd;
struct stream *wr;
time_t scantime;
int eof;
int linenum;
int depth;
int dirty;
};
static void
statusrec_init(struct statusrec *sr)
{
memset(sr, 0, sizeof(*sr));
}
static int
statusrec_cook(struct statusrec *sr, char *line)
{
char *clientattr, *serverattr;
switch (sr->sr_type) {
case SR_DIRDOWN:
/* Nothing to do. */
if (line != NULL)
return (-1);
break;
case SR_CHECKOUTLIVE:
sr->sr_tag = proto_get_ascii(&line);
sr->sr_date = proto_get_ascii(&line);
serverattr = proto_get_ascii(&line);
sr->sr_revnum = proto_get_ascii(&line);
sr->sr_revdate = proto_get_ascii(&line);
clientattr = proto_get_ascii(&line);
if (clientattr == NULL || line != NULL)
return (-1);
sr->sr_serverattr = fattr_decode(serverattr);
if (sr->sr_serverattr == NULL)
return (-1);
sr->sr_clientattr = fattr_decode(clientattr);
if (sr->sr_clientattr == NULL) {
fattr_free(sr->sr_serverattr);
return (-1);
}
break;
case SR_CHECKOUTDEAD:
sr->sr_tag = proto_get_ascii(&line);
sr->sr_date = proto_get_ascii(&line);
serverattr = proto_get_ascii(&line);
if (serverattr == NULL || line != NULL)
return (-1);
sr->sr_serverattr = fattr_decode(serverattr);
if (sr->sr_serverattr == NULL)
return (-1);
break;
case SR_DIRUP:
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;
default:
return (-1);
}
return (0);
}
static struct statusrec *
status_rd(struct status *st)
{
struct statusrec *sr;
char *line;
int error;
sr = status_rdraw(st, &line);
if (sr == NULL)
return (NULL);
error = statusrec_cook(sr, line);
if (error) {
st->error = STATUS_ERR_PARSE;
return (NULL);
}
return (sr);
}
static struct statusrec *
status_rdraw(struct status *st, char **linep)
{
struct statusrec sr;
char *cmd, *line, *file;
if (st->rd == NULL || st->eof)
return (NULL);
line = stream_getln(st->rd, NULL);
if (line == NULL) {
if (stream_eof(st->rd)) {
if (st->depth != 0) {
st->error = STATUS_ERR_TRUNC;
return (NULL);
}
st->eof = 1;
return (NULL);
}
st->error = STATUS_ERR_READ;
st->suberror = errno;
return (NULL);
}
st->linenum++;
cmd = proto_get_ascii(&line);
file = proto_get_ascii(&line);
if (file == NULL || strlen(cmd) != 1) {
st->error = STATUS_ERR_PARSE;
return (NULL);
}
switch (cmd[0]) {
case 'D':
sr.sr_type = SR_DIRDOWN;
st->depth++;
break;
case 'C':
sr.sr_type = SR_CHECKOUTLIVE;
break;
case 'c':
sr.sr_type = SR_CHECKOUTDEAD;
break;
case 'U':
sr.sr_type = SR_DIRUP;
if (st->depth <= 0) {
st->error = STATUS_ERR_BOGUS_DIRUP;
return (NULL);
}
st->depth--;
break;
default:
st->error = STATUS_ERR_BAD_TYPE;
st->suberror = cmd[0];
return (NULL);
}
sr.sr_file = xstrdup(file);
if (st->previous != NULL &&
statusrec_cmp(st->previous, &sr) >= 0) {
st->error = STATUS_ERR_UNSORTED;
free(sr.sr_file);
return (NULL);
}
if (st->previous == NULL) {
st->previous = &st->buf;
} else {
statusrec_fini(st->previous);
statusrec_init(st->previous);
}
st->previous->sr_type = sr.sr_type;
st->previous->sr_file = sr.sr_file;
*linep = line;
return (st->previous);
}
static int
status_wr(struct status *st, struct statusrec *sr)
{
struct pathcomp *pc;
const struct fattr *fa;
char *name;
int error, type, usedirupattr;
pc = st->pc;
error = 0;
usedirupattr = 0;
if (sr->sr_type == SR_DIRDOWN) {
pathcomp_put(pc, PC_DIRDOWN, sr->sr_file);
} else if (sr->sr_type == SR_DIRUP) {
pathcomp_put(pc, PC_DIRUP, sr->sr_file);
usedirupattr = 1;
} else {
pathcomp_put(pc, PC_FILE, sr->sr_file);
}
while (pathcomp_get(pc, &type, &name)) {
if (type == PC_DIRDOWN) {
error = proto_printf(st->wr, "D %s\n", name);
} else if (type == PC_DIRUP) {
if (usedirupattr)
fa = sr->sr_clientattr;
else
fa = fattr_bogus;
usedirupattr = 0;
error = proto_printf(st->wr, "U %s %f\n", name, fa);
}
if (error)
goto bad;
}
switch (sr->sr_type) {
case SR_DIRDOWN:
case SR_DIRUP:
/* Already emitted above. */
break;
case SR_CHECKOUTLIVE:
error = proto_printf(st->wr, "C %s %s %s %f %s %s %f\n",
sr->sr_file, sr->sr_tag, sr->sr_date, sr->sr_serverattr,
sr->sr_revnum, sr->sr_revdate, sr->sr_clientattr);
break;
case SR_CHECKOUTDEAD:
error = proto_printf(st->wr, "c %s %s %s %f\n", sr->sr_file,
sr->sr_tag, sr->sr_date, sr->sr_serverattr);
break;
}
if (error)
goto bad;
return (0);
bad:
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
return (-1);
}
static int
status_wrraw(struct status *st, struct statusrec *sr, char *line)
{
char *name;
char cmd;
int error, ret, type;
if (st->wr == NULL)
return (0);
/*
* Keep the compressor in sync. At this point, the necessary
* DirDowns and DirUps should have already been emitted, so the
* compressor should return exactly one value in the PC_DIRDOWN
* and PC_DIRUP case and none in the PC_FILE case.
*/
if (sr->sr_type == SR_DIRDOWN)
pathcomp_put(st->pc, PC_DIRDOWN, sr->sr_file);
else if (sr->sr_type == SR_DIRUP)
pathcomp_put(st->pc, PC_DIRUP, sr->sr_file);
else
pathcomp_put(st->pc, PC_FILE, sr->sr_file);
if (sr->sr_type == SR_DIRDOWN || sr->sr_type == SR_DIRUP) {
ret = pathcomp_get(st->pc, &type, &name);
assert(ret);
if (sr->sr_type == SR_DIRDOWN)
assert(type == PC_DIRDOWN);
else
assert(type == PC_DIRUP);
}
ret = pathcomp_get(st->pc, &type, &name);
assert(!ret);
switch (sr->sr_type) {
case SR_DIRDOWN:
cmd = 'D';
break;
case SR_DIRUP:
cmd = 'U';
break;
case SR_CHECKOUTLIVE:
cmd = 'C';
break;
case SR_CHECKOUTDEAD:
cmd = 'c';
break;
default:
assert(0);
return (-1);
}
if (sr->sr_type == SR_DIRDOWN)
error = proto_printf(st->wr, "%c %S\n", cmd, sr->sr_file);
else
error = proto_printf(st->wr, "%c %s %S\n", cmd, sr->sr_file,
line);
if (error) {
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
return (-1);
}
return (0);
}
static void
statusrec_fini(struct statusrec *sr)
{
fattr_free(sr->sr_serverattr);
fattr_free(sr->sr_clientattr);
free(sr->sr_file);
}
static int
statusrec_cmp(struct statusrec *a, struct statusrec *b)
{
size_t lena, lenb;
if (a->sr_type == SR_DIRUP || b->sr_type == SR_DIRUP) {
lena = strlen(a->sr_file);
lenb = strlen(b->sr_file);
if (a->sr_type == SR_DIRUP &&
((lena < lenb && b->sr_file[lena] == '/') || lena == lenb)
&& strncmp(a->sr_file, b->sr_file, lena) == 0)
return (1);
if (b->sr_type == SR_DIRUP &&
((lenb < lena && a->sr_file[lenb] == '/') || lenb == lena)
&& strncmp(a->sr_file, b->sr_file, lenb) == 0)
return (-1);
}
return (pathcmp(a->sr_file, b->sr_file));
}
static struct status *
status_new(char *path, time_t scantime, struct stream *file)
{
struct status *st;
st = xmalloc(sizeof(struct status));
st->path = path;
st->error = 0;
st->suberror = 0;
st->tempfile = NULL;
st->scantime = scantime;
st->rd = file;
st->wr = NULL;
st->previous = NULL;
st->current = NULL;
st->dirty = 0;
st->eof = 0;
st->linenum = 0;
st->depth = 0;
st->pc = pathcomp_new();
statusrec_init(&st->buf);
return (st);
}
static void
status_free(struct status *st)
{
if (st->previous != NULL)
statusrec_fini(st->previous);
if (st->rd != NULL)
stream_close(st->rd);
if (st->wr != NULL)
stream_close(st->wr);
if (st->tempfile != NULL)
free(st->tempfile);
free(st->path);
pathcomp_free(st->pc);
free(st);
}
static struct status *
status_fromrd(char *path, struct stream *file)
{
struct status *st;
char *id, *line;
time_t scantime;
int error, ver;
/* Get the first line of the file and validate it. */
line = stream_getln(file, NULL);
if (line == NULL) {
stream_close(file);
return (NULL);
}
id = proto_get_ascii(&line);
error = proto_get_int(&line, &ver, 10);
if (error) {
stream_close(file);
return (NULL);
}
error = proto_get_time(&line, &scantime);
if (error || line != NULL) {
stream_close(file);
return (NULL);
}
if (strcmp(id, "F") != 0 || ver != STATUS_VERSION) {
stream_close(file);
return (NULL);
}
st = status_new(path, scantime, file);
st->linenum = 1;
return (st);
}
static struct status *
status_fromnull(char *path)
{
struct status *st;
st = status_new(path, -1, NULL);
st->eof = 1;
return (st);
}
/*
* Open the status file. If scantime is not -1, the file is opened
* for updating, otherwise, it is opened read-only. If the status file
* couldn't be opened, NULL is returned and errmsg is set to the error
* message.
*/
struct status *
status_open(struct coll *coll, time_t scantime, char **errmsg)
{
struct status *st;
struct stream *file;
struct fattr *fa;
char *destpath, *path;
int error, rv;
path = coll_statuspath(coll);
file = stream_open_file(path, O_RDONLY);
if (file == NULL) {
if (errno != ENOENT) {
xasprintf(errmsg, "Could not open \"%s\": %s\n",
path, strerror(errno));
free(path);
return (NULL);
}
st = status_fromnull(path);
} else {
st = status_fromrd(path, file);
if (st == NULL) {
xasprintf(errmsg, "Error in \"%s\": Bad header line",
path);
free(path);
return (NULL);
}
}
if (scantime != -1) {
/* Open for writing too. */
xasprintf(&destpath, "%s/%s/%s/", coll->co_base,
coll->co_colldir, coll->co_name);
st->tempfile = tempname(destpath);
if (mkdirhier(destpath, coll->co_umask) != 0) {
xasprintf(errmsg, "Cannot create directories leading "
"to \"%s\": %s", destpath, strerror(errno));
free(destpath);
status_free(st);
return (NULL);
}
free(destpath);
st->wr = stream_open_file(st->tempfile,
O_CREAT | O_TRUNC | O_WRONLY, 0644);
if (st->wr == NULL) {
xasprintf(errmsg, "Cannot create \"%s\": %s",
st->tempfile, strerror(errno));
status_free(st);
return (NULL);
}
fa = fattr_new(FT_FILE, -1);
fattr_mergedefault(fa);
fattr_umask(fa, coll->co_umask);
rv = fattr_install(fa, st->tempfile, NULL);
fattr_free(fa);
if (rv == -1) {
xasprintf(errmsg,
"Cannot set attributes for \"%s\": %s",
st->tempfile, strerror(errno));
status_free(st);
return (NULL);
}
if (scantime != st->scantime)
st->dirty = 1;
error = proto_printf(st->wr, "F %d %t\n", STATUS_VERSION,
scantime);
if (error) {
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
*errmsg = status_errmsg(st);
status_free(st);
return (NULL);
}
}
return (st);
}
/*
* Get an entry from the status file. If name is NULL, the next entry
* is returned. If name is not NULL, the entry matching this name is
* returned, or NULL if it couldn't be found. If deleteto is set to 1,
* all the entries read from the status file while looking for the
* given name are deleted.
*/
int
status_get(struct status *st, char *name, int isdirup, int deleteto,
struct statusrec **psr)
{
struct statusrec key;
struct statusrec *sr;
char *line;
int c, error;
if (st->eof)
return (0);
if (st->error)
return (-1);
if (name == NULL) {
sr = status_rd(st);
if (sr == NULL) {
if (st->error)
return (-1);
return (0);
}
*psr = sr;
return (1);
}
if (st->current != NULL) {
sr = st->current;
st->current = NULL;
} else {
sr = status_rd(st);
if (sr == NULL) {
if (st->error)
return (-1);
return (0);
}
}
key.sr_file = name;
if (isdirup)
key.sr_type = SR_DIRUP;
else
key.sr_type = SR_CHECKOUTLIVE;
c = statusrec_cmp(sr, &key);
if (c < 0) {
if (st->wr != NULL && !deleteto) {
error = status_wr(st, sr);
if (error)
return (-1);
}
/* Loop until we find the good entry. */
for (;;) {
sr = status_rdraw(st, &line);
if (sr == NULL) {
if (st->error)
return (-1);
return (0);
}
c = statusrec_cmp(sr, &key);
if (c >= 0)
break;
if (st->wr != NULL && !deleteto) {
error = status_wrraw(st, sr, line);
if (error)
return (-1);
}
}
error = statusrec_cook(sr, line);
if (error) {
st->error = STATUS_ERR_PARSE;
return (-1);
}
}
st->current = sr;
if (c != 0)
return (0);
*psr = sr;
return (1);
}
/*
* Put this entry into the status file. If an entry with the same name
* existed in the status file, it is replaced by this one, otherwise,
* the entry is added to the status file.
*/
int
status_put(struct status *st, struct statusrec *sr)
{
struct statusrec *old;
int error, ret;
ret = status_get(st, sr->sr_file, sr->sr_type == SR_DIRUP, 0, &old);
if (ret == -1)
return (-1);
if (ret) {
if (old->sr_type == SR_DIRDOWN) {
/* DirUp should never match DirDown */
assert(old->sr_type != SR_DIRUP);
if (sr->sr_type == SR_CHECKOUTLIVE ||
sr->sr_type == SR_CHECKOUTDEAD) {
/* We are replacing a directory with a file.
Delete all entries inside the directory we
are replacing. */
ret = status_get(st, sr->sr_file, 1, 1, &old);
if (ret == -1)
return (-1);
assert(ret);
}
} else
st->current = NULL;
}
st->dirty = 1;
error = status_wr(st, sr);
if (error)
return (-1);
return (0);
}
/*
* Delete the specified entry from the status file.
*/
int
status_delete(struct status *st, char *name, int isdirup)
{
struct statusrec *sr;
int ret;
ret = status_get(st, name, isdirup, 0, &sr);
if (ret == -1)
return (-1);
if (ret) {
st->current = NULL;
st->dirty = 1;
}
return (0);
}
/*
* Check whether we hit the end of file.
*/
int
status_eof(struct status *st)
{
return (st->eof);
}
/*
* Returns the error message if there was an error, otherwise returns
* NULL. The error message is allocated dynamically and needs to be
* freed by the caller after use.
*/
char *
status_errmsg(struct status *st)
{
char *errmsg;
if (!st->error)
return (NULL);
switch (st->error) {
case STATUS_ERR_READ:
xasprintf(&errmsg, "Read failure on \"%s\": %s",
st->path, strerror(st->suberror));
break;
case STATUS_ERR_WRITE:
xasprintf(&errmsg, "Write failure on \"%s\": %s",
st->tempfile, strerror(st->suberror));
break;
case STATUS_ERR_PARSE:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"Could not parse status record", st->path, st->linenum);
break;
case STATUS_ERR_UNSORTED:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"File is not sorted properly", st->path, st->linenum);
break;
case STATUS_ERR_TRUNC:
xasprintf(&errmsg, "Error in \"%s\": "
"File is truncated", st->path);
break;
case STATUS_ERR_BOGUS_DIRUP:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"\"U\" entry has no matching \"D\"", st->path, st->linenum);
break;
case STATUS_ERR_BAD_TYPE:
xasprintf(&errmsg, "Error in \"%s\": %d: "
"Invalid file type \"%c\"", st->path, st->linenum,
st->suberror);
break;
case STATUS_ERR_RENAME:
xasprintf(&errmsg, "Cannot rename \"%s\" to \"%s\": %s",
st->tempfile, st->path, strerror(st->suberror));
break;
default:
assert(0);
return (NULL);
}
return (errmsg);
}
/*
* Close the status file and free any resource associated with it.
* It is OK to pass NULL for errmsg only if the status file was
* opened read-only. If it wasn't opened read-only, status_close()
* can produce an error and errmsg is not allowed to be NULL. If
* there was no errors, errmsg is set to NULL.
*/
void
status_close(struct status *st, char **errmsg)
{
struct statusrec *sr;
char *line, *name;
int error, type;
if (st->wr != NULL) {
if (st->dirty) {
if (st->current != NULL) {
error = status_wr(st, st->current);
if (error) {
*errmsg = status_errmsg(st);
goto bad;
}
st->current = NULL;
}
/* Copy the rest of the file. */
while ((sr = status_rdraw(st, &line)) != NULL) {
error = status_wrraw(st, sr, line);
if (error) {
*errmsg = status_errmsg(st);
goto bad;
}
}
if (st->error) {
*errmsg = status_errmsg(st);
goto bad;
}
/* Close off all the open directories. */
pathcomp_finish(st->pc);
while (pathcomp_get(st->pc, &type, &name)) {
assert(type == PC_DIRUP);
error = proto_printf(st->wr, "U %s %f\n",
name, fattr_bogus);
if (error) {
st->error = STATUS_ERR_WRITE;
st->suberror = errno;
*errmsg = status_errmsg(st);
goto bad;
}
}
/* Rename tempfile. */
error = rename(st->tempfile, st->path);
if (error) {
st->error = STATUS_ERR_RENAME;
st->suberror = errno;
*errmsg = status_errmsg(st);
goto bad;
}
} else {
/* Just discard the tempfile. */
unlink(st->tempfile);
}
*errmsg = NULL;
}
status_free(st);
return;
bad:
status_free(st);
}

72
contrib/csup/status.h Normal file
View File

@ -0,0 +1,72 @@
/*-
* Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _STATUS_H_
#define _STATUS_H_
#include <time.h>
struct coll;
struct fattr;
struct status;
#define SR_DIRDOWN 0
#define SR_CHECKOUTLIVE 1
#define SR_CHECKOUTDEAD 2
#define SR_FILELIVE 3
#define SR_FILEDEAD 4
#define SR_DIRUP 5
struct statusrec {
int sr_type;
char *sr_file;
char *sr_tag;
char *sr_date;
char *sr_revnum;
char *sr_revdate;
/*
* "clientrttr" contains the attributes of the client's file if there
* is one. "serverattr" contains the attributes of the corresponding
* file on the server. In CVS mode, these are identical. But in
* checkout mode, "clientattr" represents the checked-out file while
* "serverattr" represents the corresponding RCS file on the server.
*/
struct fattr *sr_serverattr;
struct fattr *sr_clientattr;
};
struct status *status_open(struct coll *, time_t, char **);
int status_get(struct status *, char *, int, int,
struct statusrec **);
int status_put(struct status *, struct statusrec *);
int status_eof(struct status *);
char *status_errmsg(struct status *);
int status_delete(struct status *, char *, int);
void status_close(struct status *, char **);
#endif /* !_STATUS_H_ */

1080
contrib/csup/stream.c Normal file

File diff suppressed because it is too large Load Diff

72
contrib/csup/stream.h Normal file
View File

@ -0,0 +1,72 @@
/*-
* Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _STREAM_H_
#define _STREAM_H_
#include "misc.h"
/* Stream filters. */
typedef enum {
STREAM_FILTER_NULL,
STREAM_FILTER_ZLIB,
STREAM_FILTER_MD5
} stream_filter_t;
struct stream;
typedef ssize_t stream_readfn_t(void *, void *, size_t);
typedef ssize_t stream_writefn_t(void *, const void *, size_t);
typedef int stream_closefn_t(void *);
/* Convenience functions for handling file descriptors. */
stream_readfn_t stream_read_fd;
stream_writefn_t stream_write_fd;
stream_closefn_t stream_close_fd;
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_file(const char *, int, ...);
int stream_fileno(struct stream *);
ssize_t stream_read(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 *, ...)
__printflike(2, 3);
int stream_flush(struct stream *);
int stream_sync(struct stream *);
int stream_truncate(struct stream *, off_t);
int stream_truncate_rel(struct stream *, off_t);
int stream_rewind(struct stream *);
int stream_eof(struct stream *);
int stream_close(struct stream *);
int stream_filter_start(struct stream *, stream_filter_t, void *);
void stream_filter_stop(struct stream *);
#endif /* !_STREAM_H_ */

176
contrib/csup/threads.c Normal file
View File

@ -0,0 +1,176 @@
/*-
* Copyright (c) 2004-2006, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <assert.h>
#include <err.h>
#include <pthread.h>
#include <stdlib.h>
#include "misc.h"
#include "queue.h"
#include "threads.h"
/*
* This API is a wrapper around the pthread(3) API, which mainly
* allows me to wait for multiple threads to exit. We use a
* condition variable to signal a thread's death. All threads
* created with this API have a common entry/exit point, so we
* don't need to add any code in the threads themselves.
*/
/* Structure describing a thread. */
struct thread {
pthread_t thread;
void *(*start)(void *);
void *data;
struct threads *threads;
LIST_ENTRY(thread) runlist;
STAILQ_ENTRY(thread) deadlist;
};
/* A set of threads. */
struct threads {
pthread_mutex_t threads_mtx;
pthread_cond_t thread_exited;
LIST_HEAD(, thread) threads_running;
STAILQ_HEAD(, thread) threads_dead;
};
static void *thread_start(void *); /* Common entry point for threads. */
static void threads_lock(struct threads *);
static void threads_unlock(struct threads *);
static void
threads_lock(struct threads *tds)
{
int error;
error = pthread_mutex_lock(&tds->threads_mtx);
assert(!error);
}
static void
threads_unlock(struct threads *tds)
{
int error;
error = pthread_mutex_unlock(&tds->threads_mtx);
assert(!error);
}
/* Create a new set of threads. */
struct threads *
threads_new(void)
{
struct threads *tds;
tds = xmalloc(sizeof(struct threads));
pthread_mutex_init(&tds->threads_mtx, NULL);
pthread_cond_init(&tds->thread_exited, NULL);
LIST_INIT(&tds->threads_running);
STAILQ_INIT(&tds->threads_dead);
return (tds);
}
/* Create a new thread in this set. */
void
threads_create(struct threads *tds, void *(*start)(void *), void *data)
{
pthread_attr_t attr;
struct thread *td;
int error;
td = xmalloc(sizeof(struct thread));
td->threads = tds;
td->start = start;
td->data = data;
/* We don't use pthread_join() to wait for the threads to finish. */
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
threads_lock(tds);
error = pthread_create(&td->thread, &attr, thread_start, td);
if (error)
err(1, "pthread_create");
LIST_INSERT_HEAD(&tds->threads_running, td, runlist);
threads_unlock(tds);
}
/* Wait for a thread in the set to exit, and return its data pointer. */
void *
threads_wait(struct threads *tds)
{
struct thread *td;
void *data;
threads_lock(tds);
while (STAILQ_EMPTY(&tds->threads_dead)) {
assert(!LIST_EMPTY(&tds->threads_running));
pthread_cond_wait(&tds->thread_exited, &tds->threads_mtx);
}
td = STAILQ_FIRST(&tds->threads_dead);
STAILQ_REMOVE_HEAD(&tds->threads_dead, deadlist);
threads_unlock(tds);
data = td->data;
free(td);
return (data);
}
/* Free a threads set. */
void
threads_free(struct threads *tds)
{
assert(LIST_EMPTY(&tds->threads_running));
assert(STAILQ_EMPTY(&tds->threads_dead));
pthread_cond_destroy(&tds->thread_exited);
pthread_mutex_destroy(&tds->threads_mtx);
free(tds);
}
/*
* Common entry point for threads. This just calls the real start
* routine, and then signals the thread's death, after having
* removed the thread from the list.
*/
static void *
thread_start(void *data)
{
struct threads *tds;
struct thread *td;
td = data;
tds = td->threads;
td->start(td->data);
threads_lock(tds);
LIST_REMOVE(td, runlist);
STAILQ_INSERT_TAIL(&tds->threads_dead, td, deadlist);
pthread_cond_signal(&tds->thread_exited);
threads_unlock(tds);
return (NULL);
}

38
contrib/csup/threads.h Normal file
View File

@ -0,0 +1,38 @@
/*-
* Copyright (c) 2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _THREADS_H_
#define _THREADS_H_
struct threads;
struct threads *threads_new(void);
void threads_create(struct threads *, void *(*)(void *), void *);
void *threads_wait(struct threads *);
void threads_free(struct threads *);
#endif /* !_THREADS_H_ */

48
contrib/csup/token.h Normal file
View File

@ -0,0 +1,48 @@
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _TOKEN_H_
#define _TOKEN_H_
void yyerror(const char *);
int yylex(void);
int yyparse(void);
/* Parsing tokens. */
#define PT_BASE 0
#define PT_DATE 1
#define PT_HOST 2
#define PT_PREFIX 3
#define PT_RELEASE 4
#define PT_TAG 5
#define PT_UMASK 6
#define PT_COMPRESS 7
#define PT_DELETE 8
#define PT_USE_REL_SUFFIX 9
#define PT_LIST 10
#endif /* !_TOKEN_H_ */

79
contrib/csup/token.l Normal file
View File

@ -0,0 +1,79 @@
%{
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <err.h>
#include <stdlib.h>
#include <string.h>
#include "parse.h"
#include "misc.h"
#include "token.h"
#define YY_NO_UNPUT
int lineno = 1;
%}
%option noyywrap
%%
[ \t]+ ;
#.* ;
\*default { return DEFAULT; }
base { yylval.i = PT_BASE; return NAME; }
date { yylval.i = PT_DATE; return NAME; }
host { yylval.i = PT_HOST; return NAME; }
prefix { yylval.i = PT_PREFIX; return NAME; }
release { yylval.i = PT_RELEASE; return NAME; }
tag { yylval.i = PT_TAG; return NAME; }
umask { yylval.i = PT_UMASK; return NAME; }
list { yylval.i = PT_LIST; return NAME; }
= { return EQUAL; }
compress { yylval.i = PT_COMPRESS; return BOOLEAN; }
delete { yylval.i = PT_DELETE; return BOOLEAN; }
use-rel-suffix { yylval.i = PT_USE_REL_SUFFIX; return BOOLEAN; }
[a-zA-Z0-9./_-]+ {
yylval.str = strdup(yytext);
if (yylval.str == NULL)
err(1, "strdup");
return STRING;
}
\n lineno++;
%%
void
yyerror(const char *s)
{
lprintf(-1, "Parse error line %d: %s: %s\n", lineno, s, yytext);
exit(1);
}

1012
contrib/csup/updater.c Normal file

File diff suppressed because it is too large Load Diff

33
contrib/csup/updater.h Normal file
View File

@ -0,0 +1,33 @@
/*-
* Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _UPDATER_H_
#define _UPDATER_H
void *updater(void *);
#endif /* !_UPDATER_H_ */