Initial import of csup.
This commit is contained in:
commit
bb215397ee
63
contrib/csup/GNUmakefile
Normal file
63
contrib/csup/GNUmakefile
Normal 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
62
contrib/csup/Makefile
Normal 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
39
contrib/csup/README
Normal 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
34
contrib/csup/TODO
Normal 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
90
contrib/csup/attrstack.c
Normal 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
40
contrib/csup/attrstack.h
Normal 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
561
contrib/csup/config.c
Normal 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
124
contrib/csup/config.h
Normal 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
880
contrib/csup/csup.1
Normal 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
339
contrib/csup/detailer.c
Normal 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
33
contrib/csup/detailer.h
Normal 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
214
contrib/csup/diff.c
Normal 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
50
contrib/csup/diff.h
Normal 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
946
contrib/csup/fattr.c
Normal 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
115
contrib/csup/fattr.h
Normal 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
52
contrib/csup/fattr_bsd.h
Normal 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
|
||||
};
|
48
contrib/csup/fattr_posix.h
Normal file
48
contrib/csup/fattr_posix.h
Normal 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
198
contrib/csup/fixups.c
Normal 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
48
contrib/csup/fixups.h
Normal 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
199
contrib/csup/fnmatch.c
Normal 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
58
contrib/csup/fnmatch.h
Normal 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
393
contrib/csup/globtree.c
Normal 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, >, &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
45
contrib/csup/globtree.h
Normal 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
502
contrib/csup/keyword.c
Normal 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
53
contrib/csup/keyword.h
Normal 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
438
contrib/csup/lister.c
Normal 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
33
contrib/csup/lister.h
Normal 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
315
contrib/csup/main.c
Normal 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
29
contrib/csup/main.h
Normal 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
503
contrib/csup/misc.c
Normal 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
128
contrib/csup/misc.h
Normal 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
1201
contrib/csup/mux.c
Normal file
File diff suppressed because it is too large
Load Diff
45
contrib/csup/mux.h
Normal file
45
contrib/csup/mux.h
Normal 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
91
contrib/csup/parse.y
Normal 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
182
contrib/csup/pathcomp.c
Normal 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
44
contrib/csup/pathcomp.h
Normal 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
1004
contrib/csup/proto.c
Normal file
File diff suppressed because it is too large
Load Diff
49
contrib/csup/proto.h
Normal file
49
contrib/csup/proto.h
Normal 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
227
contrib/csup/queue.h
Normal 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
842
contrib/csup/status.c
Normal 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
72
contrib/csup/status.h
Normal 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
1080
contrib/csup/stream.c
Normal file
File diff suppressed because it is too large
Load Diff
72
contrib/csup/stream.h
Normal file
72
contrib/csup/stream.h
Normal 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
176
contrib/csup/threads.c
Normal 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
38
contrib/csup/threads.h
Normal 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
48
contrib/csup/token.h
Normal 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
79
contrib/csup/token.l
Normal 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
1012
contrib/csup/updater.c
Normal file
File diff suppressed because it is too large
Load Diff
33
contrib/csup/updater.h
Normal file
33
contrib/csup/updater.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user