Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Add compatibility shims and basic Mac OS X build fixes.
Landon Fuller committed 11 years ago
commit 45cf3f3c7e5ae7253fc1a866631a81b3b4c908ae
parent 948c9e8
15 files changed +1516 -4
modified Makefile.am
@@ -27,6 +27,7 @@ check-local:
COCCI_ARGS=	-I ${top_srcdir} \
		-I /usr/include \
		-I /usr/local/include \
+
		-I ${top_srcdir}/compat \
		-I ${top_srcdir}/libpkg \
		-I ${top_srcdir}/src \
		-I ${top_srcdir}/external/expat/lib \
@@ -53,4 +54,4 @@ style-fix:
		uncrustify -c ${top_srcdir}/freebsd.cfg --no-backup


-
SUBDIRS = external libpkg src tests scripts docs
+
SUBDIRS = compat external libpkg src tests scripts docs
added compat/Makefile.am
@@ -0,0 +1,12 @@
+
noinst_LTLIBRARIES=	libbsd_compat.la
+
noinst_HEADERS=		bsd_compat.h
+

+
libbsd_compat_la_SOURCES=	basename.c \
+
				dirname.c \
+
				file_at.c \
+
				gr_util.c \
+
				humanize_number.c \
+
				strtonum.c
+

+
libbsd_compat_la_CFLAGS=	$(pkg_common_cflags) -static
+
libbsd_compat_la_LDFLAGS=	-all-static
added compat/basename.c
@@ -0,0 +1,90 @@
+
/*	$OpenBSD: basename.c,v 1.14 2005/08/08 08:05:33 espie Exp $	*/
+

+
/*
+
 * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+
 *
+
 * Permission to use, copy, modify, and distribute this software for any
+
 * purpose with or without fee is hereby granted, provided that the above
+
 * copyright notice and this permission notice appear in all copies.
+
 *
+
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
 */
+

+
#include <errno.h>
+
#include <libgen.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <sys/param.h>
+

+
#include "bsd_compat.h"
+

+
#if !HAVE_BASENAME_R
+
char *
+
basename_r(const char *path, char *bname)
+
{
+
	const char *endp, *startp;
+
	size_t len;
+

+
	/* Empty or NULL string gets treated as "." */
+
	if (path == NULL || *path == '\0') {
+
		bname[0] = '.';
+
		bname[1] = '\0';
+
		return (bname);
+
	}
+

+
	/* Strip any trailing slashes */
+
	endp = path + strlen(path) - 1;
+
	while (endp > path && *endp == '/')
+
		endp--;
+

+
	/* All slashes becomes "/" */
+
	if (endp == path && *endp == '/') {
+
		bname[0] = '/';
+
		bname[1] = '\0';
+
		return (bname);
+
	}
+

+
	/* Find the start of the base */
+
	startp = endp;
+
	while (startp > path && *(startp - 1) != '/')
+
		startp--;
+

+
	len = endp - startp + 1;
+
	if (len >= MAXPATHLEN) {
+
		errno = ENAMETOOLONG;
+
		return (NULL);
+
	}
+
	memcpy(bname, startp, len);
+
	bname[len] = '\0';
+
	return (bname);
+
}
+
#endif
+

+
#if HAVE_BSD_BASENAME
+
char *
+
bsd_basename(const char *path)
+
{
+
    return basename(path);
+
}
+

+
#else
+

+
char *
+
bsd_basename(const char *path)
+
{
+
	static char *bname = NULL;
+

+
	if (bname == NULL) {
+
		bname = (char *)malloc(MAXPATHLEN);
+
		if (bname == NULL)
+
			return (NULL);
+
	}
+
	return (basename_r(path, bname));
+
}
+
#endif
added compat/bsd_compat.h
@@ -0,0 +1,67 @@
+
/*-
+
 * Copyright (c) 2014 Landon Fuller <landon@landonf.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
+
 *    in this position and unchanged.
+
 * 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(S) ``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(S) 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.
+
 */
+

+
#ifndef _BSD_COMPAT_H
+
#define _BSD_COMPAT_H
+

+
#include <sys/stat.h>
+

+
#include "pkg_config.h"
+

+
char *bsd_dirname(const char *);
+
char *bsd_basename(const char *);
+

+
#if !HAVE_GR_MAKE
+
#include "gr_util.h"
+
#endif
+

+
#if !HAVE_HUMANIZE_NUMBER
+
#include "humanize_number.h"
+
#endif
+

+
#if !HAVE_FSTATAT || !HAVE_OPENAT || !HAVE_UNLINKAT
+
#define AT_FDCWD		-100
+
#endif
+

+
#if !HAVE_FSTATAT
+
#define	AT_SYMLINK_NOFOLLOW	0x200
+
int fstatat(int fd, const char *path, struct stat *buf, int flag);
+
#endif
+

+
#if !HAVE_OPENAT
+
int openat(int fd, const char *path, int flags, ...);
+
#endif
+

+
#if !HAVE_UNLINKAT
+
#define AT_REMOVEDIR	0x800
+
int unlinkat(int fd, const char *path, int flag);
+
#endif
+

+
#if !HAVE_STRTONUM
+
long long strtonum(const char *, long long, long long, const char **);
+
#endif
+

+
#endif
added compat/dirname.c
@@ -0,0 +1,89 @@
+
/*	$OpenBSD: dirname.c,v 1.13 2005/08/08 08:05:33 espie Exp $	*/
+

+
/*
+
 * Copyright (c) 1997, 2004 Todd C. Miller <Todd.Miller@courtesan.com>
+
 *
+
 * Permission to use, copy, modify, and distribute this software for any
+
 * purpose with or without fee is hereby granted, provided that the above
+
 * copyright notice and this permission notice appear in all copies.
+
 *
+
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
 */
+

+
#include "bsd_compat.h"
+

+
#if HAVE_BSD_DIRNAME
+

+
#include <libgen.h>
+

+
char *
+
bsd_dirname(const char *path)
+
{
+
	return dirname (path);
+
}
+

+
#else
+

+
#include <errno.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <sys/param.h>
+

+
char *
+
bsd_dirname(const char *path)
+
{
+
	static char *dname = NULL;
+
	size_t len;
+
	const char *endp;
+

+
	if (dname == NULL) {
+
		dname = (char *)malloc(MAXPATHLEN);
+
		if (dname == NULL)
+
			return(NULL);
+
	}
+

+
	/* Empty or NULL string gets treated as "." */
+
	if (path == NULL || *path == '\0') {
+
		dname[0] = '.';
+
		dname[1] = '\0';
+
		return (dname);
+
	}
+

+
	/* Strip any trailing slashes */
+
	endp = path + strlen(path) - 1;
+
	while (endp > path && *endp == '/')
+
		endp--;
+

+
	/* Find the start of the dir */
+
	while (endp > path && *endp != '/')
+
		endp--;
+

+
	/* Either the dir is "/" or there are no slashes */
+
	if (endp == path) {
+
		dname[0] = *endp == '/' ? '/' : '.';
+
		dname[1] = '\0';
+
		return (dname);
+
	} else {
+
		/* Move forward past the separating slashes */
+
		do {
+
			endp--;
+
		} while (endp > path && *endp == '/');
+
	}
+

+
	len = endp - path + 1;
+
	if (len >= MAXPATHLEN) {
+
		errno = ENAMETOOLONG;
+
		return (NULL);
+
	}
+
	memcpy(dname, path, len);
+
	dname[len] = '\0';
+
	return (dname);
+
}
+

+
#endif
added compat/file_at.c
@@ -0,0 +1,149 @@
+
/*-
+
 * Copyright (c) 2014 Landon Fuller <landon@landonf.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
+
 *    in this position and unchanged.
+
 * 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(S) ``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(S) 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.
+
 */
+

+
#include <assert.h>
+
#include <fcntl.h>
+
#include <pthread.h>
+
#include <stdarg.h>
+
#include <unistd.h>
+

+
#include <bsd_compat.h>
+

+
#if !HAVE_UNLINKAT || !HAVE_FSTATAT
+

+
static pthread_mutex_t file_at_lock = PTHREAD_MUTEX_INITIALIZER;
+
static int file_at_dfd = -1;
+

+
/**
+
 * Acquire the cwd mutex and perform fchdir(dfd).
+
 *
+
 * On error, the mutex will be released automatically
+
 * and a non-zero value will be returned.
+
 *
+
 * @param dfd The directory file descriptor to be passed to fchdir(), or
+
 * AT_FDCWD to use the current working directory.
+
 * @return The fchdir() result.
+
 */
+
static int
+
file_chdir_lock(int dfd)
+
{
+
	int ret;
+

+
	pthread_mutex_lock(&file_at_lock);
+

+
	assert(file_at_dfd == -1);
+
	file_at_dfd = dfd;
+

+
	if (dfd == AT_FDCWD)
+
		return 0;
+

+
	ret = fchdir(dfd);
+
	if (ret != 0) {
+
		pthread_mutex_unlock(&file_at_lock);
+
		return ret;
+
	} else {
+
		return ret;
+
	}
+
}
+

+
/**
+
 * Release the cwd mutex.
+
 */
+
static void
+
file_chdir_unlock(int dfd)
+
{
+
	assert(file_at_dfd == dfd);
+
	file_at_dfd = -1;
+

+
	if (dfd == AT_FDCWD)
+
		return;
+

+
	pthread_mutex_unlock(&file_at_lock);
+
}
+
#endif
+

+
#if !HAVE_FSTATAT
+
int
+
fstatat(int fd, const char *path, struct stat *buf, int flag)
+
{
+
	int ret;
+

+
	if ((ret = file_chdir_lock(fd) != 0))
+
		return ret;
+

+
	if (flag & AT_SYMLINK_NOFOLLOW) {
+
		ret = lstat(path, buf);
+
	} else {
+
		ret = stat(path, buf);
+
	}
+

+
	file_chdir_unlock(fd);
+
	return ret;
+
}
+
#endif
+

+
#if !HAVE_OPENAT
+
int
+
openat(int fd, const char *path, int flags, ...)
+
{
+
	int ret;
+
	va_list ap;
+

+
	if ((ret = file_chdir_lock(fd) != 0))
+
		return ret;
+

+
	if (flags & O_CREAT) {
+
		va_start(ap, flags);
+
		ret = open(path, flags, va_arg(ap, int));
+
		va_end(ap);
+
	} else {
+
		ret = open(path, flags);
+
	}
+

+
	file_chdir_unlock(fd);
+
	return ret;
+
}
+
#endif
+

+
#if !HAVE_UNLINKAT
+
int
+
unlinkat(int fd, const char *path, int flag)
+
{
+
	int ret;
+

+
	if ((ret = file_chdir_lock(fd) != 0))
+
		return ret;
+

+
	if (flag & AT_REMOVEDIR) {
+
		ret = rmdir(path);
+
	} else {
+
		ret = unlink(path);
+
	}
+

+
	file_chdir_unlock(fd);
+
	return ret;
+
}
+
#endif
added compat/gr_util.c
@@ -0,0 +1,637 @@
+
/*-
+
 * Copyright (c) 2008 Sean C. Farley <scf@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,
+
 *    without modification, immediately at the beginning of the file.
+
 * 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.
+
 */
+

+
#include "bsd_compat.h"
+

+
#if !HAVE_GR_MAKE
+

+
#include <sys/param.h>
+
#include <sys/errno.h>
+
#include <sys/stat.h>
+

+
#include <ctype.h>
+
#include <err.h>
+
#include <fcntl.h>
+
#include <grp.h>
+
#include <inttypes.h>
+
#include <paths.h>
+
#include <stdbool.h>
+
#include <stdio.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <unistd.h>
+

+
static int lockfd = -1;
+
static char group_dir[PATH_MAX];
+
static char group_file[PATH_MAX];
+
static char tempname[PATH_MAX];
+
static int initialized;
+
static size_t grmemlen(const struct group *, const char *, int *);
+
static struct group *grcopy(const struct group *gr, char *mem, const char *, int ndx);
+

+
/*
+
 * Initialize statics
+
 */
+
int
+
gr_init(const char *dir, const char *group)
+
{
+

+
	if (dir == NULL) {
+
		strcpy(group_dir, "/etc");
+
	} else {
+
		if (strlen(dir) >= sizeof(group_dir)) {
+
			errno = ENAMETOOLONG;
+
			return (-1);
+
		}
+
		strcpy(group_dir, dir);
+
	}
+

+
	if (group == NULL) {
+
		if (dir == NULL) {
+
			strcpy(group_file, _PATH_GROUP);
+
		} else if (snprintf(group_file, sizeof(group_file), "%s/group",
+
			group_dir) > (int)sizeof(group_file)) {
+
			errno = ENAMETOOLONG;
+
			return (-1);
+
		}
+
	} else {
+
		if (strlen(group) >= sizeof(group_file)) {
+
			errno = ENAMETOOLONG;
+
			return (-1);
+
		}
+
		strcpy(group_file, group);
+
	}
+

+
	initialized = 1;
+
	return (0);
+
}
+

+
/*
+
 * Lock the group file
+
 */
+
int
+
gr_lock(void)
+
{
+
	if (*group_file == '\0')
+
		return (-1);
+

+
	for (;;) {
+
		struct stat st;
+

+
#if HAVE_FLOPEN
+
		lockfd = flopen(group_file, O_RDONLY|O_NONBLOCK|O_CLOEXEC, 0);
+
#else
+
        lockfd = -1;
+
#endif
+
		if (lockfd == -1) {
+
			if (errno == EWOULDBLOCK) {
+
				errx(1, "the group file is busy");
+
			} else {
+
				err(1, "could not lock the group file: ");
+
			}
+
		}
+
		if (fstat(lockfd, &st) == -1)
+
			err(1, "fstat() failed: ");
+
		if (st.st_nlink != 0)
+
			break;
+
		close(lockfd);
+
		lockfd = -1;
+
	}
+
	return (lockfd);
+
}
+

+
/*
+
 * Create and open a presmuably safe temp file for editing group data
+
 */
+
int
+
gr_tmp(int mfd)
+
{
+
	char buf[8192];
+
	ssize_t nr;
+
	const char *p;
+
	int tfd;
+

+
	if (*group_file == '\0')
+
		return (-1);
+
	if ((p = strrchr(group_file, '/')))
+
		++p;
+
	else
+
		p = group_file;
+
	if (snprintf(tempname, sizeof(tempname), "%.*sgroup.XXXXXX",
+
		(int)(p - group_file), group_file) >= (int)sizeof(tempname)) {
+
		errno = ENAMETOOLONG;
+
		return (-1);
+
	}
+
	if ((tfd = mkstemp(tempname)) == -1)
+
		return (-1);
+
	if (mfd != -1) {
+
		while ((nr = read(mfd, buf, sizeof(buf))) > 0)
+
			if (write(tfd, buf, (size_t)nr) != nr)
+
				break;
+
		if (nr != 0) {
+
			unlink(tempname);
+
			*tempname = '\0';
+
			close(tfd);
+
			return (-1);
+
		}
+
	}
+
	return (tfd);
+
}
+

+
/*
+
 * Copy the group file from one descriptor to another, replacing, deleting
+
 * or adding a single record on the way.
+
 */
+
int
+
gr_copy(int ffd, int tfd, const struct group *gr, struct group *old_gr)
+
{
+
	char buf[8192], *end, *line, *p, *q, *r, t;
+
	struct group *fgr;
+
	const struct group *sgr;
+
	size_t len;
+
	int eof, readlen;
+

+
	sgr = gr;
+
	if (gr == NULL) {
+
		line = NULL;
+
		if (old_gr == NULL)
+
			return (-1);
+
		sgr = old_gr;
+
	} else if ((line = gr_make(gr)) == NULL)
+
		return (-1);
+

+
	eof = 0;
+
	len = 0;
+
	p = q = end = buf;
+
	for (;;) {
+
		/* find the end of the current line */
+
		for (p = q; q < end && *q != '\0'; ++q)
+
			if (*q == '\n')
+
				break;
+

+
		/* if we don't have a complete line, fill up the buffer */
+
		if (q >= end) {
+
			if (eof)
+
				break;
+
			if ((size_t)(q - p) >= sizeof(buf)) {
+
				warnx("group line too long");
+
				errno = EINVAL; /* hack */
+
				goto err;
+
			}
+
			if (p < end) {
+
				q = memmove(buf, p, end -p);
+
				end -= p - buf;
+
			} else {
+
				p = q = end = buf;
+
			}
+
			readlen = read(ffd, end, sizeof(buf) - (end -buf));
+
			if (readlen == -1)
+
				goto err;
+
			else
+
				len = (size_t)readlen;
+
			if (len == 0 && p == buf)
+
				break;
+
			end += len;
+
			len = end - buf;
+
			if (len < (ssize_t)sizeof(buf)) {
+
				eof = 1;
+
				if (len > 0 && buf[len -1] != '\n')
+
					++len, *end++ = '\n';
+
			}
+
			continue;
+
		}
+

+
		/* is it a blank line or a comment? */
+
		for (r = p; r < q && isspace(*r); ++r)
+
			/* nothing */;
+
		if (r == q || *r == '#') {
+
			/* yep */
+
			if (write(tfd, p, q -p + 1) != q - p + 1)
+
				goto err;
+
			++q;
+
			continue;
+
		}
+

+
		/* is it the one we're looking for? */
+

+
		t = *q;
+
		*q = '\0';
+

+
		fgr = gr_scan(r);
+

+
		/* fgr is either a struct group for the current line,
+
		 * or NULL if the line is malformed.
+
		 */
+

+
		*q = t;
+
		if (fgr == NULL || fgr->gr_gid != sgr->gr_gid) {
+
			/* nope */
+
			if (fgr != NULL)
+
				free(fgr);
+
			if (write(tfd, p, q - p + 1) != q - p + 1)
+
				goto err;
+
			++q;
+
			continue;
+
		}
+
		if (old_gr && !gr_equal(fgr, old_gr)) {
+
			warnx("entry inconsistent");
+
			free(fgr);
+
			errno = EINVAL; /* hack */
+
			goto err;
+
		}
+
		free(fgr);
+

+
		/* it is, replace or remove it */
+
		if (line != NULL) {
+
			len = strlen(line);
+
			if (write(tfd, line, len) != (int) len)
+
				goto err;
+
		} else {
+
			/* when removed, avoid the \n */
+
			q++;
+
		}
+
		/* we're done, just copy the rest over */
+
		for (;;) {
+
			if (write(tfd, q, end - q) != end - q)
+
				goto err;
+
			q = buf;
+
			readlen = read(ffd, buf, sizeof(buf));
+
			if (readlen == 0)
+
				break;
+
			else
+
				len = (size_t)readlen;
+
			if (readlen == -1)
+
				goto err;
+
			end = buf + len;
+
		}
+
		goto done;
+
	}
+

+
	/* if we got here, we didn't find the old entry */
+
	if (line == NULL) {
+
		errno = ENOENT;
+
		goto err;
+
	}
+
	len = strlen(line);
+
	if ((size_t)write(tfd, line, len) != len ||
+
	   write(tfd, "\n", 1) != 1)
+
		goto err;
+
 done:
+
	if (line != NULL)
+
		free(line);
+
	return (0);
+
 err:
+
	if (line != NULL)
+
		free(line);
+
	return (-1);
+
}
+

+
/*
+
 * Regenerate the group file
+
 */
+
int
+
gr_mkdb(void)
+
{
+
	if (chmod(tempname, 0644) != 0)
+
		return (-1);
+

+
	return (rename(tempname, group_file));
+
}
+

+
/*
+
 * Clean up. Preserves errno for the caller's convenience.
+
 */
+
void
+
gr_fini(void)
+
{
+
	int serrno;
+

+
	if (!initialized)
+
		return;
+
	initialized = 0;
+
	serrno = errno;
+
	if (*tempname != '\0') {
+
		unlink(tempname);
+
		*tempname = '\0';
+
	}
+
	if (lockfd != -1)
+
		close(lockfd);
+
	errno = serrno;
+
}
+

+
/*
+
 * Compares two struct group's.
+
 */
+
int
+
gr_equal(const struct group *gr1, const struct group *gr2)
+
{
+
	int gr1_ndx;
+
	int gr2_ndx;
+

+
	/* Check that the non-member information is the same. */
+
	if (gr1->gr_name == NULL || gr2->gr_name == NULL) {
+
		if (gr1->gr_name != gr2->gr_name)
+
			return (false);
+
	} else if (strcmp(gr1->gr_name, gr2->gr_name) != 0)
+
		return (false);
+
	if (gr1->gr_passwd == NULL || gr2->gr_passwd == NULL) {
+
		if (gr1->gr_passwd != gr2->gr_passwd)
+
			return (false);
+
	} else if (strcmp(gr1->gr_passwd, gr2->gr_passwd) != 0)
+
		return (false);
+
	if (gr1->gr_gid != gr2->gr_gid)
+
		return (false);
+

+
	/* Check all members in both groups.
+
	 * getgrnam can return gr_mem with a pointer to NULL.
+
	 * gr_dup and gr_add strip out this superfluous NULL, setting
+
	 * gr_mem to NULL for no members.
+
	*/
+
	if (gr1->gr_mem != NULL && gr2->gr_mem != NULL) {
+
		int i;
+

+
		for (i = 0; gr1->gr_mem[i] != NULL; i++) {
+
			if (strcmp(gr1->gr_mem[i], gr2->gr_mem[i]) != 0)
+
				return (false);
+
		}
+
	}
+
	/* Count number of members in both structs */
+
	gr2_ndx = 0;
+
	if (gr2->gr_mem != NULL)
+
		for(; gr2->gr_mem[gr2_ndx] != NULL; gr2_ndx++)
+
			/* empty */;
+
	gr1_ndx = 0;
+
	if (gr1->gr_mem != NULL)
+
		for(; gr1->gr_mem[gr1_ndx] != NULL; gr1_ndx++)
+
			/* empty */;
+
	if (gr1_ndx != gr2_ndx)
+
		return (false);
+

+
	return (true);
+
}
+

+
/*
+
 * Make a group line out of a struct group.
+
 */
+
char *
+
gr_make(const struct group *gr)
+
{
+
	const char *group_line_format = "%s:%s:%ju:";
+
	const char *sep;
+
	char *line;
+
	char *p;
+
	size_t line_size;
+
	int ndx;
+

+
	/* Calculate the length of the group line. */
+
	line_size = snprintf(NULL, 0, group_line_format, gr->gr_name,
+
	    gr->gr_passwd, (uintmax_t)gr->gr_gid) + 1;
+
	if (gr->gr_mem != NULL) {
+
		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++)
+
			line_size += strlen(gr->gr_mem[ndx]) + 1;
+
		if (ndx > 0)
+
			line_size--;
+
	}
+

+
	/* Create the group line and fill it. */
+
	if ((line = p = malloc(line_size)) == NULL)
+
		return (NULL);
+
	p += sprintf(p, group_line_format, gr->gr_name, gr->gr_passwd,
+
	    (uintmax_t)gr->gr_gid);
+
	if (gr->gr_mem != NULL) {
+
		sep = "";
+
		for (ndx = 0; gr->gr_mem[ndx] != NULL; ndx++) {
+
			p = stpcpy(p, sep);
+
			p = stpcpy(p, gr->gr_mem[ndx]);
+
			sep = ",";
+
		}
+
	}
+

+
	return (line);
+
}
+

+
/*
+
 * Duplicate a struct group.
+
 */
+
struct group *
+
gr_dup(const struct group *gr)
+
{
+
	return (gr_add(gr, NULL));
+
}
+
/*
+
 * Add a new member name to a struct group.
+
 */
+
struct group *
+
gr_add(const struct group *gr, const char *newmember)
+
{
+
	char *mem;
+
	size_t len;
+
	int num_mem;
+

+
	num_mem = 0;
+
	len = grmemlen(gr, newmember, &num_mem);
+
	/* Create new group and copy old group into it. */
+
	if ((mem = malloc(len)) == NULL)
+
		return (NULL);
+
	return (grcopy(gr, mem, newmember, num_mem));
+
}
+

+
/* It is safer to walk the pointers given at gr_mem since there is no
+
 * guarantee the gr_mem + strings are contiguous in the given struct group
+
 * but compactify the new group into the following form.
+
 *
+
 * The new struct is laid out like this in memory. The example given is
+
 * for a group with two members only.
+
 *
+
 * {
+
 * (char *name)
+
 * (char *passwd)
+
 * (int gid)
+
 * (gr_mem * newgrp + sizeof(struct group) + sizeof(**)) points to gr_mem area
+
 * gr_mem area
+
 * (member1 *) 
+
 * (member2 *)
+
 * (NULL)
+
 * (name string)
+
 * (passwd string)
+
 * (member1 string)
+
 * (member2 string)
+
 * }
+
 */
+
/*
+
 * Copy the contents of a group plus given name to a preallocated group struct
+
 */
+
static struct group *
+
grcopy(const struct group *gr, char *dst, const char *name, int ndx)
+
{
+
	int i;
+
	struct group *newgr;
+

+
	newgr = (struct group *)(void *)dst;	/* avoid alignment warning */
+
	dst += sizeof(*newgr);
+
	if (ndx != 0) {
+
		newgr->gr_mem = (char **)(void *)(dst);	/* avoid alignment warning */
+
		dst += (ndx + 1) * sizeof(*newgr->gr_mem);
+
	} else
+
		newgr->gr_mem = NULL;
+
	if (gr->gr_name != NULL) {
+
		newgr->gr_name = dst;
+
		dst = stpcpy(dst, gr->gr_name) + 1;
+
	} else
+
		newgr->gr_name = NULL;
+
	if (gr->gr_passwd != NULL) {
+
		newgr->gr_passwd = dst;
+
		dst = stpcpy(dst, gr->gr_passwd) + 1;
+
	} else
+
		newgr->gr_passwd = NULL;
+
	newgr->gr_gid = gr->gr_gid;
+
	i = 0;
+
	/* Original group struct might have a NULL gr_mem */
+
	if (gr->gr_mem != NULL) {
+
		for (; gr->gr_mem[i] != NULL; i++) {
+
			newgr->gr_mem[i] = dst;
+
			dst = stpcpy(dst, gr->gr_mem[i]) + 1;
+
		}
+
	}
+
	/* If name is not NULL, newgr->gr_mem is known to be not NULL */
+
	if (name != NULL) {
+
		newgr->gr_mem[i++] = dst;
+
		dst = stpcpy(dst, name) + 1;
+
	}
+
	/* if newgr->gr_mem is not NULL add NULL marker */
+
	if (newgr->gr_mem != NULL)
+
		newgr->gr_mem[i] = NULL;
+

+
	return (newgr);
+
}
+

+
/*
+
 *  Calculate length of a struct group + given name
+
 */
+
static size_t
+
grmemlen(const struct group *gr, const char *name, int *num_mem)
+
{
+
	size_t len;
+
	int i;
+

+
	if (gr == NULL)
+
		return (0);
+
	/* Calculate size of the group. */
+
	len = sizeof(*gr);
+
	if (gr->gr_name != NULL)
+
		len += strlen(gr->gr_name) + 1;
+
	if (gr->gr_passwd != NULL)
+
		len += strlen(gr->gr_passwd) + 1;
+
	i = 0;
+
	if (gr->gr_mem != NULL) {
+
		for (; gr->gr_mem[i] != NULL; i++) {
+
			len += strlen(gr->gr_mem[i]) + 1;
+
			len += sizeof(*gr->gr_mem);
+
		}
+
	}
+
	if (name != NULL) {
+
		i++;
+
		len += strlen(name) + 1;
+
		len += sizeof(*gr->gr_mem);
+
	}
+
	/* Allow for NULL pointer */
+
	if (i != 0)
+
		len += sizeof(*gr->gr_mem);
+
	*num_mem = i;
+
	return(len);
+
}
+

+
/*
+
 * Scan a line and place it into a group structure.
+
 */
+
static bool
+
__gr_scan(char *line, struct group *gr)
+
{
+
	char *loc;
+
	int ndx;
+

+
	/* Assign non-member information to structure. */
+
	gr->gr_name = line;
+
	if ((loc = strchr(line, ':')) == NULL)
+
		return (false);
+
	*loc = '\0';
+
	gr->gr_passwd = loc + 1;
+
	if (*gr->gr_passwd == ':')
+
		*gr->gr_passwd = '\0';
+
	else {
+
		if ((loc = strchr(loc + 1, ':')) == NULL)
+
			return (false);
+
		*loc = '\0';
+
	}
+
	if (sscanf(loc + 1, "%u", &gr->gr_gid) != 1)
+
		return (false);
+

+
	/* Assign member information to structure. */
+
	if ((loc = strchr(loc + 1, ':')) == NULL)
+
		return (false);
+
	line = loc + 1;
+
	gr->gr_mem = NULL;
+
	ndx = 0;
+
	do {
+
		gr->gr_mem = reallocf(gr->gr_mem, sizeof(*gr->gr_mem) *
+
		    (ndx + 1));
+
		if (gr->gr_mem == NULL)
+
			return (false);
+

+
		/* Skip locations without members (i.e., empty string). */
+
		do {
+
			gr->gr_mem[ndx] = strsep(&line, ",");
+
		} while (gr->gr_mem[ndx] != NULL && *gr->gr_mem[ndx] == '\0');
+
	} while (gr->gr_mem[ndx++] != NULL);
+

+
	return (true);
+
}
+

+
/*
+
 * Create a struct group from a line.
+
 */
+
struct group *
+
gr_scan(const char *line)
+
{
+
	struct group gr;
+
	char *line_copy;
+
	struct group *new_gr;
+

+
	if ((line_copy = strdup(line)) == NULL)
+
		return (NULL);
+
	if (!__gr_scan(line_copy, &gr)) {
+
		free(line_copy);
+
		return (NULL);
+
	}
+
	new_gr = gr_dup(&gr);
+
	free(line_copy);
+
	if (gr.gr_mem != NULL)
+
		free(gr.gr_mem);
+

+
	return (new_gr);
+
}
+

+
#endif
added compat/gr_util.h
@@ -0,0 +1,60 @@
+
/*
+
 * Copyright (c) 1996  Peter Wemm <peter@FreeBSD.org>.
+
 * All rights reserved.
+
 * Copyright (c) 2002 Networks Associates Technology, Inc.
+
 * All rights reserved.
+
 *
+
 * Portions of this software were developed for the FreeBSD Project by
+
 * ThinkSec AS and NAI Labs, the Security Research Division of Network
+
 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
+
 * ("CBOSS"), as part of the DARPA CHATS research program.
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, is 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. 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 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: releng/10.1/lib/libutil/libutil.h 247919 2013-03-07 19:00:00Z db $
+
 */
+

+
#ifndef _GR_UTIL_H_
+
#define	_GR_UTIL_H_
+

+
#include <grp.h>
+

+
int 	gr_copy(int __ffd, int _tfd, const struct group *_gr,
+
	    struct group *_old_gr);
+
struct group *
+
	gr_dup(const struct group *_gr);
+
struct group *
+
	gr_add(const struct group *_gr, const char *_newmember);
+
int	gr_equal(const struct group *_gr1, const struct group *_gr2);
+
void	gr_fini(void);
+
int	gr_init(const char *_dir, const char *_master);
+
int	gr_lock(void);
+
char	*gr_make(const struct group *_gr);
+
int	gr_mkdb(void);
+
struct group *
+
	gr_scan(const char *_line);
+
int	gr_tmp(int _mdf);
+

+
#endif /* !_GR_UTIL_H_ */
added compat/humanize_number.c
@@ -0,0 +1,182 @@
+
/*	$NetBSD: humanize_number.c,v 1.14 2008/04/28 20:22:59 martin Exp $	*/
+

+
/*
+
 * Copyright (c) 1997, 1998, 1999, 2002 The NetBSD Foundation, Inc.
+
 * Copyright 2013 John-Mark Gurney <jmg@FreeBSD.org>
+
 * All rights reserved.
+
 *
+
 * This code is derived from software contributed to The NetBSD Foundation
+
 * by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
+
 * NASA Ames Research Center, by Luke Mewburn and by Tomas Svensson.
+
 *
+
 * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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.
+
 */
+

+
#include "bsd_compat.h"
+

+
#if !HAVE_HUMANIZE_NUMBER
+

+
#include <sys/types.h>
+
#include <assert.h>
+
#include <inttypes.h>
+
#include <stdio.h>
+
#include <stdlib.h>
+
#include <string.h>
+
#include <locale.h>
+

+
#include "humanize_number.h"
+

+
static const int maxscale = 7;
+

+
int
+
humanize_number(char *buf, size_t len, int64_t quotient,
+
    const char *suffix, int scale, int flags)
+
{
+
	const char *prefixes, *sep;
+
	int	i, r, remainder, s1, s2, sign;
+
	int	divisordeccut;
+
	int64_t	divisor, max;
+
	size_t	baselen;
+

+
	/* Since so many callers don't check -1, NUL terminate the buffer */
+
	if (len > 0)
+
		buf[0] = '\0';
+

+
	/* validate args */
+
	if (buf == NULL || suffix == NULL)
+
		return (-1);
+
	if (scale < 0)
+
		return (-1);
+
	else if (scale >= maxscale &&
+
	    ((scale & ~(HN_AUTOSCALE|HN_GETSCALE)) != 0))
+
		return (-1);
+
	if ((flags & HN_DIVISOR_1000) && (flags & HN_IEC_PREFIXES))
+
		return (-1);
+

+
	/* setup parameters */
+
	remainder = 0;
+

+
	if (flags & HN_IEC_PREFIXES) {
+
		baselen = 2;
+
		/*
+
		 * Use the prefixes for power of two recommended by
+
		 * the International Electrotechnical Commission
+
		 * (IEC) in IEC 80000-3 (i.e. Ki, Mi, Gi...).
+
		 *
+
		 * HN_IEC_PREFIXES implies a divisor of 1024 here
+
		 * (use of HN_DIVISOR_1000 would have triggered
+
		 * an assertion earlier).
+
		 */
+
		divisor = 1024;
+
		divisordeccut = 973;	/* ceil(.95 * 1024) */
+
		if (flags & HN_B)
+
			prefixes = "B\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
+
		else
+
			prefixes = "\0\0\0Ki\0Mi\0Gi\0Ti\0Pi\0Ei";
+
	} else {
+
		baselen = 1;
+
		if (flags & HN_DIVISOR_1000) {
+
			divisor = 1000;
+
			divisordeccut = 950;
+
			if (flags & HN_B)
+
				prefixes = "B\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+
			else
+
				prefixes = "\0\0\0k\0\0M\0\0G\0\0T\0\0P\0\0E";
+
		} else {
+
			divisor = 1024;
+
			divisordeccut = 973;	/* ceil(.95 * 1024) */
+
			if (flags & HN_B)
+
				prefixes = "B\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+
			else
+
				prefixes = "\0\0\0K\0\0M\0\0G\0\0T\0\0P\0\0E";
+
		}
+
	}
+

+
#define	SCALE2PREFIX(scale)	(&prefixes[(scale) * 3])
+

+
	if (quotient < 0) {
+
		sign = -1;
+
		quotient = -quotient;
+
		baselen += 2;		/* sign, digit */
+
	} else {
+
		sign = 1;
+
		baselen += 1;		/* digit */
+
	}
+
	if (flags & HN_NOSPACE)
+
		sep = "";
+
	else {
+
		sep = " ";
+
		baselen++;
+
	}
+
	baselen += strlen(suffix);
+

+
	/* Check if enough room for `x y' + suffix + `\0' */
+
	if (len < baselen + 1)
+
		return (-1);
+

+
	if (scale & (HN_AUTOSCALE | HN_GETSCALE)) {
+
		/* See if there is additional columns can be used. */
+
		for (max = 1, i = len - baselen; i-- > 0;)
+
			max *= 10;
+

+
		/*
+
		 * Divide the number until it fits the given column.
+
		 * If there will be an overflow by the rounding below,
+
		 * divide once more.
+
		 */
+
		for (i = 0;
+
		    (quotient >= max || (quotient == max - 1 &&
+
		    remainder >= divisordeccut)) && i < maxscale; i++) {
+
			remainder = quotient % divisor;
+
			quotient /= divisor;
+
		}
+

+
		if (scale & HN_GETSCALE)
+
			return (i);
+
	} else {
+
		for (i = 0; i < scale && i < maxscale; i++) {
+
			remainder = quotient % divisor;
+
			quotient /= divisor;
+
		}
+
	}
+

+
	/* If a value <= 9.9 after rounding and ... */
+
	/*
+
	 * XXX - should we make sure there is enough space for the decimal
+
	 * place and if not, don't do HN_DECIMAL?
+
	 */
+
	if (((quotient == 9 && remainder < divisordeccut) || quotient < 9) &&
+
	    i > 0 && flags & HN_DECIMAL) {
+
		s1 = (int)quotient + ((remainder * 10 + divisor / 2) /
+
		    divisor / 10);
+
		s2 = ((remainder * 10 + divisor / 2) / divisor) % 10;
+
		r = snprintf(buf, len, "%d%s%d%s%s%s",
+
		    sign * s1, localeconv()->decimal_point, s2,
+
		    sep, SCALE2PREFIX(i), suffix);
+
	} else
+
		r = snprintf(buf, len, "%" PRId64 "%s%s%s",
+
		    sign * (quotient + (remainder + divisor / 2) / divisor),
+
		    sep, SCALE2PREFIX(i), suffix);
+

+
	return (r);
+
}
+
#endif
added compat/humanize_number.h
@@ -0,0 +1,59 @@
+
/*
+
 * Copyright (c) 1996  Peter Wemm <peter@FreeBSD.org>.
+
 * All rights reserved.
+
 * Copyright (c) 2002 Networks Associates Technology, Inc.
+
 * All rights reserved.
+
 *
+
 * Portions of this software were developed for the FreeBSD Project by
+
 * ThinkSec AS and NAI Labs, the Security Research Division of Network
+
 * Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
+
 * ("CBOSS"), as part of the DARPA CHATS research program.
+
 *
+
 * Redistribution and use in source and binary forms, with or without
+
 * modification, is 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. 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 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: releng/10.1/lib/libutil/libutil.h 247919 2013-03-07 19:00:00Z db $
+
 */
+

+
#ifndef _HUMANIZENUMBER_H_
+
#define	_HUMANIZENUMBER_H_
+

+
#include <sys/types.h>
+

+
int
+
humanize_number(char *buf, size_t len, int64_t quotient,
+
    const char *suffix, int scale, int flags);
+

+
/* Values for humanize_number(3)'s flags parameter. */
+
#define	HN_DECIMAL		0x01
+
#define	HN_NOSPACE		0x02
+
#define	HN_B			0x04
+
#define	HN_DIVISOR_1000		0x08
+
#define	HN_IEC_PREFIXES		0x10
+

+
/* Values for humanize_number(3)'s scale parameter. */
+
#define	HN_GETSCALE		0x10
+
#define	HN_AUTOSCALE		0x20
+

+
#endif /* !_HUMANIZENUMBER_H_ */
added compat/strtonum.c
@@ -0,0 +1,71 @@
+
/*-
+
 * Copyright (c) 2004 Ted Unangst and Todd Miller
+
 * All rights reserved.
+
 *
+
 * Permission to use, copy, modify, and distribute this software for any
+
 * purpose with or without fee is hereby granted, provided that the above
+
 * copyright notice and this permission notice appear in all copies.
+
 *
+
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
 *
+
 *	$OpenBSD: strtonum.c,v 1.7 2013/04/17 18:40:58 tedu Exp $
+
 */
+

+
#include "bsd_compat.h"
+

+
#if !HAVE_STRTONUM
+

+
#include <errno.h>
+
#include <limits.h>
+
#include <stdlib.h>
+

+
#define	INVALID		1
+
#define	TOOSMALL	2
+
#define	TOOLARGE	3
+

+
long long
+
strtonum(const char *numstr, long long minval, long long maxval,
+
    const char **errstrp)
+
{
+
	long long ll = 0;
+
	int error = 0;
+
	char *ep;
+
	struct errval {
+
		const char *errstr;
+
		int err;
+
	} ev[4] = {
+
		{ NULL,		0 },
+
		{ "invalid",	EINVAL },
+
		{ "too small",	ERANGE },
+
		{ "too large",	ERANGE },
+
	};
+

+
	ev[0].err = errno;
+
	errno = 0;
+
	if (minval > maxval) {
+
		error = INVALID;
+
	} else {
+
		ll = strtoll(numstr, &ep, 10);
+
		if (errno == EINVAL || numstr == ep || *ep != '\0')
+
			error = INVALID;
+
		else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
+
			error = TOOSMALL;
+
		else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
+
			error = TOOLARGE;
+
	}
+
	if (errstrp != NULL)
+
		*errstrp = ev[error].errstr;
+
	errno = ev[error].err;
+
	if (error)
+
		ll = 0;
+

+
	return (ll);
+
}
+

+
#endif
modified configure.ac
@@ -67,6 +67,8 @@ AC_LINK_IFELSE([
 	AC_MSG_RESULT([no])
])

+
gl_LD_VERSION_SCRIPT
+

AC_CHECK_HEADERS_ONCE([machine/endian.h])
AC_CHECK_HEADERS_ONCE([endian.h])
AC_CHECK_HEADERS_ONCE([sys/endian.h])
@@ -116,6 +118,9 @@ AC_CHECK_FUNCS_ONCE([usleep])
AC_CHECK_FUNCS_ONCE([localtime_r])
AC_CHECK_FUNCS_ONCE([gmtime_r])
AC_CHECK_FUNCS_ONCE([strerror_r])
+
AC_CHECK_FUNCS_ONCE([strtonum])
+
AC_CHECK_FUNCS_ONCE([fstatat], [openat], [unlinkat])
+
AC_CHECK_FUNCS_ONCE([flopen])

PKG_PROG_PKG_CONFIG

@@ -230,6 +235,34 @@ if test "$ac_cv_func_arc4random_uniform" = yes ; then
	AC_DEFINE(HAVE_ARC4RANDOM, 1, [Define 1 if you have 'arc4random_uniform' function.])
fi

+
AC_CACHE_CHECK(for BSD dirname(const char *),
+
               ac_cv_func_dirname,
+
               [ac_save_CFLAGS="$CFLAGS"
+
		CFLAGS="-Werror $CFLAGS"
+
		AC_TRY_COMPILE([#include <libgen.h>],
+
                               [static char *(*ac_test_dirname)(const char *) = dirname; ac_test_dirname("");],
+
                               [ac_cv_func_dirname=yes],
+
                               [ac_cv_func_dirname=no])
+
		CFLAGS="$ac_save_CFLAGS"])
+
if test "$ac_cv_func_dirname" = yes ; then
+
	AC_DEFINE(HAVE_BSD_DIRNAME, 1, [Define 1 if you have 'dirname(const char *)' function.])
+
fi
+

+
AC_CHECK_FUNCS(basename_r)
+
AC_CACHE_CHECK(for BSD basename(const char *),
+
               ac_cv_func_basename,
+
               [ac_save_CFLAGS="$CFLAGS"
+
		CFLAGS="-Werror $CFLAGS"
+
		AC_TRY_COMPILE([#include <libgen.h>],
+
                               [static char *(*ac_test_basename)(const char *) = basename; ac_test_basename("");],
+
                               [ac_cv_func_basename=yes],
+
                               [ac_cv_func_basename=no])
+
		CFLAGS="$ac_save_CFLAGS"])
+
if test "$ac_cv_func_basename" = yes ; then
+
	AC_DEFINE(HAVE_BSD_BASENAME, 1, [Define 1 if you have 'basename(const char *)' function.])
+
fi
+

+

AC_MSG_CHECKING([whether we should only build the static version of pkg])
AC_ARG_WITH([staticonly],
	      [AS_HELP_STRING([--with-staticonly],
@@ -285,6 +318,7 @@ AC_CONFIG_FILES(Makefile \
		scripts/Makefile \
		docs/Makefile \
		tests/Makefile \
+
		compat/Makefile \
		libpkg/Makefile \
		libpkg/repo/Makefile \
		libpkg/pkg.h \
modified libpkg/Makefile.am
@@ -2,6 +2,7 @@ EXTRA_DIST= libpkg.ver

pkg_common_cflags=	-I$(top_srcdir)/libpkg -I$(top_builddir)/libpkg \
			@LDNS_CFLAGS@ \
+
			-I$(top_srcdir)/compat \
			-I$(top_srcdir)/external/libsbuf \
			-I$(top_srcdir)/external/expat/lib \
			-I$(top_srcdir)/external/libucl/include \
@@ -59,7 +60,8 @@ libpkg_la_SOURCES= pkg.c \
			merge3.c \
			diff.c
libpkg_la_CFLAGS=	$(pkg_common_cflags) -shared
-
libpkg_la_LIBADD=	$(top_builddir)/external/libucl.la \
+
libpkg_la_LIBADD=	$(top_builddir)/compat/libbsd_compat.la \
+
			$(top_builddir)/external/libucl.la \
			$(top_builddir)/external/libsqlite.la \
			$(top_builddir)/external/libexpat.la \
			$(top_builddir)/external/libsbuf.la \
@@ -79,8 +81,13 @@ else
libpkg_la_LIBADD+=	-lelf
endif

-
libpkg_la_LDFLAGS=	-version-info @LIBPKG_SO_VERSION@ -Wl,--version-script=$(srcdir)/libpkg.ver
-
EXTRA_libpkg_la_DEPENDENCIES=	@REPOS_LDADD@ libpkg.ver
+
libpkg_la_LDFLAGS=	-version-info @LIBPKG_SO_VERSION@
+
EXTRA_libpkg_la_DEPENDENCIES=	@REPOS_LDADD@
+

+
if HAVE_LD_VERSION_SCRIPT
+
libpkg_la_LDFLAGS+=	-version-info @LIBPKG_SO_VERSION@ -Wl,--version-script=$(srcdir)/libpkg.ver
+
EXTRA_libpkg_la_DEPENDENCIES+=	libpkg.ver
+
endif

DYNLIBS=		libpkg.la
lib_LTLIBRARIES=	libpkg_static.la
modified m4/ax_cflags_warn_all.m4
@@ -68,6 +68,7 @@ VAR,[VAR="no, unknown"
ac_save_[]FLAGS="$[]FLAGS"
for ac_arg dnl
in "-warn all  % -warn all"   dnl Intel
+
   "-pedantic  % -Wall -Wno-unused-function -Wno-parentheses-equality" dnl clang
   "-pedantic  % -Wall -Wno-unused-function"       dnl GCC
   "-xstrconst % -v"          dnl Solaris C
   "-std1      % -verbose -w0 -warnprotos" dnl Digital Unix
added m4/ld-version-script.m4
@@ -0,0 +1,53 @@
+
# ld-version-script.m4 serial 3
+
dnl Copyright (C) 2008-2014 Free Software Foundation, Inc.
+
dnl This file is free software; the Free Software Foundation
+
dnl gives unlimited permission to copy and/or distribute it,
+
dnl with or without modifications, as long as this notice is preserved.
+

+
dnl From Simon Josefsson
+

+
# FIXME: The test below returns a false positive for mingw
+
# cross-compiles, 'local:' statements does not reduce number of
+
# exported symbols in a DLL.  Use --disable-ld-version-script to work
+
# around the problem.
+

+
# gl_LD_VERSION_SCRIPT
+
# --------------------
+
# Check if LD supports linker scripts, and define automake conditional
+
# HAVE_LD_VERSION_SCRIPT if so.
+
AC_DEFUN([gl_LD_VERSION_SCRIPT],
+
[
+
  AC_ARG_ENABLE([ld-version-script],
+
    AS_HELP_STRING([--enable-ld-version-script],
+
      [enable linker version script (default is enabled when possible)]),
+
      [have_ld_version_script=$enableval], [])
+
  if test -z "$have_ld_version_script"; then
+
    AC_MSG_CHECKING([if LD -Wl,--version-script works])
+
    save_LDFLAGS="$LDFLAGS"
+
    LDFLAGS="$LDFLAGS -Wl,--version-script=conftest.map"
+
    cat > conftest.map <<EOF
+
foo
+
EOF
+
    AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])],
+
                   [accepts_syntax_errors=yes], [accepts_syntax_errors=no])
+
    if test "$accepts_syntax_errors" = no; then
+
      cat > conftest.map <<EOF
+
VERS_1 {
+
        global: sym;
+
};
+

+
VERS_2 {
+
        global: sym;
+
} VERS_1;
+
EOF
+
      AC_LINK_IFELSE([AC_LANG_PROGRAM([], [])],
+
                     [have_ld_version_script=yes], [have_ld_version_script=no])
+
    else
+
      have_ld_version_script=no
+
    fi
+
    rm -f conftest.map
+
    LDFLAGS="$save_LDFLAGS"
+
    AC_MSG_RESULT($have_ld_version_script)
+
  fi
+
  AM_CONDITIONAL(HAVE_LD_VERSION_SCRIPT, test "$have_ld_version_script" = "yes")
+
])