Remove csup(1) and its associated cpasswd(1) tool.
With the move by the FreeBSD Project away from CVSup as a distribution mechanism, there is no longer a need to keep this in base. Approved by: mux (around a year ago), silence on -hackers X-MFC-after: never
This commit is contained in:
parent
3f3ce2c44a
commit
2d4f49b998
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=267863
@ -38,6 +38,11 @@
|
||||
# xargs -n1 | sort | uniq -d;
|
||||
# done
|
||||
|
||||
# 20140625: csup removal
|
||||
OLD_FILES+=usr/bin/csup
|
||||
OLD_FILES+=usr/bin/cpasswd
|
||||
OLD_FILES+=usr/share/man/man1/csup.1.gz
|
||||
OLD_FILES+=usr/share/man/man1/cpasswd.1.gz
|
||||
# 20140614: send-pr removal
|
||||
OLD_FILES+=usr/share/info/send-pr.info.gz
|
||||
OLD_FILES+=usr/share/man/man1/send-pr.1.gz
|
||||
|
@ -266,10 +266,6 @@ SUBDIR+= drill
|
||||
SUBDIR+= host
|
||||
.endif
|
||||
|
||||
.if ${MK_LIBTHR} != "no"
|
||||
SUBDIR+= csup
|
||||
.endif
|
||||
|
||||
.if ${MK_LOCATE} != "no"
|
||||
SUBDIR+= locate
|
||||
.endif
|
||||
|
@ -1,42 +0,0 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PROG= csup
|
||||
SRCS= attrstack.c \
|
||||
auth.c \
|
||||
config.c \
|
||||
detailer.c \
|
||||
diff.c \
|
||||
fattr.c \
|
||||
fixups.c \
|
||||
fnmatch.c \
|
||||
globtree.c \
|
||||
idcache.c \
|
||||
keyword.c \
|
||||
lex.rcs.c \
|
||||
lister.c \
|
||||
main.c \
|
||||
misc.c \
|
||||
mux.c \
|
||||
parse.y \
|
||||
pathcomp.c \
|
||||
proto.c \
|
||||
rcsfile.c \
|
||||
rcsparse.c \
|
||||
rsyncfile.c \
|
||||
status.c \
|
||||
stream.c \
|
||||
threads.c \
|
||||
token.l \
|
||||
updater.c
|
||||
|
||||
CFLAGS+= -I. -I${.CURDIR}
|
||||
CFLAGS+= -DHAVE_FFLAGS -DNDEBUG
|
||||
WARNS?= 1
|
||||
|
||||
DPADD= ${LIBMD} ${LIBZ} ${LIBPTHREAD}
|
||||
LDADD= -lmd -lz -lpthread
|
||||
|
||||
SCRIPTS= cpasswd.sh
|
||||
MAN= csup.1 cpasswd.1
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -1,39 +0,0 @@
|
||||
$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.
|
@ -1,28 +0,0 @@
|
||||
$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.
|
||||
- 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 shell commands sent by the server.
|
||||
- Add missing support for various CVSup options : -D, -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.
|
@ -1,90 +0,0 @@
|
||||
/*-
|
||||
* 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);
|
||||
}
|
@ -1,40 +0,0 @@
|
||||
/*-
|
||||
* 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_ */
|
@ -1,331 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007, Petar Zhivkov Petrov <pesho.petrov@gmail.com>
|
||||
* 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/param.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "auth.h"
|
||||
#include "config.h"
|
||||
#include "misc.h"
|
||||
#include "proto.h"
|
||||
#include "stream.h"
|
||||
|
||||
#define MD5_BYTES 16
|
||||
|
||||
/* This should be at least 2 * MD5_BYTES + 6 (length of "$md5$" + 1) */
|
||||
#define MD5_CHARS_MAX (2*(MD5_BYTES)+6)
|
||||
|
||||
struct srvrecord {
|
||||
char server[MAXHOSTNAMELEN];
|
||||
char client[256];
|
||||
char password[256];
|
||||
};
|
||||
|
||||
static int auth_domd5auth(struct config *);
|
||||
static int auth_lookuprecord(char *, struct srvrecord *);
|
||||
static int auth_parsetoken(char **, char *, int);
|
||||
static void auth_makesecret(struct srvrecord *, char *);
|
||||
static void auth_makeresponse(char *, char *, char *);
|
||||
static void auth_readablesum(unsigned char *, char *);
|
||||
static void auth_makechallenge(struct config *, char *);
|
||||
static int auth_checkresponse(char *, char *, char *);
|
||||
|
||||
int auth_login(struct config *config)
|
||||
{
|
||||
struct stream *s;
|
||||
char hostbuf[MAXHOSTNAMELEN];
|
||||
char *login, *host;
|
||||
int error;
|
||||
|
||||
s = config->server;
|
||||
error = gethostname(hostbuf, sizeof(hostbuf));
|
||||
hostbuf[sizeof(hostbuf) - 1] = '\0';
|
||||
if (error)
|
||||
host = NULL;
|
||||
else
|
||||
host = hostbuf;
|
||||
login = getlogin();
|
||||
proto_printf(s, "USER %s %s\n", login != NULL ? login : "?",
|
||||
host != NULL ? host : "?");
|
||||
stream_flush(s);
|
||||
error = auth_domd5auth(config);
|
||||
return (error);
|
||||
}
|
||||
|
||||
static int
|
||||
auth_domd5auth(struct config *config)
|
||||
{
|
||||
struct stream *s;
|
||||
char *line, *cmd, *challenge, *realm, *client, *srvresponse, *msg;
|
||||
char shrdsecret[MD5_CHARS_MAX], response[MD5_CHARS_MAX];
|
||||
char clichallenge[MD5_CHARS_MAX];
|
||||
struct srvrecord auth;
|
||||
int error;
|
||||
|
||||
lprintf(2, "MD5 authentication started\n");
|
||||
s = config->server;
|
||||
line = stream_getln(s, NULL);
|
||||
cmd = proto_get_ascii(&line);
|
||||
realm = proto_get_ascii(&line);
|
||||
challenge = proto_get_ascii(&line);
|
||||
if (challenge == NULL ||
|
||||
line != NULL ||
|
||||
(strcmp(cmd, "AUTHMD5") != 0)) {
|
||||
lprintf(-1, "Invalid server reply to USER\n");
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
|
||||
client = NULL;
|
||||
response[0] = clichallenge[0] = '.';
|
||||
response[1] = clichallenge[1] = 0;
|
||||
if (config->reqauth || (strcmp(challenge, ".") != 0)) {
|
||||
if (strcmp(realm, ".") == 0) {
|
||||
lprintf(-1, "Authentication required, but not enabled on server\n");
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
error = auth_lookuprecord(realm, &auth);
|
||||
if (error != STATUS_SUCCESS)
|
||||
return (error);
|
||||
client = auth.client;
|
||||
auth_makesecret(&auth, shrdsecret);
|
||||
}
|
||||
|
||||
if (strcmp(challenge, ".") != 0)
|
||||
auth_makeresponse(challenge, shrdsecret, response);
|
||||
if (config->reqauth)
|
||||
auth_makechallenge(config, clichallenge);
|
||||
proto_printf(s, "AUTHMD5 %s %s %s\n",
|
||||
client == NULL ? "." : client, response, clichallenge);
|
||||
stream_flush(s);
|
||||
line = stream_getln(s, NULL);
|
||||
cmd = proto_get_ascii(&line);
|
||||
if (cmd == NULL || line == NULL)
|
||||
goto bad;
|
||||
if (strcmp(cmd, "OK") == 0) {
|
||||
srvresponse = proto_get_ascii(&line);
|
||||
if (srvresponse == NULL)
|
||||
goto bad;
|
||||
if (config->reqauth &&
|
||||
!auth_checkresponse(srvresponse, clichallenge, shrdsecret)) {
|
||||
lprintf(-1, "Server failed to authenticate itself to client\n");
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
lprintf(2, "MD5 authentication successful\n");
|
||||
return (STATUS_SUCCESS);
|
||||
}
|
||||
if (strcmp(cmd, "!") == 0) {
|
||||
msg = proto_get_rest(&line);
|
||||
if (msg == NULL)
|
||||
goto bad;
|
||||
lprintf(-1, "Server error: %s\n", msg);
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
bad:
|
||||
lprintf(-1, "Invalid server reply to AUTHMD5\n");
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
|
||||
static int
|
||||
auth_lookuprecord(char *server, struct srvrecord *auth)
|
||||
{
|
||||
char *home, *line, authfile[FILENAME_MAX];
|
||||
struct stream *s;
|
||||
int linenum = 0, error;
|
||||
|
||||
home = getenv("HOME");
|
||||
if (home == NULL) {
|
||||
lprintf(-1, "Environment variable \"HOME\" is not set\n");
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
snprintf(authfile, sizeof(authfile), "%s/%s", home, AUTHFILE);
|
||||
s = stream_open_file(authfile, O_RDONLY);
|
||||
if (s == NULL) {
|
||||
lprintf(-1, "Could not open file %s\n", authfile);
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
|
||||
while ((line = stream_getln(s, NULL)) != NULL) {
|
||||
linenum++;
|
||||
if (line[0] == '#' || line[0] == '\0')
|
||||
continue;
|
||||
error = auth_parsetoken(&line, auth->server,
|
||||
sizeof(auth->server));
|
||||
if (error != STATUS_SUCCESS) {
|
||||
lprintf(-1, "%s:%d Missing client name\n", authfile, linenum);
|
||||
goto close;
|
||||
}
|
||||
/* Skip the rest of this line, it isn't what we are looking for. */
|
||||
if (strcasecmp(auth->server, server) != 0)
|
||||
continue;
|
||||
error = auth_parsetoken(&line, auth->client,
|
||||
sizeof(auth->client));
|
||||
if (error != STATUS_SUCCESS) {
|
||||
lprintf(-1, "%s:%d Missing password\n", authfile, linenum);
|
||||
goto close;
|
||||
}
|
||||
error = auth_parsetoken(&line, auth->password,
|
||||
sizeof(auth->password));
|
||||
if (error != STATUS_SUCCESS) {
|
||||
lprintf(-1, "%s:%d Missing comment\n", authfile, linenum);
|
||||
goto close;
|
||||
}
|
||||
stream_close(s);
|
||||
lprintf(2, "Found authentication record for server \"%s\"\n",
|
||||
server);
|
||||
return (STATUS_SUCCESS);
|
||||
}
|
||||
lprintf(-1, "Unknown server \"%s\". Fix your %s\n", server , authfile);
|
||||
memset(auth->password, 0, sizeof(auth->password));
|
||||
close:
|
||||
stream_close(s);
|
||||
return (STATUS_FAILURE);
|
||||
}
|
||||
|
||||
static int
|
||||
auth_parsetoken(char **line, char *buf, int len)
|
||||
{
|
||||
char *colon;
|
||||
|
||||
colon = strchr(*line, ':');
|
||||
if (colon == NULL)
|
||||
return (STATUS_FAILURE);
|
||||
*colon = 0;
|
||||
buf[len - 1] = 0;
|
||||
strncpy(buf, *line, len - 1);
|
||||
*line = colon + 1;
|
||||
return (STATUS_SUCCESS);
|
||||
}
|
||||
|
||||
static void
|
||||
auth_makesecret(struct srvrecord *auth, char *secret)
|
||||
{
|
||||
char *s, ch;
|
||||
const char *md5salt = "$md5$";
|
||||
unsigned char md5sum[MD5_BYTES];
|
||||
MD5_CTX md5;
|
||||
|
||||
MD5_Init(&md5);
|
||||
for (s = auth->client; *s != 0; ++s) {
|
||||
ch = tolower(*s);
|
||||
MD5_Update(&md5, &ch, 1);
|
||||
}
|
||||
MD5_Update(&md5, ":", 1);
|
||||
for (s = auth->server; *s != 0; ++s) {
|
||||
ch = tolower(*s);
|
||||
MD5_Update(&md5, &ch, 1);
|
||||
}
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, auth->password, strlen(auth->password));
|
||||
MD5_Final(md5sum, &md5);
|
||||
memset(secret, 0, MD5_CHARS_MAX);
|
||||
strcpy(secret, md5salt);
|
||||
auth_readablesum(md5sum, secret + strlen(md5salt));
|
||||
}
|
||||
|
||||
static void
|
||||
auth_makeresponse(char *challenge, char *sharedsecret, char *response)
|
||||
{
|
||||
MD5_CTX md5;
|
||||
unsigned char md5sum[MD5_BYTES];
|
||||
|
||||
MD5_Init(&md5);
|
||||
MD5_Update(&md5, sharedsecret, strlen(sharedsecret));
|
||||
MD5_Update(&md5, ":", 1);
|
||||
MD5_Update(&md5, challenge, strlen(challenge));
|
||||
MD5_Final(md5sum, &md5);
|
||||
auth_readablesum(md5sum, response);
|
||||
}
|
||||
|
||||
/*
|
||||
* Generates a challenge string which is an MD5 sum
|
||||
* of a fairly random string. The purpose is to decrease
|
||||
* the possibility of generating the same challenge
|
||||
* string (even by different clients) more then once
|
||||
* for the same server.
|
||||
*/
|
||||
static void
|
||||
auth_makechallenge(struct config *config, char *challenge)
|
||||
{
|
||||
MD5_CTX md5;
|
||||
unsigned char md5sum[MD5_BYTES];
|
||||
char buf[128];
|
||||
struct timeval tv;
|
||||
struct sockaddr_in laddr;
|
||||
pid_t pid, ppid;
|
||||
int error, addrlen;
|
||||
|
||||
gettimeofday(&tv, NULL);
|
||||
pid = getpid();
|
||||
ppid = getppid();
|
||||
srandom(tv.tv_usec ^ tv.tv_sec ^ pid);
|
||||
addrlen = sizeof(laddr);
|
||||
error = getsockname(config->socket, (struct sockaddr *)&laddr, &addrlen);
|
||||
if (error < 0) {
|
||||
memset(&laddr, 0, sizeof(laddr));
|
||||
}
|
||||
gettimeofday(&tv, NULL);
|
||||
MD5_Init(&md5);
|
||||
snprintf(buf, sizeof(buf), "%s:%jd:%ld:%ld:%d:%d",
|
||||
inet_ntoa(laddr.sin_addr), (intmax_t)tv.tv_sec, tv.tv_usec,
|
||||
random(), pid, ppid);
|
||||
MD5_Update(&md5, buf, strlen(buf));
|
||||
MD5_Final(md5sum, &md5);
|
||||
auth_readablesum(md5sum, challenge);
|
||||
}
|
||||
|
||||
static int
|
||||
auth_checkresponse(char *response, char *challenge, char *secret)
|
||||
{
|
||||
char correctresponse[MD5_CHARS_MAX];
|
||||
|
||||
auth_makeresponse(challenge, secret, correctresponse);
|
||||
return (strcmp(response, correctresponse) == 0);
|
||||
}
|
||||
|
||||
static void
|
||||
auth_readablesum(unsigned char *md5sum, char *readable)
|
||||
{
|
||||
unsigned int i;
|
||||
char *s = readable;
|
||||
|
||||
for (i = 0; i < MD5_BYTES; ++i, s+=2) {
|
||||
sprintf(s, "%.2x", md5sum[i]);
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2003-2007, Petar Zhivkov Petrov <pesho.petrov@gmail.com>
|
||||
* 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 _AUTH_H_
|
||||
#define _AUTH_H_
|
||||
|
||||
#define AUTHFILE ".csup/auth" /* user home relative */
|
||||
|
||||
struct config;
|
||||
|
||||
int auth_login(struct config *);
|
||||
|
||||
#endif /* !_AUTH_H_ */
|
||||
|
@ -1,579 +0,0 @@
|
||||
/*-
|
||||
* 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 struct coll *ovcoll;
|
||||
static int ovmask;
|
||||
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;
|
||||
ovcoll = override;
|
||||
ovmask = overridemask;
|
||||
|
||||
/* 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) {
|
||||
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;
|
||||
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 *cp, *line, *pat;
|
||||
|
||||
rd = stream_open_file(path, O_RDONLY);
|
||||
if (rd == NULL)
|
||||
return (0);
|
||||
while ((line = stream_getln(rd, NULL)) != NULL) {
|
||||
pat = line;
|
||||
for (;;) {
|
||||
/* Trim leading whitespace. */
|
||||
pat += strspn(pat, " \t");
|
||||
if (pat[0] == '\0')
|
||||
break;
|
||||
cp = strpbrk(pat, " \t");
|
||||
if (cp != NULL)
|
||||
*cp = '\0';
|
||||
pattlist_add(coll->co_refusals, pat);
|
||||
if (cp == NULL)
|
||||
break;
|
||||
pat = cp + 1;
|
||||
}
|
||||
}
|
||||
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;
|
||||
coll_override(cur_coll, ovcoll, ovmask);
|
||||
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 (!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_norsync != NULL)
|
||||
globtree_free(coll->co_norsync);
|
||||
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;
|
||||
int error, mask;
|
||||
|
||||
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:
|
||||
error = asciitoint(value, &mask, 8);
|
||||
free(value);
|
||||
if (error) {
|
||||
lprintf(-1, "Parse error in \"%s\": Invalid "
|
||||
"umask value\n", cfgfile);
|
||||
exit(1);
|
||||
}
|
||||
coll->co_umask = mask;
|
||||
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;
|
||||
case PT_NORSYNC:
|
||||
coll->co_options |= CO_NORSYNC;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Set "coll" as being the default collection. */
|
||||
void
|
||||
coll_setdef(void)
|
||||
{
|
||||
|
||||
coll_free(defaults);
|
||||
defaults = cur_coll;
|
||||
cur_coll = coll_new(defaults);
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/*-
|
||||
* 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 <sys/queue.h>
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "fattr.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;
|
||||
struct globtree *co_norsync;
|
||||
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 deletelim;
|
||||
int socket;
|
||||
struct chan *chan0;
|
||||
struct chan *chan1;
|
||||
struct stream *server;
|
||||
fattr_support_t fasupport;
|
||||
int reqauth;
|
||||
};
|
||||
|
||||
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_ */
|
@ -1,119 +0,0 @@
|
||||
.\" Copyright 1999-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.
|
||||
.\" 3. All advertising materials mentioning features or use of this software
|
||||
.\" must display the following acknowledgment:
|
||||
.\" This product includes software developed by John D. Polstra.
|
||||
.\" 4. The name of the author may not be used to endorse or promote products
|
||||
.\" derived from this software without specific prior written permission.
|
||||
.\"
|
||||
.\" 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: cvpasswd.1,v 1.4 2003/03/04 18:24:42 jdp Exp $
|
||||
.\" $FreeBSD$
|
||||
.\"
|
||||
.Dd June 27, 2007
|
||||
.Dt CPASSWD 1
|
||||
.Os FreeBSD
|
||||
.Sh NAME
|
||||
.Nm cpasswd
|
||||
.Nd scramble passwords for csup authentication
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Ar clientName
|
||||
.Ar serverName
|
||||
.Sh DESCRIPTION
|
||||
The
|
||||
.Nm
|
||||
utility creates scrambled passwords for the
|
||||
.Nm CVSup
|
||||
server's authentication database. It is invoked with a client name
|
||||
and a server name.
|
||||
.Ar ClientName
|
||||
is the name the client uses to gain access to the
|
||||
server. By convention, e-mail addresses are used for all client
|
||||
names, e.g.,
|
||||
.Ql BillyJoe@FreeBSD.org .
|
||||
Client names are case-insensitive.
|
||||
.Pp
|
||||
.Ar ServerName
|
||||
is the name of the
|
||||
.Nm CVSup
|
||||
server which the client wishes to access. By convention,
|
||||
it is the canonical fully-qualified domain name of the server, e.g.,
|
||||
.Ql CVSup.FreeBSD.ORG .
|
||||
This must agree with the server's own idea of its name. The name is
|
||||
case-insensitive.
|
||||
.Pp
|
||||
To set up authentication for a given server, one must perform the
|
||||
following steps:
|
||||
.Bl -enum
|
||||
.It
|
||||
Obtain the official
|
||||
.Ar serverName
|
||||
from the administrator of the server or from some other source.
|
||||
.It
|
||||
Choose an appropriate
|
||||
.Ar clientName .
|
||||
It should be in the form of a valid e-mail address, to make it easy
|
||||
for the server administrator to contact the user if necessary.
|
||||
.It
|
||||
Choose an arbitrary secret
|
||||
.Ar password .
|
||||
.It
|
||||
Run
|
||||
.Nm cpasswd ,
|
||||
and type in the
|
||||
.Ar password
|
||||
when prompted for it. The utility will print out a line to send
|
||||
to the server administrator, and instruct you how to modify your
|
||||
.Li $ Ns Ev HOME Ns Pa /.csup/auth
|
||||
file. You should use a secure channel to send the line to the
|
||||
server administrator.
|
||||
.El
|
||||
.Pp
|
||||
Since
|
||||
.Li $ Ns Ev HOME Ns Pa /.csup/auth
|
||||
contains passwords, you should ensure that it is not readable by
|
||||
anyone except yourself.
|
||||
.Sh FILES
|
||||
.Bl -tag -width $HOME/.csup/authxx -compact
|
||||
.It Li $ Ns Ev HOME Ns Pa /.csup/auth
|
||||
Authentication password file.
|
||||
.El
|
||||
.Sh SEE ALSO
|
||||
.Xr csup 1 ,
|
||||
.Xr cvsup 1 ,
|
||||
.Xr cvsupd 8 .
|
||||
.Bd -literal
|
||||
http://www.cvsup.org/
|
||||
.Ed
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
.An Petar Zhivkov Petrov Aq Mt pesho.petrov@gmail.com
|
||||
is the author of
|
||||
.Nm ,
|
||||
the rewrite of
|
||||
.Nm cvpasswd .
|
||||
.An John Polstra Aq Mt jdp@polstra.com
|
||||
is the author of
|
||||
.Nm CVSup .
|
||||
.Sh LEGALITIES
|
||||
CVSup is a registered trademark of John D. Polstra.
|
@ -1,135 +0,0 @@
|
||||
#! /bin/sh
|
||||
#
|
||||
# Copyright 2007. Petar Zhivkov Petrov
|
||||
# pesho.petrov@gmail.com
|
||||
#
|
||||
# $FreeBSD$
|
||||
|
||||
usage() {
|
||||
echo "Usage: $0 clientName serverName"
|
||||
echo " $0 -v"
|
||||
}
|
||||
|
||||
countChars() {
|
||||
_count="`echo "$1" | sed -e "s/[^$2]//g" | tr -d "\n" | wc -c`"
|
||||
return 0
|
||||
}
|
||||
|
||||
readPassword() {
|
||||
while [ true ]; do
|
||||
stty -echo
|
||||
read -p "$1" _password
|
||||
stty echo
|
||||
echo ""
|
||||
countChars "$_password" ":"
|
||||
if [ $_count != 0 ]; then
|
||||
echo "Sorry, password must not contain \":\" characters"
|
||||
echo ""
|
||||
else
|
||||
break
|
||||
fi
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
makeSecret() {
|
||||
local clientLower="`echo "$1" | tr "[:upper:]" "[:lower:]"`"
|
||||
local serverLower="`echo "$2" | tr "[:upper:]" "[:lower:]"`"
|
||||
local secret="`md5 -qs "$clientLower:$serverLower:$3"`"
|
||||
_secret="\$md5\$$secret"
|
||||
}
|
||||
|
||||
if [ $# -eq 1 -a "X$1" = "X-v" ]; then
|
||||
echo "Csup authentication key generator"
|
||||
usage
|
||||
exit
|
||||
elif [ $# -ne 2 ]; then
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
clientName=$1
|
||||
serverName=$2
|
||||
|
||||
#
|
||||
# Client name must contain exactly one '@' and at least one '.'.
|
||||
# It must not contain a ':'.
|
||||
#
|
||||
|
||||
countChars "$clientName" "@"
|
||||
aCount=$_count
|
||||
|
||||
countChars "$clientName" "."
|
||||
dotCount=$_count
|
||||
if [ $aCount -ne 1 -o $dotCount -eq 0 ]; then
|
||||
echo "Client name must have the form of an e-mail address,"
|
||||
echo "e.g., \"user@domain.com\""
|
||||
exit
|
||||
fi
|
||||
|
||||
countChars "$clientName" ":"
|
||||
colonCount=$_count
|
||||
if [ $colonCount -gt 0 ]; then
|
||||
echo "Client name must not contain \":\" characters"
|
||||
exit
|
||||
fi
|
||||
|
||||
#
|
||||
# Server name must not contain '@' and must have at least one '.'.
|
||||
# It also must not contain a ':'.
|
||||
#
|
||||
|
||||
countChars "$serverName" "@"
|
||||
aCount=$_count
|
||||
|
||||
countChars "$serverName" "."
|
||||
dotCount=$_count
|
||||
if [ $aCount != 0 -o $dotCount = 0 ]; then
|
||||
echo "Server name must be a fully-qualified domain name."
|
||||
echo "e.g., \"host.domain.com\""
|
||||
exit
|
||||
fi
|
||||
|
||||
countChars "$serverName" ":"
|
||||
colonCount=$_count
|
||||
if [ $colonCount -gt 0 ]; then
|
||||
echo "Server name must not contain \":\" characters"
|
||||
exit
|
||||
fi
|
||||
|
||||
#
|
||||
# Ask for password and generate secret.
|
||||
#
|
||||
|
||||
while [ true ]; do
|
||||
readPassword "Enter password: "
|
||||
makeSecret "$clientName" "$serverName" "$_password"
|
||||
secret=$_secret
|
||||
|
||||
readPassword "Enter same password again: "
|
||||
makeSecret "$clientName" "$serverName" "$_password"
|
||||
secret2=$_secret
|
||||
|
||||
if [ "X$secret" = "X$secret2" ]; then
|
||||
break
|
||||
else
|
||||
echo "Passwords did not match. Try again."
|
||||
echo ""
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Send this line to the server administrator at $serverName:"
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
echo "$clientName:$secret::"
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
echo "Be sure to send it using a secure channel!"
|
||||
echo ""
|
||||
echo "Add this line to your file \"$HOME/.csup/auth\", replacing \"XXX\""
|
||||
echo "with the password you typed in:"
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
echo "$serverName:$clientName:XXX:"
|
||||
echo "-------------------------------------------------------------------------------"
|
||||
echo "Make sure the file is readable and writable only by you!"
|
||||
echo ""
|
||||
|
@ -1,996 +0,0 @@
|
||||
.\" 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 8, 2013
|
||||
.Dt CSUP 1
|
||||
.Os FreeBSD
|
||||
.Sh NAME
|
||||
.Nm csup
|
||||
.Nd network distribution package for CVS repositories
|
||||
.Sh SYNOPSIS
|
||||
.Nm
|
||||
.Op Fl 146aksvzZ
|
||||
.Op Fl A Ar addr
|
||||
.Op Fl b Ar base
|
||||
.Op Fl c Ar collDir
|
||||
.Op Fl d Ar delLimit
|
||||
.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
|
||||
Requires the server to authenticate itself (prove its identity) to
|
||||
the client. If authentication of the server fails, the update is
|
||||
canceled. See
|
||||
.Sx AUTHENTICATION ,
|
||||
below.
|
||||
.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 d Ar delLimit
|
||||
Specifies the maximum number of files that may be deleted in a
|
||||
single update run.
|
||||
Any attempt to exceed the limit results in a fatal error.
|
||||
This can provide some protection against temporary configuration
|
||||
mistakes on the server.
|
||||
The default limit is infinity.
|
||||
.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 k
|
||||
Causes
|
||||
.Nm
|
||||
to keep the temporary copies of any incorrectly edited files, in the
|
||||
event of checksum mismatches.
|
||||
This option is for debugging, to help determine why the files were
|
||||
edited incorrectly.
|
||||
Regardless of whether this option is specified, the permanent versions
|
||||
of faulty files are replaced with correct versions obtained by
|
||||
transferring the files in their entirety.
|
||||
Such transfers are called fixups.
|
||||
.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/cvsup .
|
||||
.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.
|
||||
.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 AUTHENTICATION
|
||||
.Nm
|
||||
implements an optional authentication mechanism which can be used by the
|
||||
client and server to verify each other's identities.
|
||||
Public CVSup servers normally do not enable authentication.
|
||||
.Nm
|
||||
users may ignore this section unless they have been informed
|
||||
that authentication is required by the administrator of their server.
|
||||
.Pp
|
||||
The authentication subsystem uses a
|
||||
challenge-response protocol which is immune to packet sniffing and
|
||||
replay attacks. No passwords are sent over the network in either
|
||||
direction. Both the client and the server can independently verify
|
||||
the identities of each other.
|
||||
.Pp
|
||||
The file
|
||||
.Li $ Ns Ev HOME Ns Pa /.csup/auth
|
||||
holds the information used for authentication. This file contains a
|
||||
record for each server that the client is allowed to access. Each
|
||||
record occupies one line in the file. Lines beginning with
|
||||
.Ql #
|
||||
are ignored, as are lines containing only white space. White space is
|
||||
significant everywhere else in the file. Fields are separated by
|
||||
.Ql \&:
|
||||
characters.
|
||||
.Pp
|
||||
Each record of the file has the following form:
|
||||
.Bd -literal -offset indent
|
||||
.Sm off
|
||||
.Xo Ar serverName No : Ar clientName No :
|
||||
.Ar password No : Ar comment
|
||||
.Xc
|
||||
.Sm on
|
||||
.Ed
|
||||
.Pp
|
||||
All fields must be present even if some of them are empty.
|
||||
.Ar ServerName
|
||||
is the name of the server to which the record applies. By convention,
|
||||
it is the canonical fully-qualified domain name of the server, e.g.,
|
||||
.Ql CVSup177.FreeBSD.ORG .
|
||||
This must agree with the server's own idea of its name. The name is
|
||||
case-insensitive.
|
||||
.Pp
|
||||
.Ar ClientName
|
||||
is the name the client uses to gain access to the server. By
|
||||
convention, e-mail addresses are used for all client names, e.g.,
|
||||
.Ql BillyJoe@FreeBSD.org .
|
||||
Client names are case-insensitive.
|
||||
.Pp
|
||||
.Ar Password
|
||||
is a secret string of characters that the client uses to prove its
|
||||
identity. It may not contain any
|
||||
.Ql \&:
|
||||
or newline characters.
|
||||
.Pp
|
||||
.Ar Comment
|
||||
may contain any additional information to identify the record. It
|
||||
is not interpreted by the program.
|
||||
.Pp
|
||||
To set up authentication for a given server, one must perform the
|
||||
following steps:
|
||||
.Bl -enum
|
||||
.It
|
||||
Obtain the official
|
||||
.Ar serverName
|
||||
from the administrator of the server or from some other source.
|
||||
.It
|
||||
Choose an appropriate
|
||||
.Ar clientName .
|
||||
It should be in the form of a valid e-mail address, to make it easy
|
||||
for the server administrator to contact the user if necessary.
|
||||
.It
|
||||
Choose an arbitrary secret
|
||||
.Ar password .
|
||||
.It
|
||||
Run the
|
||||
.Nm cpasswd
|
||||
utility, and type in the
|
||||
.Ar password
|
||||
when prompted for it. The utility will print out a line to send
|
||||
to the server administrator, and instruct you how to modify your
|
||||
.Li $ Ns Ev HOME Ns Pa /.csup/auth
|
||||
file. You should use a secure channel to send the line to the
|
||||
server administrator.
|
||||
.El
|
||||
.Pp
|
||||
Since
|
||||
.Li $ Ns Ev HOME Ns Pa /.csup/auth
|
||||
contains passwords, you should ensure that it is not readable by
|
||||
anyone except yourself.
|
||||
.Pp
|
||||
Authentication works independently in both directions. The server
|
||||
administrator controls whether you must prove your identity.
|
||||
You control whether to check the server's identity, by means of the
|
||||
.Fl a
|
||||
command line option.
|
||||
.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
|
||||
.Nm tsocks
|
||||
command.
|
||||
The
|
||||
.Nm
|
||||
executable needs to be dynamically-linked with the system
|
||||
libraries for
|
||||
.Nm tsocks
|
||||
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 cpasswd 1 ,
|
||||
.Xr cvs 1 ,
|
||||
.Xr rcsintro 1 ,
|
||||
.Xr ssh 1 .
|
||||
.Sh AUTHORS
|
||||
.An -nosplit
|
||||
.An Maxime Henrion Aq Mt mux@FreeBSD.org
|
||||
is the author of
|
||||
.Nm ,
|
||||
the rewrite of
|
||||
.Nm CVSup
|
||||
in C.
|
||||
.An John Polstra Aq Mt 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.
|
@ -1,603 +0,0 @@
|
||||
/*-
|
||||
* 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 <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "detailer.h"
|
||||
#include "fixups.h"
|
||||
#include "globtree.h"
|
||||
#include "misc.h"
|
||||
#include "mux.h"
|
||||
#include "proto.h"
|
||||
#include "rcsfile.h"
|
||||
#include "rsyncfile.h"
|
||||
#include "status.h"
|
||||
#include "stream.h"
|
||||
|
||||
/* 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_co(struct detailer *, struct coll *,
|
||||
struct status *, char *);
|
||||
static int detailer_dofile_rcs(struct detailer *, struct coll *,
|
||||
char *, char *);
|
||||
static int detailer_dofile_regular(struct detailer *, char *, char *);
|
||||
static int detailer_dofile_rsync(struct detailer *, char *, char *);
|
||||
static int detailer_checkrcsattr(struct detailer *, struct coll *, char *,
|
||||
struct fattr *, int);
|
||||
int detailer_send_details(struct detailer *, struct coll *, char *,
|
||||
char *, struct fattr *);
|
||||
|
||||
void *
|
||||
detailer(void *arg)
|
||||
{
|
||||
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;
|
||||
if (coll->co_options & CO_CHECKOUTMODE)
|
||||
error = proto_printf(wr, "Y %s %s %s\n",
|
||||
fixup->f_name, coll->co_tag, coll->co_date);
|
||||
else {
|
||||
error = proto_printf(wr, "A %s\n",
|
||||
fixup->f_name);
|
||||
}
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
fixup = NULL;
|
||||
}
|
||||
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 fattr *rcsattr;
|
||||
struct stream *rd, *wr;
|
||||
char *attr, *cmd, *file, *line, *msg, *path, *target;
|
||||
int error, attic;
|
||||
|
||||
rd = d->rd;
|
||||
wr = d->wr;
|
||||
attic = 0;
|
||||
line = stream_getln(rd, NULL);
|
||||
if (line == NULL)
|
||||
return (DETAILER_ERR_READ);
|
||||
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 'I':
|
||||
case 'i':
|
||||
case 'j':
|
||||
/* Directory operations. */
|
||||
file = proto_get_ascii(&line);
|
||||
if (file == NULL || line != NULL)
|
||||
return (DETAILER_ERR_PROTO);
|
||||
error = proto_printf(wr, "%s %s\n", cmd, file);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
break;
|
||||
case 'J':
|
||||
/* Set directory attributes. */
|
||||
file = proto_get_ascii(&line);
|
||||
attr = proto_get_ascii(&line);
|
||||
if (file == NULL || line != NULL || attr == NULL)
|
||||
return (DETAILER_ERR_PROTO);
|
||||
error = proto_printf(wr, "%s %s %s\n", cmd, file, attr);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
break;
|
||||
case 'H':
|
||||
case 'h':
|
||||
/* Create a hard link. */
|
||||
file = proto_get_ascii(&line);
|
||||
target = proto_get_ascii(&line);
|
||||
if (file == NULL || target == NULL)
|
||||
return (DETAILER_ERR_PROTO);
|
||||
error = proto_printf(wr, "%s %s %s\n", cmd, file,
|
||||
target);
|
||||
break;
|
||||
case 't':
|
||||
file = proto_get_ascii(&line);
|
||||
attr = proto_get_ascii(&line);
|
||||
if (file == NULL || attr == NULL || line != NULL) {
|
||||
return (DETAILER_ERR_PROTO);
|
||||
}
|
||||
rcsattr = fattr_decode(attr);
|
||||
if (rcsattr == NULL) {
|
||||
return (DETAILER_ERR_PROTO);
|
||||
}
|
||||
error = detailer_checkrcsattr(d, coll, file, rcsattr,
|
||||
1);
|
||||
break;
|
||||
|
||||
case 'T':
|
||||
file = proto_get_ascii(&line);
|
||||
attr = proto_get_ascii(&line);
|
||||
if (file == NULL || attr == NULL || line != NULL)
|
||||
return (DETAILER_ERR_PROTO);
|
||||
rcsattr = fattr_decode(attr);
|
||||
if (rcsattr == NULL)
|
||||
return (DETAILER_ERR_PROTO);
|
||||
error = detailer_checkrcsattr(d, coll, file, rcsattr,
|
||||
0);
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
/* Add or update file. */
|
||||
file = proto_get_ascii(&line);
|
||||
if (file == NULL || line != NULL)
|
||||
return (DETAILER_ERR_PROTO);
|
||||
if (coll->co_options & CO_CHECKOUTMODE) {
|
||||
error = detailer_dofile_co(d, coll, st, file);
|
||||
} else {
|
||||
path = cvspath(coll->co_prefix, file, 0);
|
||||
rcsattr = fattr_frompath(path, FATTR_NOFOLLOW);
|
||||
error = detailer_send_details(d, coll, file,
|
||||
path, rcsattr);
|
||||
if (rcsattr != NULL)
|
||||
fattr_free(rcsattr);
|
||||
free(path);
|
||||
}
|
||||
if (error)
|
||||
return (error);
|
||||
break;
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the server to update a regular file.
|
||||
*/
|
||||
static int
|
||||
detailer_dofile_regular(struct detailer *d, char *name, char *path)
|
||||
{
|
||||
struct stream *wr;
|
||||
struct stat st;
|
||||
char md5[MD5_DIGEST_SIZE];
|
||||
int error;
|
||||
|
||||
wr = d->wr;
|
||||
error = stat(path, &st);
|
||||
/* If we don't have it or it's unaccessible, we want it again. */
|
||||
if (error) {
|
||||
proto_printf(wr, "A %s\n", name);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* If not, we want the file to be updated. */
|
||||
error = MD5_File(path, md5);
|
||||
if (error) {
|
||||
lprintf(-1, "Error reading \"%s\"\n", name);
|
||||
return (error);
|
||||
}
|
||||
error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the server to update a file with the rsync algorithm.
|
||||
*/
|
||||
static int
|
||||
detailer_dofile_rsync(struct detailer *d, char *name, char *path)
|
||||
{
|
||||
struct stream *wr;
|
||||
struct rsyncfile *rf;
|
||||
|
||||
wr = d->wr;
|
||||
rf = rsync_open(path, 0, 1);
|
||||
if (rf == NULL) {
|
||||
/* Fallback if we fail in opening it. */
|
||||
proto_printf(wr, "A %s\n", name);
|
||||
return (0);
|
||||
}
|
||||
proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf),
|
||||
rsync_blocksize(rf));
|
||||
/* Detail the blocks. */
|
||||
while (rsync_nextblock(rf) != 0)
|
||||
proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf));
|
||||
proto_printf(wr, ".\n");
|
||||
rsync_close(rf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Tell the server to update an RCS file that we have, or send it if we don't.
|
||||
*/
|
||||
static int
|
||||
detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name,
|
||||
char *path)
|
||||
{
|
||||
struct stream *wr;
|
||||
struct fattr *fa;
|
||||
struct rcsfile *rf;
|
||||
int error;
|
||||
|
||||
wr = d->wr;
|
||||
path = atticpath(coll->co_prefix, name);
|
||||
fa = fattr_frompath(path, FATTR_NOFOLLOW);
|
||||
if (fa == NULL) {
|
||||
/* We don't have it, so send request to get it. */
|
||||
error = proto_printf(wr, "A %s\n", name);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
free(path);
|
||||
return (0);
|
||||
}
|
||||
|
||||
rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag, 1);
|
||||
free(path);
|
||||
if (rf == NULL) {
|
||||
error = proto_printf(wr, "A %s\n", name);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
return (0);
|
||||
}
|
||||
/* Tell to update the RCS file. The client version details follow. */
|
||||
rcsfile_send_details(rf, wr);
|
||||
rcsfile_free(rf);
|
||||
fattr_free(fa);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st,
|
||||
char *file)
|
||||
{
|
||||
struct stream *wr;
|
||||
struct fattr *fa;
|
||||
struct statusrec *sr;
|
||||
char md5[MD5_DIGEST_SIZE];
|
||||
char *path;
|
||||
int error, ret;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
int
|
||||
detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name,
|
||||
struct fattr *server_attr, int attic)
|
||||
{
|
||||
struct fattr *client_attr;
|
||||
char *attr, *path;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* I don't think we can use the status file, since it only records file
|
||||
* attributes in cvsmode.
|
||||
*/
|
||||
client_attr = NULL;
|
||||
path = cvspath(coll->co_prefix, name, attic);
|
||||
if (path == NULL) {
|
||||
return (DETAILER_ERR_PROTO);
|
||||
}
|
||||
|
||||
if (access(path, F_OK) == 0 &&
|
||||
((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) &&
|
||||
fattr_equal(client_attr, server_attr)) {
|
||||
attr = fattr_encode(client_attr, NULL, 0);
|
||||
if (attic) {
|
||||
error = proto_printf(d->wr, "l %s %s\n", name, attr);
|
||||
} else {
|
||||
error = proto_printf(d->wr, "L %s %s\n", name, attr);
|
||||
}
|
||||
free(attr);
|
||||
free(path);
|
||||
fattr_free(client_attr);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
return (0);
|
||||
}
|
||||
/* We don't have it, so tell the server to send it. */
|
||||
error = detailer_send_details(d, coll, name, path, client_attr);
|
||||
fattr_free(client_attr);
|
||||
free(path);
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
detailer_send_details(struct detailer *d, struct coll *coll, char *name,
|
||||
char *path, struct fattr *fa)
|
||||
{
|
||||
int error;
|
||||
size_t len;
|
||||
|
||||
/*
|
||||
* Try to check if the file exists either live or dead to see if we can
|
||||
* edit it and put it live or dead, rather than receiving the entire
|
||||
* file.
|
||||
*/
|
||||
if (fa == NULL) {
|
||||
path = atticpath(coll->co_prefix, name);
|
||||
fa = fattr_frompath(path, FATTR_NOFOLLOW);
|
||||
}
|
||||
if (fa == NULL) {
|
||||
error = proto_printf(d->wr, "A %s\n", name);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
} else if (fattr_type(fa) == FT_FILE) {
|
||||
if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) {
|
||||
detailer_dofile_rcs(d, coll, name, path);
|
||||
} else if (!(coll->co_options & CO_NORSYNC) &&
|
||||
!globtree_test(coll->co_norsync, name)) {
|
||||
detailer_dofile_rsync(d, name, path);
|
||||
} else {
|
||||
detailer_dofile_regular(d, name, path);
|
||||
}
|
||||
} else {
|
||||
error = proto_printf(d->wr, "N %s\n", name);
|
||||
if (error)
|
||||
return (DETAILER_ERR_WRITE);
|
||||
}
|
||||
return (0);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*-
|
||||
* 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_ */
|
@ -1,438 +0,0 @@
|
||||
/*-
|
||||
* 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/queue.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.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
|
||||
#define MAXKEY LONG_MAX
|
||||
|
||||
/* Editing command and state. */
|
||||
struct editcmd {
|
||||
int cmd;
|
||||
long key;
|
||||
int havetext;
|
||||
int offset;
|
||||
lineno_t where;
|
||||
lineno_t count;
|
||||
lineno_t lasta;
|
||||
lineno_t lastd;
|
||||
lineno_t editline;
|
||||
/* For convenience. */
|
||||
struct keyword *keyword;
|
||||
struct diffinfo *di;
|
||||
struct stream *orig;
|
||||
struct stream *dest;
|
||||
LIST_ENTRY(editcmd) next;
|
||||
};
|
||||
|
||||
struct diffstart {
|
||||
LIST_HEAD(, editcmd) dhead;
|
||||
};
|
||||
|
||||
static int diff_geteditcmd(struct editcmd *, char *);
|
||||
static int diff_copyln(struct editcmd *, lineno_t);
|
||||
static int diff_ignoreln(struct editcmd *, lineno_t);
|
||||
static void diff_write(struct editcmd *, void *, size_t);
|
||||
static int diff_insert_edit(struct diffstart *, struct editcmd *);
|
||||
static void diff_free(struct diffstart *);
|
||||
|
||||
int
|
||||
diff_apply(struct stream *rd, struct stream *orig, struct stream *dest,
|
||||
struct keyword *keyword, struct diffinfo *di, int comode)
|
||||
{
|
||||
struct editcmd ec;
|
||||
lineno_t i;
|
||||
size_t size;
|
||||
char *line;
|
||||
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 (comode && 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 (comode && line == NULL)
|
||||
return (-1);
|
||||
/* If we got ".+", there's no ending newline. */
|
||||
if (comode && 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Reverse a diff using the same algorithm as in cvsup.
|
||||
*/
|
||||
static int
|
||||
diff_write_reverse(struct stream *dest, struct diffstart *ds)
|
||||
{
|
||||
struct editcmd *ec, *nextec;
|
||||
long editline, endline, firstoutputlinedeleted;
|
||||
long num_added, num_deleted, startline;
|
||||
int num;
|
||||
|
||||
nextec = LIST_FIRST(&ds->dhead);
|
||||
editline = 0;
|
||||
num = 0;
|
||||
while (nextec != NULL) {
|
||||
ec = nextec;
|
||||
nextec = LIST_NEXT(nextec, next);
|
||||
if (nextec == NULL)
|
||||
break;
|
||||
num++;
|
||||
num_deleted = 0;
|
||||
if (ec->havetext)
|
||||
num_deleted = ec->count;
|
||||
num_added = num_deleted + nextec->offset - ec->offset;
|
||||
if (num_deleted > 0) {
|
||||
firstoutputlinedeleted = ec->key - num_deleted + 1;
|
||||
stream_printf(dest, "d%ld %ld\n", firstoutputlinedeleted,
|
||||
num_deleted);
|
||||
if (num_added <= 0)
|
||||
continue;
|
||||
}
|
||||
if (num_added > 0) {
|
||||
stream_printf(dest, "a%ld %ld\n", ec->key, num_added);
|
||||
startline = ec->key - num_deleted + 1 + ec->offset;
|
||||
endline = startline + num_added - 1;
|
||||
|
||||
/* Copy lines from original file. First ignore some. */
|
||||
ec->editline = editline;
|
||||
diff_ignoreln(ec, startline - 1);
|
||||
diff_copyln(ec, endline);
|
||||
editline = ec->editline;
|
||||
}
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Insert a diff into the list sorted on key. Should perhaps use quicker
|
||||
* algorithms than insertion sort, but do this for now.
|
||||
*/
|
||||
static int
|
||||
diff_insert_edit(struct diffstart *ds, struct editcmd *ec)
|
||||
{
|
||||
struct editcmd *curec;
|
||||
|
||||
if (ec == NULL)
|
||||
return (0);
|
||||
|
||||
if (LIST_EMPTY(&ds->dhead)) {
|
||||
LIST_INSERT_HEAD(&ds->dhead, ec, next);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Insertion sort based on key. */
|
||||
LIST_FOREACH(curec, &ds->dhead, next) {
|
||||
if (ec->key < curec->key) {
|
||||
LIST_INSERT_BEFORE(curec, ec, next);
|
||||
return (0);
|
||||
}
|
||||
if (LIST_NEXT(curec, next) == NULL)
|
||||
break;
|
||||
}
|
||||
/* Just insert it after. */
|
||||
LIST_INSERT_AFTER(curec, ec, next);
|
||||
return (0);
|
||||
}
|
||||
|
||||
static void
|
||||
diff_free(struct diffstart *ds)
|
||||
{
|
||||
struct editcmd *ec;
|
||||
|
||||
while(!LIST_EMPTY(&ds->dhead)) {
|
||||
ec = LIST_FIRST(&ds->dhead);
|
||||
LIST_REMOVE(ec, next);
|
||||
free(ec);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Write the reverse diff from the diff in rd, and original file into
|
||||
* destination. This algorithm is the same as used in cvsup.
|
||||
*/
|
||||
int
|
||||
diff_reverse(struct stream *rd, struct stream *orig, struct stream *dest,
|
||||
struct keyword *keyword, struct diffinfo *di)
|
||||
{
|
||||
struct diffstart ds;
|
||||
struct editcmd ec, *addec, *delec;
|
||||
lineno_t i;
|
||||
char *line;
|
||||
int error, offset;
|
||||
|
||||
memset(&ec, 0, sizeof(ec));
|
||||
ec.orig = orig;
|
||||
ec.dest = dest;
|
||||
ec.keyword = keyword;
|
||||
ec.di = di;
|
||||
addec = NULL;
|
||||
delec = NULL;
|
||||
ec.havetext = 0;
|
||||
offset = 0;
|
||||
LIST_INIT(&ds.dhead);
|
||||
|
||||
/* Start with next since we need it. */
|
||||
line = stream_getln(rd, NULL);
|
||||
/* First we build up the list of diffs from input. */
|
||||
while (line != NULL) {
|
||||
error = diff_geteditcmd(&ec, line);
|
||||
if (error)
|
||||
break;
|
||||
if (ec.cmd == EC_ADD) {
|
||||
addec = xmalloc(sizeof(struct editcmd));
|
||||
*addec = ec;
|
||||
addec->havetext = 1;
|
||||
/* Ignore the lines we was supposed to add. */
|
||||
for (i = 0; i < ec.count; i++) {
|
||||
line = stream_getln(rd, NULL);
|
||||
if (line == NULL)
|
||||
return (-1);
|
||||
}
|
||||
|
||||
/* Get the next diff command if we have one. */
|
||||
addec->key = addec->where + addec->count - offset;
|
||||
if (delec != NULL &&
|
||||
delec->key == addec->key - addec->count) {
|
||||
delec->key = addec->key;
|
||||
delec->havetext = addec->havetext;
|
||||
delec->count = addec->count;
|
||||
diff_insert_edit(&ds, delec);
|
||||
free(addec);
|
||||
delec = NULL;
|
||||
addec = NULL;
|
||||
} else {
|
||||
if (delec != NULL) {
|
||||
diff_insert_edit(&ds, delec);
|
||||
}
|
||||
delec = NULL;
|
||||
addec->offset = offset;
|
||||
diff_insert_edit(&ds, addec);
|
||||
addec = NULL;
|
||||
}
|
||||
offset -= ec.count;
|
||||
} else if (ec.cmd == EC_DEL) {
|
||||
if (delec != NULL) {
|
||||
/* Update offset to our next. */
|
||||
diff_insert_edit(&ds, delec);
|
||||
delec = NULL;
|
||||
}
|
||||
delec = xmalloc(sizeof(struct editcmd));
|
||||
*delec = ec;
|
||||
delec->key = delec->where - 1 - offset;
|
||||
delec->offset = offset;
|
||||
delec->count = 0;
|
||||
delec->havetext = 0;
|
||||
/* Important to use the count we had before reset.*/
|
||||
offset += ec.count;
|
||||
}
|
||||
line = stream_getln(rd, NULL);
|
||||
}
|
||||
|
||||
while (line != NULL)
|
||||
line = stream_getln(rd, NULL);
|
||||
if (delec != NULL) {
|
||||
diff_insert_edit(&ds, delec);
|
||||
delec = NULL;
|
||||
}
|
||||
|
||||
addec = xmalloc(sizeof(struct editcmd));
|
||||
/* Should be filesize, but we set it to max value. */
|
||||
addec->key = MAXKEY;
|
||||
addec->offset = offset;
|
||||
addec->havetext = 0;
|
||||
addec->count = 0;
|
||||
diff_insert_edit(&ds, addec);
|
||||
addec = NULL;
|
||||
diff_write_reverse(dest, &ds);
|
||||
diff_free(&ds);
|
||||
stream_flush(dest);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Get an editing command from the diff. */
|
||||
static int
|
||||
diff_geteditcmd(struct editcmd *ec, char *line)
|
||||
{
|
||||
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)
|
||||
{
|
||||
size_t size;
|
||||
char *line;
|
||||
|
||||
while (ec->editline < to) {
|
||||
line = stream_getln(ec->orig, &size);
|
||||
if (line == NULL)
|
||||
return (-1);
|
||||
ec->editline++;
|
||||
diff_write(ec, line, size);
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Ignore lines from the original version of the file up to line "to". */
|
||||
static int
|
||||
diff_ignoreln(struct editcmd *ec, lineno_t to)
|
||||
{
|
||||
size_t size;
|
||||
char *line;
|
||||
|
||||
while (ec->editline < to) {
|
||||
line = stream_getln(ec->orig, &size);
|
||||
if (line == NULL)
|
||||
return (-1);
|
||||
ec->editline++;
|
||||
}
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Write a new line to the file, expanding RCS keywords appropriately. */
|
||||
static void
|
||||
diff_write(struct editcmd *ec, void *buf, size_t size)
|
||||
{
|
||||
size_t newsize;
|
||||
char *line, *newline;
|
||||
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);
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
/*-
|
||||
* 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 *, int);
|
||||
int diff_reverse(struct stream *, struct stream *,
|
||||
struct stream *, struct keyword *, struct diffinfo *);
|
||||
|
||||
#endif /* !_DIFF_H_ */
|
@ -1,981 +0,0 @@
|
||||
/*-
|
||||
* 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 <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "fattr.h"
|
||||
#include "idcache.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 a 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;
|
||||
}
|
||||
/* Initialize the uid/gid lookup cache. */
|
||||
idcache_init();
|
||||
}
|
||||
|
||||
void
|
||||
fattr_fini(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
idcache_fini();
|
||||
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;
|
||||
char *cp, *s, *username, *groupname;
|
||||
size_t len, vallen;
|
||||
mode_t mode, modemask;
|
||||
int mask, n, i;
|
||||
|
||||
username = NULL;
|
||||
groupname = NULL;
|
||||
if (support == NULL)
|
||||
mask = fa->mask;
|
||||
else
|
||||
mask = fa->mask & support[fa->type];
|
||||
mask &= ~ignore;
|
||||
if (fa->mask & FA_OWNER) {
|
||||
username = getuserbyid(fa->uid);
|
||||
if (username == NULL)
|
||||
mask &= ~FA_OWNER;
|
||||
}
|
||||
if (fa->mask & FA_GROUP) {
|
||||
groupname = getgroupbyid(fa->gid);
|
||||
if (groupname == 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(username);
|
||||
piece->extval = 1;
|
||||
piece->ext = username;
|
||||
len += snprintf(piece->len, sizeof(piece->len), "%lld",
|
||||
(long long)vallen) + vallen + 1;
|
||||
piece++;
|
||||
}
|
||||
if (mask & FA_GROUP) {
|
||||
vallen = strlen(groupname);
|
||||
piece->extval = 1;
|
||||
piece->ext = groupname;
|
||||
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), "%llx",
|
||||
(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);
|
||||
}
|
||||
|
||||
char *
|
||||
fattr_getlinktarget(const struct fattr *fa)
|
||||
{
|
||||
|
||||
return (fa->linktarget);
|
||||
}
|
||||
|
||||
/*
|
||||
* Eat the specified attribute and put it in the file attribute
|
||||
* structure. Returns NULL on error, or a pointer to the next
|
||||
* 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)
|
||||
{
|
||||
char *attrend, *attrstart, *end;
|
||||
size_t len;
|
||||
unsigned long attrlen;
|
||||
int error;
|
||||
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:
|
||||
error = getuidbyname(attrstart, &fa->uid);
|
||||
if (error)
|
||||
fa->mask &= ~FA_OWNER;
|
||||
break;
|
||||
case FA_GROUP:
|
||||
error = getgidbyname(attrstart, &fa->gid);
|
||||
if (error)
|
||||
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;
|
||||
|
||||
error = 0;
|
||||
|
||||
if (fa->mask & FA_OWNER && fa->mask & FA_GROUP)
|
||||
modemask = FA_SETIDMASK | FA_PERMMASK;
|
||||
else
|
||||
modemask = FA_PERMMASK;
|
||||
|
||||
/* We only implement fattr_makenode() for dirs for now. */
|
||||
if (fa->mask & FA_MODE)
|
||||
mode = fa->mode & modemask;
|
||||
else
|
||||
mode = 0700;
|
||||
|
||||
if (fa->type == FT_DIRECTORY)
|
||||
error = mkdir(path, mode);
|
||||
else if (fa->type == FT_SYMLINK) {
|
||||
error = symlink(fa->linktarget, path);
|
||||
} else if (fa->type == FT_CDEV) {
|
||||
lprintf(-1, "Character devices not supported!\n");
|
||||
} else if (fa->type == FT_BDEV) {
|
||||
lprintf(-1, "Block devices not supported!\n");
|
||||
}
|
||||
return (error);
|
||||
}
|
||||
|
||||
int
|
||||
fattr_delete(const char *path)
|
||||
{
|
||||
struct fattr *fa;
|
||||
int error;
|
||||
|
||||
fa = fattr_frompath(path, FATTR_NOFOLLOW);
|
||||
if (fa == NULL) {
|
||||
if (errno == ENOENT)
|
||||
return (0);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
#ifdef HAVE_FFLAGS
|
||||
/* Clear flags. */
|
||||
if (fa->mask & FA_FLAGS && fa->flags != 0) {
|
||||
fa->flags = 0;
|
||||
(void)chflags(path, fa->flags);
|
||||
}
|
||||
#endif
|
||||
|
||||
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) {
|
||||
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
|
||||
|
||||
/*
|
||||
* If it is changed from a file to a symlink, remove the file
|
||||
* and create the symlink.
|
||||
*/
|
||||
if (inplace && (fa->type == FT_SYMLINK) &&
|
||||
(old->type == FT_FILE)) {
|
||||
error = unlink(topath);
|
||||
if (error)
|
||||
goto bad;
|
||||
error = symlink(fa->linktarget, topath);
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
/* Determine whether we need to remove the target first. */
|
||||
if (!inplace && (fa->type == FT_DIRECTORY) !=
|
||||
(old->type == FT_DIRECTORY)) {
|
||||
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. */
|
||||
if (old != NULL && 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_FILETYPE)
|
||||
if (fa1->type != fa2->type)
|
||||
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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Must have to get the correct filesize sendt by the server.
|
||||
*/
|
||||
off_t
|
||||
fattr_filesize(const struct fattr *fa)
|
||||
{
|
||||
return (fa->size);
|
||||
}
|
@ -1,118 +0,0 @@
|
||||
/*-
|
||||
* 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 *);
|
||||
char *fattr_getlinktarget(const struct fattr *);
|
||||
void fattr_umask(struct fattr *, mode_t);
|
||||
void fattr_merge(struct fattr *, const struct fattr *);
|
||||
void fattr_mergedefault(struct fattr *);
|
||||
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);
|
||||
off_t fattr_filesize(const struct fattr *);
|
||||
|
||||
|
||||
#endif /* !_FATTR_H_ */
|
@ -1,52 +0,0 @@
|
||||
/*-
|
||||
* 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
|
||||
};
|
@ -1,48 +0,0 @@
|
||||
/*-
|
||||
* 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
|
||||
};
|
@ -1,199 +0,0 @@
|
||||
/*-
|
||||
* 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/queue.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "fixups.h"
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* A synchronized queue to implement fixups. The updater thread adds
|
||||
* fixup requests to the queue with fixups_put() when a checksum
|
||||
* mismatch error occurred. 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 && f->size == 0) {
|
||||
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);
|
||||
}
|
@ -1,48 +0,0 @@
|
||||
/*-
|
||||
* 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 <sys/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_ */
|
@ -1,197 +0,0 @@
|
||||
/*
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* 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);
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
/*-
|
||||
* 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.
|
||||
* 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.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*
|
||||
* @(#)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_ */
|
@ -1,393 +0,0 @@
|
||||
/*-
|
||||
* 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 <regex.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "fnmatch.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);
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
/*-
|
||||
* 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_ */
|
@ -1,421 +0,0 @@
|
||||
/*-
|
||||
* 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 <grp.h>
|
||||
#include <pthread.h>
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "idcache.h"
|
||||
#include "misc.h"
|
||||
|
||||
/*
|
||||
* Constants and data structures used to implement the thread-safe
|
||||
* group and password file caches. Cache sizes must be prime.
|
||||
*/
|
||||
#define UIDTONAME_SZ 317 /* Size of uid -> user name cache */
|
||||
#define NAMETOUID_SZ 317 /* Size of user name -> uid cache */
|
||||
#define GIDTONAME_SZ 317 /* Size of gid -> group name cache */
|
||||
#define NAMETOGID_SZ 317 /* Size of group name -> gid cache */
|
||||
|
||||
/* Node structures used to cache lookups. */
|
||||
struct uidc {
|
||||
char *name; /* user name */
|
||||
uid_t uid; /* cached uid */
|
||||
int valid; /* is this a valid or a miss entry */
|
||||
struct uidc *next; /* for collisions */
|
||||
};
|
||||
|
||||
struct gidc {
|
||||
char *name; /* group name */
|
||||
gid_t gid; /* cached gid */
|
||||
int valid; /* is this a valid or a miss entry */
|
||||
struct gidc *next; /* for collisions */
|
||||
};
|
||||
|
||||
static struct uidc **uidtoname; /* uid to user name cache */
|
||||
static struct gidc **gidtoname; /* gid to group name cache */
|
||||
static struct uidc **nametouid; /* user name to uid cache */
|
||||
static struct gidc **nametogid; /* group name to gid cache */
|
||||
|
||||
static pthread_mutex_t uid_mtx;
|
||||
static pthread_mutex_t gid_mtx;
|
||||
|
||||
static void uid_lock(void);
|
||||
static void uid_unlock(void);
|
||||
static void gid_lock(void);
|
||||
static void gid_unlock(void);
|
||||
|
||||
static uint32_t hash(const char *);
|
||||
|
||||
/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm,
|
||||
as used by ELF for hashing function names. */
|
||||
static uint32_t
|
||||
hash(const char *name)
|
||||
{
|
||||
uint32_t g, h;
|
||||
|
||||
h = 0;
|
||||
while(*name != '\0') {
|
||||
h = (h << 4) + *name++;
|
||||
if ((g = h & 0xF0000000)) {
|
||||
h ^= g >> 24;
|
||||
h &= 0x0FFFFFFF;
|
||||
}
|
||||
}
|
||||
return (h);
|
||||
}
|
||||
|
||||
static void
|
||||
uid_lock(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = pthread_mutex_lock(&uid_mtx);
|
||||
assert(!error);
|
||||
}
|
||||
|
||||
static void
|
||||
uid_unlock(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = pthread_mutex_unlock(&uid_mtx);
|
||||
assert(!error);
|
||||
}
|
||||
|
||||
static void
|
||||
gid_lock(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = pthread_mutex_lock(&gid_mtx);
|
||||
assert(!error);
|
||||
}
|
||||
|
||||
static void
|
||||
gid_unlock(void)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = pthread_mutex_unlock(&gid_mtx);
|
||||
assert(!error);
|
||||
}
|
||||
|
||||
static void
|
||||
uidc_insert(struct uidc **tbl, struct uidc *uidc, uint32_t key)
|
||||
{
|
||||
|
||||
uidc->next = tbl[key];
|
||||
tbl[key] = uidc;
|
||||
}
|
||||
|
||||
static void
|
||||
gidc_insert(struct gidc **tbl, struct gidc *gidc, uint32_t key)
|
||||
{
|
||||
|
||||
gidc->next = tbl[key];
|
||||
tbl[key] = gidc;
|
||||
}
|
||||
|
||||
/* Return the user name for this uid, or NULL if it's not found. */
|
||||
char *
|
||||
getuserbyid(uid_t uid)
|
||||
{
|
||||
struct passwd *pw;
|
||||
struct uidc *uidc, *uidc2;
|
||||
uint32_t key, key2;
|
||||
|
||||
key = uid % UIDTONAME_SZ;
|
||||
uid_lock();
|
||||
uidc = uidtoname[key];
|
||||
while (uidc != NULL) {
|
||||
if (uidc->uid == uid)
|
||||
break;
|
||||
uidc = uidc->next;
|
||||
}
|
||||
|
||||
if (uidc == NULL) {
|
||||
/* We didn't find this uid, look it up and add it. */
|
||||
uidc = xmalloc(sizeof(struct uidc));
|
||||
uidc->uid = uid;
|
||||
pw = getpwuid(uid);
|
||||
if (pw != NULL) {
|
||||
/* This uid is in the password file. */
|
||||
uidc->name = xstrdup(pw->pw_name);
|
||||
uidc->valid = 1;
|
||||
/* Also add it to the name -> gid table. */
|
||||
uidc2 = xmalloc(sizeof(struct uidc));
|
||||
uidc2->uid = uid;
|
||||
uidc2->name = uidc->name; /* We reuse the pointer. */
|
||||
uidc2->valid = 1;
|
||||
key2 = hash(uidc->name) % NAMETOUID_SZ;
|
||||
uidc_insert(nametouid, uidc2, key2);
|
||||
} else {
|
||||
/* Add a miss entry for this uid. */
|
||||
uidc->name = NULL;
|
||||
uidc->valid = 0;
|
||||
}
|
||||
uidc_insert(uidtoname, uidc, key);
|
||||
}
|
||||
/* It is safe to unlock here since the cache structure
|
||||
is not going to get freed or changed. */
|
||||
uid_unlock();
|
||||
return (uidc->name);
|
||||
}
|
||||
|
||||
/* Return the group name for this gid, or NULL if it's not found. */
|
||||
char *
|
||||
getgroupbyid(gid_t gid)
|
||||
{
|
||||
struct group *gr;
|
||||
struct gidc *gidc, *gidc2;
|
||||
uint32_t key, key2;
|
||||
|
||||
key = gid % GIDTONAME_SZ;
|
||||
gid_lock();
|
||||
gidc = gidtoname[key];
|
||||
while (gidc != NULL) {
|
||||
if (gidc->gid == gid)
|
||||
break;
|
||||
gidc = gidc->next;
|
||||
}
|
||||
|
||||
if (gidc == NULL) {
|
||||
/* We didn't find this gid, look it up and add it. */
|
||||
gidc = xmalloc(sizeof(struct gidc));
|
||||
gidc->gid = gid;
|
||||
gr = getgrgid(gid);
|
||||
if (gr != NULL) {
|
||||
/* This gid is in the group file. */
|
||||
gidc->name = xstrdup(gr->gr_name);
|
||||
gidc->valid = 1;
|
||||
/* Also add it to the name -> gid table. */
|
||||
gidc2 = xmalloc(sizeof(struct gidc));
|
||||
gidc2->gid = gid;
|
||||
gidc2->name = gidc->name; /* We reuse the pointer. */
|
||||
gidc2->valid = 1;
|
||||
key2 = hash(gidc->name) % NAMETOGID_SZ;
|
||||
gidc_insert(nametogid, gidc2, key2);
|
||||
} else {
|
||||
/* Add a miss entry for this gid. */
|
||||
gidc->name = NULL;
|
||||
gidc->valid = 0;
|
||||
}
|
||||
gidc_insert(gidtoname, gidc, key);
|
||||
}
|
||||
/* It is safe to unlock here since the cache structure
|
||||
is not going to get freed or changed. */
|
||||
gid_unlock();
|
||||
return (gidc->name);
|
||||
}
|
||||
|
||||
/* Finds the uid for this user name. If it's found, the gid is stored
|
||||
in *uid and 0 is returned. Otherwise, -1 is returned. */
|
||||
int
|
||||
getuidbyname(const char *name, uid_t *uid)
|
||||
{
|
||||
struct passwd *pw;
|
||||
struct uidc *uidc, *uidc2;
|
||||
uint32_t key, key2;
|
||||
|
||||
uid_lock();
|
||||
key = hash(name) % NAMETOUID_SZ;
|
||||
uidc = nametouid[key];
|
||||
while (uidc != NULL) {
|
||||
if (strcmp(uidc->name, name) == 0)
|
||||
break;
|
||||
uidc = uidc->next;
|
||||
}
|
||||
|
||||
if (uidc == NULL) {
|
||||
uidc = xmalloc(sizeof(struct uidc));
|
||||
uidc->name = xstrdup(name);
|
||||
pw = getpwnam(name);
|
||||
if (pw != NULL) {
|
||||
/* This user name is in the password file. */
|
||||
uidc->valid = 1;
|
||||
uidc->uid = pw->pw_uid;
|
||||
/* Also add it to the uid -> name table. */
|
||||
uidc2 = xmalloc(sizeof(struct uidc));
|
||||
uidc2->name = uidc->name; /* We reuse the pointer. */
|
||||
uidc2->uid = uidc->uid;
|
||||
uidc2->valid = 1;
|
||||
key2 = uidc2->uid % UIDTONAME_SZ;
|
||||
uidc_insert(uidtoname, uidc2, key2);
|
||||
} else {
|
||||
/* Add a miss entry for this user name. */
|
||||
uidc->valid = 0;
|
||||
uidc->uid = (uid_t)-1; /* Should not be accessed. */
|
||||
}
|
||||
uidc_insert(nametouid, uidc, key);
|
||||
}
|
||||
/* It is safe to unlock here since the cache structure
|
||||
is not going to get freed or changed. */
|
||||
uid_unlock();
|
||||
if (!uidc->valid)
|
||||
return (-1);
|
||||
*uid = uidc->uid;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Finds the gid for this group name. If it's found, the gid is stored
|
||||
in *gid and 0 is returned. Otherwise, -1 is returned. */
|
||||
int
|
||||
getgidbyname(const char *name, gid_t *gid)
|
||||
{
|
||||
struct group *gr;
|
||||
struct gidc *gidc, *gidc2;
|
||||
uint32_t key, key2;
|
||||
|
||||
gid_lock();
|
||||
key = hash(name) % NAMETOGID_SZ;
|
||||
gidc = nametogid[key];
|
||||
while (gidc != NULL) {
|
||||
if (strcmp(gidc->name, name) == 0)
|
||||
break;
|
||||
gidc = gidc->next;
|
||||
}
|
||||
|
||||
if (gidc == NULL) {
|
||||
gidc = xmalloc(sizeof(struct gidc));
|
||||
gidc->name = xstrdup(name);
|
||||
gr = getgrnam(name);
|
||||
if (gr != NULL) {
|
||||
/* This group name is in the group file. */
|
||||
gidc->gid = gr->gr_gid;
|
||||
gidc->valid = 1;
|
||||
/* Also add it to the gid -> name table. */
|
||||
gidc2 = xmalloc(sizeof(struct gidc));
|
||||
gidc2->name = gidc->name; /* We reuse the pointer. */
|
||||
gidc2->gid = gidc->gid;
|
||||
gidc2->valid = 1;
|
||||
key2 = gidc2->gid % GIDTONAME_SZ;
|
||||
gidc_insert(gidtoname, gidc2, key2);
|
||||
} else {
|
||||
/* Add a miss entry for this group name. */
|
||||
gidc->gid = (gid_t)-1; /* Should not be accessed. */
|
||||
gidc->valid = 0;
|
||||
}
|
||||
gidc_insert(nametogid, gidc, key);
|
||||
}
|
||||
/* It is safe to unlock here since the cache structure
|
||||
is not going to get freed or changed. */
|
||||
gid_unlock();
|
||||
if (!gidc->valid)
|
||||
return (-1);
|
||||
*gid = gidc->gid;
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Initialize the cache structures. */
|
||||
void
|
||||
idcache_init(void)
|
||||
{
|
||||
|
||||
pthread_mutex_init(&uid_mtx, NULL);
|
||||
pthread_mutex_init(&gid_mtx, NULL);
|
||||
uidtoname = xmalloc(UIDTONAME_SZ * sizeof(struct uidc *));
|
||||
gidtoname = xmalloc(GIDTONAME_SZ * sizeof(struct gidc *));
|
||||
nametouid = xmalloc(NAMETOUID_SZ * sizeof(struct uidc *));
|
||||
nametogid = xmalloc(NAMETOGID_SZ * sizeof(struct gidc *));
|
||||
memset(uidtoname, 0, UIDTONAME_SZ * sizeof(struct uidc *));
|
||||
memset(gidtoname, 0, GIDTONAME_SZ * sizeof(struct gidc *));
|
||||
memset(nametouid, 0, NAMETOUID_SZ * sizeof(struct uidc *));
|
||||
memset(nametogid, 0, NAMETOGID_SZ * sizeof(struct gidc *));
|
||||
}
|
||||
|
||||
/* Cleanup the cache structures. */
|
||||
void
|
||||
idcache_fini(void)
|
||||
{
|
||||
struct uidc *uidc, *uidc2;
|
||||
struct gidc *gidc, *gidc2;
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < UIDTONAME_SZ; i++) {
|
||||
uidc = uidtoname[i];
|
||||
while (uidc != NULL) {
|
||||
if (uidc->name != NULL) {
|
||||
assert(uidc->valid);
|
||||
free(uidc->name);
|
||||
}
|
||||
uidc2 = uidc->next;
|
||||
free(uidc);
|
||||
uidc = uidc2;
|
||||
}
|
||||
}
|
||||
free(uidtoname);
|
||||
for (i = 0; i < NAMETOUID_SZ; i++) {
|
||||
uidc = nametouid[i];
|
||||
while (uidc != NULL) {
|
||||
assert(uidc->name != NULL);
|
||||
/* If it's a valid entry, it has been added to both the
|
||||
uidtoname and nametouid tables, and the name pointer
|
||||
has been reused for both entries. Thus, the name
|
||||
pointer has already been freed in the loop above. */
|
||||
if (!uidc->valid)
|
||||
free(uidc->name);
|
||||
uidc2 = uidc->next;
|
||||
free(uidc);
|
||||
uidc = uidc2;
|
||||
}
|
||||
}
|
||||
free(nametouid);
|
||||
for (i = 0; i < GIDTONAME_SZ; i++) {
|
||||
gidc = gidtoname[i];
|
||||
while (gidc != NULL) {
|
||||
if (gidc->name != NULL) {
|
||||
assert(gidc->valid);
|
||||
free(gidc->name);
|
||||
}
|
||||
gidc2 = gidc->next;
|
||||
free(gidc);
|
||||
gidc = gidc2;
|
||||
}
|
||||
}
|
||||
free(gidtoname);
|
||||
for (i = 0; i < NAMETOGID_SZ; i++) {
|
||||
gidc = nametogid[i];
|
||||
while (gidc != NULL) {
|
||||
assert(gidc->name != NULL);
|
||||
/* See above comment. */
|
||||
if (!gidc->valid)
|
||||
free(gidc->name);
|
||||
gidc2 = gidc->next;
|
||||
free(gidc);
|
||||
gidc = gidc2;
|
||||
}
|
||||
}
|
||||
free(nametogid);
|
||||
pthread_mutex_destroy(&uid_mtx);
|
||||
pthread_mutex_destroy(&gid_mtx);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*-
|
||||
* 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 _IDCACHE_H_
|
||||
#define _IDCACHE_H_
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
void idcache_init(void);
|
||||
void idcache_fini(void);
|
||||
|
||||
char *getuserbyid(uid_t);
|
||||
char *getgroupbyid(gid_t);
|
||||
int getuidbyname(const char *, uid_t *);
|
||||
int getgidbyname(const char *, gid_t *);
|
||||
|
||||
#endif /* !_IDCACHE_H_ */
|
@ -1,526 +0,0 @@
|
||||
/*-
|
||||
* 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/queue.h>
|
||||
|
||||
#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 "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);
|
||||
}
|
||||
|
||||
const char *
|
||||
keyword_encode_expand(int expand)
|
||||
{
|
||||
|
||||
switch (expand) {
|
||||
case EXPAND_DEFAULT:
|
||||
return (".");
|
||||
case EXPAND_KEYVALUE:
|
||||
return ("kv");
|
||||
case EXPAND_KEYVALUELOCKER:
|
||||
return ("kvl");
|
||||
case EXPAND_KEY:
|
||||
return ("k");
|
||||
case EXPAND_OLD:
|
||||
return ("o");
|
||||
case EXPAND_BINARY:
|
||||
return ("b");
|
||||
case EXPAND_VALUE:
|
||||
return ("v");
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
void
|
||||
keyword_free(struct keyword *keyword)
|
||||
{
|
||||
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);
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
/*-
|
||||
* 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 *);
|
||||
const char *keyword_encode_expand(int);
|
||||
int keyword_alias(struct keyword *, const char *, const char *);
|
||||
int keyword_enable(struct keyword *, const char *);
|
||||
int keyword_disable(struct keyword *, const char *);
|
||||
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_ */
|
File diff suppressed because it is too large
Load Diff
@ -1,569 +0,0 @@
|
||||
/*-
|
||||
* 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 *);
|
||||
static int lister_dorcsfile(struct lister *, struct coll *,
|
||||
struct statusrec *);
|
||||
static int lister_dorcsdead(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;
|
||||
size_t i;
|
||||
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;
|
||||
case SR_FILEDEAD:
|
||||
if (depth < prunedepth) {
|
||||
if (!(coll->co_options & CO_CHECKOUTMODE)) {
|
||||
error = lister_dorcsdead(l, coll, sr);
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case SR_FILELIVE:
|
||||
if (depth < prunedepth) {
|
||||
if (!(coll->co_options & CO_CHECKOUTMODE)) {
|
||||
error = lister_dorcsfile(l, coll, sr);
|
||||
if (error)
|
||||
goto bad;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ret == -1) {
|
||||
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:
|
||||
for (i = 0; i < attrstack_size(as); i++) {
|
||||
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 rcs file live entry found in the status file. */
|
||||
static int
|
||||
lister_dorcsfile(struct lister *l, struct coll *coll, struct statusrec *sr)
|
||||
{
|
||||
struct config *config;
|
||||
struct stream *wr;
|
||||
const struct fattr *sendattr;
|
||||
struct fattr *fa;
|
||||
char *path, *spath;
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
if (!globtree_test(coll->co_filefilter, sr->sr_file))
|
||||
return (0);
|
||||
config = l->config;
|
||||
wr = l->wr;
|
||||
if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
|
||||
path = cvspath(coll->co_prefix, sr->sr_file, 0);
|
||||
if (path == NULL) {
|
||||
spath = coll_statuspath(coll);
|
||||
xasprintf(&l->errmsg, "Error in \"%s\": "
|
||||
"Invalid filename \"%s\"", spath, sr->sr_file);
|
||||
free(spath);
|
||||
return (LISTER_ERR_STATUS);
|
||||
}
|
||||
fa = fattr_frompath(path, FATTR_NOFOLLOW);
|
||||
free(path);
|
||||
} else
|
||||
fa = sr->sr_clientattr;
|
||||
if (fa != NULL && fattr_equal(fa, sr->sr_clientattr)) {
|
||||
/*
|
||||
* If the file is an RCS file, we use "loose" equality, so sizes
|
||||
* may disagress because of differences in whitespace.
|
||||
*/
|
||||
if (isrcs(sr->sr_file, &len) &&
|
||||
!(coll->co_options & CO_NORCS) &&
|
||||
!(coll->co_options & CO_STRICTCHECKRCS)) {
|
||||
fattr_maskout(fa, FA_SIZE);
|
||||
}
|
||||
sendattr = fa;
|
||||
} else {
|
||||
/*
|
||||
* If different, the user may have changed it, so we report
|
||||
* bogus attributes to force a full comparison.
|
||||
*/
|
||||
sendattr = fattr_bogus;
|
||||
}
|
||||
error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr,
|
||||
config->fasupport, coll->co_attrignore);
|
||||
if (error)
|
||||
return (LISTER_ERR_WRITE);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/* Handle a checkout dead entry found in the status file. */
|
||||
static int
|
||||
lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/* Handle a rcs file dead entry found in the status file. */
|
||||
static int
|
||||
lister_dorcsdead(struct lister *l, struct coll *coll, struct statusrec *sr)
|
||||
{
|
||||
struct config *config;
|
||||
struct stream *wr;
|
||||
const struct fattr *sendattr;
|
||||
struct fattr *fa;
|
||||
char *path, *spath;
|
||||
size_t len;
|
||||
int error;
|
||||
|
||||
if (!globtree_test(coll->co_filefilter, sr->sr_file))
|
||||
return (0);
|
||||
config = l->config;
|
||||
wr = l->wr;
|
||||
if (!(coll->co_options & CO_TRUSTSTATUSFILE)) {
|
||||
path = cvspath(coll->co_prefix, sr->sr_file, 1);
|
||||
if (path == NULL) {
|
||||
spath = coll_statuspath(coll);
|
||||
xasprintf(&l->errmsg, "Error in \"%s\": "
|
||||
"Invalid filename \"%s\"", spath, sr->sr_file);
|
||||
free(spath);
|
||||
return (LISTER_ERR_STATUS);
|
||||
}
|
||||
fa = fattr_frompath(path, FATTR_NOFOLLOW);
|
||||
free(path);
|
||||
} else
|
||||
fa = sr->sr_clientattr;
|
||||
if (fattr_equal(fa, sr->sr_clientattr)) {
|
||||
/*
|
||||
* If the file is an RCS file, we use "loose" equality, so sizes
|
||||
* may disagress because of differences in whitespace.
|
||||
*/
|
||||
if (isrcs(sr->sr_file, &len) &&
|
||||
!(coll->co_options & CO_NORCS) &&
|
||||
!(coll->co_options & CO_STRICTCHECKRCS)) {
|
||||
fattr_maskout(fa, FA_SIZE);
|
||||
}
|
||||
sendattr = fa;
|
||||
} else {
|
||||
/*
|
||||
* If different, the user may have changed it, so we report
|
||||
* bogus attributes to force a full comparison.
|
||||
*/
|
||||
sendattr = fattr_bogus;
|
||||
}
|
||||
error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr,
|
||||
config->fasupport, coll->co_attrignore);
|
||||
if (error)
|
||||
return (LISTER_ERR_WRITE);
|
||||
return (0);
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
/*-
|
||||
* 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_ */
|
@ -1,346 +0,0 @@
|
||||
/*-
|
||||
* 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",
|
||||
"Require server to authenticate itself to us");
|
||||
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, "-d delLimit",
|
||||
"Allow at most \"delLimit\" file deletions (default unlimited)");
|
||||
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, "-k",
|
||||
"Keep bad temporary files when fixups are required");
|
||||
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;
|
||||
int family, error, lockfd, lflag, overridemask;
|
||||
int c, i, deletelim, port, retries, status, reqauth;
|
||||
time_t nexttry;
|
||||
|
||||
error = 0;
|
||||
family = PF_UNSPEC;
|
||||
deletelim = -1;
|
||||
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;
|
||||
reqauth = 0;
|
||||
|
||||
while ((c = getopt(argc, argv,
|
||||
"146aA:b:c:d:gh:i:kl: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':
|
||||
/* Require server authentication */
|
||||
reqauth = 1;
|
||||
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 'd':
|
||||
error = asciitoint(optarg, &deletelim, 0);
|
||||
if (error || deletelim < 0) {
|
||||
lprintf(-1, "Invalid deletion limit\n");
|
||||
usage(argv0);
|
||||
return (1);
|
||||
}
|
||||
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 'k':
|
||||
override->co_options |= CO_KEEPBADFILES;
|
||||
overridemask |= CO_KEEPBADFILES;
|
||||
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':
|
||||
error = asciitoint(optarg, &verbose, 0);
|
||||
if (error) {
|
||||
lprintf(-1, "Invalid verbosity\n");
|
||||
usage(argv0);
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'p':
|
||||
/* Use specified server port. */
|
||||
error = asciitoint(optarg, &port, 0);
|
||||
if (error) {
|
||||
lprintf(-1, "Invalid server port\n");
|
||||
usage(argv0);
|
||||
return (1);
|
||||
}
|
||||
if (port <= 0 || port >= 65536) {
|
||||
lprintf(-1, "Invalid port %d\n", port);
|
||||
return (1);
|
||||
}
|
||||
if (port < 1024) {
|
||||
lprintf(-1, "Reserved port %d not permitted\n",
|
||||
port);
|
||||
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':
|
||||
error = asciitoint(optarg, &retries, 0);
|
||||
if (error || 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);
|
||||
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 (config_checkcolls(config) == 0) {
|
||||
lprintf(-1, "No collections selected\n");
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (laddr != NULL) {
|
||||
config->laddr = laddr;
|
||||
config->laddrlen = laddrlen;
|
||||
}
|
||||
config->deletelim = deletelim;
|
||||
config->reqauth = reqauth;
|
||||
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);
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
/*-
|
||||
* 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;
|
@ -1,644 +0,0 @@
|
||||
/*-
|
||||
* 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 <assert.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.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
|
||||
asciitoint(const char *s, int *val, int base)
|
||||
{
|
||||
char *end;
|
||||
long longval;
|
||||
|
||||
errno = 0;
|
||||
longval = strtol(s, &end, base);
|
||||
if (errno || *end != '\0')
|
||||
return (-1);
|
||||
if (longval > INT_MAX || longval < INT_MIN) {
|
||||
errno = ERANGE;
|
||||
return (-1);
|
||||
}
|
||||
*val = longval;
|
||||
return (0);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
const char *
|
||||
pathlast(const char *path)
|
||||
{
|
||||
const 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);
|
||||
}
|
||||
|
||||
/*
|
||||
* Checks if a file is an RCS file.
|
||||
*/
|
||||
int
|
||||
isrcs(const char *file, size_t *len)
|
||||
{
|
||||
const char *cp;
|
||||
|
||||
if (file[0] == '/')
|
||||
return (0);
|
||||
cp = file;
|
||||
while ((cp = strstr(cp, "..")) != NULL) {
|
||||
if (cp == file || cp[2] == '\0' ||
|
||||
(cp[-1] == '/' && cp[2] == '/'))
|
||||
return (0);
|
||||
cp += 2;
|
||||
}
|
||||
*len = strlen(file);
|
||||
if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') {
|
||||
return (0);
|
||||
}
|
||||
|
||||
return (1);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a buffer allocated with malloc() containing the absolute
|
||||
* pathname to the checkout file made from the prefix and the path
|
||||
* of the corresponding RCS file relatively to the prefix. If the
|
||||
* filename is not an RCS filename, NULL will be returned.
|
||||
*/
|
||||
char *
|
||||
checkoutpath(const char *prefix, const char *file)
|
||||
{
|
||||
char *path;
|
||||
size_t len;
|
||||
|
||||
if (!isrcs(file, &len))
|
||||
return (NULL);
|
||||
xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file);
|
||||
return (path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns a cvs path allocated with malloc() containing absolute pathname to a
|
||||
* file in cvs mode which can reside in the attic. XXX: filename has really no
|
||||
* restrictions.
|
||||
*/
|
||||
char *
|
||||
cvspath(const char *prefix, const char *file, int attic)
|
||||
{
|
||||
const char *last;
|
||||
char *path;
|
||||
|
||||
last = pathlast(file);
|
||||
if (attic)
|
||||
xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file),
|
||||
file, last);
|
||||
else
|
||||
xasprintf(&path, "%s/%s", prefix, file);
|
||||
|
||||
return (path);
|
||||
}
|
||||
|
||||
/*
|
||||
* Regular or attic path if regular fails.
|
||||
* XXX: This should perhaps also check if the Attic file exists too, and return
|
||||
* NULL if not.
|
||||
*/
|
||||
char *
|
||||
atticpath(const char *prefix, const char *file)
|
||||
{
|
||||
char *path;
|
||||
|
||||
path = cvspath(prefix, file, 0);
|
||||
if (access(path, F_OK) != 0) {
|
||||
free(path);
|
||||
path = cvspath(prefix, file, 1);
|
||||
}
|
||||
return (path);
|
||||
}
|
||||
|
||||
int
|
||||
mkdirhier(char *path, mode_t mask)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
/* Compare two revisions. */
|
||||
int
|
||||
rcsnum_cmp(char *revision1, char *revision2)
|
||||
{
|
||||
char *ptr1, *ptr2, *dot1, *dot2;
|
||||
int num1len, num2len, ret;
|
||||
|
||||
ptr1 = revision1;
|
||||
ptr2 = revision2;
|
||||
while (*ptr1 != '\0' && *ptr2 != '\0') {
|
||||
dot1 = strchr(ptr1, '.');
|
||||
dot2 = strchr(ptr2, '.');
|
||||
if (dot1 == NULL)
|
||||
dot1 = strchr(ptr1, '\0');
|
||||
if (dot2 == NULL)
|
||||
dot2 = strchr(ptr2, '\0');
|
||||
|
||||
num1len = dot1 - ptr1;
|
||||
num2len = dot2 - ptr2;
|
||||
/* Check the distance between each, showing how many digits */
|
||||
if (num1len > num2len)
|
||||
return (1);
|
||||
else if (num1len < num2len)
|
||||
return (-1);
|
||||
|
||||
/* Equal distance means we must check each character. */
|
||||
ret = strncmp(ptr1, ptr2, num1len);
|
||||
if (ret != 0)
|
||||
return (ret);
|
||||
ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1;
|
||||
ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2;
|
||||
}
|
||||
|
||||
if (*ptr1 != '\0' && *ptr2 == '\0')
|
||||
return (1);
|
||||
if (*ptr1 == '\0' && *ptr2 != '\0')
|
||||
return (-1);
|
||||
return (0);
|
||||
|
||||
}
|
||||
|
||||
/* Returns 0 if a rcsrev is not a trunk revision number. */
|
||||
int
|
||||
rcsrev_istrunk(char *revnum)
|
||||
{
|
||||
char *tmp;
|
||||
|
||||
tmp = strchr(revnum, '.');
|
||||
tmp++;
|
||||
if (strchr(tmp, '.') != NULL)
|
||||
return (0);
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Return prefix of rcsfile. */
|
||||
char *
|
||||
rcsrev_prefix(char *revnum)
|
||||
{
|
||||
char *modrev, *pos;
|
||||
|
||||
modrev = xstrdup(revnum);
|
||||
pos = strrchr(modrev, '.');
|
||||
if (pos == NULL) {
|
||||
free(modrev);
|
||||
return (NULL);
|
||||
}
|
||||
*pos = '\0';
|
||||
return (modrev);
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/*-
|
||||
* 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 <sys/types.h>
|
||||
|
||||
#ifdef __FreeBSD__
|
||||
#include <md5.h>
|
||||
#define MD5_DIGEST_LENGTH 16
|
||||
#define MD5_Init MD5Init
|
||||
#define MD5_Final MD5Final
|
||||
#define MD5_Update MD5Update
|
||||
#else
|
||||
#include <openssl/md5.h>
|
||||
#endif
|
||||
|
||||
/* 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 asciitoint(const char *, int *, int);
|
||||
int lprintf(int, const char *, ...) __printflike(2, 3);
|
||||
int MD5_File(char *, char *);
|
||||
void MD5_End(char *, MD5_CTX *);
|
||||
int rcsdatetotm(const char *, struct tm *);
|
||||
time_t rcsdatetotime(const char *);
|
||||
int pathcmp(const char *, const char *);
|
||||
size_t commonpathlength(const char *, size_t, const char *, size_t);
|
||||
const char *pathlast(const char *);
|
||||
int isrcs(const char *, size_t *);
|
||||
char *checkoutpath(const char *, const char *);
|
||||
char *cvspath(const char *, const char *, int);
|
||||
char *atticpath(const char *, const char *);
|
||||
char *path_prefix(char *);
|
||||
char *path_first(char *);
|
||||
int mkdirhier(char *, mode_t);
|
||||
char *tempname(const char *);
|
||||
void *xmalloc(size_t);
|
||||
void *xrealloc(void *, size_t);
|
||||
char *xstrdup(const char *);
|
||||
int xasprintf(char **, const char *, ...) __printflike(2, 3);
|
||||
int rcsnum_cmp(char *, char *);
|
||||
int rcsrev_istrunk(char *);
|
||||
char *rcsrev_prefix(char *);
|
||||
|
||||
struct pattlist *pattlist_new(void);
|
||||
void pattlist_add(struct pattlist *, const char *);
|
||||
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_ */
|
1202
usr.bin/csup/mux.c
1202
usr.bin/csup/mux.c
File diff suppressed because it is too large
Load Diff
@ -1,45 +0,0 @@
|
||||
/*-
|
||||
* 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_ */
|
@ -1,91 +0,0 @@
|
||||
%{
|
||||
/*-
|
||||
* 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); }
|
||||
;
|
||||
|
||||
%%
|
@ -1,182 +0,0 @@
|
||||
/*-
|
||||
* 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);
|
||||
}
|
@ -1,44 +0,0 @@
|
||||
/*-
|
||||
* 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 */
|
1006
usr.bin/csup/proto.c
1006
usr.bin/csup/proto.c
File diff suppressed because it is too large
Load Diff
@ -1,50 +0,0 @@
|
||||
/*-
|
||||
* 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_sizet(char **, size_t *, int);
|
||||
int proto_get_time(char **, time_t *);
|
||||
|
||||
#endif /* !_PROTO_H_ */
|
File diff suppressed because it is too large
Load Diff
@ -1,74 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _RCSFILE_H_
|
||||
#define _RCSFILE_H_
|
||||
|
||||
/* RCSFILE fields. */
|
||||
#define RCSFILE_HEAD 0
|
||||
#define RCSFILE_BRANCH 1
|
||||
#define RCSFILE_STRICT 2
|
||||
#define RCSFILE_COMMENT 3
|
||||
#define RCSFILE_EXPAND 4
|
||||
#define RCSFILE_DESC 5
|
||||
|
||||
struct rcsfile;
|
||||
struct delta;
|
||||
struct stream;
|
||||
|
||||
/* Fetching, sending and writing an RCS file. */
|
||||
struct rcsfile *rcsfile_frompath(const char *, const char *, const char *,
|
||||
const char *, int);
|
||||
int rcsfile_send_details(struct rcsfile *, struct stream *);
|
||||
int rcsfile_write(struct rcsfile *, struct stream *);
|
||||
void rcsfile_print(struct rcsfile *);
|
||||
void rcsfile_free(struct rcsfile *);
|
||||
|
||||
/* Used for adding and setting rcsfile values. */
|
||||
void rcsfile_addaccess(struct rcsfile *, char *);
|
||||
void rcsfile_addtag(struct rcsfile *, char *, char *);
|
||||
void rcsfile_importtag(struct rcsfile *, char *, char *);
|
||||
void rcsfile_deleterev(struct rcsfile *, char *);
|
||||
void rcsfile_deletetag(struct rcsfile *, char *, char *);
|
||||
struct delta *rcsfile_getdelta(struct rcsfile *, char *);
|
||||
void rcsfile_setval(struct rcsfile *, int, char *);
|
||||
|
||||
/* Functions used for operating on RCS deltas. */
|
||||
struct delta *rcsfile_addelta(struct rcsfile *, char *, char *, char *,
|
||||
char *);
|
||||
void rcsfile_importdelta(struct rcsfile *, char *, char *, char *,
|
||||
char *, char *);
|
||||
|
||||
int rcsdelta_addlog(struct delta *, char *, int);
|
||||
int rcsdelta_addtext(struct delta *, char *, int);
|
||||
int rcsdelta_appendlog(struct delta *, char *, size_t);
|
||||
int rcsdelta_appendtext(struct delta *, char *, size_t);
|
||||
void rcsdelta_setstate(struct delta *, char *);
|
||||
void rcsdelta_truncatetext(struct delta *, off_t);
|
||||
void rcsdelta_truncatelog(struct delta *, off_t);
|
||||
#endif /* !_RCSFILE_H_ */
|
@ -1,366 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "rcsfile.h"
|
||||
#include "rcsparse.h"
|
||||
#include "rcstokenizer.h"
|
||||
|
||||
/*
|
||||
* This is an RCS-parser using lex for tokenizing and makes sure the RCS syntax
|
||||
* is correct as it constructs an RCS file that is used by csup.
|
||||
*/
|
||||
|
||||
static void asserttoken(yyscan_t *, int);
|
||||
static int parse_admin(struct rcsfile *, yyscan_t *);
|
||||
static int parse_deltas(struct rcsfile *, yyscan_t *, int);
|
||||
static int parse_deltatexts(struct rcsfile *, yyscan_t *, int);
|
||||
static char *duptext(yyscan_t *, int *);
|
||||
|
||||
struct string {
|
||||
char *str;
|
||||
STAILQ_ENTRY(string) next;
|
||||
};
|
||||
|
||||
static void
|
||||
asserttoken(yyscan_t *sp, int token)
|
||||
{
|
||||
int t;
|
||||
|
||||
t = token;
|
||||
t = rcslex(*sp);
|
||||
assert(t == token);
|
||||
}
|
||||
|
||||
static char *
|
||||
duptext(yyscan_t *sp, int *arglen)
|
||||
{
|
||||
char *tmp, *val;
|
||||
int len;
|
||||
|
||||
tmp = rcsget_text(*sp);
|
||||
len = rcsget_leng(*sp);
|
||||
val = xmalloc(len + 1);
|
||||
memcpy(val, tmp, len);
|
||||
val[len] = '\0';
|
||||
if (arglen != NULL)
|
||||
*arglen = len;
|
||||
return (val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Start up parser, and use the rcsfile hook to add objects.
|
||||
*/
|
||||
int
|
||||
rcsparse_run(struct rcsfile *rf, FILE *infp, int ro)
|
||||
{
|
||||
yyscan_t scanner;
|
||||
char *desc;
|
||||
int error, tok;
|
||||
|
||||
error = 0;
|
||||
rcslex_init(&scanner);
|
||||
rcsset_in(infp, scanner);
|
||||
tok = parse_admin(rf, &scanner);
|
||||
tok = parse_deltas(rf, &scanner, tok);
|
||||
assert(tok == KEYWORD);
|
||||
asserttoken(&scanner, STRING);
|
||||
desc = duptext(&scanner, NULL);
|
||||
rcsfile_setval(rf, RCSFILE_DESC, desc);
|
||||
free(desc);
|
||||
tok = rcslex(scanner);
|
||||
/* Parse deltatexts if we need to edit. */
|
||||
if (!ro) {
|
||||
error = parse_deltatexts(rf, &scanner, tok);
|
||||
if (error)
|
||||
return (error);
|
||||
}
|
||||
rcslex_destroy(scanner);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the admin part of a RCS file.
|
||||
*/
|
||||
static int
|
||||
parse_admin(struct rcsfile *rf, yyscan_t *sp)
|
||||
{
|
||||
char *branch, *comment, *expand, *head, *id, *revnum, *tag, *tmp;
|
||||
int strict, token;
|
||||
|
||||
strict = 0;
|
||||
branch = NULL;
|
||||
|
||||
/* head {num}; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
asserttoken(sp, NUM);
|
||||
head = duptext(sp, NULL);
|
||||
rcsfile_setval(rf, RCSFILE_HEAD, head);
|
||||
free(head);
|
||||
asserttoken(sp, SEMIC);
|
||||
|
||||
/* { branch {num}; } */
|
||||
token = rcslex(*sp);
|
||||
if (token == KEYWORD_TWO) {
|
||||
asserttoken(sp, NUM);
|
||||
branch = duptext(sp, NULL);
|
||||
rcsfile_setval(rf, RCSFILE_BRANCH, branch);
|
||||
free(branch);
|
||||
asserttoken(sp, SEMIC);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
|
||||
/* access {id]*; */
|
||||
assert(token == KEYWORD);
|
||||
token = rcslex(*sp);
|
||||
while (token == ID) {
|
||||
id = duptext(sp, NULL);
|
||||
rcsfile_addaccess(rf, id);
|
||||
free(id);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
assert(token == SEMIC);
|
||||
|
||||
/* symbols {sym : num}*; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
token = rcslex(*sp);
|
||||
while (token == ID) {
|
||||
tag = duptext(sp, NULL);
|
||||
asserttoken(sp, COLON);
|
||||
asserttoken(sp, NUM);
|
||||
revnum = duptext(sp, NULL);
|
||||
rcsfile_importtag(rf, tag, revnum);
|
||||
free(tag);
|
||||
free(revnum);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
assert(token == SEMIC);
|
||||
|
||||
/* locks {id : num}*; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
token = rcslex(*sp);
|
||||
while (token == ID) {
|
||||
/* XXX: locks field is skipped */
|
||||
asserttoken(sp, COLON);
|
||||
asserttoken(sp, NUM);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
assert(token == SEMIC);
|
||||
token = rcslex(*sp);
|
||||
while (token == KEYWORD) {
|
||||
tmp = rcsget_text(*sp);
|
||||
|
||||
/* {strict ;} */
|
||||
if (!strcmp(tmp, "strict")) {
|
||||
rcsfile_setval(rf, RCSFILE_STRICT, tmp);
|
||||
asserttoken(sp, SEMIC);
|
||||
/* { comment {string}; } */
|
||||
} else if (!strcmp(tmp, "comment")) {
|
||||
token = rcslex(*sp);
|
||||
if (token == STRING) {
|
||||
comment = duptext(sp, NULL);
|
||||
rcsfile_setval(rf, RCSFILE_COMMENT, comment);
|
||||
free(comment);
|
||||
}
|
||||
asserttoken(sp, SEMIC);
|
||||
/* { expand {string}; } */
|
||||
} else if (!strcmp(tmp, "expand")) {
|
||||
token = rcslex(*sp);
|
||||
if (token == STRING) {
|
||||
expand = duptext(sp, NULL);
|
||||
rcsfile_setval(rf, RCSFILE_EXPAND, expand);
|
||||
free(expand);
|
||||
}
|
||||
asserttoken(sp, SEMIC);
|
||||
}
|
||||
/* {newphrase }* */
|
||||
token = rcslex(*sp);
|
||||
while (token == ID) {
|
||||
token = rcslex(*sp);
|
||||
/* XXX: newphrases ignored */
|
||||
while (token == ID || token == NUM || token == STRING ||
|
||||
token == COLON) {
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
asserttoken(sp, SEMIC);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
}
|
||||
return (token);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse RCS deltas.
|
||||
*/
|
||||
static int
|
||||
parse_deltas(struct rcsfile *rf, yyscan_t *sp, int token)
|
||||
{
|
||||
STAILQ_HEAD(, string) branchlist;
|
||||
char *revnum, *revdate, *author, *state, *next;
|
||||
|
||||
/* In case we don't have deltas. */
|
||||
if (token != NUM)
|
||||
return (token);
|
||||
do {
|
||||
next = NULL;
|
||||
state = NULL;
|
||||
|
||||
/* num */
|
||||
assert(token == NUM);
|
||||
revnum = duptext(sp, NULL);
|
||||
/* date num; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
asserttoken(sp, NUM);
|
||||
revdate = duptext(sp, NULL);
|
||||
asserttoken(sp, SEMIC);
|
||||
/* author id; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
asserttoken(sp, ID);
|
||||
author = duptext(sp, NULL);
|
||||
asserttoken(sp, SEMIC);
|
||||
/* state {id}; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
token = rcslex(*sp);
|
||||
if (token == ID) {
|
||||
state = duptext(sp, NULL);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
assert(token == SEMIC);
|
||||
/* branches {num}*; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
token = rcslex(*sp);
|
||||
STAILQ_INIT(&branchlist);
|
||||
while (token == NUM)
|
||||
token = rcslex(*sp);
|
||||
assert(token == SEMIC);
|
||||
/* next {num}; */
|
||||
asserttoken(sp, KEYWORD);
|
||||
token = rcslex(*sp);
|
||||
if (token == NUM) {
|
||||
next = duptext(sp, NULL);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
assert(token == SEMIC);
|
||||
/* {newphrase }* */
|
||||
token = rcslex(*sp);
|
||||
while (token == ID) {
|
||||
token = rcslex(*sp);
|
||||
/* XXX: newphrases ignored. */
|
||||
while (token == ID || token == NUM || token == STRING ||
|
||||
token == COLON) {
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
asserttoken(sp, SEMIC);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
rcsfile_importdelta(rf, revnum, revdate, author, state, next);
|
||||
free(revnum);
|
||||
free(revdate);
|
||||
free(author);
|
||||
if (state != NULL)
|
||||
free(state);
|
||||
if (next != NULL)
|
||||
free(next);
|
||||
} while (token == NUM);
|
||||
|
||||
return (token);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse RCS deltatexts.
|
||||
*/
|
||||
static int
|
||||
parse_deltatexts(struct rcsfile *rf, yyscan_t *sp, int token)
|
||||
{
|
||||
struct delta *d;
|
||||
char *log, *revnum, *text;
|
||||
int error, len;
|
||||
|
||||
error = 0;
|
||||
/* In case we don't have deltatexts. */
|
||||
if (token != NUM)
|
||||
return (-1);
|
||||
do {
|
||||
/* num */
|
||||
assert(token == NUM);
|
||||
revnum = duptext(sp, NULL);
|
||||
/* Get delta we're adding text to. */
|
||||
d = rcsfile_getdelta(rf, revnum);
|
||||
free(revnum);
|
||||
|
||||
/*
|
||||
* XXX: The RCS file is corrupt, but lie and say it is ok.
|
||||
* If it is actually broken, then the MD5 mismatch will
|
||||
* trigger a fixup.
|
||||
*/
|
||||
if (d == NULL)
|
||||
return (0);
|
||||
|
||||
/* log string */
|
||||
asserttoken(sp, KEYWORD);
|
||||
asserttoken(sp, STRING);
|
||||
log = duptext(sp, &len);
|
||||
error = rcsdelta_addlog(d, log, len);
|
||||
free(log);
|
||||
if (error)
|
||||
return (-1);
|
||||
/* { newphrase }* */
|
||||
token = rcslex(*sp);
|
||||
while (token == ID) {
|
||||
token = rcslex(*sp);
|
||||
/* XXX: newphrases ignored. */
|
||||
while (token == ID || token == NUM || token == STRING ||
|
||||
token == COLON) {
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
asserttoken(sp, SEMIC);
|
||||
token = rcslex(*sp);
|
||||
}
|
||||
/* text string */
|
||||
assert(token == KEYWORD);
|
||||
asserttoken(sp, STRING);
|
||||
text = duptext(sp, &len);
|
||||
error = rcsdelta_addtext(d, text, len);
|
||||
/*
|
||||
* If this happens, something is wrong with the RCS file, and it
|
||||
* should be resent.
|
||||
*/
|
||||
free(text);
|
||||
if (error)
|
||||
return (-1);
|
||||
token = rcslex(*sp);
|
||||
} while (token == NUM);
|
||||
|
||||
return (0);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _RCSPARSE_H_
|
||||
#define _RCSPARSE_H_
|
||||
#define ID 0
|
||||
#define NUM 1
|
||||
#define KEYWORD 2
|
||||
#define KEYWORD_TWO 3
|
||||
#define STRING 4
|
||||
#define SEMIC 5
|
||||
#define COLON 6
|
||||
|
||||
struct rcsfile;
|
||||
int rcsparse_run(struct rcsfile *, FILE *, int);
|
||||
#endif /* !_RCSPARSE_H_ */
|
@ -1,333 +0,0 @@
|
||||
#ifndef rcsHEADER_H
|
||||
#define rcsHEADER_H 1
|
||||
#define rcsIN_HEADER 1
|
||||
|
||||
#line 6 "rcstokenizer.h"
|
||||
|
||||
#define YY_INT_ALIGNED short int
|
||||
|
||||
/* A lexical scanner generated by flex */
|
||||
|
||||
#define FLEX_SCANNER
|
||||
#define YY_FLEX_MAJOR_VERSION 2
|
||||
#define YY_FLEX_MINOR_VERSION 5
|
||||
#define YY_FLEX_SUBMINOR_VERSION 35
|
||||
#if YY_FLEX_SUBMINOR_VERSION > 0
|
||||
#define FLEX_BETA
|
||||
#endif
|
||||
|
||||
/* First, we deal with platform-specific or compiler-specific issues. */
|
||||
|
||||
/* begin standard C headers. */
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
/* end standard C headers. */
|
||||
|
||||
/* flex integer type definitions */
|
||||
|
||||
#ifndef FLEXINT_H
|
||||
#define FLEXINT_H
|
||||
|
||||
/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
|
||||
|
||||
#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
|
||||
|
||||
/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h,
|
||||
* if you want the limit (max/min) macros for int types.
|
||||
*/
|
||||
#ifndef __STDC_LIMIT_MACROS
|
||||
#define __STDC_LIMIT_MACROS 1
|
||||
#endif
|
||||
|
||||
#include <inttypes.h>
|
||||
typedef int8_t flex_int8_t;
|
||||
typedef uint8_t flex_uint8_t;
|
||||
typedef int16_t flex_int16_t;
|
||||
typedef uint16_t flex_uint16_t;
|
||||
typedef int32_t flex_int32_t;
|
||||
typedef uint32_t flex_uint32_t;
|
||||
#else
|
||||
typedef signed char flex_int8_t;
|
||||
typedef short int flex_int16_t;
|
||||
typedef int flex_int32_t;
|
||||
typedef unsigned char flex_uint8_t;
|
||||
typedef unsigned short int flex_uint16_t;
|
||||
typedef unsigned int flex_uint32_t;
|
||||
#endif /* ! C99 */
|
||||
|
||||
/* Limits of integral types. */
|
||||
#ifndef INT8_MIN
|
||||
#define INT8_MIN (-128)
|
||||
#endif
|
||||
#ifndef INT16_MIN
|
||||
#define INT16_MIN (-32767-1)
|
||||
#endif
|
||||
#ifndef INT32_MIN
|
||||
#define INT32_MIN (-2147483647-1)
|
||||
#endif
|
||||
#ifndef INT8_MAX
|
||||
#define INT8_MAX (127)
|
||||
#endif
|
||||
#ifndef INT16_MAX
|
||||
#define INT16_MAX (32767)
|
||||
#endif
|
||||
#ifndef INT32_MAX
|
||||
#define INT32_MAX (2147483647)
|
||||
#endif
|
||||
#ifndef UINT8_MAX
|
||||
#define UINT8_MAX (255U)
|
||||
#endif
|
||||
#ifndef UINT16_MAX
|
||||
#define UINT16_MAX (65535U)
|
||||
#endif
|
||||
#ifndef UINT32_MAX
|
||||
#define UINT32_MAX (4294967295U)
|
||||
#endif
|
||||
|
||||
#endif /* ! FLEXINT_H */
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
||||
/* The "const" storage-class-modifier is valid. */
|
||||
#define YY_USE_CONST
|
||||
|
||||
#else /* ! __cplusplus */
|
||||
|
||||
/* C99 requires __STDC__ to be defined as 1. */
|
||||
#if defined (__STDC__)
|
||||
|
||||
#define YY_USE_CONST
|
||||
|
||||
#endif /* defined (__STDC__) */
|
||||
#endif /* ! __cplusplus */
|
||||
|
||||
#ifdef YY_USE_CONST
|
||||
#define yyconst const
|
||||
#else
|
||||
#define yyconst
|
||||
#endif
|
||||
|
||||
/* An opaque pointer. */
|
||||
#ifndef YY_TYPEDEF_YY_SCANNER_T
|
||||
#define YY_TYPEDEF_YY_SCANNER_T
|
||||
typedef void* yyscan_t;
|
||||
#endif
|
||||
|
||||
/* For convenience, these vars (plus the bison vars far below)
|
||||
are macros in the reentrant scanner. */
|
||||
#define yyin yyg->yyin_r
|
||||
#define yyout yyg->yyout_r
|
||||
#define yyextra yyg->yyextra_r
|
||||
#define yyleng yyg->yyleng_r
|
||||
#define yytext yyg->yytext_r
|
||||
#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno)
|
||||
#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column)
|
||||
#define yy_flex_debug yyg->yy_flex_debug_r
|
||||
|
||||
/* Size of default input buffer. */
|
||||
#ifndef YY_BUF_SIZE
|
||||
#define YY_BUF_SIZE 16384
|
||||
#endif
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_BUFFER_STATE
|
||||
#define YY_TYPEDEF_YY_BUFFER_STATE
|
||||
typedef struct yy_buffer_state *YY_BUFFER_STATE;
|
||||
#endif
|
||||
|
||||
#ifndef YY_TYPEDEF_YY_SIZE_T
|
||||
#define YY_TYPEDEF_YY_SIZE_T
|
||||
typedef size_t yy_size_t;
|
||||
#endif
|
||||
|
||||
#ifndef YY_STRUCT_YY_BUFFER_STATE
|
||||
#define YY_STRUCT_YY_BUFFER_STATE
|
||||
struct yy_buffer_state
|
||||
{
|
||||
FILE *yy_input_file;
|
||||
|
||||
char *yy_ch_buf; /* input buffer */
|
||||
char *yy_buf_pos; /* current position in input buffer */
|
||||
|
||||
/* Size of input buffer in bytes, not including room for EOB
|
||||
* characters.
|
||||
*/
|
||||
yy_size_t yy_buf_size;
|
||||
|
||||
/* Number of characters read into yy_ch_buf, not including EOB
|
||||
* characters.
|
||||
*/
|
||||
int yy_n_chars;
|
||||
|
||||
/* Whether we "own" the buffer - i.e., we know we created it,
|
||||
* and can realloc() it to grow it, and should free() it to
|
||||
* delete it.
|
||||
*/
|
||||
int yy_is_our_buffer;
|
||||
|
||||
/* Whether this is an "interactive" input source; if so, and
|
||||
* if we're using stdio for input, then we want to use getc()
|
||||
* instead of fread(), to make sure we stop fetching input after
|
||||
* each newline.
|
||||
*/
|
||||
int yy_is_interactive;
|
||||
|
||||
/* Whether we're considered to be at the beginning of a line.
|
||||
* If so, '^' rules will be active on the next match, otherwise
|
||||
* not.
|
||||
*/
|
||||
int yy_at_bol;
|
||||
|
||||
int yy_bs_lineno; /**< The line count. */
|
||||
int yy_bs_column; /**< The column count. */
|
||||
|
||||
/* Whether to try to fill the input buffer when we reach the
|
||||
* end of it.
|
||||
*/
|
||||
int yy_fill_buffer;
|
||||
|
||||
int yy_buffer_status;
|
||||
|
||||
};
|
||||
#endif /* !YY_STRUCT_YY_BUFFER_STATE */
|
||||
|
||||
void rcsrestart (FILE *input_file ,yyscan_t yyscanner );
|
||||
void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE rcs_create_buffer (FILE *file,int size ,yyscan_t yyscanner );
|
||||
void rcs_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
|
||||
void rcs_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner );
|
||||
void rcspush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner );
|
||||
void rcspop_buffer_state (yyscan_t yyscanner );
|
||||
|
||||
YY_BUFFER_STATE rcs_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE rcs_scan_string (yyconst char *yy_str ,yyscan_t yyscanner );
|
||||
YY_BUFFER_STATE rcs_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner );
|
||||
|
||||
void *rcsalloc (yy_size_t ,yyscan_t yyscanner );
|
||||
void *rcsrealloc (void *,yy_size_t ,yyscan_t yyscanner );
|
||||
void rcsfree (void * ,yyscan_t yyscanner );
|
||||
|
||||
/* Begin user sect3 */
|
||||
|
||||
#define rcswrap(n) 1
|
||||
#define YY_SKIP_YYWRAP
|
||||
|
||||
#define yytext_ptr yytext_r
|
||||
|
||||
#ifdef YY_HEADER_EXPORT_START_CONDITIONS
|
||||
#define INITIAL 0
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_UNISTD_H
|
||||
/* Special case for "unistd.h", since it is non-ANSI. We include it way
|
||||
* down here because we want the user's section 1 to have been scanned first.
|
||||
* The user has a chance to override it with an option.
|
||||
*/
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef YY_EXTRA_TYPE
|
||||
#define YY_EXTRA_TYPE void *
|
||||
#endif
|
||||
|
||||
int rcslex_init (yyscan_t* scanner);
|
||||
|
||||
int rcslex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner);
|
||||
|
||||
/* Accessor methods to globals.
|
||||
These are made visible to non-reentrant scanners for convenience. */
|
||||
|
||||
int rcslex_destroy (yyscan_t yyscanner );
|
||||
|
||||
int rcsget_debug (yyscan_t yyscanner );
|
||||
|
||||
void rcsset_debug (int debug_flag ,yyscan_t yyscanner );
|
||||
|
||||
YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner );
|
||||
|
||||
void rcsset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner );
|
||||
|
||||
FILE *rcsget_in (yyscan_t yyscanner );
|
||||
|
||||
void rcsset_in (FILE * in_str ,yyscan_t yyscanner );
|
||||
|
||||
FILE *rcsget_out (yyscan_t yyscanner );
|
||||
|
||||
void rcsset_out (FILE * out_str ,yyscan_t yyscanner );
|
||||
|
||||
int rcsget_leng (yyscan_t yyscanner );
|
||||
|
||||
char *rcsget_text (yyscan_t yyscanner );
|
||||
|
||||
int rcsget_lineno (yyscan_t yyscanner );
|
||||
|
||||
void rcsset_lineno (int line_number ,yyscan_t yyscanner );
|
||||
|
||||
/* Macros after this point can all be overridden by user definitions in
|
||||
* section 1.
|
||||
*/
|
||||
|
||||
#ifndef YY_SKIP_YYWRAP
|
||||
#ifdef __cplusplus
|
||||
extern "C" int rcswrap (yyscan_t yyscanner );
|
||||
#else
|
||||
extern int rcswrap (yyscan_t yyscanner );
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef yytext_ptr
|
||||
static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner);
|
||||
#endif
|
||||
|
||||
#ifdef YY_NEED_STRLEN
|
||||
static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner);
|
||||
#endif
|
||||
|
||||
#ifndef YY_NO_INPUT
|
||||
|
||||
#endif
|
||||
|
||||
/* Amount of stuff to slurp up with each read. */
|
||||
#ifndef YY_READ_BUF_SIZE
|
||||
#define YY_READ_BUF_SIZE 8192
|
||||
#endif
|
||||
|
||||
/* Number of entries by which start-condition stack grows. */
|
||||
#ifndef YY_START_STACK_INCR
|
||||
#define YY_START_STACK_INCR 25
|
||||
#endif
|
||||
|
||||
/* Default declaration of generated scanner - a define so the user can
|
||||
* easily add parameters.
|
||||
*/
|
||||
#ifndef YY_DECL
|
||||
#define YY_DECL_IS_OURS 1
|
||||
|
||||
extern int rcslex (yyscan_t yyscanner);
|
||||
|
||||
#define YY_DECL int rcslex (yyscan_t yyscanner)
|
||||
#endif /* !YY_DECL */
|
||||
|
||||
/* yy_get_previous_state - get the state just before the EOB char was reached */
|
||||
|
||||
#undef YY_NEW_FILE
|
||||
#undef YY_FLUSH_BUFFER
|
||||
#undef yy_set_bol
|
||||
#undef yy_new_buffer
|
||||
#undef yy_set_interactive
|
||||
#undef YY_DO_BEFORE_ACTION
|
||||
|
||||
#ifdef YY_DECL_IS_OURS
|
||||
#undef YY_DECL_IS_OURS
|
||||
#undef YY_DECL
|
||||
#endif
|
||||
|
||||
#line 73 "rcstokenizer.l"
|
||||
|
||||
|
||||
#line 332 "rcstokenizer.h"
|
||||
#undef rcsIN_HEADER
|
||||
#endif /* rcsHEADER_H */
|
@ -1,73 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
/*
|
||||
* This tokenizer must be generated by a lexxer with support for reentrancy.
|
||||
*/
|
||||
%{
|
||||
#include <string.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "rcsparse.h"
|
||||
|
||||
%}
|
||||
%option reentrant noyywrap
|
||||
%option header-file="rcstokenizer.h"
|
||||
|
||||
everything (.|\n)*
|
||||
num [0-9\.]+
|
||||
whitespace [\t\n ]
|
||||
digit [0-9]
|
||||
idchar [^$,.:;\t\n ]
|
||||
string @([^@]|\n|"@@")*@
|
||||
keyword head|access|symbols|locks|comment|expand|strict|date|author|state|branches|next|desc|log|text
|
||||
keyword2 branch
|
||||
newline \n
|
||||
%%
|
||||
|
||||
{keyword2} {
|
||||
return (KEYWORD_TWO);
|
||||
}
|
||||
{keyword} {
|
||||
return (KEYWORD);
|
||||
}
|
||||
{string} {
|
||||
return (STRING);
|
||||
}
|
||||
{num} {
|
||||
return (NUM);
|
||||
}
|
||||
{num}?{idchar}({idchar}|{num})* {
|
||||
/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/
|
||||
return (ID);
|
||||
}
|
||||
; { return (SEMIC); }
|
||||
: { return (COLON); }
|
||||
\n ;
|
||||
[ \t]+ ;
|
||||
%%
|
@ -1,223 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/mman.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "misc.h"
|
||||
#include "rsyncfile.h"
|
||||
|
||||
#define MINBLOCKSIZE 1024
|
||||
#define MAXBLOCKSIZE (16 * 1024)
|
||||
#define RECEIVEBUFFERSIZE (15 * 1024)
|
||||
#define BLOCKINFOSIZE 26
|
||||
#define SEARCHREGION 10
|
||||
#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE)
|
||||
|
||||
#define CHAR_OFFSET 3
|
||||
#define RSUM_SIZE 9
|
||||
|
||||
struct rsyncfile {
|
||||
char *start;
|
||||
char *buf;
|
||||
char *end;
|
||||
size_t blocksize;
|
||||
size_t fsize;
|
||||
int fd;
|
||||
|
||||
char *blockptr;
|
||||
int blocknum;
|
||||
char blockmd5[MD5_DIGEST_SIZE];
|
||||
char rsumstr[RSUM_SIZE];
|
||||
uint32_t rsum;
|
||||
};
|
||||
|
||||
static size_t rsync_chooseblocksize(size_t);
|
||||
static uint32_t rsync_rollsum(char *, size_t);
|
||||
|
||||
/* Open a file and initialize variable for rsync operation. */
|
||||
struct rsyncfile *
|
||||
rsync_open(char *path, size_t blocksize, int rdonly)
|
||||
{
|
||||
struct rsyncfile *rf;
|
||||
struct stat st;
|
||||
int error;
|
||||
|
||||
rf = xmalloc(sizeof(*rf));
|
||||
error = stat(path, &st);
|
||||
if (error) {
|
||||
free(rf);
|
||||
return (NULL);
|
||||
}
|
||||
rf->fsize = st.st_size;
|
||||
|
||||
rf->fd = open(path, rdonly ? O_RDONLY : O_RDWR);
|
||||
if (rf->fd < 0) {
|
||||
free(rf);
|
||||
return (NULL);
|
||||
}
|
||||
rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0);
|
||||
if (rf->buf == MAP_FAILED) {
|
||||
free(rf);
|
||||
return (NULL);
|
||||
}
|
||||
rf->start = rf->buf;
|
||||
rf->end = rf->buf + rf->fsize;
|
||||
rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) :
|
||||
blocksize);
|
||||
rf->blockptr = rf->buf;
|
||||
rf->blocknum = 0;
|
||||
return (rf);
|
||||
}
|
||||
|
||||
/* Close and free all resources related to an rsync file transfer. */
|
||||
int
|
||||
rsync_close(struct rsyncfile *rf)
|
||||
{
|
||||
int error;
|
||||
|
||||
error = munmap(rf->buf, rf->fsize);
|
||||
if (error)
|
||||
return (error);
|
||||
close(rf->fd);
|
||||
free(rf);
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Choose the most appropriate block size for an rsync transfer. Modeled
|
||||
* algorithm after cvsup.
|
||||
*/
|
||||
static size_t
|
||||
rsync_chooseblocksize(size_t fsize)
|
||||
{
|
||||
size_t bestrem, blocksize, bs, hisearch, losearch, rem;
|
||||
|
||||
blocksize = fsize / MAXBLOCKS;
|
||||
losearch = blocksize - SEARCHREGION;
|
||||
hisearch = blocksize + SEARCHREGION;
|
||||
|
||||
if (losearch < MINBLOCKSIZE) {
|
||||
losearch = MINBLOCKSIZE;
|
||||
hisearch = losearch + (2 * SEARCHREGION);
|
||||
} else if (hisearch > MAXBLOCKSIZE) {
|
||||
hisearch = MAXBLOCKSIZE;
|
||||
losearch = hisearch - (2 * SEARCHREGION);
|
||||
}
|
||||
|
||||
bestrem = MAXBLOCKSIZE;
|
||||
for (bs = losearch; bs <= hisearch; bs++) {
|
||||
rem = fsize % bs;
|
||||
if (rem < bestrem) {
|
||||
bestrem = rem;
|
||||
blocksize = bs;
|
||||
}
|
||||
}
|
||||
return (bestrem);
|
||||
}
|
||||
|
||||
/* Get the next rsync block of a file. */
|
||||
int
|
||||
rsync_nextblock(struct rsyncfile *rf)
|
||||
{
|
||||
MD5_CTX ctx;
|
||||
size_t blocksize;
|
||||
|
||||
if (rf->blockptr >= rf->end)
|
||||
return (0);
|
||||
blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize);
|
||||
/* Calculate MD5 of the block. */
|
||||
MD5_Init(&ctx);
|
||||
MD5_Update(&ctx, rf->blockptr, blocksize);
|
||||
MD5_End(rf->blockmd5, &ctx);
|
||||
|
||||
rf->rsum = rsync_rollsum(rf->blockptr, blocksize);
|
||||
snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum);
|
||||
rf->blocknum++;
|
||||
rf->blockptr += blocksize;
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* Get the rolling checksum of a file. */
|
||||
static uint32_t
|
||||
rsync_rollsum(char *buf, size_t len)
|
||||
{
|
||||
uint32_t a, b;
|
||||
char *ptr, *limit;
|
||||
|
||||
a = b = 0;
|
||||
ptr = buf;
|
||||
limit = buf + len;
|
||||
|
||||
while (ptr < limit) {
|
||||
a += *ptr + CHAR_OFFSET;
|
||||
b += a;
|
||||
ptr++;
|
||||
}
|
||||
return ((b << 16) | a);
|
||||
}
|
||||
|
||||
/* Get running sum so far. */
|
||||
char *
|
||||
rsync_rsum(struct rsyncfile *rf)
|
||||
{
|
||||
|
||||
return (rf->rsumstr);
|
||||
}
|
||||
|
||||
/* Get MD5 of current block. */
|
||||
char *
|
||||
rsync_blockmd5(struct rsyncfile *rf)
|
||||
{
|
||||
|
||||
return (rf->blockmd5);
|
||||
}
|
||||
|
||||
/* Accessor for blocksize. */
|
||||
size_t
|
||||
rsync_blocksize(struct rsyncfile *rf)
|
||||
{
|
||||
|
||||
return (rf->blocksize);
|
||||
}
|
||||
|
||||
/* Accessor for filesize. */
|
||||
size_t
|
||||
rsync_filesize(struct rsyncfile *rf)
|
||||
{
|
||||
|
||||
return (rf->fsize);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
/*-
|
||||
* Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*
|
||||
* $FreeBSD$
|
||||
*/
|
||||
|
||||
#ifndef _RSYNCFILE_H_
|
||||
#define _RSYNCFILE_H_
|
||||
|
||||
struct rsyncfile;
|
||||
struct rsyncfile *rsync_open(char *, size_t, int);
|
||||
int rsync_nextblock(struct rsyncfile *);
|
||||
char *rsync_rsum(struct rsyncfile *);
|
||||
char *rsync_blockmd5(struct rsyncfile *);
|
||||
int rsync_close(struct rsyncfile *);
|
||||
size_t rsync_blocksize(struct rsyncfile *);
|
||||
size_t rsync_filesize(struct rsyncfile *);
|
||||
|
||||
#endif /* !_RSYNCFILE_H_ */
|
@ -1,875 +0,0 @@
|
||||
/*-
|
||||
* 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/queue.h>
|
||||
|
||||
#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 "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_FILEDEAD:
|
||||
case SR_FILELIVE:
|
||||
clientattr = proto_get_ascii(&line);
|
||||
if (clientattr == NULL || line != NULL)
|
||||
return (-1);
|
||||
sr->sr_clientattr = fattr_decode(clientattr);
|
||||
if (sr->sr_clientattr == NULL)
|
||||
return (-1);
|
||||
break;
|
||||
case SR_DIRDOWN:
|
||||
/* Nothing to do. */
|
||||
if (line != NULL)
|
||||
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 'A':
|
||||
sr.sr_type = SR_FILELIVE;
|
||||
break;
|
||||
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;
|
||||
case 'V':
|
||||
sr.sr_type = SR_FILELIVE;
|
||||
break;
|
||||
case 'v':
|
||||
sr.sr_type = SR_FILEDEAD;
|
||||
break;
|
||||
default:
|
||||
st->error = STATUS_ERR_BAD_TYPE;
|
||||
st->suberror = cmd[0];
|
||||
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;
|
||||
case SR_FILELIVE:
|
||||
error = proto_printf(st->wr, "V %s %f\n", sr->sr_file,
|
||||
sr->sr_clientattr);
|
||||
break;
|
||||
case SR_FILEDEAD:
|
||||
error = proto_printf(st->wr, "v %s %f\n", sr->sr_file,
|
||||
sr->sr_clientattr);
|
||||
break;
|
||||
}
|
||||
if (error)
|
||||
goto bad;
|
||||
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;
|
||||
case SR_FILELIVE:
|
||||
cmd = 'V';
|
||||
break;
|
||||
case SR_FILEDEAD:
|
||||
cmd = 'v';
|
||||
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);
|
||||
}
|
@ -1,72 +0,0 @@
|
||||
/*-
|
||||
* 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_ */
|
File diff suppressed because it is too large
Load Diff
@ -1,84 +0,0 @@
|
||||
/*-
|
||||
* 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_MD5RCS
|
||||
} stream_filter_t;
|
||||
|
||||
struct stream;
|
||||
struct buf;
|
||||
|
||||
typedef ssize_t stream_readfn_t(void *, void *, size_t);
|
||||
typedef ssize_t stream_writefn_t(void *, const void *, size_t);
|
||||
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;
|
||||
|
||||
/* Convenience functions for handling character buffers. */
|
||||
stream_readfn_t stream_read_buf;
|
||||
stream_writefn_t stream_append_buf;
|
||||
stream_closefn_t stream_close_buf;
|
||||
|
||||
struct stream *stream_open(void *, stream_readfn_t *, stream_writefn_t *,
|
||||
stream_closefn_t *);
|
||||
struct stream *stream_open_fd(int, stream_readfn_t *, stream_writefn_t *,
|
||||
stream_closefn_t *);
|
||||
struct stream *stream_open_buf(struct buf *);
|
||||
struct stream *stream_open_file(const char *, int, ...);
|
||||
int stream_fileno(struct stream *);
|
||||
ssize_t stream_read(struct stream *, void *, size_t);
|
||||
ssize_t stream_read_blocking(struct stream *, void *, size_t);
|
||||
ssize_t stream_write(struct stream *, const void *, size_t);
|
||||
char *stream_getln(struct stream *, size_t *);
|
||||
int stream_printf(struct stream *, const char *, ...)
|
||||
__printflike(2, 3);
|
||||
int stream_flush(struct stream *);
|
||||
int stream_sync(struct stream *);
|
||||
int stream_truncate(struct stream *, off_t);
|
||||
void stream_truncate_buf(struct buf *, off_t);
|
||||
int stream_truncate_rel(struct stream *, off_t);
|
||||
int stream_rewind(struct stream *);
|
||||
int stream_eof(struct stream *);
|
||||
int stream_close(struct stream *);
|
||||
int stream_filter_start(struct stream *, stream_filter_t, void *);
|
||||
void stream_filter_stop(struct stream *);
|
||||
|
||||
struct buf *buf_new(size_t);
|
||||
void buf_free(struct buf *);
|
||||
#endif /* !_STREAM_H_ */
|
@ -1,177 +0,0 @@
|
||||
/*-
|
||||
* 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 <sys/queue.h>
|
||||
|
||||
#include <assert.h>
|
||||
#include <err.h>
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "misc.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);
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
/*-
|
||||
* 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_ */
|
@ -1,49 +0,0 @@
|
||||
/*-
|
||||
* 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
|
||||
#define PT_NORSYNC 11
|
||||
|
||||
#endif /* !_TOKEN_H_ */
|
@ -1,79 +0,0 @@
|
||||
%{
|
||||
/*-
|
||||
* 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"
|
||||
|
||||
int lineno = 1;
|
||||
|
||||
%}
|
||||
|
||||
%option nounput
|
||||
%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; }
|
||||
norsync { yylval.i = PT_NORSYNC; 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);
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,33 +0,0 @@
|
||||
/*-
|
||||
* 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