Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge pull request #1113 from landonf/osx/release/0.1
Baptiste Daroussin committed 11 years ago
commit 3eafef19f76f518393eccf220429426ee6d5d254
parent 06f5db3
57 files changed +3741 -67
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 \
+
				closefrom.c \
+
				dirname.c \
+
				file_at.c \
+
				gr_util.c \
+
				humanize_number.c \
+
				strtonum.c
+

+
libbsd_compat_la_CFLAGS=	$(pkg_common_cflags) -shared
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,91 @@
+
/*-
+
 * 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"
+
#include "endian_util.h"
+

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

+
#if !HAVE_EACCESS
+
#define eaccess(_p, _m) access(_p, _m)
+
#endif
+

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

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

+
#if !HAVE_CLOSEFROM
+
void closefrom(int lowfd);
+
#endif
+

+
#ifndef AT_FDCWD
+
#define AT_FDCWD		-100
+
#endif
+

+
#ifndef AT_EACCESS
+
#define AT_EACCESS		0x100
+
#endif
+

+
#ifndef AT_SYMLINK_NOFOLLOW
+
#define	AT_SYMLINK_NOFOLLOW	0x200
+
#endif
+

+
#if !HAVE_FACCESSAT
+
int faccessat(int fd, const char *path, int mode, int flag);
+
#endif
+

+
#if !HAVE_FSTATAT
+
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_READLINKAT
+
ssize_t readlinkat(int fd, const char *restrict path, char *restrict buf, size_t bufsize);
+
#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/closefrom.c
@@ -0,0 +1,109 @@
+
/*
+
 * Copyright (c) 2004-2005 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"
+

+
#ifndef HAVE_CLOSEFROM
+

+
#include <sys/types.h>
+
#include <sys/param.h>
+
#include <unistd.h>
+
#include <stdio.h>
+
#ifdef HAVE_FCNTL_H
+
# include <fcntl.h>
+
#endif
+
#include <limits.h>
+
#include <stdlib.h>
+
#include <stddef.h>
+
#include <string.h>
+
#include <unistd.h>
+
#ifdef HAVE_DIRENT_H
+
# include <dirent.h>
+
# define NAMLEN(dirent) strlen((dirent)->d_name)
+
#else
+
# define dirent direct
+
# define NAMLEN(dirent) (dirent)->d_namlen
+
# ifdef HAVE_SYS_NDIR_H
+
#  include <sys/ndir.h>
+
# endif
+
# ifdef HAVE_SYS_DIR_H
+
#  include <sys/dir.h>
+
# endif
+
# ifdef HAVE_NDIR_H
+
#  include <ndir.h>
+
# endif
+
#endif
+

+
#ifndef OPEN_MAX
+
# define OPEN_MAX	256
+
#endif
+

+
#if 0
+
__unused static const char rcsid[] = "$Sudo: closefrom.c,v 1.11 2006/08/17 15:26:54 millert Exp $";
+
#endif /* lint */
+

+
/*
+
 * Close all file descriptors greater than or equal to lowfd.
+
 */
+
#ifdef HAVE_FCNTL_CLOSEM
+
void
+
closefrom(int lowfd)
+
{
+
    (void) fcntl(lowfd, F_CLOSEM, 0);
+
}
+
#else
+
void
+
closefrom(int lowfd)
+
{
+
    long fd, maxfd;
+
#if defined(HAVE_DIRFD) && defined(HAVE_PROC_PID)
+
    char fdpath[PATH_MAX], *endp;
+
    struct dirent *dent;
+
    DIR *dirp;
+
    int len;
+

+
    /* Check for a /proc/$$/fd directory. */
+
    len = snprintf(fdpath, sizeof(fdpath), "/proc/%ld/fd", (long)getpid());
+
    if (len > 0 && (size_t)len <= sizeof(fdpath) && (dirp = opendir(fdpath))) {
+
	while ((dent = readdir(dirp)) != NULL) {
+
	    fd = strtol(dent->d_name, &endp, 10);
+
	    if (dent->d_name != endp && *endp == '\0' &&
+
		fd >= 0 && fd < INT_MAX && fd >= lowfd && fd != dirfd(dirp))
+
		(void) close((int) fd);
+
	}
+
	(void) closedir(dirp);
+
    } else
+
#endif
+
    {
+
	/*
+
	 * Fall back on sysconf() or getdtablesize().  We avoid checking
+
	 * resource limits since it is possible to open a file descriptor
+
	 * and then drop the rlimit such that it is below the open fd.
+
	 */
+
#ifdef HAVE_SYSCONF
+
	maxfd = sysconf(_SC_OPEN_MAX);
+
#else
+
	maxfd = getdtablesize();
+
#endif /* HAVE_SYSCONF */
+
	if (maxfd < 0)
+
	    maxfd = OPEN_MAX;
+

+
	for (fd = lowfd; fd < maxfd; fd++)
+
	    (void) close((int) fd);
+
    }
+
}
+
#endif /* !HAVE_FCNTL_CLOSEM */
+
#endif /* HAVE_CLOSEFROM */
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/endian_util.h
@@ -0,0 +1,169 @@
+
/*-
+
 * Copyright (c) 2002 Thomas Moestl <tmm@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: releng/10.1/sys/sys/endian.h 208331 2010-05-20 06:16:13Z phk $
+
 */
+

+
#ifndef _ENDIAN_UTIL_H_
+
#define _ENDIAN_UTIL_H_
+

+
#include "pkg_config.h"
+

+
#include <sys/types.h>
+
#include <inttypes.h>
+

+
/* Alignment-agnostic encode/decode bytestream to/from little/big endian. */
+

+
#if defined(HAVE_DECL_BE16DEC) && !HAVE_DECL_BE16DEC
+
static __inline uint16_t
+
be16dec(const void *pp)
+
{
+
	uint8_t const *p = (uint8_t const *)pp;
+

+
	return ((p[0] << 8) | p[1]);
+
}
+
#endif
+

+
#if defined(HAVE_DECL_BE32DEC) && !HAVE_DECL_BE32DEC
+
static __inline uint32_t
+
be32dec(const void *pp)
+
{
+
	uint8_t const *p = (uint8_t const *)pp;
+

+
	return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
+
}
+
#endif
+

+
#if defined(HAVE_DECL_BE64DEC) && !HAVE_DECL_BE64DEC
+
static __inline uint64_t
+
be64dec(const void *pp)
+
{
+
	uint8_t const *p = (uint8_t const *)pp;
+

+
	return (((uint64_t)be32dec(p) << 32) | be32dec(p + 4));
+
}
+
#endif
+

+
#if defined(HAVE_DECL_LE16DEC) && !HAVE_DECL_LE16DEC
+
static __inline uint16_t
+
le16dec(const void *pp)
+
{
+
	uint8_t const *p = (uint8_t const *)pp;
+

+
	return ((p[1] << 8) | p[0]);
+
}
+
#endif
+

+
#if defined(HAVE_DECL_LE32DEC) && !HAVE_DECL_LE32DEC
+
static __inline uint32_t
+
le32dec(const void *pp)
+
{
+
	uint8_t const *p = (uint8_t const *)pp;
+

+
	return (((unsigned)p[3] << 24) | (p[2] << 16) | (p[1] << 8) | p[0]);
+
}
+
#endif
+

+
#if defined(HAVE_DECL_LE64DEC) && !HAVE_DECL_LE64DEC
+
static __inline uint64_t
+
le64dec(const void *pp)
+
{
+
	uint8_t const *p = (uint8_t const *)pp;
+

+
	return (((uint64_t)le32dec(p + 4) << 32) | le32dec(p));
+
}
+
#endif
+

+
#if defined(HAVE_DECL_BE16ENC) && !HAVE_DECL_BE16ENC
+
static __inline void
+
be16enc(void *pp, uint16_t u)
+
{
+
	uint8_t *p = (uint8_t *)pp;
+

+
	p[0] = (u >> 8) & 0xff;
+
	p[1] = u & 0xff;
+
}
+
#endif
+

+
#if defined(HAVE_DECL_BE32ENC) && !HAVE_DECL_BE32ENC
+
static __inline void
+
be32enc(void *pp, uint32_t u)
+
{
+
	uint8_t *p = (uint8_t *)pp;
+

+
	p[0] = (u >> 24) & 0xff;
+
	p[1] = (u >> 16) & 0xff;
+
	p[2] = (u >> 8) & 0xff;
+
	p[3] = u & 0xff;
+
}
+
#endif
+

+
#if defined(HAVE_DECL_BE64ENC) && !HAVE_DECL_BE64ENC
+
static __inline void
+
be64enc(void *pp, uint64_t u)
+
{
+
	uint8_t *p = (uint8_t *)pp;
+

+
	be32enc(p, (uint32_t)(u >> 32));
+
	be32enc(p + 4, (uint32_t)(u & 0xffffffffU));
+
}
+
#endif
+

+
#if defined(HAVE_DECL_LE16ENC) && !HAVE_DECL_LE16ENC
+
static __inline void
+
le16enc(void *pp, uint16_t u)
+
{
+
	uint8_t *p = (uint8_t *)pp;
+

+
	p[0] = u & 0xff;
+
	p[1] = (u >> 8) & 0xff;
+
}
+
#endif
+

+
#if defined(HAVE_DECL_LE32ENC) && !HAVE_DECL_LE32ENC
+
static __inline void
+
le32enc(void *pp, uint32_t u)
+
{
+
	uint8_t *p = (uint8_t *)pp;
+

+
	p[0] = u & 0xff;
+
	p[1] = (u >> 8) & 0xff;
+
	p[2] = (u >> 16) & 0xff;
+
	p[3] = (u >> 24) & 0xff;
+
}
+
#endif
+

+
#if defined(HAVE_DECL_LE64ENC) && !HAVE_DECL_LE64ENC
+
static __inline void
+
le64enc(void *pp, uint64_t u)
+
{
+
	uint8_t *p = (uint8_t *)pp;
+

+
	le32enc(p, (uint32_t)(u & 0xffffffffU));
+
	le32enc(p + 4, (uint32_t)(u >> 32));
+
}
+
#endif
+

+
#endif	/* _ENDIAN_UTIL_H_ */
added compat/file_at.c
@@ -0,0 +1,186 @@
+
/*-
+
 * 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_FACCESSAT
+
int
+
faccessat(int fd, const char *path, int mode, int flag)
+
{
+
	int ret;
+

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

+
	if (flag & AT_EACCESS) {
+
		ret = eaccess(path, mode);
+
	} else {
+
		ret = access(path, mode);
+
	}
+

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

+
#if !HAVE_READLINKAT
+
ssize_t
+
readlinkat(int fd, const char *restrict path, char *restrict buf,
+
	   size_t bufsize)
+
{
+
	int ret;
+

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

+
	ret = readlink(path, buf, bufsize);
+

+
	file_chdir_unlock(fd);
+
	return ret;
+
}
+
#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])
@@ -104,6 +106,7 @@ AC_CHECK_HEADERS_ONCE([stdio.h])
AC_CHECK_HEADERS_ONCE([float.h])
AC_CHECK_HEADERS_ONCE([math.h])
AC_CHECK_HEADERS_ONCE([osreldate.h])
+
AC_CHECK_HEADERS_ONCE([dirent.h], [sys/ndir.h], [sys/dir.h], [ndir.h])

AC_CHECK_HEADER([regex.h], [
	AC_DEFINE(HAVE_REGEX_H, 1, [Define to 1 if you have the <regex.h> header file.])
@@ -116,6 +119,18 @@ 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], [readlinkat], [faccessat])
+
AC_CHECK_DECLS(
+
	[[be16dec], [be32dec], [be64dec], [le16dec], [le32dec], [le64dec],
+
	 [be16enc], [be32enc], [be64enc], [le16enc], [le32enc], [le64enc]],
+
	[], [], [#include <sys/endian.h>]
+
)
+
AC_CHECK_FUNCS_ONCE([flopen])
+
AC_CHECK_FUNCS_ONCE([eaccess])
+
AC_CHECK_FUNCS_ONCE([closefrom])
+
AC_CHECK_FUNCS_ONCE([dirfd])
+
AC_CHECK_FUNCS_ONCE([sysconf])

PKG_PROG_PKG_CONFIG

@@ -230,6 +245,48 @@ 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([for /proc/pid/fd directory])
+
if test -d "/proc/$$/fd" ; then
+
        AC_DEFINE([HAVE_PROC_PID], [1], [Define if you have /proc/$pid/fd])
+
        AC_MSG_RESULT([yes])
+
else
+
        AC_MSG_RESULT([no])
+
fi
+

+
AC_CHECK_DECL([F_CLOSEM],
+
        AC_DEFINE([HAVE_FCNTL_CLOSEM], [1], [Use F_CLOSEM fcntl for closefrom]),
+
        [],
+
        [ #include <limits.h>
+
          #include <fcntl.h> ]
+
)
+

AC_MSG_CHECKING([whether we should only build the static version of pkg])
AC_ARG_WITH([staticonly],
	      [AS_HELP_STRING([--with-staticonly],
@@ -242,7 +299,17 @@ else
fi

AM_CONDITIONAL(DYNAMIC, test "$with_staticonly" != "no")
-
AM_CONDITIONAL(LIBELF_BUNDLED, test "$libelf_bundled" != "no")
+

+
dnl Select appropriate ABI
+
AS_CASE(
+
	[$host_os],
+
	[darwin*],
+
	[ac_cv_binary_abi=macho],
+
	[ac_cv_binary_abi=elf]
+
)
+
AM_CONDITIONAL(HAVE_MACHO_ABI, test "$ac_cv_binary_abi" = "macho")
+
AM_CONDITIONAL(HAVE_ELF_ABI, test "$ac_cv_binary_abi" = "elf")
+
AM_CONDITIONAL(LIBELF_BUNDLED, test "$ac_cv_binary_abi" = "elf" -a "$libelf_bundled" != "no")

AC_SUBST([LIBJAIL_LIB])
AC_SUBST([LIBEXECINFO_LIB])
@@ -285,6 +352,7 @@ AC_CONFIG_FILES(Makefile \
		scripts/Makefile \
		docs/Makefile \
		tests/Makefile \
+
		compat/Makefile \
		libpkg/Makefile \
		libpkg/repo/Makefile \
		libpkg/pkg.h \
modified external/Makefile.am
@@ -45,6 +45,8 @@ noinst_HEADERS= expat/amiga/expat_68k.h \
		libelf/elfdefinitions.h \
		libelf/gelf.h \
		libelf/libelf.h \
+
		libmachista/hashmap.h \
+
		libmachista/libmachista.h \
		libsbuf/sys/sbuf.h \
		libucl/include/ucl.h \
		libucl/src/xxhash.h \
@@ -79,6 +81,10 @@ DYNLIBS+= libelf.la
noinst_LTLIBRARIES+=	libelf_static.la
endif

+
if HAVE_MACHO_ABI
+
noinst_LTLIBRARIES+=	libmachista_static.la
+
endif
+

if DYNAMIC
noinst_LTLIBRARIES+=	$(DYNLIBS)
endif
@@ -146,6 +152,11 @@ libelf_static_la_SOURCES= ${libelf_la_SOURCES}
libelf_static_la_CFLAGS=	$(libelf_la_CFLAGS) -static
libelf_static_la_LDFLAGS=	-all-static

+
libmachista_static_la_SOURCES=	libmachista/libmachista.c \
+
				libmachista/hashmap.c
+
libmachista_static_la_CFLAGS=	-I$(tip_srcdir)/external/libmachista -static
+
libmachista_static_la_LDFLAGS=	-all-static
+

libsbuf_la_SOURCES=	libsbuf/subr_sbuf.c
sbuf_common_CFLAGS=	-I$(top_srcdir)/external/libsbuf
libsbuf_la_CFLAGS=	$(sbuf_common_CFLAGS) -shared
@@ -226,6 +237,7 @@ libsqlite_static_la_SOURCES= $(libsqlite_la_SOURCES)
libsqlite_static_la_CFLAGS=	$(sqlite_common_cflags) -static
libsqlite_static_la_LDFLAGS=	-all-static

+
if HAVE_ELF_ABI
CLEANFILES=	libelf/libelf_fsize.c \
			libelf/libelf_msize.c \
			libelf/libelf_convert.c \
@@ -248,3 +260,4 @@ libelf/libelf_msize.c:
	@m4 -D SRCDIR=$(srcdir)/libelf $(srcdir)/libelf/elf_types.m4 $(srcdir)/libelf/libelf_msize.m4 > $@
libelf/native-elf-format.h:
	$(srcdir)/libelf/native-elf-format > libelf/native-elf-format.h
+
endif
added external/libmachista/LICENSE
@@ -0,0 +1,52 @@
+
Copyright (c) 2011 Clemens Lang <cal@macports.org>
+
Copyright (c) 2011 - 2014 Landon Fuller <landonf@macports.org>
+
Copyright (c) 2004 - 2014, The MacPorts Project.
+
Copyright (c) 2002 - 2003, Apple Inc.
+
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. Neither the name of Apple Inc., The MacPorts Project 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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+

+
hashmap.c is subject to the following license:
+

+
Copyright (c) 2011 Christoph Erhardt. 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 CHRISTOPH ERHARDT ``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 CHRISTOPH ERHARDT 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.
added external/libmachista/hashmap.c
@@ -0,0 +1,295 @@
+
/* vim:expandtab:tw=80:ts=2:sts=2:sw=2
+
 */
+
/*-
+
 * Copyright (c) 2011 Christoph Erhardt. 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 CHRISTOPH ERHARDT ``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 CHRISTOPH ERHARDT 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.
+
 */
+
/*-
+
 * Modified by Clemens Lang to accept values of arbitrary type.
+
 */
+

+

+
#ifndef _BSD_SOURCE
+
  #define _BSD_SOURCE
+
#endif
+
#ifndef _CRT_NONSTDC_NO_DEPRECATE
+
  #define _CRT_NONSTDC_NO_DEPRECATE
+
#endif
+

+
#include "hashmap.h"
+
#include <limits.h>
+
#include <stdint.h>
+
#include <string.h>
+

+

+
static const size_t INITIAL_CAPACITY = 16; /* Must be a power of 2 */
+
static const size_t MAXIMUM_CAPACITY = (1U << 31);
+
static const float  LOAD_FACTOR      = 0.75;
+

+

+
typedef struct HashMapEntry {
+
  char                *key;
+
  const void          *value;
+
  struct HashMapEntry *next;
+
  uint32_t             hash;
+
} HashMapEntry;
+

+
struct HashMap {
+
  HashMapEntry **table;
+
  size_t         capacity;
+
  size_t         size;
+
  size_t         threshold;
+
  void         (*freeFunc)(const void *);
+
};
+

+

+
static void setTable(HashMap *map, HashMapEntry **table, size_t capacity) {
+
  map->table     = table;
+
  map->capacity  = capacity;
+
  map->threshold = (size_t) (capacity * LOAD_FACTOR);
+
}
+

+

+
static uint32_t doHash(const char key[]) {
+
  size_t   length;
+
  size_t   i;
+
  uint32_t h = 0;
+
  if (key == NULL)
+
    return 0;
+
  length = strlen(key);
+
  for (i = 0; i < length; ++i) {
+
    h = (31 * h) + key[i];
+
  }
+
  h ^= (h >> 20) ^ (h >> 12);
+
  return h ^ (h >> 7) ^ (h >> 4);
+
}
+

+

+
static size_t indexFor(uint32_t hash, size_t length) {
+
  return hash & (length - 1);
+
}
+

+

+
static int isHit(HashMapEntry *e, const char key[], uint32_t hash) {
+
  return (e->hash == hash
+
          && (e->key == key || (key != NULL && strcmp(e->key, key) == 0)));
+
}
+

+

+
static void copyOrFree(void (*freeFunc)(const void *),
+
                       const void *value, const void **valPtr) {
+
  if (valPtr != NULL)
+
    *valPtr = value;
+
  else
+
    freeFunc(value);
+
}
+

+

+
static int updateValue(HashMap *map, HashMapEntry *e, const void *newVal,
+
                       const void **oldValPtr) {
+
  copyOrFree(map->freeFunc, e->value, oldValPtr);
+
  e->value = newVal;
+
  return 1;
+
}
+

+

+
/* Creates a hash map. */
+
HashMap *hashMapCreate(void (*freeFunc)(const void *)) {
+
  HashMapEntry **table;
+
  HashMap       *map = malloc(sizeof(*map));
+
  if (map == NULL)
+
    return NULL;
+
  table = calloc(INITIAL_CAPACITY, sizeof(*map->table));
+
  if (table == NULL) {
+
    free(map);
+
    return NULL;
+
  }
+
  setTable(map, table, INITIAL_CAPACITY);
+
  map->size = 0;
+
  map->freeFunc = freeFunc;
+
  return map;
+
}
+

+

+
/* Inserts a key-value pair into a hash map. */
+
int hashMapPut(HashMap *map, const char key[], const void * const value,
+
               const void **oldValPtr) {
+

+
  HashMapEntry  *e;
+
  size_t         newCapacity;
+
  HashMapEntry **newTable;
+
  size_t         i;
+

+
  /* If an entry with the same key exists, update it */
+
  uint32_t hash  = doHash(key);
+
  size_t   index = indexFor(hash, map->capacity);
+
  for (e = map->table[index]; e != NULL; e = e->next) {
+
    if (isHit(e, key, hash) == 0)
+
      continue;
+
    return updateValue(map, e, value, oldValPtr);
+
  }
+

+
  /* Create a new entry */
+
  e = calloc(1, sizeof(HashMapEntry)); /* Must be zeroed */
+
  if (e == NULL)
+
    return 0;
+

+
  /* Copy key and value into the entry */
+
  if (key != NULL) {
+
    e->key = strdup(key);
+
    if (e->key == NULL) {
+
      free(e);
+
      return 0;
+
    }
+
  }
+
  if (updateValue(map, e, value, oldValPtr) == 0) {
+
    free(e->key);
+
    free(e);
+
    return 0;
+
  }
+

+
  /* Insert entry into the table */
+
  e->hash = hash;
+
  e->next = map->table[index];
+
  map->table[index] = e;
+
  if (map->size++ < map->threshold)
+
    return 1;
+

+
  /* If the size exceeds the threshold, double the table's capacity */
+
  newCapacity = 2 * map->capacity;
+
  if (map->capacity == MAXIMUM_CAPACITY) {
+
    map->threshold = UINT_MAX;
+
    return 1;
+
  }
+
  newTable = calloc(newCapacity, sizeof(*newTable));
+
  if (newTable == NULL)
+
    return 0;
+

+
  /* Copy entries from the old table into the new one */
+
  for (i = 0; i < map->capacity; ++i) {
+
    HashMapEntry *next;
+
    for (e = map->table[i]; e != NULL; e = next) {
+
      index   = indexFor(e->hash, newCapacity);
+
      next    = e->next;
+
      e->next = newTable[index];
+
      newTable[index] = e;
+
    }
+
  }
+

+
  /* Release the old table and set the new one */
+
  free(map->table);
+
  setTable(map, newTable, newCapacity);
+
  return 1;
+
}
+

+

+
/* Performs a hash map lookup. */
+
const void *hashMapGet(HashMap *map, const char key[]) {
+
  HashMapEntry *e;
+
  uint32_t      hash  = doHash(key);
+
  size_t        index = indexFor(hash, map->capacity);
+
  for (e = map->table[index]; e != NULL; e = e->next) {
+
    if (isHit(e, key, hash))
+
      return e->value;
+
  }
+
  return NULL;
+
}
+

+

+
/* Checks whether a hash map contains an entry with a certain key. */
+
int hashMapContainsKey(HashMap *map, const char key[]) {
+
  HashMapEntry *e;
+
  uint32_t      hash  = doHash(key);
+
  size_t        index = indexFor(hash, map->capacity);
+
  for (e = map->table[index]; e != NULL; e = e->next) {
+
    if (isHit(e, key, hash))
+
      return 1;
+
  }
+
  return 0;
+
}
+

+

+
/* Removes a key-value pair from a hash map. */
+
void hashMapRemove(HashMap *map, const char key[], const void **valPtr) {
+
  uint32_t      hash  = doHash(key);
+
  size_t        index = indexFor(hash, map->capacity);
+
  HashMapEntry *prev  = map->table[index];
+
  HashMapEntry *e     = prev;
+
  while (e != NULL) {
+
    HashMapEntry *next = e->next;
+
    if (isHit(e, key, hash)) {
+
      map->size--;
+
      if (prev == e)
+
        map->table[index] = next;
+
      else
+
        prev->next = next;
+
      break;
+
    }
+
    prev = e;
+
    e    = next;
+
  }
+
  if (e == NULL) {
+
    copyOrFree(map->freeFunc, NULL, valPtr);
+
    return;
+
  }
+
  free(e->key);
+
  copyOrFree(map->freeFunc, e->value, valPtr);
+
  free(e);
+
}
+

+

+
/* Returns the number of elements stored in a hash map. */
+
size_t hashMapSize(const HashMap *map) {
+
  return map->size;
+
}
+

+

+
/* Checks whether a hash map is empty. */
+
int hashMapIsEmpty(const HashMap *map) {
+
  return (map->size == 0);
+
}
+

+

+
/* Removes all entries from a hash map. */
+
void hashMapClear(HashMap *map) {
+
  size_t i;
+
  for (i = 0; i < map->capacity; ++i) {
+
    HashMapEntry *e;
+
    HashMapEntry *next;
+
    for (e = map->table[i]; e != NULL; e = next) {
+
      free(e->key);
+
      map->freeFunc(e->value);
+
      next = e->next;
+
      free(e);
+
    }
+
    map->table[i] = NULL;
+
  }
+
}
+

+

+
/* Destroys a hash map. */
+
void hashMapDestroy(HashMap *map) {
+
  if (map == NULL)
+
    return;
+
  hashMapClear(map);
+
  free(map->table);
+
  free(map);
+
}
added external/libmachista/hashmap.h
@@ -0,0 +1,135 @@
+
/* vim:tw=80:expandtab
+
 */
+
/**
+
 * @file  hashmap.h
+
 * @brief A hash map implementation in C.
+
 * @author Christoph Erhardt <erhardt@cs.fau.de>
+
 */
+

+
/*-
+
 * Copyright (c) 2011 Christoph Erhardt. 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 CHRISTOPH ERHARDT ``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 CHRISTOPH ERHARDT 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.
+
 */
+
/*-
+
 * Modified by Clemens Lang to accept values of arbitrary type.
+
 */
+

+

+
#ifndef HASHMAP_H
+
#define HASHMAP_H
+

+

+
#include <stdlib.h>
+

+

+
/** Hash map type. */
+
typedef struct HashMap HashMap;
+

+

+
/**
+
 * @brief Creates a hash map.
+
 *
+
 * The keys and values managed in the map can be arbitrary C strings.
+
 * @param freeFunc Function to call in order to free a stored value
+
 * @return Pointer to the newly created hash map, or @c NULL on error.
+
 */
+
HashMap *hashMapCreate(void (*freeFunc)(const void *));
+

+
/**
+
 * @brief Inserts a key-value pair into a hash map.
+
 *
+
 * Both key and value are copied internally, so the caller can reuse the
+
 * original variables.
+
 * If oldValPtr is @c NULL, the previously stored value corresponding to the key
+
 * is freed. Otherwise it is written into @c *valPtr and the caller is
+
 * responsible for freeing it.
+
 * @param map       Hash map.
+
 * @param key       Key.
+
 * @param value     Value.
+
 * @param oldValPtr Output parameter receiving the previously stored value
+
 *                  corresponding to the key (@c NULL if no mapping existed
+
 *                  before).
+
 * @return Nonzero on success, 0 on error.
+
 */
+
int hashMapPut(HashMap *map, const char key[], const void * const value,
+
               const void **oldValPtr);
+

+
/**
+
 * @brief Performs a hash map lookup.
+
 *
+
 * The returned value must not be freed or otherwise manipulated by the caller.
+
 * @param map Hash map.
+
 * @param key Key.
+
 * @return Value corresponding to the key on success, @c NULL if no matching
+
 *         entry was found.
+
 */
+
const void *hashMapGet(HashMap *map, const char key[]);
+

+
/**
+
 * @brief Checks whether a hash map contains an entry with a certain key.
+
 * @param map Hash map.
+
 * @param key Key.
+
 * @return Nonzero if the map contains an entry with the given key, 0 if it does
+
 *         not.
+
 */
+
int hashMapContainsKey(HashMap *map, const char key[]);
+

+
/**
+
 * @brief Removes a key-value pair from a hash map and frees the stored key.
+
 *
+
 * If @c valPtr is @c NULL, the internally stored value corresponding to the key
+
 * is freed. Otherwise it is written into @c *valPtr and the caller is
+
 * responsible for freeing it.
+
 * @param map    Hash map.
+
 * @param key    Key.
+
 * @param valPtr Output parameter receiving the internally stored value
+
 *               corresponding to the key.
+
 */
+
void hashMapRemove(HashMap *map, const char key[], const void **valPtr);
+

+
/**
+
 * @brief Returns the number of elements stored in a hash map.
+
 * @param map Hash map.
+
 * @return Number of elements stored in the map.
+
 */
+
size_t hashMapSize(const HashMap *map);
+

+
/**
+
 * @brief Checks whether a hash map is empty.
+
 * @param map Hash map.
+
 * @return Nonzero if the map contains no entries, 0 otherwise.
+
 */
+
int hashMapIsEmpty(const HashMap *map);
+

+
/**
+
 * @brief Removes all entries from a hash map.
+
 * @param map Hash map.
+
 */
+
void hashMapClear(HashMap *map);
+

+
/**
+
 * @brief Destroys a hash map.
+
 * @param map Hash map to be destroyed.
+
 */
+
void hashMapDestroy(HashMap *map);
+

+

+
#endif /* HASHMAP_H */
added external/libmachista/libmachista.c
@@ -0,0 +1,569 @@
+
/*
+
 * -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=c:et:sw=4:ts=4:sts=4:tw=100
+
 * libmachista.c
+
 * $Id: libmachista.c 120067 2014-05-14 22:18:53Z cal@macports.org $
+
 *
+
 * Copyright (c) 2011 The MacPorts Project
+
 * Copyright (c) 2011 Landon Fuller <landonf@macports.org>
+
 * Copyright (c) 2011 Clemens Lang <cal@macports.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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+
 */
+

+
#ifdef HAVE_CONFIG_H
+
#include <pkg_config.h>
+
#endif
+

+
/* required for asprintf(3) on OS X */
+
#define _DARWIN_C_SOURCE
+
/* required for asprintf(3) on Linux */
+
#define _GNU_SOURCE
+

+
#include <stdbool.h>
+
#include <stdio.h>
+
#include <stdlib.h>
+

+
#include <fcntl.h>
+
#include <sys/mman.h>
+
#include <sys/stat.h>
+
#include <unistd.h>
+

+
#include <err.h>
+
#include <string.h>
+
#include <strings.h>
+

+
#ifdef __MACH__
+
#include <mach-o/fat.h>
+
#include <mach-o/loader.h>
+

+
#include <libkern/OSAtomic.h>
+
#endif
+

+
#include "libmachista.h"
+
#include "hashmap.h"
+

+
#ifdef __MACH__
+
/* Tiger compatibility */
+
#ifndef LC_RPATH
+
#define LC_RPATH       (0x1c | LC_REQ_DYLD)    /* runpath additions */
+
/*
+
 * The rpath_command contains a path which at runtime should be added to
+
 * the current run path used to find @rpath prefixed dylibs.
+
 */
+
struct rpath_command {
+
    uint32_t     cmd;       /* LC_RPATH */
+
    uint32_t     cmdsize;   /* includes string */
+
    union lc_str path;      /* path to add to run path */
+
};
+
#endif
+
#ifndef LC_REEXPORT_DYLIB
+
#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) /* load and re-export dylib */
+
#endif
+
#endif /* __MACH__ */
+

+
typedef struct macho_input {
+
    const void *data;
+
    size_t length;
+
} macho_input_t;
+

+
/* This is macho_handle_t. The corresponding typedef is in the header */
+
struct macho_handle {
+
    HashMap *result_map;
+
};
+

+
#ifdef __MACH__
+
/* Verify that the given range is within bounds. */
+
static const void *macho_read (macho_input_t *input, const void *address, size_t length) {
+
    if ((((uint8_t *) address) - ((uint8_t *) input->data)) + length > input->length) {
+
       // warnx("Short read parsing Mach-O input");
+
        return NULL;
+
    }
+

+
    return address;
+
}
+

+
/* Verify that address + offset + length is within bounds. */
+
static const void *macho_offset (macho_input_t *input, const void *address, size_t offset, size_t length) {
+
    void *result = ((uint8_t *) address) + offset;
+
    return macho_read(input, result, length);
+
}
+
#endif
+

+
/* return a human readable formatted version number. the result must be free()'d. */
+
char *macho_format_dylib_version (uint32_t version) {
+
    char *result;
+
    asprintf(&result, "%"PRIu32".%"PRIu32".%"PRIu32, (version >> 16) & 0xFFFF, (version >> 8) & 0xFF, version & 0xFF);
+
    return result;
+
}
+

+
#ifdef __MACH__
+
const char *macho_get_arch_name (cpu_type_t cputype) {
+
    const NXArchInfo *archInfo = NXGetArchInfoFromCpuType(cputype, CPU_SUBTYPE_MULTIPLE);	
+
    if (!archInfo) {
+
        return NULL;
+
    }
+
    return archInfo->name;
+
#else
+
const char *macho_get_arch_name (cpu_type_t cputype UNUSED) {
+
    return NULL;
+
#endif
+
}
+

+
#ifdef __MACH__
+
/* Some byteswap wrappers */
+
static uint32_t macho_swap32 (uint32_t input) {
+
    return OSSwapInt32(input);
+
}
+

+
static uint32_t macho_nswap32(uint32_t input) {
+
    return input;
+
}
+

+
/* Creates a new macho_t.
+
 * Returns NULL on failure or a pointer to a 0-initialized macho_t on success */
+
static macho_t *create_macho_t (void) {
+
    macho_t *mt = malloc(sizeof(macho_t));
+
    if (mt == NULL)
+
        return NULL;
+

+
    memset(mt, 0, sizeof(macho_t));
+
    return mt;
+
}
+

+
/* Creates a new macho_arch_t.
+
 * Returns NULL on failure or a pointer to a 0-initialized macho_arch_t on success */
+
static macho_arch_t *create_macho_arch_t (void) {
+
    macho_arch_t *mat = malloc(sizeof(macho_arch_t));
+
    if (mat == NULL)
+
        return NULL;
+
    
+
    memset(mat, 0, sizeof(macho_arch_t));
+
    return mat;
+
}
+

+
/* Creates a new macho_loadcmd_t.
+
 * Returns NULL on failure or a pointer to a 0-initialized macho_loadcmd_t on success */
+
static macho_loadcmd_t *create_macho_loadcmd_t (void) {
+
    macho_loadcmd_t *mlt = malloc(sizeof(macho_loadcmd_t));
+
    if (mlt == NULL)
+
        return NULL;
+

+
    memset(mlt, 0, sizeof(macho_loadcmd_t));
+
    return mlt;
+
}
+
#endif
+

+
/* Frees a previously allocated macho_loadcmd_t and all it's associated resources */
+
static void free_macho_loadcmd_t (macho_loadcmd_t *mlt) {
+
    if (mlt == NULL)
+
        return;
+

+
    free(mlt->mlt_install_name);
+
    free(mlt);
+
}
+

+
/* Frees a previously allocated macho_arch_t and all it's associated resources */
+
static void free_macho_arch_t (macho_arch_t *mat) {
+
    if (mat == NULL)
+
        return;
+

+
    macho_loadcmd_t *current = mat->mat_loadcmds;
+
    while (current != NULL) {
+
        macho_loadcmd_t *freeme = current;
+
        current = current->next;
+
        free_macho_loadcmd_t(freeme);
+
    }
+

+
    free(mat->mat_install_name);
+
    free(mat->mat_rpath);
+
    free(mat);
+
}
+

+
/* Frees a previously allocated macho_t and all it's associated resources */
+
static void free_macho_t (macho_t *mt) {
+
    if (mt == NULL)
+
        return;
+

+
    macho_arch_t *current = mt->mt_archs;
+
    while (current != NULL) {
+
        macho_arch_t *freeme = current;
+
        current = current->next;
+
        free_macho_arch_t(freeme);
+
    }
+

+
    free(mt);
+
}
+

+
#ifdef __MACH__
+
/* Creates a new element in the architecture list of a macho_t (mt_archs), increases the counter of
+
 * architectures (mt_arch_count) and returns a pointer to the newly allocated element or NULL on
+
 * error */
+
static macho_arch_t *macho_archlist_append (macho_t *mt) {
+
    macho_arch_t *old_head = mt->mt_archs;
+

+
    macho_arch_t *new_head = create_macho_arch_t();
+
    if (new_head == NULL)
+
        return NULL;
+
    new_head->next = old_head;
+
    mt->mt_archs = new_head;
+

+
    return mt->mt_archs;
+
}
+

+
/* Creates a new element in the load command list of a macho_arch_t (mat_loadcmds), increases the
+
 * counter of load commands (mat_loadcmd_count) and returns a pointer to the newly allocated element
+
 * or NULL on error */
+
static macho_loadcmd_t *macho_loadcmdlist_append (macho_arch_t *mat) {
+
    macho_loadcmd_t *old_head = mat->mat_loadcmds;
+

+
    macho_loadcmd_t *new_head = create_macho_loadcmd_t();
+
    if (new_head == NULL)
+
        return NULL;
+
    new_head->next = old_head;
+
    mat->mat_loadcmds = new_head;
+

+
    return mat->mat_loadcmds;
+
}
+
#endif
+

+
/* Parse a Mach-O header */
+
#ifdef __MACH__
+
static int parse_macho (macho_t *mt, macho_input_t *input) {
+
    /* Read the file type. */
+
    const uint32_t *magic = macho_read(input, input->data, sizeof(uint32_t));
+
    if (magic == NULL)
+
        return MACHO_ERANGE;
+

+
    /* Parse the Mach-O header */
+
    bool universal = false;
+
    uint32_t (*swap32)(uint32_t) = macho_nswap32;
+

+
    const struct mach_header *header;
+
    const struct mach_header_64 *header64;
+
    size_t header_size;
+
    const struct fat_header *fat_header;
+

+
    macho_arch_t *mat = NULL;
+
    switch (*magic) {
+
        case MH_CIGAM:
+
            swap32 = macho_swap32;
+
            // Fall-through
+

+
        case MH_MAGIC:
+

+
            header_size = sizeof(*header);
+
            header = macho_read(input, input->data, header_size);
+
            if (header == NULL)
+
                return MACHO_ERANGE;
+
            mat = macho_archlist_append(mt);
+
            if (mat == NULL)
+
                return MACHO_EMEM;
+

+
            /* 32-bit Mach-O */
+
            mat->mat_cputype = swap32(header->cputype);
+
            mat->mat_cpusubtype = swap32(header->cpusubtype);
+
            break;
+

+

+
        case MH_CIGAM_64:
+
            swap32 = macho_swap32;
+
            // Fall-through
+

+
        case MH_MAGIC_64:
+
            header_size = sizeof(*header64);
+
            header64 = macho_read(input, input->data, sizeof(*header64));
+
            if (header64 == NULL)
+
                return MACHO_ERANGE;
+
            mat = macho_archlist_append(mt);
+
            if (mat == NULL)
+
                return MACHO_EMEM;
+

+
            /* The 64-bit header is a direct superset of the 32-bit header */
+
            header = (struct mach_header *) header64;
+

+
            /* 64-bit Macho-O */
+
            mat->mat_cputype = swap32(header->cputype);
+
            mat->mat_cpusubtype = swap32(header->cpusubtype);
+
            break;
+

+
        case FAT_CIGAM:
+
        case FAT_MAGIC:
+
            fat_header = macho_read(input, input->data, sizeof(*fat_header));
+
            universal = true;
+
            /* Universal binary */
+
            break;
+

+
        default:
+
            /* Unknown binary type */
+
            //warnx("Unknown Mach-O magic: 0x%" PRIx32 "", *magic);
+
            return MACHO_EMAGIC;
+
    }
+

+
    /* Parse universal file. */
+
    if (universal) {
+
        uint32_t nfat = OSSwapBigToHostInt32(fat_header->nfat_arch);
+
        const struct fat_arch *archs = macho_offset(input, fat_header, sizeof(struct fat_header), sizeof(struct fat_arch));
+
        if (archs == NULL)
+
            return MACHO_ERANGE;
+

+
        for (uint32_t i = 0; i < nfat; i++) { // foreach architecture
+
            const struct fat_arch *arch = macho_read(input, archs + i, sizeof(struct fat_arch));
+
            if (arch == NULL)
+
                return MACHO_ERANGE;
+

+
            /* Fetch a pointer to the architecture's Mach-O header. */
+
            macho_input_t arch_input;
+
            arch_input.length = OSSwapBigToHostInt32(arch->size);
+
            arch_input.data = macho_offset(input, input->data, OSSwapBigToHostInt32(arch->offset), arch_input.length);
+
            if (arch_input.data == NULL)
+
                return MACHO_ERANGE;
+

+
            /* Parse the architecture's Mach-O header */
+
            int res = parse_macho(mt, &arch_input);
+
            if (res != MACHO_SUCCESS)
+
                return res;
+
        }
+

+
        return MACHO_SUCCESS;
+
    }
+

+
    /* Copy the architecture */
+
    mat->mat_cputype = swap32(header->cputype);
+
    mat->mat_cpusubtype = swap32(header->cpusubtype);
+

+
    /* Parse the Mach-O load commands */
+
    uint32_t ncmds = swap32(header->ncmds);
+

+
    /* Setup to jump over the header on the first pass through instead of the previous command */
+
    const struct load_command *cmd = (void *)header;
+
    uint32_t cmdsize = header_size;
+

+
    /* Iterate over the load commands */
+
    for (uint32_t i = 0; i < ncmds; i++) {
+
        /* Load the next command */
+
        cmd = macho_offset(input, cmd, cmdsize, sizeof(struct load_command));
+
        if (cmd == NULL)
+
            return MACHO_ERANGE;
+

+
        /* Load the full command */
+
        cmdsize = swap32(cmd->cmdsize);
+
        cmd = macho_read(input, cmd, cmdsize);
+
        if (cmd == NULL)
+
            return MACHO_ERANGE;
+

+
        /* Handle known types */
+
        uint32_t cmd_type = swap32(cmd->cmd);
+
        switch (cmd_type) {
+
            case LC_RPATH: {
+
                /* Copy the rpath */
+
                if (cmdsize < sizeof(struct rpath_command)) {
+
                    //warnx("Incorrect cmd size");
+
                    return MACHO_ERANGE;
+
                }
+

+
                size_t pathlen = cmdsize - sizeof(struct rpath_command);
+
                const void *pathptr = macho_offset(input, cmd, sizeof(struct rpath_command), pathlen);
+
                if (pathptr == NULL)
+
                    return MACHO_ERANGE;
+

+
                mat->mat_rpath = malloc(pathlen);
+
                if (mat->mat_rpath == NULL)
+
                    return MACHO_EMEM;
+
                strlcpy(mat->mat_rpath, pathptr, pathlen);
+
                break;
+
            }
+

+
            case LC_ID_DYLIB:
+
            case LC_LOAD_WEAK_DYLIB:
+
            case LC_REEXPORT_DYLIB:
+
            case LC_LOAD_DYLIB: {
+
                const struct dylib_command *dylib_cmd = (const struct dylib_command *) cmd;
+

+
                /* Extract the install name */
+
                if (cmdsize < sizeof(struct dylib_command)) {
+
                    //warnx("Incorrect name size");
+
                    return MACHO_ERANGE;
+
                }
+

+
                size_t namelen = cmdsize - sizeof(struct dylib_command);
+
                const void *nameptr = macho_offset(input, cmd, sizeof(struct dylib_command), namelen);
+
                if (nameptr == NULL)
+
                    return MACHO_ERANGE;
+

+
                if (cmd_type == LC_ID_DYLIB) {
+
                    /* Copy install name */
+
                    mat->mat_install_name = malloc(namelen);
+
                    if (mat->mat_install_name == NULL)
+
                        return MACHO_EMEM;
+
                    strlcpy(mat->mat_install_name, nameptr, namelen);
+

+
                    /* Copy version numbers (raw, for easier comparison) */
+
                    mat->mat_version = swap32(dylib_cmd->dylib.current_version);
+
                    mat->mat_comp_version = swap32(dylib_cmd->dylib.compatibility_version);
+
                } else {
+
                    /* Append loadcmd to list of loadcommands */
+
                    macho_loadcmd_t *mlt = macho_loadcmdlist_append(mat);
+
                    if (mlt == NULL)
+
                        return MACHO_EMEM;
+

+
                    /* Copy install name */
+
                    mlt->mlt_install_name = malloc(namelen);
+
                    if (mlt->mlt_install_name == NULL)
+
                        return MACHO_EMEM;
+
                    strlcpy(mlt->mlt_install_name, nameptr, namelen);
+

+
                    /* Copy version numbers (raw, for easier comparison) */
+
                    mlt->mlt_version = swap32(dylib_cmd->dylib.current_version);
+
                    mlt->mlt_comp_version = swap32(dylib_cmd->dylib.compatibility_version);
+

+
                    /* Copy command type */
+
                    mlt->mlt_type = cmd_type;
+
                }
+
                break;
+
            }
+

+
            default:
+
                break;
+
        }
+
    }
+

+
    return MACHO_SUCCESS;
+
}
+
#endif
+

+
/* Parse a (possible Mach-O) file. For a more detailed description, see the header */
+
#ifdef __MACH__
+
int macho_parse_file(macho_handle_t *handle, const char *filepath, const macho_t **res) {
+
    int fd;
+
    struct stat st;
+
    void *data;
+
    macho_input_t input_file;
+

+
    /* Check hashmap for precomputed results */
+
    const macho_t *cached_res = hashMapGet(handle->result_map, filepath);
+
    if (cached_res != NULL) {
+
        *res = cached_res;
+
        return MACHO_SUCCESS;
+
    }
+

+
    
+
    /* Open input file */
+
    if ((fd = open(filepath, O_RDONLY)) < 0) {
+
        return MACHO_EFILE;
+
    }
+

+
    /* Get file length */
+
    if (fstat(fd, &st) != 0) {
+
        close(fd);
+
        return MACHO_EFILE;
+
    }
+

+
    /* Map file into address space */
+
    if ((data = mmap(NULL, st.st_size, PROT_READ, MAP_FILE | MAP_PRIVATE, fd, 0)) == MAP_FAILED) {
+
        close(fd);
+
        return MACHO_EMMAP;
+
    }
+

+
    /* Parse file */
+
    input_file.data = data;
+
    input_file.length = st.st_size;
+

+
    *res = create_macho_t();
+
    if (*res == NULL)
+
        return MACHO_EMEM;
+

+
    /* The output parameter *res should be read-only for the user of the lib only, but writable for
+
     * us */
+
    int ret = parse_macho((macho_t *)*res, &input_file);
+
    if (ret == MACHO_SUCCESS) {
+
        /* Insert into hashmap for caching */
+
        if (0 == hashMapPut(handle->result_map, filepath, *res, NULL)) {
+
            free_macho_t((macho_t *)*res);
+
            *res = NULL;
+
            ret = MACHO_EMEM;
+
        }
+
    } else {
+
        /* An error occured, free mt */
+
        free_macho_t((macho_t *)*res);
+
        *res = NULL;
+
    }
+

+
    /* Cleanup */
+
    munmap(data, st.st_size);
+
    close(fd);
+

+
    return ret;
+
#else
+
int macho_parse_file(macho_handle_t *handle UNUSED, const char *filepath UNUSED, const macho_t **res UNUSED) {
+
    return 0;
+
#endif
+
}
+

+
/* Create a new macho_handle_t. More information on this function is available in the header */
+
macho_handle_t *macho_create_handle (void) {
+
    macho_handle_t *mht = malloc(sizeof(macho_handle_t));
+
    if (mht == NULL)
+
        return NULL;
+
    mht->result_map = hashMapCreate((void (*)(const void *))free_macho_t);
+
    if (mht->result_map == NULL) {
+
        free(mht);
+
        return NULL;
+
    }
+
    return mht;
+
}
+

+
/* Release a macho_handle_t. For more documentation, see the header */
+
void macho_destroy_handle(macho_handle_t *handle) {
+
    if (handle == NULL)
+
        return;
+
    
+
    hashMapDestroy(handle->result_map);
+

+
    free(handle);
+
}
+

+
/* Returns string representation of the MACHO_* error code constants */
+
const char *macho_strerror(int err) {
+
    int num;
+
#ifdef HAVE_FLS
+
    num = fls(err);
+
#else
+
    /* Tiger compatibility, see #42186 */
+
    num = 0;
+
    while (err > 0) {
+
        err >>= 1;
+
        num++;
+
    }
+
#endif
+

+
    static char *errors[] = {
+
        /* 0x00 */ "Success",
+
        /* 0x01 */ "Error opening or reading file",
+
        /* 0x02 */ "Error mapping file into memory",
+
        /* 0x04 */ "Error allocating memory",
+
        /* 0x08 */ "Premature end of data, possibly corrupt file",
+
        /* 0x10 */ "Not a Mach-O file",
+
    };
+
    return errors[num];
+
}
+

added external/libmachista/libmachista.h
@@ -0,0 +1,150 @@
+
/*
+
 * -*- coding: utf-8; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- vim:fenc=utf-8:filetype=c:et:sw=4:ts=4:sts=4:tw=100
+
 * libmachista.h
+
 * $Id$
+
 *
+
 * Copyright (c) 2011 The MacPorts Project
+
 * Copyright (c) 2011 Clemens Lang <cal@macports.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 COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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.
+
 */
+

+
#ifndef __LIBMACHISTA_H__
+
#define __LIBMACHISTA_H__
+

+
/*
+
 * This is a library to parse Mach-O files in single architecture _and_ universal variant and return
+
 * a list of architectures and their load commands and properties
+
 * The name a pun: machista is the spanish translation of "macho".
+
 */
+

+
#ifdef __MACH__
+
#include <mach-o/arch.h>
+
#else
+
typedef int cpu_type_t;
+
#endif
+
#include <inttypes.h>
+

+
#define MACHO_SUCCESS   (0x00)
+
#define MACHO_EFILE     (0x01)
+
#define MACHO_EMMAP     (0x02)
+
#define MACHO_EMEM      (0x04)
+
#define MACHO_ERANGE    (0x08)
+
#define MACHO_EMAGIC    (0x10)
+

+
/* Blind structure; this essentially contains the hash map used to cache
+
 * entries, but users should not have to look into this structure. struct
+
 * macho_handle is defined in libmachista.c */
+
typedef struct macho_handle macho_handle_t;
+

+
/** Structure describing a load command within a Mach-O file */
+
typedef struct macho_loadcmd {
+
    char *mlt_install_name;         /* install name of the library to be loaded by this load command */
+
    uint32_t mlt_type;              /* type of the load command; see mach-o/loader.h for possible
+
                                       values */
+
    uint32_t mlt_comp_version;      /* compatibility version of the file to be loaded by this
+
                                       command (at build time of this file) */
+
    uint32_t mlt_version;           /* version of the library to be loaded by this command (at build
+
                                       time of this file) */
+
    struct macho_loadcmd *next;     /* pointer to the next entry in the linked list of
+
                                       macho_loadcmd_t's (NULL if there's no further element) */
+
} macho_loadcmd_t;
+

+
/** Stucture describing an architecture within a Mach-O file */
+
typedef struct macho_arch {
+
    char *mat_install_name;         /* install name of the library or NULL if none */
+
    char *mat_rpath;                /* rpath of the binary of NULL if none */
+
    cpu_type_t mat_cputype;         /* cpu_type_t describing the CPU this part of the binary is
+
                                       intended for */
+
    cpu_type_t mat_cpusubtype;      /* cpu_subtype_t describing the CPU subtype this part of the
+
                                       binary is intended for */
+
    uint32_t mat_comp_version;      /* compatibility version of this part of the binary */
+
    uint32_t mat_version;           /* current version of this part of the binary */
+
    macho_loadcmd_t *mat_loadcmds;  /* array of macho_loadcmd_t's describing the different load
+
                                       commands */
+
    struct macho_arch *next;        /* pointer to the next entry in the linked list of
+
                                       macho_arch_t's (NULL if there's no further element) */
+
} macho_arch_t;
+

+
/** Structure describing a Mach-O file */
+
typedef struct macho {
+
    macho_arch_t *mt_archs;         /* linked list of macho_arch_t's describing the different
+
                                       architectures */
+
} macho_t;
+

+
/**
+
 * Creates and returns a macho_handle_t to be passed to subsequent calls to macho_parse_file. No
+
 * assumptions should be made about the contents of a macho_handle_t; it is declared to be a blind
+
 * structure.
+
 *
+
 * Returns either a pointer to a valid macho_handle_t or NULL on failure. errno will be set on
+
 * failure. The resources associated with a macho_handle_t must be freed by passing it to
+
 * macho_destroy_handle.
+
 */
+
macho_handle_t *macho_create_handle(void);
+

+
/**
+
 * Frees resources associated with a macho_handle_t and invalidates all results returned by
+
 * macho_parse_file called with this handle.
+
 */
+
void macho_destroy_handle(macho_handle_t *handle);
+

+
/**
+
 * Formats a dylib version number given by an uint32_t into a human-readable format and returns a
+
 * Pointer to the beginning of that string. The result is either a valid pointer or NULL on error
+
 * (in which case the errno is set to indicate the error). The pointer must be free()'d after use.
+
 */
+
char *macho_format_dylib_version(uint32_t version);
+

+
/**
+
 * Returns a readable version of any cpu_type_t constant. Returns a valid pointer to the first
+
 * character in a 0-terminated string or NULL on error. The pointer must not be free()'d after use.
+
 */
+
const char *macho_get_arch_name(cpu_type_t cputype);
+

+
/**
+
 * Parses the Mach-O file indicated by filepath and writes a pointer to a macho_t describing the
+
 * Mach-O file into the location idicated by res. Returns MACHO_SUCCESS on success or any of the
+
 * following error codes on error:
+
 *
+
 * code             description                                     errno set?
+
 * MACHO_EFILE      error stat()'ing, opening or reading the file   yes
+
 * MACHO_EMMAP      error mmap()'ing the file                       yes
+
 * MACHO_EMEM       error allocating memory                         yes
+
 * MACHO_ERANGE     unexpected end of file                          no
+
 * MACHO_EMAGIC     unknown magic number/not a Mach-O file          no
+
 *
+
 * On error, the contents of res are undefined and should not be used. The memory associated with
+
 * the result *res will be free()'d and should thus not be used after calling macho_destroy_handle
+
 * on the macho_handle_t used for the call. *res should also never be modified or otherwise
+
 * free()'d.
+
 */
+
int macho_parse_file(macho_handle_t *handle, const char *filepath, const macho_t **res);
+

+
/**
+
 * Returns a string representation of the MACHO_* error code constants
+
 */
+
const char *macho_strerror(int err);
+

+
#endif
+

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 \
@@ -19,10 +20,10 @@ endif
libpkg_la_SOURCES=	pkg.c \
			backup.c \
			dns_utils.c \
-
			elfhints.c \
			fetch.c \
			packing.c \
			pkg_add.c \
+
			pkg_arch.c \
			pkg_attributes.c \
			pkg_audit.c \
			pkg_checksum.c \
@@ -30,7 +31,6 @@ libpkg_la_SOURCES= pkg.c \
			pkg_cudf.c \
			pkg_create.c \
			pkg_delete.c \
-
			pkg_elf.c \
			pkg_event.c \
			pkg_jobs.c \
			pkg_jobs_conflicts.c \
@@ -58,8 +58,20 @@ libpkg_la_SOURCES= pkg.c \
			pkg_old.c \
			merge3.c \
			diff.c
+

+
if HAVE_ELF_ABI
+
libpkg_la_SOURCES+=	elfhints.c \
+
			pkg_elf.c
+
endif
+

+
if HAVE_MACHO_ABI
+
libpkg_la_SOURCES+=	pkg_macho.c
+
pkg_common_cflags+=	-I$(top_srcdir)/external/libmachista
+
endif
+

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 \
@@ -73,14 +85,26 @@ libpkg_la_LIBADD= $(top_builddir)/external/libucl.la \
			-lssl \
			-lcrypto \
			-lm
+

+
if HAVE_ELF_ABI
if LIBELF_BUNDLED
libpkg_la_LIBADD+=	$(top_builddir)/external/libelf.la
else
libpkg_la_LIBADD+=	-lelf
endif
+
endif
+

+
if HAVE_MACHO_ABI
+
libpkg_la_LIBADD+=	$(top_builddir)/external/libmachista_static.la
+
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 libpkg/backup.c
@@ -35,6 +35,8 @@
#include "private/pkg.h"
#include "private/pkgdb.h"

+
#include <bsd_compat.h>
+

/* Number of pages to copy per call to sqlite3_backup_step()
   Default page size is 1024 bytes on Unix */
#define NPAGES	4
@@ -129,8 +131,8 @@ pkgdb_dump(struct pkgdb *db, const char *dest)
		}

		/* Could we create the Sqlite DB file? */
-
		if (eaccess(dirname(dest), W_OK)) {
-
			pkg_emit_error("eaccess(%s) -- %s", dirname(dest),
+
		if (eaccess(bsd_dirname(dest), W_OK)) {
+
			pkg_emit_error("eaccess(%s) -- %s", bsd_dirname(dest),
			    strerror(errno));
			return (EPKG_FATAL);
		}
modified libpkg/fetch.c
@@ -29,6 +29,7 @@
#include <sys/param.h>
#include <sys/wait.h>
#include <sys/socket.h>
+
#include <sys/time.h>

#include <ctype.h>
#include <fcntl.h>
@@ -40,6 +41,8 @@
#include <paths.h>
#include <poll.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
@@ -174,7 +177,7 @@ ssh_read(void *data, char *buf, int len)
		timeout.tv_sec += fetchTimeout;
	}

-
	deltams = INFTIM;
+
	deltams = -1;
	memset(&pfd, 0, sizeof pfd);
	pfd.fd = repo->sshio.in;
	pfd.events = POLLIN | POLLERR;
added libpkg/pkg_arch.c
@@ -0,0 +1,75 @@
+
/*-
+
 * Copyright (c) 2014 Landon Fuller <landon@landonf.org>
+
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2012-2013 Matthew Seaman <matthew@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
+
 *    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.
+
 */
+

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

+
#include <bsd_compat.h>
+

+
#include "pkg.h"
+
#include "private/pkg.h"
+
#include "private/event.h"
+

+
int
+
pkg_suggest_arch(struct pkg *pkg, const char *arch, bool isdefault)
+
{
+
	bool iswildcard;
+

+
	iswildcard = (strchr(arch, '*') != NULL);
+

+
	if (iswildcard && isdefault)
+
		pkg_emit_developer_mode("Configuration error: arch \"%s\" "
+
		    "cannot use wildcards as default", arch);
+

+
	if (pkg->flags & (PKG_CONTAINS_ELF_OBJECTS|PKG_CONTAINS_STATIC_LIBS)) {
+
		if (iswildcard) {
+
			/* Definitely has to be arch specific */
+
			pkg_emit_developer_mode("Error: arch \"%s\" -- package "
+
			    "installs architecture specific files", arch);
+
		}
+
	} else {
+
		if (pkg->flags & PKG_CONTAINS_H_OR_LA) {
+
			if (iswildcard) {
+
				/* Could well be arch specific */
+
				pkg_emit_developer_mode("Warning: arch \"%s\" "
+
				    "-- package installs C/C++ headers or "
+
				    "libtool files,\n**** which are often "
+
				    "architecture specific", arch);
+
			}
+
		} else {
+
			/* Might be arch independent */
+
			if (!iswildcard)
+
				pkg_emit_developer_mode("Notice: arch \"%s\" -- "
+
				    "no architecture specific files found:\n"
+
				    "**** could this package use a wildcard "
+
				    "architecture?", arch);
+
		}
+
	}
+
	return (EPKG_OK);
+
}
modified libpkg/pkg_create.c
@@ -33,6 +33,8 @@
#include <regex.h>
#include <fcntl.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
modified libpkg/pkg_delete.c
@@ -35,6 +35,8 @@
#include <stdlib.h>
#include <fcntl.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
modified libpkg/pkg_elf.c
@@ -59,6 +59,8 @@
#include <libelf.h>
#endif

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/pkg.h"
#include "private/event.h"
@@ -377,7 +379,7 @@ analyse_elf(struct pkg *pkg, const char *fpath,
			continue;
		
		shlib_list_from_rpath(elf_strptr(e, sh_link, dyn->d_un.d_val),
-
				      dirname(fpath));
+
				      bsd_dirname(fpath));
		break;
	}
	if (!is_shlib) {
@@ -387,7 +389,7 @@ analyse_elf(struct pkg *pkg, const char *fpath,
		 */
		if (elfhdr.e_type == ET_DYN) {
			is_shlib = true;
-
			pkg_addshlib_provided(pkg, basename(fpath));
+
			pkg_addshlib_provided(pkg, bsd_basename(fpath));
		}
	}

@@ -972,40 +974,3 @@ pkg_get_myarch(char *dest, size_t sz)
}
#endif

-
int
-
pkg_suggest_arch(struct pkg *pkg, const char *arch, bool isdefault)
-
{
-
	bool iswildcard;
-

-
	iswildcard = (strchr(arch, '*') != NULL);
-

-
	if (iswildcard && isdefault)
-
		pkg_emit_developer_mode("Configuration error: arch \"%s\" "
-
		    "cannot use wildcards as default", arch);
-

-
	if (pkg->flags & (PKG_CONTAINS_ELF_OBJECTS|PKG_CONTAINS_STATIC_LIBS)) {
-
		if (iswildcard) {
-
			/* Definitely has to be arch specific */
-
			pkg_emit_developer_mode("Error: arch \"%s\" -- package "
-
			    "installs architecture specific files", arch);
-
		}
-
	} else {
-
		if (pkg->flags & PKG_CONTAINS_H_OR_LA) {
-
			if (iswildcard) {
-
				/* Could well be arch specific */
-
				pkg_emit_developer_mode("Warning: arch \"%s\" "
-
				    "-- package installs C/C++ headers or "
-
				    "libtool files,\n**** which are often "
-
				    "architecture specific", arch);
-
			}
-
		} else {
-
			/* Might be arch independent */
-
			if (!iswildcard)
-
				pkg_emit_developer_mode("Notice: arch \"%s\" -- "
-
				    "no architecture specific files found:\n"
-
				    "**** could this package use a wildcard "
-
				    "architecture?", arch);
-
		}
-
	}
-
	return (EPKG_OK);
-
}
modified libpkg/pkg_jobs.c
@@ -28,6 +28,10 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

#include <sys/param.h>
#include <sys/mount.h>
#include <sys/types.h>
@@ -36,13 +40,17 @@
#include <archive_entry.h>
#include <assert.h>
#include <errno.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <ctype.h>

+
#include <bsd_compat.h>
+

#include "utarray.h"

#include "pkg.h"
modified libpkg/pkg_jobs_universe.c
@@ -21,12 +21,18 @@
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

#include <sys/param.h>
#include <sys/types.h>

#include <assert.h>
#include <errno.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
added libpkg/pkg_macho.c
@@ -0,0 +1,409 @@
+
/*-
+
 * Copyright (c) 2014 Landon Fuller <landon@landonf.org>
+
 * Copyright (c) 2011-2012 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2012-2013 Matthew Seaman <matthew@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
+
 *    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.
+
 */
+

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

+
#include <sys/types.h>
+
#include <sys/sysctl.h>
+
#include <sys/utsname.h>
+

+
#include <mach/machine.h>
+
#include <mach-o/arch.h>
+
#include <mach-o/loader.h>
+

+
#include <ctype.h>
+
#include <limits.h>
+

+
#include <libmachista.h>
+
#include <bsd_compat.h>
+

+
#include "pkg.h"
+
#include "private/pkg.h"
+
#include "private/event.h"
+

+
static const char * const system_dylib_prefixes[] = {
+
	"/System/",
+
	"/usr/lib",
+
	"/lib",
+
	NULL
+
 };
+

+
static int
+
analyse_macho(struct pkg *pkg, const char *fpath,
+
	      cpu_type_t cpu_type, macho_handle_t *macho_handle,
+
	      int (action)(void *, struct pkg *, const char *, const char *, bool),
+
	      void *actdata)
+
{
+
	const macho_t *macho = NULL;
+
	struct stat sb;
+
	int mret;
+

+
	/* We're only interested in the generic CPU type */
+
	cpu_type = cpu_type & ~CPU_ARCH_MASK;
+

+
	if (lstat(fpath, &sb) != 0)
+
		pkg_emit_errno("fstat() failed for", fpath);
+

+
	/* ignore empty files and non regular files */
+
	if (sb.st_size == 0 || !S_ISREG(sb.st_mode))
+
		return EPKG_END; /* Empty file or sym-link: no results */
+

+

+
	/* Try to parse the file */
+
	if ((mret = macho_parse_file(macho_handle, fpath, &macho)) != MACHO_SUCCESS) {
+
		if (mret != MACHO_EMAGIC && mret != MACHO_ERANGE) {
+
			pkg_emit_error("macho_parse_file() for %s failed: %s", fpath, macho_strerror(mret));
+
			return EPKG_FATAL; /* Empty file or sym-link: no results */
+
		}
+

+
		/* Not a Mach-O file; no results */
+
		return EPKG_END;
+
	}
+

+
	for (macho_arch_t *march = macho->mt_archs; march != NULL; march = march->next) {
+
		const NXArchInfo *ai;
+
		bool is_shlib = false;
+

+
		/* Determine the architecture name */
+
		ai = NXGetArchInfoFromCpuType(march->mat_cputype, march->mat_cpusubtype);
+
		if (ai == NULL) {
+
			pkg_emit_notice("Could not determine architecture type for cpu %d subtype %d", march->mat_cputype, march->mat_cpusubtype);
+
			continue;
+
		}
+

+
		/* Register non-relative libraries as provided. */
+
		// MACTODO: How to handle @rpath/@loader_path/etc?
+
		if (march->mat_install_name != NULL && march->mat_install_name[0] != '/') {
+
			// XXX MACTODO: To get things working, we're shoving library metadata into the library name;
+
			// these should instead be added as supported attributes of package shared library declarations.
+
			char *libname;
+
			asprintf(&libname, "%s.%s", march->mat_install_name, ai->name);
+
			pkg_addshlib_provided(pkg, libname);
+
			is_shlib = true;
+
		}
+

+
		/* Now find all dependencies */
+
		for (macho_loadcmd_t *cmd = march->mat_loadcmds; cmd != NULL; cmd = cmd->next) {
+
			/* Skip everything except for non-weak dylib references */
+
			if (cmd->mlt_type != LC_LOAD_DYLIB)
+
				continue;
+

+
			/* Prevent cyclic self-references. A valid dylib shouldn't include a
+
			 * LC_LOAD_DYLIB referencing itself, but there's nothing that would
+
			 * actually prevent it */
+
			if (strcmp(cmd->mlt_install_name, march->mat_install_name) == 0)
+
				continue;
+

+
			/* Skip non-resolvable library paths. */
+
			switch (cmd->mlt_install_name[0]) {
+
				case '/':
+
					break;
+
				case '@':
+
					// MACTODO: How to handle @rpath/@loader_path/etc?
+
					continue;
+
				default:
+
					continue;
+
			}
+

+
			// XXX MACTODO: To get things working, we're shoving library metadata into the library name;
+
			// these should instead be added as supported attributes of package shared library declarations.
+
			// XXX: This duplicates (and must be kept identical to) the libname construction above.
+
			char *libname;
+
			asprintf(&libname, "%s.%s", cmd->mlt_install_name, ai->name);
+

+
			action(actdata, pkg, fpath, libname, is_shlib);
+
		}
+
	}
+

+
	return EPKG_OK;
+
}
+

+
static int
+
add_dylibs_to_pkg(__unused void *actdata, struct pkg *pkg, const char *fpath,
+
		  const char *name, bool is_shlib)
+
{
+
	/* Skip references to system libraries */
+
	for (size_t i = 0; i < sizeof(system_dylib_prefixes) / sizeof(system_dylib_prefixes[0]); i++) {
+
		const char *prefix = system_dylib_prefixes[i];
+
		if (strncmp(name, prefix, strlen(prefix)) == 0)
+
			return EPKG_OK;
+
	}
+

+
	/* Record the library requirement. */
+
	pkg_addshlib_required(pkg, name);
+

+
	return EPKG_OK;
+
}
+

+
/**
+
 * Extract the major release number from an XNU kernel
+
 * version returned by uname().
+
 *
+
 * @param src A major.minor.revision version string, e.g., as returned from uname(3).
+
 * @param release On success, the parsed major version.
+
 */
+
static int
+
parse_major_release(const char *src, long long *release)
+
{
+
	int ret = EPKG_OK;
+
	char *parsed = NULL;
+
	const char *errstr;
+
	char *eos;
+

+
	parsed = strdup(src);
+
	eos = strchr(parsed, '.');
+
	if (eos == NULL) {
+
		pkg_emit_error("failed to parse major release version from %s", src);
+
		ret = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
	*eos = '\0';
+
	*release = strtonum(parsed, 1, LONG_LONG_MAX, &errstr);
+
	if (errstr != NULL) {
+
		pkg_emit_error("failed to parse major release version from %s: %s", src, errstr);
+
		ret = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
cleanup:
+
	free(parsed);
+
	return ret;
+
}
+

+
/**
+
 * Fetch the host's CPU type.
+
 *
+
 * @param result On success, the fetched CPU type.
+
 */
+
static int
+
host_cpu_type(cpu_type_t *result)
+
{
+
	size_t len;
+
	int resint;
+

+
	/* Fetch CPU type */
+
	len = sizeof(resint);
+
	if (sysctlbyname("hw.cputype", &resint, &len, NULL, 0) != 0) {
+
		pkg_emit_errno("sysctlbyname", "hw.cputype");
+
		return EPKG_FATAL;
+
	}
+

+
	*result = resint;
+
	return EPKG_OK;
+
}
+

+
/**
+
 * Fetch the OS name and major version.
+
 *
+
 * @param osname On success, the OS name (e.g. Darwin).
+
 * @param sz The maximum number of bytes to be written to osname.
+
 * @param major_version On success, the major version of the host.
+
 */
+
static int
+
host_os_info(char *osname, size_t sz, long long *major_version)
+
{
+
	struct utsname ut;
+

+
	/* Fetch OS info from uname() */
+
	if (uname(&ut) != 0) {
+
		pkg_emit_errno("uname", "&ut");
+
		return EPKG_FATAL;
+
	}
+

+
	/* Provide the OS name to the caller. */
+
	if (sz < strlen(ut.sysname) + 1) {
+
		pkg_emit_error("provided buffer is too small for os name: %s", strlen(ut.sysname));
+
		return EPKG_FATAL;
+
	}
+

+
	strlcpy(osname, ut.sysname, sz);
+

+
	/* Parse the major release version */
+
	return parse_major_release(ut.release, major_version);
+
}
+

+
int
+
pkg_analyse_files(struct pkgdb *db, struct pkg *pkg, const char *stage)
+
{
+
	macho_handle_t *macho_handle = NULL;
+
	struct pkg_file *file = NULL;
+
	bool failures = false;
+
	cpu_type_t cpu_type;
+
	char *fpath = NULL;
+
	int ret = EPKG_OK;
+

+
	/* Determine our system's CPU type */
+
	if ((ret = host_cpu_type(&cpu_type)) != EPKG_OK)
+
		goto cleanup;
+

+
	/* Create our mach-o handle */
+
	macho_handle = macho_create_handle();
+
	if (macho_handle == NULL) {
+
			pkg_emit_error("macho_create_handle() failed");
+
			ret = EPKG_FATAL;
+
			goto cleanup;
+
	}
+

+
	/* Evaluate all package files */
+
	while ((ret = pkg_files(pkg, &file)) == EPKG_OK) {
+
		if (stage != NULL)
+
			free(fpath);
+

+
		if (stage != NULL)
+
			asprintf(&fpath, "%s/%s", stage, file->path);
+
		else
+
			fpath = file->path;
+

+
		if (fpath == NULL) {
+
			pkg_emit_error("pkg_analyse_files(): path allocation failed");
+
			ret = EPKG_FATAL;
+
			goto cleanup;
+
		}
+

+
		ret = analyse_macho(pkg, fpath, cpu_type, macho_handle, add_dylibs_to_pkg, db);
+
		if (ret != EPKG_OK && ret != EPKG_END) {
+
			failures = true;
+
		}
+
	}
+

+
	if (ret != EPKG_OK)
+
		goto cleanup;
+

+
cleanup:
+
	macho_destroy_handle(macho_handle);
+

+
	if (stage != NULL)
+
		free(fpath);
+

+
	if (failures)
+
		ret = EPKG_FATAL;
+

+
	return (ret);
+
}
+

+
int
+
pkg_arch_to_legacy(const char *arch, char *dest, size_t sz)
+
{
+
	int i = 0;
+
	const NXArchInfo *ai;
+
	const char *arch_name;
+
	bool is64;
+

+
	bzero(dest, sz);
+
	/* Lower case the OS */
+
	while (arch[i] != ':' && arch[i] != '\0') {
+
		dest[i] = tolower(arch[i]);
+
		i++;
+
	}
+
	if (arch[i] == '\0')
+
		return (0);
+

+
	dest[i++] = ':';
+

+
	/* Copy the version */
+
	while (arch[i] != ':' && arch[i] != '\0') {
+
		dest[i] = arch[i];
+
		i++;
+
	}
+
	if (arch[i] == '\0')
+
		return (0);
+

+
	dest[i++] = ':';
+

+
	/* Map the architecture name to its CPU type */
+
	ai = NXGetArchInfoFromName(arch + i);
+
	if (ai == NULL) {
+
		pkg_emit_error("could not find architecture info for %s", arch + i);
+
		return EPKG_FATAL;
+
	}
+

+
	/* Fetch the base architecture name */
+
	arch_name = macho_get_arch_name(ai->cputype & ~CPU_ARCH_ABI64);
+
	if (arch_name == NULL) {
+
		pkg_emit_error("macho_get_arch_name() failed for %x", ai->cputype);
+
		return EPKG_FATAL;
+
	}
+

+
	/* Determine word size */
+
	is64 = (ai->cputype & CPU_ARCH_ABI64) != 0;
+

+
	/* Emit the result */
+
	snprintf(dest + i, sz - (arch + i - dest), "%s:%s", arch_name, is64 ? "64" : "32");
+
	return EPKG_OK;
+
}
+

+
int
+
pkg_get_myarch_legacy(char *dest, size_t sz)
+
{
+
	char current[sz];
+
	int ret;
+

+
	if ((ret = pkg_get_myarch(current, sizeof(current))) != EPKG_OK)
+
		return ret;
+

+
	return pkg_arch_to_legacy(current, dest, sz);
+
}
+

+
int
+
pkg_get_myarch(char *dest, size_t sz)
+
{
+
	cpu_type_t cpu_type;
+
	const char *cpu_name = NULL;
+
	long long major_version;
+
	char os_name[BUFSIZ];
+
	char *spec = NULL;
+
	int ret = EPKG_OK;
+

+
	/* Fetch basic OS info */
+
	if ((ret = host_os_info(os_name, sizeof(os_name), &major_version)) != EPKG_OK)
+
		goto cleanup;
+

+
	/* Fetch host CPU type */
+
	if ((ret = host_cpu_type(&cpu_type)) != EPKG_OK)
+
		goto cleanup;
+

+
	/* Fetch the name for the base CPU family */
+
	cpu_name = macho_get_arch_name(cpu_type);
+

+
	/* Produce the result */
+
	asprintf(&spec, "%s:%lld:%s", os_name, major_version, cpu_name);
+
	if (spec == NULL) {
+
		pkg_emit_error("asprintf() failed to allocate output string");
+
		ret = EPKG_FATAL;
+
		goto cleanup;
+
	}
+

+
	strlcpy(dest, spec, sz);
+

+
cleanup:
+
	free(spec);
+
	return ret;
+
}
modified libpkg/pkg_status.c
@@ -33,6 +33,8 @@

#include <sqlite3.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"


modified libpkg/pkgdb.c
@@ -32,6 +32,10 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

#include <sys/param.h>
#include <sys/mount.h>

@@ -39,7 +43,9 @@
#include <errno.h>
#include <regex.h>
#include <grp.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
@@ -49,6 +55,8 @@

#include <sqlite3.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
modified libpkg/pkgdb_iterator.c
@@ -32,11 +32,17 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

#include <assert.h>
#include <errno.h>
#include <regex.h>
#include <grp.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
@@ -46,6 +52,8 @@

#include <sqlite3.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
modified libpkg/pkgdb_query.c
@@ -32,11 +32,17 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

#include <assert.h>
#include <errno.h>
#include <regex.h>
#include <grp.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
modified libpkg/repo/binary/Makefile.am
@@ -1,4 +1,5 @@
pkg_common_cflags=	-I$(top_srcdir)/libpkg -I$(top_builddir)/libpkg \
+
			-I$(top_srcdir)/compat \
			-I$(top_srcdir)/external/libsbuf \
			@LDNS_CFLAGS@ \
			-I$(top_srcdir)/external/expat/lib \
modified libpkg/repo/binary/binary_private.h
@@ -539,7 +539,7 @@ typedef enum _sql_prstmt_index {
	ANNOTATE1,
	ANNOTATE2,
	EXISTS,
-
	VERSION,
+
	REPO_VERSION,
	DELETE,
	FTS_APPEND,
	PRSTMT_LAST,
modified libpkg/repo/binary/common.c
@@ -121,7 +121,7 @@ static sql_prstmt sql_prepared_statements[PRSTMT_LAST] = {
		" (SELECT annotation_id FROM annotation WHERE annotation=?3))",
		"ITT",
	},
-
	[VERSION] = {
+
	[REPO_VERSION] = {
		NULL,
		"SELECT version FROM packages WHERE origin=?1",
		"T",
modified libpkg/repo/binary/init.c
@@ -37,6 +37,8 @@

#include <sqlite3.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"
#include "private/pkg.h"
@@ -50,7 +52,7 @@ sqlite_file_exists(sqlite3_context *ctx, int argc, sqlite3_value **argv)
{
	char	 fpath[MAXPATHLEN];
	sqlite3	*db = sqlite3_context_db_handle(ctx);
-
	char	*path = dirname(sqlite3_db_filename(db, "main"));
+
	char	*path = bsd_dirname(sqlite3_db_filename(db, "main"));
	char	 cksum[SHA256_DIGEST_LENGTH * 2 +1];

	if (argc != 2) {
modified libpkg/repo/binary/update.c
@@ -27,6 +27,7 @@
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/mman.h>
+
#include <sys/time.h>

#define _WITH_GETLINE
#include <stdio.h>
@@ -89,11 +90,11 @@ pkg_repo_binary_delete_conflicting(const char *origin, const char *version,
	int ret = EPKG_FATAL;
	const char *oversion;

-
	if (pkg_repo_binary_run_prstatement(VERSION, origin) != SQLITE_ROW) {
+
	if (pkg_repo_binary_run_prstatement(REPO_VERSION, origin) != SQLITE_ROW) {
		ret = EPKG_FATAL;
		goto cleanup;
	}
-
	oversion = sqlite3_column_text(pkg_repo_binary_stmt_prstatement(VERSION), 0);
+
	oversion = sqlite3_column_text(pkg_repo_binary_stmt_prstatement(REPO_VERSION), 0);
	if (!forced) {
		switch(pkg_version_cmp(oversion, version)) {
		case -1:
@@ -125,7 +126,7 @@ pkg_repo_binary_delete_conflicting(const char *origin, const char *version,
	}

cleanup:
-
	sqlite3_reset(pkg_repo_binary_stmt_prstatement(VERSION));
+
	sqlite3_reset(pkg_repo_binary_stmt_prstatement(REPO_VERSION));

	return (ret);
}
modified libpkg/ssh.c
@@ -43,6 +43,8 @@
#include <unistd.h>
#include <fcntl.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"

modified libpkg/utils.c
@@ -50,6 +50,8 @@
#include <float.h>
#include <math.h>

+
#include <bsd_compat.h>
+

#include "pkg.h"
#include "private/event.h"
#include "private/utils.h"
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")
+
])
modified scripts/sign_pkg.sh
@@ -11,4 +11,4 @@ fi
pkg="$1"
sign_cmd="${2:-./sign.sh}"
rm -f "${pkg}.sig"
-
sha256 -q "${pkg}" | "${sign_cmd}" > "${pkg}.sig"
+
openssl dgst -sha256 -binary "${pkg}" | hexdump -v -e '/1 "%x"' | "${sign_cmd}" > "${pkg}.sig"
modified src/Makefile.am
@@ -43,12 +43,15 @@ pkg_LDADD= $(top_builddir)/libpkg/libpkg.la \
			-lcrypto
pkg_CFLAGS=		-I$(top_srcdir)/libpkg \
			-I$(top_builddir)/libpkg \
+
			-I$(top_srcdir)/compat \
+
			-I$(top_srcdir)/external/libsbuf \
			-I$(top_srcdir)/external/uthash \
			-I$(top_srcdir)/external/expat/lib \
			-DGITHASH=\"$(GIT_HEAD)\" \
			-Werror
pkg_static_SOURCES=
pkg_static_LDADD= $(top_builddir)/libpkg/libpkg_static.la \
+
			$(top_builddir)/compat/libbsd_compat.la \
			$(top_builddir)/external/libsbuf_static.la \
			$(pkg_OBJECTS) \
			@LIBJAIL_LIB@ \
@@ -62,13 +65,19 @@ pkg_static_LDADD= $(top_builddir)/libpkg/libpkg_static.la \
			-llzma \
			-lssl \
			-lcrypto \
-
			-lmd \
			-lm
+

+
if HAVE_ELF_ABI
if LIBELF_BUNDLED
pkg_static_LDADD+=	$(top_builddir)/external/libelf_static.la
else
pkg_static_LDADD+=	-lelf
endif
+
endif
+

+
if HAVE_MACHO_ABI
+
pkg_static_LDADD+=	$(top_builddir)/external/libmachista_static.la
+
endif

pkg_static_LDFLAGS=	-all-static
DYNPROG=		pkg
modified src/add.c
@@ -26,6 +26,7 @@
 */

#include <sys/param.h>
+
#include <sys/sbuf.h>

#include <err.h>
#include <errno.h>
modified src/clean.c
@@ -26,6 +26,10 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

#include <sys/stat.h>
#include <sys/queue.h>
/* For MIN */
@@ -35,7 +39,9 @@
#include <err.h>
#include <fts.h>
#include <getopt.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <pkg.h>
#include <stdbool.h>
#include <string.h>
@@ -43,6 +49,8 @@
#include <unistd.h>
#include <uthash.h>

+
#include <bsd_compat.h>
+

#include "pkgcli.h"

struct deletion_list {
modified src/convert.c
@@ -40,6 +40,8 @@

#include <pkg.h>

+
#include <bsd_compat.h>
+

#include "pkgcli.h"

void
modified src/event.c
@@ -46,7 +46,11 @@
#include <unistd.h>
#include <errno.h>
#include <signal.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
+

+
#include <bsd_compat.h>

#include "pkg.h"
#include "pkgcli.h"
modified src/set.c
@@ -36,6 +36,8 @@

#include <pkg.h>

+
#include <bsd_compat.h>
+

#include "pkgcli.h"

#define AUTOMATIC 1U<<0
modified src/ssh.c
@@ -52,7 +52,7 @@ usage_ssh(void)
int
exec_ssh(int argc, char **argv __unused)
{
-
	int fd = AT_FDCWD;
+
	int fd = -1;
	const char *restricted = NULL;

#ifdef HAVE_CAPSICUM
modified src/stats.c
@@ -25,16 +25,24 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

#include <err.h>
#include <getopt.h>
#include <inttypes.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <stdio.h>
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>

+
#include <bsd_compat.h>
+

#include "pkgcli.h"

void
modified src/utils.c
@@ -28,13 +28,19 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#ifdef HAVE_CONFIG_H
+
#include "pkg_config.h"
+
#endif
+

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

#include <err.h>
#include <fcntl.h>
#include <inttypes.h>
+
#ifdef HAVE_LIBUTIL_H
#include <libutil.h>
+
#endif
#include <string.h>
#include <unistd.h>
#include <stdarg.h>
@@ -44,6 +50,8 @@
#include <errno.h>
#include <pkg.h>

+
#include <bsd_compat.h>
+

#include "utlist.h"
#include "pkgcli.h"

@@ -56,7 +64,7 @@ query_tty_yesno(bool r, const char *msg, ...)
	FILE	*tty;
	int	 tty_flags = O_RDWR;

-
#ifndef __DragonFly__
+
#if !defined(__DragonFly__) && !defined(__APPLE__)
	tty_flags |= O_TTY_INIT;
#endif
	tty_fd = open(_PATH_TTY, tty_flags);
modified tests/Makefile.am
@@ -2,9 +2,10 @@ AUTOMAKE_OPTIONS= subdir-objects

GENERIC_LDADD=	$(top_builddir)/libpkg/libpkg_static.la \
		$(top_builddir)/external/libsbuf_static.la \
+
		$(top_builddir)/compat/libbsd_compat.la \
+
		@LDNS_LIBS@ \
		@LIBJAIL_LIB@ \
		@LIBEXECINFO_LIB@ \
-
		-lelf \
		-lfetch \
		-larchive \
		-lutil \
@@ -13,15 +14,23 @@ GENERIC_LDADD= $(top_builddir)/libpkg/libpkg_static.la \
		-lcrypto \
		-L/usr/local/lib \
		-latf-c
+

+
if HAVE_ELF_ABI
if LIBELF_BUNDLED
-
GENERIC_LDADD+=	$(top_builddir)/external/libelf/libelf_static.la
+
GENERIC_LDADD+=	$(top_builddir)/external/libelf_static.la
else
GENERIC_LDADD+=	-lelf
endif
+
endif
+

+
if HAVE_MACHO_ABI
+
GENERIC_LDADD+=	$(top_builddir)/external/libmachista_static.la
+
endif

PUBLIC_INCS=	-I$(top_srcdir)/libpkg -DTESTING \
		-I/usr/local/include
PRIVATE_INCS=	$(PUBLIC_INCS) \
+
		-I$(top_srcdir)/external/libsbuf \
		-I$(top_srcdir)/external/sqlite \
		-I$(top_srcdir)/external/uthash \
		-I$(top_srcdir)/external/libucl/include
@@ -34,7 +43,8 @@ pkg_printf_CFLAGS= $(PRIVATE_INCS)
pkg_printf_LDADD=	$(GENERIC_LDADD)

pkg_validation_SOURCES=	lib/pkg_validation.c
-
pkg_validation_CFLAGS=	$(PUBLIC_INCS)
+
pkg_validation_CFLAGS=	$(PUBLIC_INCS) \
+
			-I$(top_srcdir)/external/libsbuf
pkg_validation_LDADD=	$(GENERIC_LDADD)

plist_SOURCES=		lib/plist.c
modified tests/frontend/pkg.shin
@@ -46,7 +46,7 @@ pkg_config_defaults_body()
	-o match:'^ *SSH_RESTRICT_DIR = "";$' \
	-e empty              \
	-s exit:0             \
-
	env -i PATH=${PATH} LD_LIBRARY_PATH=${LD_LIBRARY_PATH} pkg -C "" -R "" -vv
+
	env -i PATH="${PATH}" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" pkg -C "" -R "" -vv
}

atf_test_case pkg_create_manifest_bad_syntax
@@ -81,7 +81,7 @@ EOF
	atf_check \
		-o empty \
		-e empty \
-
		-s exit:0 test -f test-1.txz
+
		-s exit:1 test -f test-1.txz
}

atf_test_case pkg_repo_load_order
modified tests/frontend/register.shin
@@ -11,7 +11,7 @@ register_conflicts_body() {

	mkdir teststage
	echo a > teststage/plop
-
	sum=$(sha256 -q teststage/plop)
+
	sum=$(openssl dgst -sha256 -binary plop | hexdump -v -e '/1 "%x"')
	cat > test.ucl << EOF
name: "test"
origin: "osef"
@@ -36,7 +36,7 @@ EOF
		-o inline:"$sum\n" \
		-e empty \
		-s exit:0 \
-
		sha256 -q plop
+
		openssl dgst -sha256 -binary plop | hexdump -v -e '/1 "%x"'
	rm -f test.ucl
	echo b > teststage/plop
	cat > test.ucl << EOF
@@ -63,7 +63,7 @@ EOF
		-o inline:"$sum\n" \
		-e empty \
		-s exit:0 \
-
		sha256 -q plop
+
		openssl dgst -sha256 -binary plop | hexdump -v -e '/1 "%x"'
}

atf_init_test_cases() {
modified tests/lib/pkg_validation.c
@@ -24,6 +24,8 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#include <sys/sbuf.h>
+

#include <string.h>

#include <atf-c.h>