Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Merge commit '74fd3389a4ee2611603ee8658d2f3b4182ace73e'
Shawn Webb committed 5 years ago
commit b759c48cef8c3368927b565f0e3dc7839eb1e334
parent 4130f22
139 files changed +5194 -3654
modified .cirrus.yml
@@ -38,7 +38,7 @@ freebsd_task:
#                - ./configure
#                - make -j4 || make V=1
#        check_script: make check || { kyua report --verbose ; exit 1 ;}
-

+
#
#fedora_task:
#        container:
#                image: fedora:latest
modified NEWS
@@ -1,6 +1,56 @@
+
Changes from 1.15.99.7 to 1.15.99.8
+
- Update documentation about configuration options
+
- Fix variable expansion while parsing pkg.conf and repositories
+
- Fix scripts never ending on MacOS
+
- Do not execute post-install script if extraction fails
+
- Fix fetching of meta.conf if it is missing
+
- Update lua to lua 5.3.6
+
- Improve solver missing some reverse dependencies
+
- 'audit' now accept a -R/--raw option for parseable output
+
- 'audit' can now ignore packages based on use define globs or regexp
+
- 'set' now reports when a package provided in command line is not installed
+
- all commands are now consistently using --no-scritps instead of different
+
  flavor per commands
+
- when request to install many packages unavailable in the repo, print all the
+
  missing one instead of stop at the first one only
+
- 'info' now prints the timestamp and repository in the --raw output
+
- Improve error messaging when parsing the configuration file
+
- when upgrading in dry-run mode: stop first checking for pkg, but show all
+
  the packages to be upgraded, pkg included
+
- Fix crash when pkg is told to package a directory as a file
+
- 'create' now accept -e to create package with an expanded version of the
+
  manifest aka readable
+
- Relax keywords attributes syntax by allowing spaces
+

+
Changes from 1.15.99.6 to 1.15.99.7
+
- Merge keywords preinstall script into a single "prepackaging script"
+
- implement triggers (lua and shell)
+
- fix issues with script leaving process in the background
+
- fix an issue with metalog duplicated entries
+
- improve performances of pkg query -F
+
- get rid of sysexits
+

+
Changes from 1.15.99.5 to 1.15.99.6
+
- lots of keywords improvements:
+
  * validation: lua script to validate the keyword line
+
  * deprecated: boolean to mark a keyword as deprecated
+
  * deprecation_message: message if deprecated
+
  * actions_script: script version of 'actions' in lua
+
  * lua scripts: now can have real arguments instead of
+
  pre-expended ones
+

+
Changes from 1.15.99.4 to 1.15.99.5
+
- Use open_memstream instead of utstring
+
- add @include support for plist
+

+
Changes from 1.15.99.3 to 1.15.99.4
+
- Fix build with non recent compiler
+

Changes from 1.15.99.2 to 1.15.99.3
- Fix cases where lua scripts are not properly recognized
- Fix %# in scripts
+
- Implement @include mechanism for plist
+
- Fix lua_copy on arm64

Changes from 1.15.99.1 to 1.15.99.2
- Fix '%X' to include the file name and sum
modified TODO.md
@@ -48,23 +48,6 @@ Allow multiple packages having the same name as long as they don't conflict

    * check for saveme annotation, remove upgrade flag

-
# Add the notion of triggers
-

-
Trigger would allow to only run once from scripts at the end of the upgrade
-
process.
-

-
There should be 2 kind of triggers: pre and post the entire process
-
Triggers should deduplicate themselves and fexible so packages can provide some themselves
-

-
for example:
-
gtk-update-icon-cache should provide a trigger for everything that provides an icon
-

-
if gtk-update-icon-cache is installed on the system and packages are flagged for
-
"icons" then the gtk icon cache would be updated.
-

-
That would remove the gtk-update-icon-cache dependency from every application
-
that provides icons and make it run only once.
-

# Teach pkg repo to keep a list of unavailable packages

When a build fail the package is not added to the repository. As such, a user who attempts to install the package is told that it does not exist, rather than it is just temporarily unavailable.
@@ -91,3 +74,8 @@ OS version which is lower or equal that the one we are running on.
# new groupinstall concept

Add some new ucl files in the ports tree do define groups of files instead of meta ports
+

+
# Test suite
+

+
* Tests for pkg audit
+
* Improve tests for triggers
modified auto.def
@@ -6,7 +6,7 @@ use cc cc-lib cc-shared pkg-config
set maj_ver 1
set med_ver 15
set min_ver 99
-
set dev_ver 1
+
set dev_ver 8
define PKG_API [expr $maj_ver * 1000000 + $med_ver * 1000 + $min_ver]
define VERSION $maj_ver.$med_ver.$min_ver[expr {$dev_ver ? ".$dev_ver" : ""}]

@@ -16,6 +16,8 @@ options {
	with-ldns => "add support for libldns"
	with-libarchive.pc => "build with libarchive getting flags via pc files"
	with-coverage => "build with llvm coverage support"
+
	with-asan => "build with libasan support"
+
	with-ubsan => "Build with libubsan support"
}

if {[opt-str pkgconfigdir dir]} {
@@ -255,6 +257,15 @@ cc-check-includes bsd/err.h bsd/libutil.h bsd/readpassphrase.h \
	bsd/stdio.h bsd/strlib.h bsd/string.h bsd/sys/cdefs.h \
	bsd/unistd.h
 
+
if {[opt-bool with-asan]} {
+
	define-append ASAN_CFLAGS -O0 -ggdb -fsanitize=address
+
	define-append ASAN_LDFLAGS -fsanitize=address
+
	define asan 1
+
	undefine HAVE_STATIC
+
}
+
if {[opt-bool with-ubsan]} {
+
	define ubsan 1
+
}
if {[opt-bool with-coverage]} {
	define-append COVERAGE_CFLAGS -O0 -ggdb -fprofile-instr-generate -fcoverage-mapping
	define-append COVERAGE_LDFLAGS -fprofile-instr-generate -fuse-ld=/usr/local/bin/ld.lld
modified compat/Makefile.autosetup
@@ -1,8 +1,6 @@
include @builddir@/mk/defs.mk
LIB=	bsd_compat
-
SRCS=	basename.c \
-
	closefrom.c \
-
	dirname.c \
+
SRCS=	closefrom.c \
	file_at.c \
	humanize_number.c \
	strtonum.c \
deleted compat/basename.c
@@ -1,96 +0,0 @@
-
/*	$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>
-
#ifndef __GLIBC__
-
/*
-
 * GLIBC provides basename in string.h, and defines basename to
-
 * __xpg_basename (which can modify its input argument) in libgen.h.
-
 */
-
#include <libgen.h>
-
#endif
-
#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
modified compat/bsd_compat.h
@@ -75,9 +75,6 @@
#include <sys/stat.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
deleted compat/dirname.c
@@ -1,89 +0,0 @@
-
/*	$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
modified docs/pkg-audit.8
@@ -23,11 +23,13 @@
.Sh SYNOPSIS
.Nm
.Op Fl Fqr
+
.Op Fl R Ns Op Ar format
.Op Fl f Ar filename
.Ar pkg-name
.Pp
.Nm
.Op Cm --{fetch,quiet,recursive}
+
.Op Cm --raw Ns Op = Ns Ar format
.Op Cm --file Ar filename
.Ar pkg-name
.Sh DESCRIPTION
@@ -38,7 +40,9 @@ Its intended audience is system
administrators and individual users.
.Pp
.Nm
-
uses a database maintained by port committers and the FreeBSD security team
+
uses a database maintained by port committers and the
+
.Fx
+
security team
to check if security advisories for any installed packages exist.
Note that a current ports tree (or any local copy of the ports tree) is not
required for operation.
@@ -77,6 +81,10 @@ displaying many hints.
.It Fl r , Cm --recursive
Prints packages that depend on vulnerable packages and are thus
potentially vulnerable as well.
+
.It Fl R , Cm --raw Ns Op = Ns Ar format
+
The output will be formatted in a parseable
+
.Ar format .
+
It can be ucl (default), json, json-compact and yaml.
.El
.Sh ENVIRONMENT
The following environment variables affect the execution of
modified docs/pkg-create.8
@@ -14,7 +14,7 @@
.\"
.\"     @(#)pkg.8
.\"
-
.Dd April 13, 2020
+
.Dd October 13, 2020
.Dt PKG-CREATE 8
.Os
.\" ---------------------------------------------------------------------------
@@ -24,7 +24,7 @@
.\" ---------------------------------------------------------------------------
.Sh SYNOPSIS
.Nm
-
.Op Fl nqv
+
.Op Fl enqv
.Op Fl f Ar format
.Op Fl l Ar level
.Op Fl o Ar outdir
@@ -33,7 +33,7 @@
.Op Fl t Ar timestamp
.Fl m Ar metadatadir
.Nm
-
.Op Fl nqv
+
.Op Fl enqv
.Op Fl f Ar format
.Op Fl l Ar level
.Op Fl o Ar outdir
@@ -41,7 +41,7 @@
.Op Fl t Ar timestamp
.Fl M Ar manifest
.Nm
-
.Op Fl gnqvx
+
.Op Fl egnqvx
.Op Fl f Ar format
.Op Fl l Ar level
.Op Fl o Ar outdir
@@ -49,7 +49,7 @@
.Op Fl t Ar timestamp
.Ar pkg-name ...
.Nm
-
.Op Fl nqv
+
.Op Fl enqv
.Op Fl f Ar format
.Op Fl l Ar level
.Op Fl o Ar outdir
@@ -59,6 +59,7 @@
.\" ---------------------------------------------------------------------------
.Pp
.Nm
+
.Op Cm --expand-manifest
.Op Cm --no-clobber
.Op Cm --quiet
.Op Cm --verbose
@@ -69,6 +70,7 @@
.Op Cm --root-dir Ar rootdir
.Cm --metadata Ar metadatadir
.Nm
+
.Op Cm --expand-manifest
.Op Cm --no-clobber
.Op Cm --quiet
.Op Cm --verbose
@@ -78,6 +80,7 @@
.Op Cm --root-dir Ar rootdir
.Cm --manifest Ar manifest
.Nm
+
.Op Cm --expand-manifest
.Op Cm --{glob,no-clobber,regex}
.Op Cm --quiet
.Op Cm --verbose
@@ -87,6 +90,7 @@
.Op Cm --root-dir Ar rootdir
.Ar pkg-name ...
.Nm
+
.Op Cm --expand-manifest
.Op Cm --no-clobber
.Op Cm --quiet
.Op Cm --verbose
@@ -129,6 +133,8 @@ The following options are supported by
.It Fl a , Cm --all
Create package tarballs from all packages installed on your system.
This option is incompatible with the
+
.It Fl e, Cm --expand-manifest
+
The manifest contained in pkg will be expanded to readable UCL format.
.Fl g , x
or
.Fl m Ar metadatadir
@@ -497,6 +503,15 @@ These directives should appear at the end of the package list.
If the directory is not empty a warning will be printed, and the
directory will not be removed.
(Subdirectories should be listed before parent directories.)
+
.It Cm @include Ar name
+
Include the
+
.Pa name
+
plist file to the plist currently being parsed. the
+
.Pa name
+
will be opened relatively to the main plist file being parsed.
+
Note: only one level of
+
.Cm @include
+
is allowed
.El
.\" ---------------------------------------------------------------------------
.Sh ENVIRONMENT
modified docs/pkg-lua-script.5
@@ -21,7 +21,7 @@
.Sh DESCRIPTION
.Xr pkg 8
run scripts at given phases of the process it is running.
-
Those scripts are always run withing a
+
Those scripts are always run within a
.Xr capsicum 4
sandbox if available on the system.
.Pp
modified docs/pkg-repo.8
@@ -14,7 +14,7 @@
.\"
.\"     @(#)pkg.8
.\"
-
.Dd April 11, 2017
+
.Dd November 5, 2020
.Dt PKG-REPO 8
.Os
.Sh NAME
@@ -195,6 +195,15 @@ Force quiet output.
.Sh FILES
See
.Xr pkg.conf 5 .
+
.Sh ENVIRONMENT
+
.Bl -tag -width "PKG_REPO_SYMLINK"
+
.It Ev PKG_REPO_HASH
+
When set, rename packages with the short hash of contents appended to the
+
filename.
+
.It Ev PKG_REPO_SYMLINK
+
When set, create a symlink between the short hash filename and the regular
+
filename.
+
.El
.Sh SEE ALSO
.Xr pkg_printf 3 ,
.Xr pkg_repos 3 ,
modified docs/pkg.conf.5
@@ -15,7 +15,7 @@
.\"     @(#)pkg.1
.\" $FreeBSD$
.\"
-
.Dd February 25, 2020
+
.Dd November 5, 2020
.Dt PKG.CONF 5
.Os
.Sh NAME
@@ -98,6 +98,21 @@ the
.Fl y
flag was specified.
Default: NO.
+
.It Cm BACKUP_LIBRARIES: boolean
+
If set to
+
.Sy true
+
.Xr pkg 8
+
will backup ancient libraries if they are removed as the result of an upgrade
+
and keep a copy in the path define in
+
.Cm BACKUP_LIBRARY_PATH .
+
If it does not exist yet a new package
+
.Qq compat-libraries
+
will be created. the version will be bumped each time a new library is backed up
+
Default: NO.
+
.It Cm BACKUP_LIBRARY_PATH: string
+
Location where the libraries are backed up.
+
Default:
+
.Pa /usr/local/lib/compat/pkg .
.It Cm CONSERVATIVE_UPGRADE: boolean
Ensure in multi repository mode that the priority is given as much as possible
to the repository where a package was first installed from.
@@ -128,6 +143,10 @@ suggestions to the output of
as an aid to port maintainers, including indicating when the port
might be marked as architecture independent.
Default: NO.
+
.It Cm DOT_FILE: string
+
When defined to a valid path, pkg will generate a
+
.Xr dot 1
+
file with the dependency graph of the concerned transaction.
.It Cm EVENT_PIPE: string
Send all event messages to the specified FIFO or Unix socket.
Events messages should be formatted as JSON.
@@ -209,7 +228,7 @@ value, make
.Xr pkg-create 8
use verbose mode as standard.
Default:
-
.Sy false
+
.Sy NO
.It Cm PKG_DBDIR: string
Specifies the directory to use for storing the package
database files.
@@ -237,22 +256,13 @@ Directory which
will load plugins from.
Default:
.Pa /usr/local/lib/pkg
-
.It Cm PKG_REPO_HASH: boolean
-
When set to a
-
.Sy true
-
value, make
-
.Xr pkg-repo 8
-
rename packages with the short hash of contents appended to the filename.
+
.It Cm PKG_TRIGGERS_ENABLE
+
Enable or disable execution of triggers at the end of the transactions.
+
Default: YES
+
.It Cm PKG_TRIGGERS_DIR
+
Directory where the triggers are stored
Default:
-
.Sy false
-
.It Cm PKG_REPO_SYMLINK: boolean
-
When set to a
-
.Sy true
-
value, make
-
.Xr pkg-repo 8
-
create a symlink between the short hash filename and the regular filename.
-
Default:
-
.Sy false
+
.Pa /usr/local/share/pkg/triggers .
.It Cm PKG_SSH_ARGS: string
Extra arguments to pass to
.Xr ssh 1 .
@@ -332,12 +342,21 @@ for remote.
Default: If unset, the algorithm described in
.Xr pkg-version 8
is used to determine the version source automatically.
+
.It Cm VALID_URL_SCHEME
+
List of url scheme that pkg can accept while parsing the repository
+
confguration file.
.It Cm VULNXML_SITE: string
Specifies the URL to fetch the
.Pa vuln.xml
vulnerability database from.
Default:
.Pa http://vuxml.freebsd.org/freebsd/vuln.xml.bz2 .
+
.It Cm WARN_SIZE_LIMIT: integer
+
When download and total change is less than than this option, don't ask
+
the user to confirm the desired action.
+
Default:
+
.Sy 1048576
+
.Pq 1MB .
.It Cm WORKERS_COUNT: integer
How many workers are used for pkg-repo.
If set to 0,
@@ -530,7 +549,7 @@ The following variables will be expanded during the parsing of
.It Va OSNAME
Expands to the full version of the name of the operating system.
.It Va RELEASE
-
Expands to the full version of the operaring system.
+
Expands to the full version of the operating system.
.It Va VERSION_MAJOR
If supported, expands to the major version of the operating system.
(Only supported on
@@ -572,7 +591,6 @@ repos_dir: [
     "/usr/local/etc/pkg/repos",
]
syslog: true
-
autodeps: true
developer_mode: false
pkg_env: {
    http_proxy: "http://myproxy:3128",
modified docs/pkg_printf.3
@@ -30,14 +30,12 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-
.Dd August 15, 2015
+
.Dd October 20, 2015
.Dt PKG_PRINTF 3
.Os
.Sh NAME
.Nm pkg_printf , pkg_fprintf , pkg_dprintf , pkg_snprintf , pkg_asprintf ,
-
.Nm pkg_utstring_printf ,
-
.Nm pkg_vprintf , pkg_vfprintf , pkg_vdprintf , pkg_vsnprintf , pkg_vasprintf ,
-
.Nm pkg_utstring_vprintf
+
.Nm pkg_vprintf , pkg_vfprintf , pkg_vdprintf , pkg_vsnprintf , pkg_vasprintf
.Nd formatted output of package data
.Sh LIBRARY
.Lb libpkg
@@ -53,8 +51,6 @@
.Fn pkg_snprintf "char * restrict str" "size_t size" "const char * restrict format" ...
.Ft int
.Fn pkg_asprintf "char **ret" "const char * restrict format" ...
-
.Ft struct utstring *
-
.Fn pkg_utstring_printf "struct utstring * restrict utstring" "const char * restrict format" ...
.In stdarg.h
.Ft int
.Fn pkg_vprintf "const char * restrict format" "va_list ap"
@@ -66,8 +62,6 @@
.Fn pkg_vsnprintf "char * restrict str" "size_t size" "const char * restrict format" "va_list ap"
.Ft int
.Fn pkg_vasprintf "char **ret" "const char * restrict format" "va_list ap"
-
.Ft struct utstring *
-
.Fn pkg_utstring_vprintf "struct utstring * restrict utstring" "const char * restrict format" "va_list ap"
.Sh DESCRIPTION
The
.Fn pkg_printf
@@ -103,11 +97,7 @@ and
.Fn pkg_vasprintf
dynamically allocate a new string with
.Xr malloc 3
-
to write to;
-
.Fn pkg_utstring_printf
-
and
-
.Fn pkg_utstring_vprintf
-
write to the given utstring structure.
+
to write to.
.Pp
These functions write the output under the control of a
.Fa format
@@ -128,14 +118,7 @@ which return the number of characters that would have been printed if the
.Fa size
were unlimited
(again, not including the final
-
.Ql \e0 )
-
and the two functions
-
.Fn pkg_utstring_printf
-
and
-
.Fn pkg_utstring_vprintf
-
which return the given utstring pointer, or
-
.Dv NULL
-
in the case of errors.
+
.Ql \e0 ) .
.Pp
The
.Fn pkg_asprintf
modified external/lua/Makefile
@@ -46,7 +46,7 @@ TO_MAN= lua.1 luac.1

# Lua version and release.
V= 5.3
-
R= $V.4
+
R= $V.6

# Targets start here.
all:	$(PLAT)
modified external/lua/README
@@ -1,5 +1,5 @@

-
This is Lua 5.3.5, released on 26 Jun 2018.
+
This is Lua 5.3.6, released on 14 Sep 2020.

For installation instructions, license details, and
further information about Lua, see doc/readme.html.
modified external/lua/doc/contents.html
@@ -32,7 +32,7 @@ For a complete introduction to Lua programming, see the book

<P>
<SMALL>
-
Copyright &copy; 2015&ndash;2018 Lua.org, PUC-Rio.
+
Copyright &copy; 2015&ndash;2020 Lua.org, PUC-Rio.
Freely available under the terms of the
<A HREF="http://www.lua.org/license.html">Lua license</A>.
</SMALL>
@@ -318,6 +318,37 @@ Freely available under the terms of the
<A HREF="manual.html#pdf-utf8.len">utf8.len</A><BR>
<A HREF="manual.html#pdf-utf8.offset">utf8.offset</A><BR>

+
<H3><A NAME="metamethods">metamethods</A></H3>
+
<P>
+
<A HREF="manual.html#2.4">__add</A><BR>
+
<A HREF="manual.html#2.4">__band</A><BR>
+
<A HREF="manual.html#2.4">__bnot</A><BR>
+
<A HREF="manual.html#2.4">__bor</A><BR>
+
<A HREF="manual.html#2.4">__bxor</A><BR>
+
<A HREF="manual.html#2.4">__call</A><BR>
+
<A HREF="manual.html#2.4">__concat</A><BR>
+
<A HREF="manual.html#2.4">__div</A><BR>
+
<A HREF="manual.html#2.4">__eq</A><BR>
+
<A HREF="manual.html#2.5.1">__gc</A><BR>
+
<A HREF="manual.html#2.4">__idiv</A><BR>
+
<A HREF="manual.html#2.4">__index</A><BR>
+
<A HREF="manual.html#2.4">__le</A><BR>
+
<A HREF="manual.html#2.4">__len</A><BR>
+
<A HREF="manual.html#2.4">__lt</A><BR>
+
<A HREF="manual.html#pdf-getmetatable">__metatable</A><BR>
+
<A HREF="manual.html#2.4">__mod</A><BR>
+
<A HREF="manual.html#2.5.2">__mode</A><BR>
+
<A HREF="manual.html#2.4">__mul</A><BR>
+
<A HREF="manual.html#luaL_newmetatable">__name</A><BR>
+
<A HREF="manual.html#2.4">__newindex</A><BR>
+
<A HREF="manual.html#pdf-pairs">__pairs</A><BR>
+
<A HREF="manual.html#2.4">__pow</A><BR>
+
<A HREF="manual.html#2.4">__shl</A><BR>
+
<A HREF="manual.html#2.4">__shr</A><BR>
+
<A HREF="manual.html#2.4">__sub</A><BR>
+
<A HREF="manual.html#pdf-tostring">__tostring</A><BR>
+
<A HREF="manual.html#2.4">__unm</A><BR>
+

<H3><A NAME="env">environment<BR>variables</A></H3>
<P>
<A HREF="manual.html#pdf-LUA_CPATH">LUA_CPATH</A><BR>
@@ -609,10 +640,10 @@ Freely available under the terms of the

<P CLASS="footer">
Last update:
-
Mon Jun 18 22:56:06 -03 2018
+
Tue Aug 25 13:45:14 UTC 2020
</P>
<!--
-
Last change: revised for Lua 5.3.5
+
Last change: revised for Lua 5.3.6
-->

</BODY>
modified external/lua/doc/manual.html
@@ -19,7 +19,7 @@ by Roberto Ierusalimschy, Luiz Henrique de Figueiredo, Waldemar Celes

<P>
<SMALL>
-
Copyright &copy; 2015&ndash;2018 Lua.org, PUC-Rio.
+
Copyright &copy; 2015&ndash;2020 Lua.org, PUC-Rio.
Freely available under the terms of the
<a href="http://www.lua.org/license.html">Lua license</a>.
</SMALL>
@@ -10972,10 +10972,10 @@ and LiteralString, see <a href="#3.1">&sect;3.1</a>.)

<P CLASS="footer">
Last update:
-
Tue Jun 26 13:16:37 -03 2018
+
Tue Jul 14 10:32:39 UTC 2020
</P>
<!--
-
Last change: revised for Lua 5.3.5
+
Last change: revised for Lua 5.3.6
-->

</body></html>
modified external/lua/doc/readme.html
@@ -107,7 +107,7 @@ Here are the details.
<OL>
<LI>
Open a terminal window and move to
-
the top-level directory, which is named <TT>lua-5.3.5</TT>.
+
the top-level directory, which is named <TT>lua-5.3.6</TT>.
The <TT>Makefile</TT> there controls both the build process and the installation process.
<P>
<LI>
@@ -328,7 +328,7 @@ For details, see
<A HREF="http://www.lua.org/license.html">this</A>.

<BLOCKQUOTE STYLE="padding-bottom: 0em">
-
Copyright &copy; 1994&ndash;2017 Lua.org, PUC-Rio.
+
Copyright &copy; 1994&ndash;2020 Lua.org, PUC-Rio.

<P>
Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -355,10 +355,10 @@ THE SOFTWARE.

<P CLASS="footer">
Last update:
-
Mon Jun 18 22:57:33 -03 2018
+
Tue Jul 14 10:33:01 UTC 2020
</P>
<!--
-
Last change: revised for Lua 5.3.5
+
Last change: revised for Lua 5.3.6
-->

</BODY>
modified external/lua/src/Makefile
@@ -102,7 +102,7 @@ c89:


freebsd:
-
	$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -DLUA_USE_READLINE -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"
+
	$(MAKE) $(ALL) SYSCFLAGS="-DLUA_USE_LINUX -I/usr/include/edit" SYSLIBS="-Wl,-E -ledit" CC="cc"

generic: $(ALL)

modified external/lua/src/lapi.c
@@ -1254,13 +1254,12 @@ LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) {
}


-
static UpVal **getupvalref (lua_State *L, int fidx, int n, LClosure **pf) {
+
static UpVal **getupvalref (lua_State *L, int fidx, int n) {
  LClosure *f;
  StkId fi = index2addr(L, fidx);
  api_check(L, ttisLclosure(fi), "Lua function expected");
  f = clLvalue(fi);
  api_check(L, (1 <= n && n <= f->p->sizeupvalues), "invalid upvalue index");
-
  if (pf) *pf = f;
  return &f->upvals[n - 1];  /* get its upvalue pointer */
}

@@ -1269,7 +1268,7 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {
  StkId fi = index2addr(L, fidx);
  switch (ttype(fi)) {
    case LUA_TLCL: {  /* lua closure */
-
      return *getupvalref(L, fidx, n, NULL);
+
      return *getupvalref(L, fidx, n);
    }
    case LUA_TCCL: {  /* C closure */
      CClosure *f = clCvalue(fi);
@@ -1286,9 +1285,10 @@ LUA_API void *lua_upvalueid (lua_State *L, int fidx, int n) {

LUA_API void lua_upvaluejoin (lua_State *L, int fidx1, int n1,
                                            int fidx2, int n2) {
-
  LClosure *f1;
-
  UpVal **up1 = getupvalref(L, fidx1, n1, &f1);
-
  UpVal **up2 = getupvalref(L, fidx2, n2, NULL);
+
  UpVal **up1 = getupvalref(L, fidx1, n1);
+
  UpVal **up2 = getupvalref(L, fidx2, n2);
+
  if (*up1 == *up2)
+
    return;
  luaC_upvdeccount(L, *up1);
  *up1 = *up2;
  (*up1)->refcount++;
modified external/lua/src/lauxlib.c
@@ -1011,8 +1011,13 @@ static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) {
    free(ptr);
    return NULL;
  }
-
  else
-
    return realloc(ptr, nsize);
+
  else {  /* cannot fail when shrinking a block */
+
    void *newptr = realloc(ptr, nsize);
+
    if (newptr == NULL && ptr != NULL && nsize <= osize)
+
      return ptr;  /* keep the original block */
+
    else  /* no fail or not shrinking */
+
     return newptr;  /* use the new block */
+
  }
}


modified external/lua/src/lcode.c
@@ -1061,7 +1061,7 @@ static void codecomp (FuncState *fs, BinOpr opr, expdesc *e1, expdesc *e2) {


/*
-
** Aplly prefix operation 'op' to expression 'e'.
+
** Apply prefix operation 'op' to expression 'e'.
*/
void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e, int line) {
  static const expdesc ef = {VKINT, {0}, NO_JUMP, NO_JUMP};
modified external/lua/src/ldebug.c
@@ -133,10 +133,11 @@ static const char *upvalname (Proto *p, int uv) {

static const char *findvararg (CallInfo *ci, int n, StkId *pos) {
  int nparams = clLvalue(ci->func)->p->numparams;
-
  if (n >= cast_int(ci->u.l.base - ci->func) - nparams)
+
  int nvararg = cast_int(ci->u.l.base - ci->func) - nparams;
+
  if (n <= -nvararg)
    return NULL;  /* no such vararg */
  else {
-
    *pos = ci->func + nparams + n;
+
    *pos = ci->func + nparams - n;
    return "(*vararg)";  /* generic name for any vararg */
  }
}
@@ -148,7 +149,7 @@ static const char *findlocal (lua_State *L, CallInfo *ci, int n,
  StkId base;
  if (isLua(ci)) {
    if (n < 0)  /* access to vararg values? */
-
      return findvararg(ci, -n, pos);
+
      return findvararg(ci, n, pos);
    else {
      base = ci->u.l.base;
      name = luaF_getlocalname(ci_func(ci)->p, n, currentpc(ci));
modified external/lua/src/liolib.c
@@ -277,6 +277,8 @@ static int io_popen (lua_State *L) {
  const char *filename = luaL_checkstring(L, 1);
  const char *mode = luaL_optstring(L, 2, "r");
  LStream *p = newprefile(L);
+
  luaL_argcheck(L, ((mode[0] == 'r' || mode[0] == 'w') && mode[1] == '\0'),
+
                   2, "invalid mode");
  p->f = l_popen(L, filename, mode);
  p->closef = &io_pclose;
  return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
modified external/lua/src/llex.c
@@ -244,12 +244,12 @@ static int read_numeral (LexState *ls, SemInfo *seminfo) {


/*
-
** skip a sequence '[=*[' or ']=*]'; if sequence is well formed, return
-
** its number of '='s; otherwise, return a negative number (-1 iff there
-
** are no '='s after initial bracket)
+
** reads a sequence '[=*[' or ']=*]', leaving the last bracket.
+
** If sequence is well formed, return its number of '='s + 2; otherwise,
+
** return 1 if there is no '='s or 0 otherwise (an unfinished '[==...').
*/
-
static int skip_sep (LexState *ls) {
-
  int count = 0;
+
static size_t skip_sep (LexState *ls) {
+
  size_t count = 0;
  int s = ls->current;
  lua_assert(s == '[' || s == ']');
  save_and_next(ls);
@@ -257,11 +257,14 @@ static int skip_sep (LexState *ls) {
    save_and_next(ls);
    count++;
  }
-
  return (ls->current == s) ? count : (-count) - 1;
+
  return (ls->current == s) ? count + 2
+
         : (count == 0) ? 1
+
         : 0;
+

}


-
static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
+
static void read_long_string (LexState *ls, SemInfo *seminfo, size_t sep) {
  int line = ls->linenumber;  /* initial line (for error message) */
  save_and_next(ls);  /* skip 2nd '[' */
  if (currIsNewline(ls))  /* string starts with a newline? */
@@ -295,8 +298,8 @@ static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) {
    }
  } endloop:
  if (seminfo)
-
    seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep),
-
                                     luaZ_bufflen(ls->buff) - 2*(2 + sep));
+
    seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + sep,
+
                                     luaZ_bufflen(ls->buff) - 2 * sep);
}


@@ -444,9 +447,9 @@ static int llex (LexState *ls, SemInfo *seminfo) {
        /* else is a comment */
        next(ls);
        if (ls->current == '[') {  /* long comment? */
-
          int sep = skip_sep(ls);
+
          size_t sep = skip_sep(ls);
          luaZ_resetbuffer(ls->buff);  /* 'skip_sep' may dirty the buffer */
-
          if (sep >= 0) {
+
          if (sep >= 2) {
            read_long_string(ls, NULL, sep);  /* skip long comment */
            luaZ_resetbuffer(ls->buff);  /* previous call may dirty the buff. */
            break;
@@ -458,12 +461,12 @@ static int llex (LexState *ls, SemInfo *seminfo) {
        break;
      }
      case '[': {  /* long string or simply '[' */
-
        int sep = skip_sep(ls);
-
        if (sep >= 0) {
+
        size_t sep = skip_sep(ls);
+
        if (sep >= 2) {
          read_long_string(ls, seminfo, sep);
          return TK_STRING;
        }
-
        else if (sep != -1)  /* '[=...' missing second bracket */
+
        else if (sep == 0)  /* '[=...' missing second bracket */
          lexerror(ls, "invalid long string delimiter", TK_STRING);
        return '[';
      }
modified external/lua/src/lobject.c
@@ -266,7 +266,7 @@ static const char *l_str2dloc (const char *s, lua_Number *result, int mode) {
** - 'n'/'N' means 'inf' or 'nan' (which should be rejected)
** - '.' just optimizes the search for the common case (nothing special)
** This function accepts both the current locale or a dot as the radix
-
** mark. If the convertion fails, it may mean number has a dot but
+
** mark. If the conversion fails, it may mean number has a dot but
** locale accepts something else. In that case, the code copies 's'
** to a buffer (because 's' is read-only), changes the dot to the
** current locale radix mark, and tries to convert again.
modified external/lua/src/lparser.c
@@ -544,6 +544,7 @@ static void open_func (LexState *ls, FuncState *fs, BlockCnt *bl) {
  fs->bl = NULL;
  f = fs->f;
  f->source = ls->source;
+
  luaC_objbarrier(ls->L, f, f->source);
  f->maxstacksize = 2;  /* registers 0/1 are always valid */
  enterblock(fs, bl, 0);
}
@@ -1616,6 +1617,7 @@ static void mainfunc (LexState *ls, FuncState *fs) {
  fs->f->is_vararg = 1;  /* main function is always declared vararg */
  init_exp(&v, VLOCAL, 0);  /* create and... */
  newupvalue(fs, ls->envn, &v);  /* ...set environment upvalue */
+
  luaC_objbarrier(ls->L, fs->f, ls->envn);
  luaX_next(ls);  /* read first token */
  statlist(ls);  /* parse main body */
  check(ls, TK_EOS);
@@ -1634,6 +1636,7 @@ LClosure *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff,
  sethvalue(L, L->top, lexstate.h);  /* anchor it */
  luaD_inctop(L);
  funcstate.f = cl->p = luaF_newproto(L);
+
  luaC_objbarrier(L, cl, cl->p);
  funcstate.f->source = luaS_new(L, name);  /* create and anchor TString */
  lua_assert(iswhite(funcstate.f));  /* do not need barrier here */
  lexstate.buff = buff;
modified external/lua/src/lua.h
@@ -1,5 +1,4 @@
/*
-
** $Id: lua.h,v 1.332.1.2 2018/06/13 16:58:17 roberto Exp $
** Lua - A Scripting Language
** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
** See Copyright Notice at the end of this file
@@ -19,11 +18,11 @@
#define LUA_VERSION_MAJOR	"5"
#define LUA_VERSION_MINOR	"3"
#define LUA_VERSION_NUM		503
-
#define LUA_VERSION_RELEASE	"5"
+
#define LUA_VERSION_RELEASE	"6"

#define LUA_VERSION	"Lua " LUA_VERSION_MAJOR "." LUA_VERSION_MINOR
#define LUA_RELEASE	LUA_VERSION "." LUA_VERSION_RELEASE
-
#define LUA_COPYRIGHT	LUA_RELEASE "  Copyright (C) 1994-2018 Lua.org, PUC-Rio"
+
#define LUA_COPYRIGHT	LUA_RELEASE "  Copyright (C) 1994-2020 Lua.org, PUC-Rio"
#define LUA_AUTHORS	"R. Ierusalimschy, L. H. de Figueiredo, W. Celes"


@@ -460,7 +459,7 @@ struct lua_Debug {


/******************************************************************************
-
* Copyright (C) 1994-2018 Lua.org, PUC-Rio.
+
* Copyright (C) 1994-2020 Lua.org, PUC-Rio.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
modified external/lua/src/lundump.c
@@ -85,8 +85,10 @@ static lua_Integer LoadInteger (LoadState *S) {
}


-
static TString *LoadString (LoadState *S) {
+
static TString *LoadString (LoadState *S, Proto *p) {
+
  lua_State *L = S->L;
  size_t size = LoadByte(S);
+
  TString *ts;
  if (size == 0xFF)
    LoadVar(S, size);
  if (size == 0)
@@ -94,13 +96,17 @@ static TString *LoadString (LoadState *S) {
  else if (--size <= LUAI_MAXSHORTLEN) {  /* short string? */
    char buff[LUAI_MAXSHORTLEN];
    LoadVector(S, buff, size);
-
    return luaS_newlstr(S->L, buff, size);
+
    ts = luaS_newlstr(L, buff, size);
  }
  else {  /* long string */
-
    TString *ts = luaS_createlngstrobj(S->L, size);
+
    ts = luaS_createlngstrobj(L, size);
+
    setsvalue2s(L, L->top, ts);  /* anchor it ('loadVector' can GC) */
+
    luaD_inctop(L);
    LoadVector(S, getstr(ts), size);  /* load directly in final place */
-
    return ts;
+
    L->top--;  /* pop string */
  }
+
  luaC_objbarrier(L, p, ts);
+
  return ts;
}


@@ -140,7 +146,7 @@ static void LoadConstants (LoadState *S, Proto *f) {
      break;
    case LUA_TSHRSTR:
    case LUA_TLNGSTR:
-
      setsvalue2n(S->L, o, LoadString(S));
+
      setsvalue2n(S->L, o, LoadString(S, f));
      break;
    default:
      lua_assert(0);
@@ -158,6 +164,7 @@ static void LoadProtos (LoadState *S, Proto *f) {
    f->p[i] = NULL;
  for (i = 0; i < n; i++) {
    f->p[i] = luaF_newproto(S->L);
+
    luaC_objbarrier(S->L, f, f->p[i]);
    LoadFunction(S, f->p[i], f->source);
  }
}
@@ -189,18 +196,18 @@ static void LoadDebug (LoadState *S, Proto *f) {
  for (i = 0; i < n; i++)
    f->locvars[i].varname = NULL;
  for (i = 0; i < n; i++) {
-
    f->locvars[i].varname = LoadString(S);
+
    f->locvars[i].varname = LoadString(S, f);
    f->locvars[i].startpc = LoadInt(S);
    f->locvars[i].endpc = LoadInt(S);
  }
  n = LoadInt(S);
  for (i = 0; i < n; i++)
-
    f->upvalues[i].name = LoadString(S);
+
    f->upvalues[i].name = LoadString(S, f);
}


static void LoadFunction (LoadState *S, Proto *f, TString *psource) {
-
  f->source = LoadString(S);
+
  f->source = LoadString(S, f);
  if (f->source == NULL)  /* no source in dump? */
    f->source = psource;  /* reuse parent's source */
  f->linedefined = LoadInt(S);
@@ -271,6 +278,7 @@ LClosure *luaU_undump(lua_State *L, ZIO *Z, const char *name) {
  setclLvalue(L, L->top, cl);
  luaD_inctop(L);
  cl->p = luaF_newproto(L);
+
  luaC_objbarrier(L, cl, cl->p);
  LoadFunction(&S, cl->p, NULL);
  lua_assert(cl->nupvalues == cl->p->sizeupvalues);
  luai_verifycode(L, buff, cl->p);
deleted external/uthash/utstring.h
@@ -1,398 +0,0 @@
-
/*
-
Copyright (c) 2008-2016, Troy D. Hanson   http://troydhanson.github.com/uthash/
-
All rights reserved.
-

-
Redistribution and use in source and binary forms, with or without
-
modification, are permitted provided that the following conditions are met:
-

-
    * Redistributions of source code must retain the above copyright
-
      notice, this list of conditions and the following disclaimer.
-

-
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.
-
*/
-

-
/* a dynamic string implementation using macros
-
 */
-
#ifndef UTSTRING_H
-
#define UTSTRING_H
-

-
#define UTSTRING_VERSION 2.0.1
-

-
#ifdef __GNUC__
-
#define _UNUSED_ __attribute__ ((__unused__))
-
#else
-
#define _UNUSED_
-
#endif
-

-
#include <stdlib.h>
-
#include <string.h>
-
#include <stdio.h>
-
#include <stdarg.h>
-

-
#ifndef oom
-
#define oom() exit(-1)
-
#endif
-

-
typedef struct {
-
    char *d;
-
    size_t n; /* allocd size */
-
    size_t i; /* index of first unused byte */
-
} UT_string;
-

-
#define utstring_reserve(s,amt)                            \
-
do {                                                       \
-
  if (((s)->n - (s)->i) < (size_t)(amt)) {                 \
-
    char *utstring_tmp = (char*)realloc(                   \
-
      (s)->d, (s)->n + (amt));                             \
-
    if (utstring_tmp == NULL) oom();                       \
-
    (s)->d = utstring_tmp;                                 \
-
    (s)->n += (amt);                                       \
-
  }                                                        \
-
} while(0)
-

-
#define utstring_init(s)                                   \
-
do {                                                       \
-
  (s)->n = 0; (s)->i = 0; (s)->d = NULL;                   \
-
  utstring_reserve(s,100);                                 \
-
  (s)->d[0] = '\0';                                        \
-
} while(0)
-

-
#define utstring_done(s)                                   \
-
do {                                                       \
-
  if ((s)->d != NULL) free((s)->d);                        \
-
  (s)->n = 0;                                              \
-
} while(0)
-

-
#define utstring_free(s)                                   \
-
do {                                                       \
-
  utstring_done(s);                                        \
-
  free(s);                                                 \
-
} while(0)
-

-
#define utstring_new(s)                                    \
-
do {                                                       \
-
   s = (UT_string*)calloc(sizeof(UT_string),1);            \
-
   if (!s) oom();                                          \
-
   utstring_init(s);                                       \
-
} while(0)
-

-
#define utstring_renew(s)                                  \
-
do {                                                       \
-
   if (s) {                                                \
-
     utstring_clear(s);                                    \
-
   } else {                                                \
-
     utstring_new(s);                                      \
-
   }                                                       \
-
} while(0)
-

-
#define utstring_clear(s)                                  \
-
do {                                                       \
-
  (s)->i = 0;                                              \
-
  (s)->d[0] = '\0';                                        \
-
} while(0)
-

-
#define utstring_bincpy(s,b,l)                             \
-
do {                                                       \
-
  utstring_reserve((s),(l)+1);                             \
-
  if (l) memcpy(&(s)->d[(s)->i], b, l);                    \
-
  (s)->i += (l);                                           \
-
  (s)->d[(s)->i]='\0';                                     \
-
} while(0)
-

-
#define utstring_concat(dst,src)                                 \
-
do {                                                             \
-
  utstring_reserve((dst),((src)->i)+1);                          \
-
  if ((src)->i) memcpy(&(dst)->d[(dst)->i], (src)->d, (src)->i); \
-
  (dst)->i += (src)->i;                                          \
-
  (dst)->d[(dst)->i]='\0';                                       \
-
} while(0)
-

-
#define utstring_len(s) ((unsigned)((s)->i))
-

-
#define utstring_body(s) ((s)->d)
-

-
_UNUSED_ static void utstring_printf_va(UT_string *s, const char *fmt, va_list ap) {
-
   int n;
-
   va_list cp;
-
   for (;;) {
-
#ifdef _WIN32
-
      cp = ap;
-
#else
-
      va_copy(cp, ap);
-
#endif
-
      n = vsnprintf (&s->d[s->i], s->n-s->i, fmt, cp);
-
      va_end(cp);
-

-
      if ((n > -1) && ((size_t) n < (s->n-s->i))) {
-
        s->i += n;
-
        return;
-
      }
-

-
      /* Else try again with more space. */
-
      if (n > -1) utstring_reserve(s,n+1); /* exact */
-
      else utstring_reserve(s,(s->n)*2);   /* 2x */
-
   }
-
}
-
#ifdef __GNUC__
-
/* support printf format checking (2=the format string, 3=start of varargs) */
-
static void utstring_printf(UT_string *s, const char *fmt, ...)
-
  __attribute__ (( format( printf, 2, 3) ));
-
#endif
-
_UNUSED_ static void utstring_printf(UT_string *s, const char *fmt, ...) {
-
   va_list ap;
-
   va_start(ap,fmt);
-
   utstring_printf_va(s,fmt,ap);
-
   va_end(ap);
-
}
-

-
/*******************************************************************************
-
 * begin substring search functions                                            *
-
 ******************************************************************************/
-
/* Build KMP table from left to right. */
-
_UNUSED_ static void _utstring_BuildTable(
-
    const char *P_Needle,
-
    size_t P_NeedleLen,
-
    long *P_KMP_Table)
-
{
-
    long i, j;
-

-
    i = 0;
-
    j = i - 1;
-
    P_KMP_Table[i] = j;
-
    while (i < (long) P_NeedleLen)
-
    {
-
        while ( (j > -1) && (P_Needle[i] != P_Needle[j]) )
-
        {
-
           j = P_KMP_Table[j];
-
        }
-
        i++;
-
        j++;
-
        if (i < (long) P_NeedleLen)
-
        {
-
            if (P_Needle[i] == P_Needle[j])
-
            {
-
                P_KMP_Table[i] = P_KMP_Table[j];
-
            }
-
            else
-
            {
-
                P_KMP_Table[i] = j;
-
            }
-
        }
-
        else
-
        {
-
            P_KMP_Table[i] = j;
-
        }
-
    }
-

-
    return;
-
}
-

-

-
/* Build KMP table from right to left. */
-
_UNUSED_ static void _utstring_BuildTableR(
-
    const char *P_Needle,
-
    size_t P_NeedleLen,
-
    long *P_KMP_Table)
-
{
-
    long i, j;
-

-
    i = P_NeedleLen - 1;
-
    j = i + 1;
-
    P_KMP_Table[i + 1] = j;
-
    while (i >= 0)
-
    {
-
        while ( (j < (long) P_NeedleLen) && (P_Needle[i] != P_Needle[j]) )
-
        {
-
           j = P_KMP_Table[j + 1];
-
        }
-
        i--;
-
        j--;
-
        if (i >= 0)
-
        {
-
            if (P_Needle[i] == P_Needle[j])
-
            {
-
                P_KMP_Table[i + 1] = P_KMP_Table[j + 1];
-
            }
-
            else
-
            {
-
                P_KMP_Table[i + 1] = j;
-
            }
-
        }
-
        else
-
        {
-
            P_KMP_Table[i + 1] = j;
-
        }
-
    }
-

-
    return;
-
}
-

-

-
/* Search data from left to right. ( Multiple search mode. ) */
-
_UNUSED_ static long _utstring_find(
-
    const char *P_Haystack,
-
    size_t P_HaystackLen,
-
    const char *P_Needle,
-
    size_t P_NeedleLen,
-
    long *P_KMP_Table)
-
{
-
    long i, j;
-
    long V_FindPosition = -1;
-

-
    /* Search from left to right. */
-
    i = j = 0;
-
    while ( (j < (int)P_HaystackLen) && (((P_HaystackLen - j) + i) >= P_NeedleLen) )
-
    {
-
        while ( (i > -1) && (P_Needle[i] != P_Haystack[j]) )
-
        {
-
            i = P_KMP_Table[i];
-
        }
-
        i++;
-
        j++;
-
        if (i >= (int)P_NeedleLen)
-
        {
-
            /* Found. */
-
            V_FindPosition = j - i;
-
            break;
-
        }
-
    }
-

-
    return V_FindPosition;
-
}
-

-

-
/* Search data from right to left. ( Multiple search mode. ) */
-
_UNUSED_ static long _utstring_findR(
-
    const char *P_Haystack,
-
    size_t P_HaystackLen,
-
    const char *P_Needle,
-
    size_t P_NeedleLen,
-
    long *P_KMP_Table)
-
{
-
    long i, j;
-
    long V_FindPosition = -1;
-

-
    /* Search from right to left. */
-
    j = (P_HaystackLen - 1);
-
    i = (P_NeedleLen - 1);
-
    while ( (j >= 0) && (j >= i) )
-
    {
-
        while ( (i < (int)P_NeedleLen) && (P_Needle[i] != P_Haystack[j]) )
-
        {
-
            i = P_KMP_Table[i + 1];
-
        }
-
        i--;
-
        j--;
-
        if (i < 0)
-
        {
-
            /* Found. */
-
            V_FindPosition = j + 1;
-
            break;
-
        }
-
    }
-

-
    return V_FindPosition;
-
}
-

-

-
/* Search data from left to right. ( One time search mode. ) */
-
_UNUSED_ static long utstring_find(
-
    UT_string *s,
-
    long P_StartPosition,   /* Start from 0. -1 means last position. */
-
    const char *P_Needle,
-
    size_t P_NeedleLen)
-
{
-
    long V_StartPosition;
-
    long V_HaystackLen;
-
    long *V_KMP_Table;
-
    long V_FindPosition = -1;
-

-
    if (P_StartPosition < 0)
-
    {
-
        V_StartPosition = s->i + P_StartPosition;
-
    }
-
    else
-
    {
-
        V_StartPosition = P_StartPosition;
-
    }
-
    V_HaystackLen = s->i - V_StartPosition;
-
    if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
-
    {
-
        V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
-
        if (V_KMP_Table != NULL)
-
        {
-
            _utstring_BuildTable(P_Needle, P_NeedleLen, V_KMP_Table);
-

-
            V_FindPosition = _utstring_find(s->d + V_StartPosition,
-
                                            V_HaystackLen,
-
                                            P_Needle,
-
                                            P_NeedleLen,
-
                                            V_KMP_Table);
-
            if (V_FindPosition >= 0)
-
            {
-
                V_FindPosition += V_StartPosition;
-
            }
-

-
            free(V_KMP_Table);
-
        }
-
    }
-

-
    return V_FindPosition;
-
}
-

-

-
/* Search data from right to left. ( One time search mode. ) */
-
_UNUSED_ static long utstring_findR(
-
    UT_string *s,
-
    long P_StartPosition,   /* Start from 0. -1 means last position. */
-
    const char *P_Needle,
-
    size_t P_NeedleLen)
-
{
-
    long V_StartPosition;
-
    long V_HaystackLen;
-
    long *V_KMP_Table;
-
    long V_FindPosition = -1;
-

-
    if (P_StartPosition < 0)
-
    {
-
        V_StartPosition = s->i + P_StartPosition;
-
    }
-
    else
-
    {
-
        V_StartPosition = P_StartPosition;
-
    }
-
    V_HaystackLen = V_StartPosition + 1;
-
    if ( (V_HaystackLen >= (long) P_NeedleLen) && (P_NeedleLen > 0) )
-
    {
-
        V_KMP_Table = (long *)malloc(sizeof(long) * (P_NeedleLen + 1));
-
        if (V_KMP_Table != NULL)
-
        {
-
            _utstring_BuildTableR(P_Needle, P_NeedleLen, V_KMP_Table);
-

-
            V_FindPosition = _utstring_findR(s->d,
-
                                             V_HaystackLen,
-
                                             P_Needle,
-
                                             P_NeedleLen,
-
                                             V_KMP_Table);
-

-
            free(V_KMP_Table);
-
        }
-
    }
-

-
    return V_FindPosition;
-
}
-
/*******************************************************************************
-
 * end substring search functions                                              *
-
 ******************************************************************************/
-

-
#endif /* UTSTRING_H */
modified libpkg/Makefile.autosetup
@@ -14,6 +14,7 @@ SRCS= backup.c \
	pkg_manifest.c \
	pkg_repo_update.c \
	pkgdb_iterator.c \
+
	lua.c \
	lua_scripts.c \
	scripts.c \
	diff.c \
@@ -46,7 +47,8 @@ SRCS= backup.c \
	extattr.c \
	fetch_ssh.c \
	fetch_libfetch.c \
-
	fetch_file.c
+
	fetch_file.c \
+
	triggers.c

LOCAL_CFLAGS=	-I$(top_srcdir)/compat \
		-I$(top_srcdir)/external/blake2 \
@@ -153,7 +155,7 @@ $(OBJS) $(SHOBJS): $(top_builddir)/pkg_config.h
all: lib$(LIB)_flat.a

@if libmachista
-
lib$(LIB)_flat.a:
+
lib$(LIB)_flat.a: $(STATIC_LIBS)
	libtool -static -o lib$(LIB)_flat.a $(STATIC_LIBS)
@else
lib$(LIB)_flat.a: mergelib_script
@@ -171,9 +173,11 @@ mergelib_script: $(STATIC_LIBS)
install: all pkg.h lib$(LIB)$(LIBSOEXT) lib$(LIB).a
	install -d -m 755 $(DESTDIR)$(libdir)
	install -d -m 755 $(DESTDIR)$(includedir)
+
	install -d -m 755 $(DESTDIR)$(includedir)/pkg
	install -d -m 755 $(DESTDIR)$(pkgconfigdir)
	install -m 644 lib$(LIB)$(LIBSOEXT) $(DESTDIR)$(libdir)/
	ln -sf lib$(LIB)$(LIBSOEXT) $(DESTDIR)$(libdir)/lib$(LIB)$(SH_SOEXT)
	install -m 644 lib$(LIB).a $(DESTDIR)$(libdir)/
	install -m 644 pkg.h $(DESTDIR)$(includedir)/
+
	install -m 644 $(top_srcdir)/libpkg/pkg/audit.h $(DESTDIR)$(includedir)/pkg
	install -m 644 pkg.pc $(DESTDIR)$(pkgconfigdir)/
modified libpkg/backup.c
@@ -123,16 +123,18 @@ pkgdb_dump(struct pkgdb *db, const char *dest)
	int	 ret;
	int destdbfd;
	int savedfd;
+
	char *d;

-
	destdbfd = open(bsd_dirname(dest), O_DIRECTORY|O_CLOEXEC);
-
	if (destdbfd == -1) {
-
		pkg_fatal_errno("Unable to access '%s'",
-
		    bsd_dirname(dest));
-
	}
+
	d = xstrdup(dest);
+
	d = get_dirname(d);
+
	destdbfd = open(d, O_DIRECTORY|O_CLOEXEC);
+
	if (destdbfd == -1)
+
		pkg_fatal_errno("Unable to access '%s'", d);

	savedfd = pkg_get_dbdirfd();
	ctx.pkg_dbdirfd = destdbfd;
	ret = sqlite3_open(dest, &backup);
+
	free(d);

	if (ret != SQLITE_OK) {
		ERROR_SQLITE(backup, "sqlite3_open");
modified libpkg/diff.c
@@ -22,7 +22,6 @@

#include <string.h>
#include <stdlib.h>
-
#include <utstring.h>

#include "private/utils.h"
#include "xmalloc.h"
modified libpkg/elfhints.c
@@ -387,7 +387,7 @@ read_dirs_from_file(const char *hintsfile, const char *listfile)
	char	 buf[MAXPATHLEN];
	int	 linenum;

-
	if ((fp = fopen(listfile, "r")) == NULL)
+
	if ((fp = fopen(listfile, "re")) == NULL)
		err(1, "%s", listfile);

	linenum = 0;
modified libpkg/fetch_libfetch.c
@@ -37,6 +37,7 @@
#include <fetch.h>
#include <paths.h>
#include <poll.h>
+
#include <xstring.h>

#include <bsd_compat.h>

@@ -90,13 +91,13 @@ static int
fetch_connect(struct pkg_repo *repo, struct url *u)
{
	struct url *repourl;
-
	UT_string *fetchOpts = NULL;
+
	xstring *fetchOpts = NULL;
	int64_t max_retry, retry;
	int64_t fetch_timeout;
	int retcode = EPKG_OK;
	char docpath[MAXPATHLEN];
	char zone[MAXHOSTNAMELEN + 24];
-
	char *doc, *reldoc;
+
	char *doc, *reldoc, *opts;
	struct dns_srvinfo *srv_current = NULL;
	struct http_mirror *http_current = NULL;
	struct url_stat st;
@@ -158,31 +159,31 @@ fetch_connect(struct pkg_repo *repo, struct url *u)
			u->doc = docpath;
			u->port = http_current->url->port;
		}
-
		utstring_new(fetchOpts);
-
		utstring_printf(fetchOpts, "i");
+
		fetchOpts = xstring_new();
+
		fputs("i", fetchOpts->fp);
		if (repo != NULL) {
			if ((repo->flags & REPO_FLAGS_USE_IPV4) ==
			    REPO_FLAGS_USE_IPV4)
-
				utstring_printf(fetchOpts, "4");
+
				fputs("4", fetchOpts->fp);
			else if ((repo->flags & REPO_FLAGS_USE_IPV6) ==
			    REPO_FLAGS_USE_IPV6)
-
				utstring_printf(fetchOpts, "6");
+
				fputs("6", fetchOpts->fp);
		}

		if (ctx.debug_level >= 4)
-
			utstring_printf(fetchOpts, "v");
+
			fputs("v", fetchOpts->fp);

+
		opts = xstring_get(fetchOpts);
		pkg_debug(1,"Fetch: fetching from: %s://%s%s%s%s with opts \"%s\"",
		    u->scheme,
		    u->user,
		    u->user[0] != '\0' ? "@" : "",
		    u->host,
		    u->doc,
-
		    utstring_body(fetchOpts));
+
		    opts);

-
		repo->fh = fetchXGet(u, &st, utstring_body(fetchOpts));
+
		repo->fh = fetchXGet(u, &st, opts);
		u->ims_time = st.mtime;
-
		utstring_free(fetchOpts);
		if (repo->fh == NULL) {
			if (fetchLastErrCode == FETCH_OK) {
				retcode = EPKG_UPTODATE;
modified libpkg/fetch_ssh.c
@@ -56,7 +56,8 @@ ssh_connect(struct pkg_repo *repo, struct url *u)
	size_t linecap = 0;
	int sshin[2];
	int sshout[2];
-
	UT_string *cmd = NULL;
+
	xstring *cmd = NULL;
+
	char *cmdline;
	int retcode = EPKG_FATAL;
	const char *ssh_args;
	const char *argv[4];
@@ -82,26 +83,26 @@ ssh_connect(struct pkg_repo *repo, struct url *u)
			goto ssh_cleanup;
		}

-
		utstring_new(cmd);
-
		utstring_printf(cmd, "/usr/bin/ssh -e none -T ");
+
		cmd = xstring_new();
+
		fputs("/usr/bin/ssh -e none -T ", cmd->fp);

		ssh_args = pkg_object_string(pkg_config_get("PKG_SSH_ARGS"));
		if (ssh_args != NULL)
-
			utstring_printf(cmd, "%s ", ssh_args);
+
			fprintf(cmd->fp, "%s ", ssh_args);
		if ((repo->flags & REPO_FLAGS_USE_IPV4) == REPO_FLAGS_USE_IPV4)
-
			utstring_printf(cmd, "-4 ");
+
			fputs("-4 ", cmd->fp);
		else if ((repo->flags & REPO_FLAGS_USE_IPV6) == REPO_FLAGS_USE_IPV6)
-
			utstring_printf(cmd, "-6 ");
+
			fputs("-6 ", cmd->fp);
		if (u->port > 0)
-
			utstring_printf(cmd, "-p %d ", u->port);
+
			fprintf(cmd->fp, "-p %d ", u->port);
		if (u->user[0] != '\0')
-
			utstring_printf(cmd, "%s@", u->user);
-
		utstring_printf(cmd, "%s", u->host);
-
		utstring_printf(cmd, " pkg ssh");
-
		pkg_debug(1, "Fetch: running '%s'", utstring_body(cmd));
+
			fprintf(cmd->fp, "%s@", u->user);
+
		fprintf(cmd->fp, "%s pkg ssh", u->host);
+
		cmdline = xstring_get(cmd);
+
		pkg_debug(1, "Fetch: running '%s'", cmdline);
		argv[0] = _PATH_BSHELL;
		argv[1] = "-c";
-
		argv[2] = utstring_body(cmd);
+
		argv[2] = cmdline;
		argv[3] = NULL;

		if (sshin[0] != STDIN_FILENO)
modified libpkg/gen-version-script.sh
@@ -1,3 +1,3 @@
#!/bin/sh
-
exctags -f /dev/stdout --c-kinds=fp pkg.h | awk 'BEGIN { print "LIBPKG_1.4 {"; print "global:" } /^[^!]/ { print "\t"$1";" } END { print "# Symbols from libcsu\n\t__progname;\n\tenviron;\nlocal:\n\t*;\n};" }' > libpkg.ver
+
exctags -f /dev/stdout --c-kinds=fp pkg.h pkg/*.h | awk 'BEGIN { print "LIBPKG_1.4 {"; print "global:" } /^[^!]/ { print "\t"$1";" } END { print "# Symbols from libcsu\n\t__progname;\n\tenviron;\nlocal:\n\t*;\n};" }' > libpkg.ver

modified libpkg/libpkg.ver
@@ -167,8 +167,6 @@ global:
	pkg_users;
	pkg_utils_count_spaces;
	pkg_utils_tokenize;
-
	pkg_utstring_printf;
-
	pkg_utstring_vprintf;
	pkg_vasprintf;
	pkg_vdprintf;
	pkg_version_change;
added libpkg/lua.c
@@ -0,0 +1,437 @@
+
/*-
+
 * Copyright (c) 2019 Baptiste Daroussin <bapt@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.
+
 */
+

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

+
#include <errno.h>
+
#include <fcntl.h>
+
#include <stdbool.h>
+
#include <xstring.h>
+

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

+
lua_CFunction
+
stack_dump(lua_State *L)
+
{
+
	int i;
+
	int top = lua_gettop(L);
+
	xstring *stack;
+
	char *stackstr;
+

+
	stack = xstring_new();
+

+
	fputs("\nLua Stack\n---------\n"
+
	    "\tType   Data\n\t-----------\n", stack->fp);
+

+
	for (i = 1; i <= top; i++) {  /* repeat for each level */
+
		int t = lua_type(L, i);
+
		fprintf(stack->fp, "%i", i);
+
		switch (t) {
+
		case LUA_TSTRING:  /* strings */
+
			fprintf(stack->fp, "\tString: `%s'\n", lua_tostring(L, i));
+
			break;
+
		case LUA_TBOOLEAN:  /* booleans */
+
			fprintf(stack->fp, "\tBoolean: %s", lua_toboolean(L, i) ? "\ttrue\n" : "\tfalse\n");
+
			break;
+
		case LUA_TNUMBER:  /* numbers */
+
			fprintf(stack->fp, "\tNumber: %g\n", lua_tonumber(L, i));
+
			break;
+
		default:  /* other values */
+
			fprintf(stack->fp, "\tOther: %s\n", lua_typename(L, t));
+
			break;
+
		}
+
	}
+
	stackstr = xstring_get(stack);
+
	pkg_emit_error("%s\n", stackstr);
+
	free(stackstr);
+

+
	return (0);
+
}
+

+
int
+
lua_print_msg(lua_State *L)
+
{
+
	int n = lua_gettop(L);
+
	luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
+
	    "pkg.print_msg takes exactly one argument");
+
	const char* str = luaL_checkstring(L, 1);
+
	lua_getglobal(L, "msgfd");
+
	int fd = lua_tointeger(L, -1);
+

+
	dprintf(fd, "%s\n", str);
+

+
	return (0);
+
}
+

+
 int
+
lua_pkg_copy(lua_State *L)
+
{
+
	int n = lua_gettop(L);
+
	luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
+
	    "pkg.copy takes exactly two arguments");
+
	const char* src = luaL_checkstring(L, 1);
+
	const char* dst = luaL_checkstring(L, 2);
+
	char *buf1, *buf2;
+
	struct stat s1;
+
	int fd1, fd2;
+
	struct timespec ts[2];
+

+
	bool install_as_user = (getenv("INSTALL_AS_USER") != NULL);
+

+
	lua_getglobal(L, "package");
+
	struct pkg *pkg = lua_touserdata(L, -1);
+

+
	if (fstatat(pkg->rootfd, RELATIVE_PATH(src), &s1, 0) == -1) {
+
		lua_pushinteger(L, 2);
+
		return (1);
+
	}
+
	fd1 = openat(pkg->rootfd, RELATIVE_PATH(src), O_RDONLY, DEFFILEMODE);
+
	if (fd1 == -1) {
+
		lua_pushinteger(L, 2);
+
		return (1);
+
	}
+
	/* 
+
	 * We should be using O_WRONLY but a weird aarch64 pmap
+
	 * bug is preventing us doing that
+
	 * See https://bugs.freebsd.org/250271
+
	 */
+
	fd2 = openat(pkg->rootfd, RELATIVE_PATH(dst), O_RDWR | O_CREAT | O_TRUNC | O_EXCL, DEFFILEMODE);
+
	if (fd2 == -1) {
+
		lua_pushinteger(L, 2);
+
		return (1);
+
	}
+
	if (ftruncate(fd2, s1.st_size) != 0) {
+
		lua_pushinteger(L, -1);
+
		return (1);
+
	}
+
	buf1 = mmap(NULL, s1.st_size, PROT_READ, MAP_SHARED, fd1, 0);
+
	if (buf1 == NULL) {
+
		lua_pushinteger(L, -1);
+
		return (1);
+
	}
+
	/* 
+
	 * We should be using only PROT_WRITE but a weird aarch64 pmap
+
	 * bug is preventing us doing that
+
	 * https://bugs.freebsd.org/250271
+
	 */
+
	buf2 = mmap(NULL, s1.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);
+
	if (buf2 == NULL) {
+
		lua_pushinteger(L, -1);
+
		return (1);
+
	}
+

+
	memcpy(buf2, buf1, s1.st_size);
+

+
	munmap(buf1, s1.st_size);
+
	munmap(buf2, s1.st_size);
+
	fsync(fd2);
+

+
	close(fd1);
+
	close(fd2);
+

+
#ifdef HAVE_STRUCT_STAT_ST_MTIM
+
	ts[0] = s1.st_atim;
+
	ts[1] = s1.st_mtim;
+
#else
+
#if defined(_DARWIN_C_SOURCE) || defined(__APPLE__)
+
	ts[0] = s1.st_atimespec;
+
	ts[1] = s1.st_mtimespec;
+
#else
+
	ts[0].tv_sec = s1.st_atime;
+
	ts[0].tv_nsec = 0;
+
	ts[1].tv_sec = s1.st_mtime;
+
	ts[1].tv_nsec = 0;
+
#endif
+
#endif
+

+
	if (set_attrsat(pkg->rootfd, RELATIVE_PATH(dst), s1.st_mode, s1.st_uid,
+
	  s1.st_gid, &ts[0], &ts[1]) != EPKG_OK) {
+
		lua_pushinteger(L, -1);
+
		return (1);
+
	}
+

+
#ifdef HAVE_CHFLAGSAT
+
	if (!install_as_user && s1.st_flags != 0) {
+
		if (chflagsat(pkg->rootfd, RELATIVE_PATH(dst),
+
		    s1.st_flags, AT_SYMLINK_NOFOLLOW) == -1) {
+
			pkg_fatal_errno("Fail to chflags %s", dst);
+
			lua_pushinteger(L, -1);
+
			return (1);
+
		}
+
	}
+
#endif
+
	return (0);
+
}
+

+
int
+
lua_pkg_filecmp(lua_State *L)
+
{
+
	int n = lua_gettop(L);
+
	luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
+
	    "pkg.filecmp takes exactly two arguments");
+
	const char* file1 = luaL_checkstring(L, 1);
+
	const char* file2 = luaL_checkstring(L, 2);
+
	char *buf1, *buf2;
+
	struct stat s1, s2;
+
	int fd1, fd2;
+
	int ret = 0;
+

+
	lua_getglobal(L, "package");
+
	struct pkg *pkg = lua_touserdata(L, -1);
+

+
	if (fstatat(pkg->rootfd, RELATIVE_PATH(file1), &s1, AT_SYMLINK_NOFOLLOW) == -1) {
+
		lua_pushinteger(L, 2);
+
		return (1);
+
	}
+
	if (fstatat(pkg->rootfd, RELATIVE_PATH(file2), &s2, AT_SYMLINK_NOFOLLOW) == -1) {
+
		lua_pushinteger(L, 2);
+
		return (1);
+
	}
+
	if (!S_ISREG(s1.st_mode) || !S_ISREG(s2.st_mode)) {
+
		lua_pushinteger(L, -1);
+
		return (1);
+
	}
+
	if (s1.st_size != s2.st_size) {
+
		lua_pushinteger(L, 1);
+
		return (1);
+
	}
+
	fd1 = openat(pkg->rootfd, RELATIVE_PATH(file1), O_RDONLY, DEFFILEMODE);
+
	if (fd1 == -1) {
+
		lua_pushinteger(L, 2);
+
		return (1);
+
	}
+
	fd2 = openat(pkg->rootfd, RELATIVE_PATH(file2), O_RDONLY, DEFFILEMODE);
+
	if (fd2 == -1) {
+
		lua_pushinteger(L, 2);
+
		return (1);
+
	}
+

+
	buf1 = mmap(NULL, s1.st_size, PROT_READ, MAP_SHARED, fd1, 0);
+
	if (buf1 == NULL) {
+
		lua_pushinteger(L, -1);
+
		return (1);
+
	}
+
	buf2 = mmap(NULL, s2.st_size, PROT_READ, MAP_SHARED, fd2, 0);
+
	if (buf2 == NULL) {
+
		lua_pushinteger(L, -1);
+
		return (1);
+
	}
+
	if (memcmp(buf1, buf2, s1.st_size) != 0)
+
		ret = 1;
+

+
	munmap(buf1, s1.st_size);
+
	munmap(buf2, s2.st_size);
+
	close(fd1);
+
	close(fd2);
+

+
	lua_pushinteger(L, ret);
+
	return (1);
+
}
+

+
int
+
lua_prefix_path(lua_State *L)
+
{
+
	int n = lua_gettop(L);
+
	luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
+
	    "pkg.prefix_path takes exactly one argument");
+
	const char *str = luaL_checkstring(L, 1);
+
	lua_getglobal(L, "package");
+
	struct pkg *p = lua_touserdata(L, -1);
+

+
	char path[MAXPATHLEN];
+
	path[0] = '\0';
+

+
	if (*str == '/') {
+
		strlcat(path, str, MAXPATHLEN);
+
	} else {
+
		strlcat(path, p->prefix, MAXPATHLEN);
+
		strlcat(path, "/", MAXPATHLEN);
+
		strlcat(path, str, MAXPATHLEN);
+
	}
+

+
	lua_pushstring(L, path);
+
	return (1);
+
}
+

+
int
+
lua_stat(lua_State *L)
+
{
+
	int n = lua_gettop(L);
+
	luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
+
	    "pkg.stat takes exactly one argument");
+
	const char *path = RELATIVE_PATH(luaL_checkstring(L, 1));
+
	lua_getglobal(L, "package");
+
	struct pkg *pkg = lua_touserdata(L, -1);
+
	struct stat s;
+
	const char *type = "unknown";
+

+
	if (fstatat(pkg->rootfd, path, &s, AT_SYMLINK_NOFOLLOW) == -1) {
+
		return lua_pushnil(L), 1;
+
	}
+

+
	lua_settop(L, 2);
+
	if (!lua_istable(L, 2))
+
		lua_newtable(L);
+

+
	lua_pushinteger(L, s.st_size);
+
	lua_setfield(L, -2, "size");
+
	lua_pushinteger(L, s.st_uid);
+
	lua_setfield(L, -2, "uid");
+
	lua_pushinteger(L, s.st_gid);
+
	lua_setfield(L, -2, "gid");
+
	if (S_ISREG(s.st_mode))
+
		type = "reg";
+
	else if (S_ISDIR(s.st_mode))
+
		type = "dir";
+
	else if (S_ISCHR(s.st_mode))
+
		type = "chr";
+
	else if (S_ISLNK(s.st_mode))
+
		type = "lnk";
+
	else if (S_ISSOCK(s.st_mode))
+
		type = "sock";
+
	else if (S_ISBLK(s.st_mode))
+
		type = "blk";
+
	else if (S_ISFIFO(s.st_mode))
+
		type = "fifo";
+
	lua_pushstring(L, type);
+
	lua_setfield(L, -2, "type");
+

+
	return (1);
+
}
+

+
/* stolen from lua.c */
+
void
+
lua_args_table(lua_State *L, char **argv, int argc)
+
{
+
	lua_createtable(L, argc, 1);
+
	for (int i = 0; i < argc; i++) {
+
		lua_pushstring(L, argv[i]);
+
		/* lua starts counting by 1 */
+
		lua_rawseti(L, -2, i + 1);
+
	}
+
	lua_setglobal(L, "arg");
+
}
+

+

+
/*
+
 * this is a copy of lua code to be able to override open
+
 * merge of newprefile and newfile
+
 */
+

+
static int
+
my_iofclose(lua_State *L)
+
{
+
	luaL_Stream *p = ((luaL_Stream *)luaL_checkudata(L, 1, LUA_FILEHANDLE));
+
	int res = fclose(p->f);
+
	return (luaL_fileresult(L, (res == 0), NULL));
+
}
+

+
static luaL_Stream *
+
newfile(lua_State *L) {
+
	luaL_Stream *p = (luaL_Stream *)lua_newuserdata(L, sizeof(luaL_Stream));
+
	p->f = NULL;
+
	p->closef = &my_iofclose;
+
	luaL_setmetatable(L, LUA_FILEHANDLE);
+
	return (p);
+
}
+

+
static int
+
lua_io_open(lua_State *L)
+
{
+
	const char *filename = luaL_checkstring(L, 1);
+
	const char *mode = luaL_optstring(L, 2, "r");
+
	lua_getglobal(L, "package");
+
	struct pkg *pkg = lua_touserdata(L, -1);
+
	int oflags;
+
	luaL_Stream *p = newfile(L);
+
	const char *md = mode;
+
	luaL_argcheck(L, checkflags(md, &oflags), 2, "invalid mode");
+
	int fd = openat(pkg->rootfd, RELATIVE_PATH(filename), oflags, DEFFILEMODE);
+
	if (fd == -1)
+
		return (luaL_fileresult(L, 0, filename));
+
	p->f = fdopen(fd, mode);
+
	return ((p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1);
+
}
+

+
static int
+
lua_os_remove(lua_State *L) {
+
	const char *filename = RELATIVE_PATH(luaL_checkstring(L, 1));
+
	lua_getglobal(L, "package");
+
	struct pkg *pkg = lua_touserdata(L, -1);
+
	int flag = 0;
+
	struct stat st;
+

+
	if (fstatat(pkg->rootfd, filename, &st, AT_SYMLINK_NOFOLLOW) == -1)
+
		return (luaL_fileresult(L, 1, NULL));
+

+
	if (S_ISDIR(st.st_mode))
+
		flag = AT_REMOVEDIR;
+

+
	return (luaL_fileresult(L, unlinkat(pkg->rootfd, filename, flag) == 0, NULL));
+
}
+

+
static int
+
lua_os_rename(lua_State *L)
+
{
+
	const char *fromname = RELATIVE_PATH(luaL_checkstring(L, 1));
+
	const char *toname = RELATIVE_PATH(luaL_checkstring(L, 2));
+
	lua_getglobal(L, "package");
+
	struct pkg *pkg = lua_touserdata(L, -1);
+
	return luaL_fileresult(L, renameat(pkg->rootfd, fromname, pkg->rootfd, toname) == 0, NULL);
+
}
+

+
static int
+
lua_os_execute(lua_State *L)
+
{
+
	return (luaL_error(L, "os.execute not available"));
+
}
+

+
static int
+
lua_os_exit(lua_State *L)
+
{
+
	return (luaL_error(L, "os.exit not available"));
+
}
+

+
void
+
lua_override_ios(lua_State *L)
+
{
+
	lua_getglobal(L, "io");
+
	lua_pushcfunction(L, lua_io_open);
+
	lua_setfield(L, -2, "open");
+

+
	lua_getglobal(L, "os");
+
	lua_pushcfunction(L, lua_os_remove);
+
	lua_setfield(L, -2, "remove");
+
	lua_pushcfunction(L, lua_os_rename);
+
	lua_setfield(L, -2, "rename");
+
	lua_pushcfunction(L, lua_os_execute);
+
	lua_setfield(L, -2, "execute");
+
	lua_pushcfunction(L, lua_os_exit);
+
	lua_setfield(L, -2, "exit");
+
}
modified libpkg/lua_scripts.c
@@ -34,386 +34,22 @@
#include <sys/capsicum.h>
#endif

-
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
-
#include <sys/mman.h>

#include <errno.h>
#include <poll.h>
-
#include <utstring.h>
-
#include <lauxlib.h>
-
#include <lualib.h>
-
#include <fcntl.h>
+
#include <xstring.h>
#include <err.h>
#include <stdio.h>

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

extern char **environ;

-
static lua_CFunction
-
stack_dump(lua_State *L)
-
{
-
	int i;
-
	int top = lua_gettop(L);
-
	UT_string *stack;
-

-
	utstring_new(stack);
-

-
	utstring_printf(stack, "\nLua Stack\n---------\n");
-
	utstring_printf(stack, "\tType   Data\n\t-----------\n" );
-

-
	for (i = 1; i <= top; i++) {  /* repeat for each level */
-
		int t = lua_type(L, i);
-
		utstring_printf(stack, "%i", i);
-
		switch (t) {
-
		case LUA_TSTRING:  /* strings */
-
			utstring_printf(stack, "\tString: `%s'\n", lua_tostring(L, i));
-
			break;
-
		case LUA_TBOOLEAN:  /* booleans */
-
			utstring_printf(stack, "\tBoolean: %s", lua_toboolean(L, i) ? "\ttrue\n" : "\tfalse\n");
-
			break;
-
		case LUA_TNUMBER:  /* numbers */
-
			utstring_printf(stack, "\tNumber: %g\n", lua_tonumber(L, i));
-
			break;
-
		default:  /* other values */
-
			utstring_printf(stack, "\tOther: %s\n", lua_typename(L, t));
-
			break;
-
		}
-
	}
-
	pkg_emit_error("%s\n", utstring_body(stack));
-
	utstring_free(stack);
-

-
	return (0);
-
}
-

-
static int
-
lua_print_msg(lua_State *L)
-
{
-
	int n = lua_gettop(L);
-
	luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
-
	    "pkg.print_msg takes exactly one argument");
-
	const char* str = luaL_checkstring(L, 1);
-
	lua_getglobal(L, "msgfd");
-
	int fd = lua_tointeger(L, -1);
-

-
	dprintf(fd, "%s\n", str);
-

-
	return (0);
-
}
-

-
static int
-
lua_pkg_copy(lua_State *L)
-
{
-
	int n = lua_gettop(L);
-
	luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
-
	    "pkg.copy takes exactly two arguments");
-
	const char* src = luaL_checkstring(L, 1);
-
	const char* dst = luaL_checkstring(L, 2);
-
	char *buf1, *buf2;
-
	struct stat s1;
-
	int fd1, fd2;
-

-
	bool install_as_user = (getenv("INSTALL_AS_USER") != NULL);
-

-
	lua_getglobal(L, "package");
-
	struct pkg *pkg = lua_touserdata(L, -1);
-

-
	if (fstatat(pkg->rootfd, RELATIVE_PATH(src), &s1, 0) == -1) {
-
		lua_pushinteger(L, 2);
-
		return (1);
-
	}
-
	fd1 = openat(pkg->rootfd, RELATIVE_PATH(src), O_RDONLY, DEFFILEMODE);
-
	if (fd1 == -1) {
-
		lua_pushinteger(L, 2);
-
		return (1);
-
	}
-
	/* 
-
	 * We should be using O_WRONLY but a weird aarch64 pmap
-
	 * bug is preventing us doing that
-
	 * See https://bugs.freebsd.org/250271
-
	 */
-
	fd2 = openat(pkg->rootfd, RELATIVE_PATH(dst), O_RDWR | O_CREAT | O_TRUNC | O_EXCL, DEFFILEMODE);
-
	if (fd2 == -1) {
-
		lua_pushinteger(L, 2);
-
		return (1);
-
	}
-
	if (ftruncate(fd2, s1.st_size) != 0) {
-
		lua_pushinteger(L, -1);
-
		return (1);
-
	}
-
	buf1 = mmap(NULL, s1.st_size, PROT_READ, MAP_SHARED, fd1, 0);
-
	if (buf1 == NULL) {
-
		lua_pushinteger(L, -1);
-
		return (1);
-
	}
-
	/* 
-
	 * We should be using only PROT_WRITE but a weird aarch64 pmap
-
	 * bug is preventing us doing that
-
	 * https://bugs.freebsd.org/250271
-
	 */
-
	buf2 = mmap(NULL, s1.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd2, 0);
-
	if (buf2 == NULL) {
-
		lua_pushinteger(L, -1);
-
		return (1);
-
	}
-

-
	memcpy(buf2, buf1, s1.st_size);
-

-
	munmap(buf1, s1.st_size);
-
	munmap(buf2, s1.st_size);
-
	fsync(fd2);
-

-
	close(fd1);
-
	close(fd2);
-

-
	if (set_attrsat(pkg->rootfd, RELATIVE_PATH(dst), s1.st_mode, s1.st_uid,
-
	  s1.st_gid, &s1.st_atim, &s1.st_mtim) != EPKG_OK) {
-
		lua_pushinteger(L, -1);
-
		return (1);
-
	}
-

-
#ifdef HAVE_CHFLAGSAT
-
	if (!install_as_user && s1.st_flags != 0) {
-
		if (chflagsat(pkg->rootfd, RELATIVE_PATH(dst),
-
		    s1.st_flags, AT_SYMLINK_NOFOLLOW) == -1) {
-
			pkg_fatal_errno("Fail to chflags %s", dst);
-
			lua_pushinteger(L, -1);
-
			return (1);
-
		}
-
	}
-
#endif
-
	return (0);
-
}
-

-
static int
-
lua_pkg_filecmp(lua_State *L)
-
{
-
	int n = lua_gettop(L);
-
	luaL_argcheck(L, n == 2, n > 2 ? 3 : n,
-
	    "pkg.filecmp takes exactly two arguments");
-
	const char* file1 = luaL_checkstring(L, 1);
-
	const char* file2 = luaL_checkstring(L, 2);
-
	char *buf1, *buf2;
-
	struct stat s1, s2;
-
	int fd1, fd2;
-
	int ret = 0;
-

-
	lua_getglobal(L, "package");
-
	struct pkg *pkg = lua_touserdata(L, -1);
-

-
	if (fstatat(pkg->rootfd, RELATIVE_PATH(file1), &s1, AT_SYMLINK_NOFOLLOW) == -1) {
-
		lua_pushinteger(L, 2);
-
		return (1);
-
	}
-
	if (fstatat(pkg->rootfd, RELATIVE_PATH(file2), &s2, AT_SYMLINK_NOFOLLOW) == -1) {
-
		lua_pushinteger(L, 2);
-
		return (1);
-
	}
-
	if (!S_ISREG(s1.st_mode) || !S_ISREG(s2.st_mode)) {
-
		lua_pushinteger(L, -1);
-
		return (1);
-
	}
-
	if (s1.st_size != s2.st_size) {
-
		lua_pushinteger(L, 1);
-
		return (1);
-
	}
-
	fd1 = openat(pkg->rootfd, RELATIVE_PATH(file1), O_RDONLY, DEFFILEMODE);
-
	if (fd1 == -1) {
-
		lua_pushinteger(L, 2);
-
		return (1);
-
	}
-
	fd2 = openat(pkg->rootfd, RELATIVE_PATH(file2), O_RDONLY, DEFFILEMODE);
-
	if (fd2 == -1) {
-
		lua_pushinteger(L, 2);
-
		return (1);
-
	}
-

-
	buf1 = mmap(NULL, s1.st_size, PROT_READ, MAP_SHARED, fd1, 0);
-
	if (buf1 == NULL) {
-
		lua_pushinteger(L, -1);
-
		return (1);
-
	}
-
	buf2 = mmap(NULL, s2.st_size, PROT_READ, MAP_SHARED, fd2, 0);
-
	if (buf2 == NULL) {
-
		lua_pushinteger(L, -1);
-
		return (1);
-
	}
-
	if (memcmp(buf1, buf2, s1.st_size) != 0)
-
		ret = 1;
-

-
	munmap(buf1, s1.st_size);
-
	munmap(buf2, s2.st_size);
-
	close(fd1);
-
	close(fd2);
-

-
	lua_pushinteger(L, ret);
-
	return (1);
-
}
-

-
static int
-
lua_prefix_path(lua_State *L)
-
{
-
	int n = lua_gettop(L);
-
	luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
-
	    "pkg.prefix_path takes exactly one argument");
-
	const char *str = luaL_checkstring(L, 1);
-
	lua_getglobal(L, "package");
-
	struct pkg *p = lua_touserdata(L, -1);
-

-
	char path[MAXPATHLEN];
-
	path[0] = '\0';
-

-
	if (*str == '/') {
-
		strlcat(path, str, MAXPATHLEN);
-
	} else {
-
		strlcat(path, p->prefix, MAXPATHLEN);
-
		strlcat(path, "/", MAXPATHLEN);
-
		strlcat(path, str, MAXPATHLEN);
-
	}
-

-
	lua_pushstring(L, path);
-
	return (1);
-
}
-

-
/*
-
 * this is a copy of lua code to be able to override open
-
 * merge of newprefile and newfile
-
 */
-

-
static int
-
my_iofclose(lua_State *L)
-
{
-
	luaL_Stream *p = ((luaL_Stream *)luaL_checkudata(L, 1, LUA_FILEHANDLE));
-
	int res = fclose(p->f);
-
	return (luaL_fileresult(L, (res == 0), NULL));
-
}
-

-
static luaL_Stream *
-
newfile(lua_State *L) {
-
	luaL_Stream *p = (luaL_Stream *)lua_newuserdata(L, sizeof(luaL_Stream));
-
	p->f = NULL;
-
	p->closef = &my_iofclose;
-
	luaL_setmetatable(L, LUA_FILEHANDLE);
-
	return (p);
-
}
-

-
static int
-
lua_io_open(lua_State *L)
-
{
-
	const char *filename = luaL_checkstring(L, 1);
-
	const char *mode = luaL_optstring(L, 2, "r");
-
	lua_getglobal(L, "package");
-
	struct pkg *pkg = lua_touserdata(L, -1);
-
	int oflags;
-
	luaL_Stream *p = newfile(L);
-
	const char *md = mode;
-
	luaL_argcheck(L, checkflags(md, &oflags), 2, "invalid mode");
-
	int fd = openat(pkg->rootfd, RELATIVE_PATH(filename), oflags, DEFFILEMODE);
-
	if (fd == -1)
-
		return (luaL_fileresult(L, 0, filename));
-
	p->f = fdopen(fd, mode);
-
	return ((p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1);
-
}
-

-
static int
-
lua_os_remove(lua_State *L) {
-
	const char *filename = RELATIVE_PATH(luaL_checkstring(L, 1));
-
	lua_getglobal(L, "package");
-
	struct pkg *pkg = lua_touserdata(L, -1);
-
	int flag = 0;
-
	struct stat st;
-

-
	if (fstatat(pkg->rootfd, filename, &st, AT_SYMLINK_NOFOLLOW) == -1)
-
		return (luaL_fileresult(L, 1, NULL));
-

-
	if (S_ISDIR(st.st_mode))
-
		flag = AT_REMOVEDIR;
-

-
	return (luaL_fileresult(L, unlinkat(pkg->rootfd, filename, flag) == 0, NULL));
-
}
-

-
static int
-
lua_os_rename(lua_State *L)
-
{
-
	const char *fromname = RELATIVE_PATH(luaL_checkstring(L, 1));
-
	const char *toname = RELATIVE_PATH(luaL_checkstring(L, 2));
-
	lua_getglobal(L, "package");
-
	struct pkg *pkg = lua_touserdata(L, -1);
-
	return luaL_fileresult(L, renameat(pkg->rootfd, fromname, pkg->rootfd, toname) == 0, NULL);
-
}
-

-
static int
-
lua_stat(lua_State *L)
-
{
-
	int n = lua_gettop(L);
-
	luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
-
	    "pkg.stat takes exactly one argument");
-
	const char *path = RELATIVE_PATH(luaL_checkstring(L, 1));
-
	lua_getglobal(L, "package");
-
	struct pkg *pkg = lua_touserdata(L, -1);
-
	struct stat s;
-
	const char *type = "unknown";
-

-
	if (fstatat(pkg->rootfd, path, &s, AT_SYMLINK_NOFOLLOW) == -1) {
-
		return lua_pushnil(L), 1;
-
	}
-

-
	lua_settop(L, 2);
-
	if (!lua_istable(L, 2))
-
		lua_newtable(L);
-

-
	lua_pushinteger(L, s.st_size);
-
	lua_setfield(L, -2, "size");
-
	lua_pushinteger(L, s.st_uid);
-
	lua_setfield(L, -2, "uid");
-
	lua_pushinteger(L, s.st_gid);
-
	lua_setfield(L, -2, "gid");
-
	if (S_ISREG(s.st_mode))
-
		type = "reg";
-
	else if (S_ISDIR(s.st_mode))
-
		type = "dir";
-
	else if (S_ISCHR(s.st_mode))
-
		type = "chr";
-
	else if (S_ISLNK(s.st_mode))
-
		type = "lnk";
-
	else if (S_ISSOCK(s.st_mode))
-
		type = "sock";
-
	else if (S_ISBLK(s.st_mode))
-
		type = "blk";
-
	else if (S_ISFIFO(s.st_mode))
-
		type = "fifo";
-
	lua_pushstring(L, type);
-
	lua_setfield(L, -2, "type");
-

-
	return (1);
-
}
-

-
static int
-
lua_os_execute(lua_State *L)
-
{
-
	return (luaL_error(L, "os.execute not available"));
-
}
-

-
static void
-
lua_override_ios(lua_State *L)
-
{
-
	lua_getglobal(L, "io");
-
	lua_pushcfunction(L, lua_io_open);
-
	lua_setfield(L, -2, "open");
-

-
	lua_getglobal(L, "os");
-
	lua_pushcfunction(L, lua_os_remove);
-
	lua_setfield(L, -2, "remove");
-
	lua_pushcfunction(L, lua_os_rename);
-
	lua_setfield(L, -2, "rename");
-
	lua_pushcfunction(L, lua_os_execute);
-
	lua_setfield(L, -2, "execute");
-
}
-

int
pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type, bool upgrade)
{
@@ -426,12 +62,8 @@ pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type, bool upgrade)
	struct procctl_reaper_status info;
	struct procctl_reaper_kill killemall;
#endif
-
	struct pollfd pfd;
	int cur_pipe[2];
-
	bool should_waitpid;
	char *line = NULL;
-
	FILE *f;
-
	ssize_t linecap = 0;

	if (pkg->lua_scripts[type] == NULL)
		return (EPKG_OK);
@@ -486,16 +118,38 @@ pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type, bool upgrade)
				err(1, "cap_enter failed");
			}
#endif
+
			/* parse and set arguments of the line is in the comments */
+
			if (STARTS_WITH(lscript->script, "-- args: ")) {
+
				char *walk, *begin, *line = NULL;
+
				int spaces, argc = 0;
+
				char **args = NULL;
+

+
				walk = strchr(lscript->script, '\n');
+
				begin = lscript->script + strlen("-- args: ");
+
				line = xstrndup(begin, walk - begin);
+
				spaces = pkg_utils_count_spaces(line);
+
				args = xmalloc((spaces + 1)* sizeof(char *));
+
				walk = xstrdup(line);
+
				while (walk != NULL) {
+
					args[argc++] = pkg_utils_tokenize(&walk);
+
				}
+
				lua_args_table(L, args, argc);
+
			}

			pkg_debug(3, "Scripts: executing lua\n--- BEGIN ---\n%s\nScripts: --- END ---", lscript->script);
			if (luaL_dostring(L, lscript->script)) {
				pkg_emit_error("Failed to execute lua script: %s", lua_tostring(L, -1));
				lua_close(L);
-
				exit(1);
+
				_exit(1);
+
			}
+

+
			if (lua_tonumber(L, -1) != 0) {
+
				lua_close(L);
+
				_exit(1);
			}

			lua_close(L);
-
			exit(0);
+
			_exit(0);
		} else if (pid < 0) {
			pkg_emit_errno("Cannot fork", "lua_script");
			ret = EPKG_FATAL;
@@ -503,68 +157,8 @@ pkg_lua_script_run(struct pkg * const pkg, pkg_lua_script type, bool upgrade)
		}

		close(cur_pipe[1]);
-
		memset(&pfd, 0, sizeof(pfd));
-
		pfd.fd = cur_pipe[0];
-
		pfd.events = POLLIN | POLLERR | POLLHUP;

-
		f = fdopen(pfd.fd, "r");
-
		should_waitpid = true;
-
		for (;;) {
-
			errno = 0;
-
			int pres = poll(&pfd, 1, 1000);
-
			if (pres == -1) {
-
				if (errno == EINTR) {
-
					continue;
-
				} else {
-
					pkg_emit_error("poll() failed: %s",
-
					    strerror(errno));
-
					ret = EPKG_FATAL;
-
					goto cleanup;
-
				}
-
			}
-
			if (pres == 0) {
-
				pid_t p;
-
				assert(should_waitpid);
-
				while ((p = waitpid(pid, &pstat, WNOHANG)) == -1) {
-
					if (errno != EINTR) {
-
						pkg_emit_error("waitpid() "
-
						    "failed: %s", strerror(errno));
-
						ret = EPKG_FATAL;
-
						goto cleanup;
-
					}
-
				}
-
				if (p > 0) {
-
					should_waitpid = false;
-
					break;
-
				}
-
				continue;
-
			}
-
			if (pfd.revents & (POLLERR|POLLHUP))
-
				break;
-
			if (getline(&line, &linecap, f) > 0)
-
				pkg_emit_message(line);
-
			if (feof(f))
-
				break;
-
		}
-
		/* Gather any remaining output */
-
		while (!feof(f) && !ferror(f) && getline(&line, &linecap, f) > 0) {
-
			pkg_emit_message(line);
-
		}
-
		fclose(f);
-

-
		while (should_waitpid && waitpid(pid, &pstat, 0) == -1) {
-
			if (errno != EINTR) {
-
				pkg_emit_error("waitpid() failed: %s",
-
				    strerror(errno));
-
				ret = EPKG_FATAL;
-
				goto cleanup;
-
			}
-
		}
-
		if (WEXITSTATUS(pstat) != 0) {
-
			pkg_emit_error("lua script failed");
-
			ret = EPKG_FATAL;
-
			goto cleanup;
-
		}
+
		ret = pkg_script_run_child(pid, &pstat, cur_pipe[0], "lua");
	}


modified libpkg/merge3.c
@@ -50,7 +50,7 @@
*/

#include <sys/types.h>
-
#include <utstring.h>
+
#include <xstring.h>

#include <string.h>
#include <stdlib.h>
@@ -113,7 +113,7 @@ static int sameEdit(
*/

static int
-
buf_copy_lines(UT_string *to, const char *from, int N)
+
buf_copy_lines(xstring *to, const char *from, int N)
{
	int cnt = 0;
	int i;
@@ -132,7 +132,7 @@ buf_copy_lines(UT_string *to, const char *from, int N)
		i++;
	}
	if (to)
-
		utstring_bincpy(to, from, i);
+
		fwrite(from, i, 1, to->fp);
	return (i);
}

@@ -149,14 +149,14 @@ buf_copy_lines(UT_string *to, const char *from, int N)
** of conflicts is returns
*/
static int
-
buf_merge(char *pPivot, char *pV1, char *pV2, UT_string *pOut){
+
buf_merge(char *pPivot, char *pV1, char *pV2, xstring *pOut){
  int *aC1;              /* Changes from pPivot to pV1 */
  int *aC2;              /* Changes from pPivot to pV2 */
  int i1, i2;            /* Index into aC1[] and aC2[] */
  int nCpy, nDel, nIns;  /* Number of lines to copy, delete, or insert */
  int limit1, limit2;    /* Sizes of aC1[] and aC2[] */

-
  utstring_clear(pOut);         /* Merge results stored in pOut */
+
  xstring_reset(pOut);         /* Merge results stored in pOut */

  /* Compute the edits that occur from pPivot => pV1 (into aC1)
  ** and pPivot => pV2 (into aC2).  Each of the aC1 and aC2 arrays is
@@ -274,7 +274,7 @@ int merge_3way(
  char *pPivot,       /* Common ancestor (older) */
  char *pV1,    /* Name of file for version merging into (mine) */
  char *pV2,          /* Version merging from (yours) */
-
  UT_string *pOut         /* Output written here */
+
  xstring *pOut         /* Output written here */
){
  int rc;             /* Return code of subroutines and this routine */

modified libpkg/metalog.c
@@ -36,7 +36,7 @@ FILE *metalogfp = NULL;
int
metalog_open(const char *metalog)
{
-
	metalogfp = fopen(metalog, "a");
+
	metalogfp = fopen(metalog, "ae");
	if (metalogfp == NULL) {
		pkg_fatal_errno("Unable to open metalog '%s'", metalog);
	} 
modified libpkg/packing.c
@@ -274,7 +274,7 @@ packing_append_tree(struct packing *pack, const char *treepath,
	FTS *fts = NULL;
	FTSENT *fts_e = NULL;
	size_t treelen;
-
	UT_string *sb;
+
	xstring *sb = NULL;
	char *paths[2] = { __DECONST(char *, treepath), NULL };

	treelen = strlen(treepath);
@@ -282,8 +282,8 @@ packing_append_tree(struct packing *pack, const char *treepath,
	if (fts == NULL)
		goto cleanup;

-
	utstring_new(sb);
	while ((fts_e = fts_read(fts)) != NULL) {
+
		xstring_renew(sb);
		switch(fts_e->fts_info) {
		case FTS_D:
		case FTS_DEFAULT:
@@ -293,14 +293,15 @@ packing_append_tree(struct packing *pack, const char *treepath,
			 /* Entries not within this tree are irrelevant. */
			 if (fts_e->fts_pathlen <= treelen)
				  break;
-
			 utstring_clear(sb);
+
			 xstring_reset(sb);
			 /* Strip the prefix to obtain the target path */
			 if (newroot) /* Prepend a root if one is specified */
-
				  utstring_printf(sb, "%s", newroot);
+
				  fputs(newroot, sb->fp);
			 /* +1 = skip trailing slash */
-
			 utstring_printf(sb, "%s", fts_e->fts_path + treelen + 1);
+
			 fputs(fts_e->fts_path + treelen + 1, sb->fp);
+
			 fflush(sb->fp);
			 packing_append_file_attr(pack, fts_e->fts_name,
-
			    utstring_body(sb), NULL, NULL, 0, 0);
+
			    sb->buf, NULL, NULL, 0, 0);
			 break;
		case FTS_DC:
		case FTS_DNR:
@@ -313,7 +314,7 @@ packing_append_tree(struct packing *pack, const char *treepath,
			 break;
		}
	}
-
	utstring_free(sb);
+
	xstring_free(sb);
cleanup:
	fts_close(fts);
	return EPKG_OK;
modified libpkg/pkg.c
@@ -86,8 +86,7 @@ pkg_free(struct pkg *pkg)
	free(pkg->dep_formula);

	for (int i = 0; i < PKG_NUM_SCRIPTS; i++)
-
		if (pkg->scripts[i])
-
			utstring_free(pkg->scripts[i]);
+
		xstring_free(pkg->scripts[i]);

	pkg_list_free(pkg, PKG_DEPS);
	pkg_list_free(pkg, PKG_RDEPS);
@@ -819,8 +818,8 @@ pkg_addscript(struct pkg *pkg, const char *data, pkg_script type)
{

	assert(pkg != NULL);
-
	utstring_renew(pkg->scripts[type]);
-
	utstring_printf(pkg->scripts[type], "%s", data);
+
	xstring_renew(pkg->scripts[type]);
+
	fprintf(pkg->scripts[type]->fp, "%s", data);

	return (EPKG_OK);
}
@@ -931,9 +930,9 @@ pkg_appendscript(struct pkg *pkg, const char *cmd, pkg_script type)
	assert(cmd != NULL && cmd[0] != '\0');

	if (pkg->scripts[type] == NULL)
-
		utstring_new(pkg->scripts[type]);
+
		pkg->scripts[type] = xstring_new();

-
	utstring_printf(pkg->scripts[type], "%s", cmd);
+
	fprintf(pkg->scripts[type]->fp, "%s", cmd);

	return (EPKG_OK);
}
modified libpkg/pkg.h.in
@@ -85,10 +85,10 @@ extern "C" {
#endif

/* Special exit status for worker processes indicating that a restart
-
 * is desired -- eg. after a child has updated pkg(8) itself.  Don't
-
 * clash with any of the sysexits values */
+
 * is desired -- eg. after a child has updated pkg(8) itself.
+
 */

-
#define EX_NEEDRESTART	(EX__MAX + 1)
+
#define EX_NEEDRESTART	4

struct pkg;
struct pkg_dep;
@@ -753,6 +753,7 @@ void pkg_manifest_parser_free(struct pkg_manifest_parser *p);
#define PKG_MANIFEST_EMIT_PRETTY (0x1 << 2)
#define PKG_MANIFEST_EMIT_JSON (0x1 << 3)
#define PKG_MANIFEST_EMIT_UCL (0x1 << 4)
+
#define PKG_MANIFEST_EMIT_LOCAL_METADATA (0x1 << 5)

/**
 * Emit a manifest according to the attributes of pkg.
@@ -1291,6 +1292,9 @@ typedef enum {
	PKG_EVENT_CLEANUP_CALLBACK_REGISTER,
	PKG_EVENT_CLEANUP_CALLBACK_UNREGISTER,
	PKG_EVENT_CONFLICTS,
+
	PKG_EVENT_TRIGGERS_BEGIN,
+
	PKG_EVENT_TRIGGER,
+
	PKG_EVENT_TRIGGERS_FINISHED
} pkg_event_t;

struct pkg_event {
@@ -1456,6 +1460,10 @@ struct pkg_event {
			struct pkg *p2;
			const char *path;
		} e_conflicts;
+
		struct {
+
			char *name;
+
			bool cleanup;
+
		} e_trigger;
	};
};

@@ -1600,29 +1608,6 @@ int pkg_asprintf(char **ret, const char * restrict format, ...);
 */
int pkg_vasprintf(char **ret, const char * restrict format, va_list ap);

-
#if defined(UTSTRING_H)
-
/**
-
 * store data from pkg into sbuf as indicated by the format code format.
-
 * @param sbuf contains the result
-
 * @param ... Varargs list of struct pkg etc. supplying the data
-
 * @param format String with embedded %-escapes indicating what to output
-
 * @return count of the number of characters in the result
-
 */
-
UT_string *pkg_utstring_printf(UT_string * restrict sbuf,
-
	const char * restrict format, ...);
-

-
/**
-
 * store data from pkg into sbuf as indicated by the format code format.
-
 * This is the core function called by all the other pkg_printf() family.
-
 * @param sbuf contains the result
-
 * @param ap Arglist with struct pkg etc. supplying the data
-
 * @param format String with embedded %-escapes indicating what to output
-
 * @return count of the number of characters in the result
-
 */
-
UT_string *pkg_utstring_vprintf(UT_string * restrict sbuf,
-
	const char * restrict format, va_list ap);
-
#endif
-

bool pkg_has_message(struct pkg *p);
bool pkg_is_locked(const struct pkg * restrict p);

@@ -1644,47 +1629,6 @@ bool pkg_is_locked(const struct pkg * restrict p);
 */
#define PKG_FILE_CKSUM_CHARS 10

-
struct pkg_audit;
-

-
/**
-
 * Creates new pkg_audit structure
-
 */
-
struct pkg_audit * pkg_audit_new(void);
-

-
/**
-
 * Fetch and extract audit file from url `src` to the file `dest`
-
 * If no update is required then this function returns `EPKG_UPTODATE`
-
 * @return error code
-
 */
-
int pkg_audit_fetch(const char *src, const char *dest);
-

-
/**
-
 * Load audit file into memory
-
 * @return error code
-
 */
-
int pkg_audit_load(struct pkg_audit *audit, const char *fname);
-

-
/**
-
 * Process loaded audit structure.
-
 * Can and should be executed after cap_enter(3) or another sandboxing call
-
 * @return error code
-
 */
-
int pkg_audit_process(struct pkg_audit *audit);
-

-
#if defined(UTSTRING_H)
-
/**
-
 * Check whether `pkg` is vulnerable against processed `audit` structure.
-
 * If a package is vulnerable, then `result` is set to sbuf describing the
-
 * vulnerability. If `quiet` is true, then this function produces reduced output
-
 * just returning a name of vulnerable package.
-
 * It's caller responsibility to free `result` after use
-
 * @return true and `*result` is set if a package is vulnerable
-
 */
-
bool pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
-
		bool quiet, UT_string **result, int *affected);
-
#endif
-

-
void pkg_audit_free (struct pkg_audit *audit);
char *pkg_utils_tokenize(char **);
int pkg_utils_count_spaces(const char *);
int pkg_add_port(struct pkgdb *db, struct pkg *pkg, const char *root, \
@@ -1707,6 +1651,7 @@ void pkg_create_set_overwrite(struct pkg_create *, bool);
void pkg_create_set_rootdir(struct pkg_create *, const char *);
void pkg_create_set_output_dir(struct pkg_create *, const char *);
void pkg_create_set_timestamp(struct pkg_create *, time_t);
+
void pkg_create_set_expand_manifest(struct pkg_create *, bool);
int pkg_create(struct pkg_create *, const char *, const char *, bool);
int pkg_create_i(struct pkg_create *, struct pkg *, bool);
/* deprecated */
added libpkg/pkg/audit.h
@@ -0,0 +1,121 @@
+
/*
+
 * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2014-2016 Vsevolod Stakhov <vsevolod@FreeBSD.org>
+
 *
+
 * 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
+
 */
+

+
#ifndef _PKG_AUDIT_H
+
#define _PKG_AUDIT_H
+

+
#define EQ 1
+
#define LT 2
+
#define LTE 3
+
#define GT 4
+
#define GTE 5
+

+
struct pkg_audit_version {
+
	char *version;
+
	int type;
+
};
+

+
struct pkg_audit_versions_range {
+
	struct pkg_audit_version v1;
+
	struct pkg_audit_version v2;
+
	struct pkg_audit_versions_range *next;
+
};
+

+
struct pkg_audit_cve {
+
	char *cvename;
+
	struct pkg_audit_cve *next;
+
};
+

+
struct pkg_audit_pkgname {
+
	char *pkgname;
+
	struct pkg_audit_pkgname *next;
+
};
+

+
struct pkg_audit_package {
+
	struct pkg_audit_pkgname *names;
+
	struct pkg_audit_versions_range *versions;
+
	struct pkg_audit_package *next;
+
};
+

+
struct pkg_audit_entry {
+
	const char *pkgname;
+
	struct pkg_audit_package *packages;
+
	struct pkg_audit_pkgname *names;
+
	struct pkg_audit_versions_range *versions;
+
	struct pkg_audit_cve *cve;
+
	char *url;
+
	char *desc;
+
	char *id;
+
	bool ref;
+
	struct pkg_audit_entry *next;
+
};
+

+
struct pkg_audit_issue {
+
	const struct pkg_audit_entry *audit;
+
	struct pkg_audit_issue *next;
+
};
+

+
struct pkg_audit_issues {
+
	int count;
+
	struct pkg_audit_issue *issues;
+
};
+

+
/**
+
 * Creates new pkg_audit structure
+
 */
+
struct pkg_audit * pkg_audit_new(void);
+

+
/**
+
 * Fetch and extract audit file from url `src` to the file `dest`
+
 * If no update is required then this function returns `EPKG_UPTODATE`
+
 * @return error code
+
 */
+
int pkg_audit_fetch(const char *src, const char *dest);
+

+
/**
+
 * Load audit file into memory
+
 * @return error code
+
 */
+
int pkg_audit_load(struct pkg_audit *audit, const char *fname);
+

+
/**
+
 * Process loaded audit structure.
+
 * Can and should be executed after cap_enter(3) or another sandboxing call
+
 * @return error code
+
 */
+
int pkg_audit_process(struct pkg_audit *audit);
+

+
/**
+
 * Check whether `pkg` is vulnerable against processed `audit` structure.
+
 * If a package is vulnerable, then `result` is set to sbuf describing the
+
 * vulnerability. If `quiet` is true, then this function produces reduced output
+
 * just returning a name of vulnerable package.
+
 * It's caller responsibility to free `result` after use
+
 * @return true and `*result` is set if a package is vulnerable
+
 */
+
bool pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg, struct pkg_audit_issues **issues, bool stop_quick);
+

+
void pkg_audit_free(struct pkg_audit *audit);
+
void pkg_audit_issues_free(struct pkg_audit_issues *issues);
+
#endif
modified libpkg/pkg_add.c
@@ -41,7 +41,7 @@
#include <grp.h>
#include <sys/time.h>
#include <time.h>
-
#include <utstring.h>
+
#include <xstring.h>

#include <sys/types.h>
#include <sys/extattr.h>
@@ -114,7 +114,7 @@ attempt_to_merge(int rootfd, struct pkg_config_file *rcf, struct pkg *local,
{
	const struct pkg_file *lf = NULL;
	struct stat st;
-
	UT_string *newconf;
+
	xstring *newconf;
	struct pkg_config_file *lcf = NULL;

	char *localconf = NULL;
@@ -171,14 +171,15 @@ attempt_to_merge(int rootfd, struct pkg_config_file *rcf, struct pkg *local,
	}

	pkg_debug(1, "Attempting to merge %s", rcf->path);
-
	utstring_new(newconf);
+
	newconf = xstring_new();
	if (merge_3way(lcf->content, localconf, rcf->content, newconf) != 0) {
+
		xstring_free(newconf);
		pkg_emit_error("Impossible to merge configuration file");
	} else {
-
		rcf->newcontent = xstrdup(utstring_body(newconf));
+
		char *conf = xstring_get(newconf);
+
		rcf->newcontent = conf;
		rcf->status = MERGE_SUCCESS;
	}
-
	utstring_free(newconf);
	free(localconf);
}

@@ -413,6 +414,20 @@ do_extract_dir(struct pkg* pkg, struct archive *a __unused, struct archive_entry
	return (EPKG_OK);
}

+

+
static bool
+
try_mkdir(int fd, const char *path)
+
{
+
	char *p = get_dirname(xstrdup(path));
+

+
	if (!mkdirat_p(fd, RELATIVE_PATH(p))) {
+
		free(p);
+
		return (false);
+
	}
+
	free(p);
+
	return (true);
+
}
+

static int
create_symlinks(struct pkg *pkg, struct pkg_file *f, const char *target)
{
@@ -422,7 +437,7 @@ create_symlinks(struct pkg *pkg, struct pkg_file *f, const char *target)
retry:
	if (symlinkat(target, pkg->rootfd, RELATIVE_PATH(f->temppath)) == -1) {
		if (!tried_mkdir) {
-
			if (!mkdirat_p(pkg->rootfd, RELATIVE_PATH(bsd_dirname(f->path))))
+
			if (!try_mkdir(pkg->rootfd, f->path))
				return (EPKG_FATAL);
			tried_mkdir = true;
			goto retry;
@@ -490,8 +505,7 @@ retry:
	if (linkat(pkg->rootfd, RELATIVE_PATH(fh->temppath),
	    pkg->rootfd, RELATIVE_PATH(f->temppath), 0) == -1) {
		if (!tried_mkdir) {
-
			if (!mkdirat_p(pkg->rootfd,
-
			    RELATIVE_PATH(bsd_dirname(f->path))))
+
			if (!try_mkdir(pkg->rootfd, f->path))
				return (EPKG_FATAL);
			tried_mkdir = true;
			goto retry;
@@ -549,10 +563,8 @@ retry:
	    O_CREAT|O_WRONLY|O_EXCL, f->perm);
	if (fd == -1) {
		if (!tried_mkdir) {
-
			if (!mkdirat_p(pkg->rootfd,
-
			    RELATIVE_PATH(bsd_dirname(f->path)))) {
+
			if (!try_mkdir(pkg->rootfd, f->path))
				return (EPKG_FATAL);
-
			}
			tried_mkdir = true;
			goto retry;
		}
@@ -762,6 +774,7 @@ pkg_extract_finalize(struct pkg *pkg)
	install_as_user = (getenv("INSTALL_AS_USER") != NULL);

	while (pkg_files(pkg, &f) == EPKG_OK) {
+
		append_touched_file(f->path);
		if (*f->temppath == '\0')
			continue;
		fto = f->path;
@@ -848,7 +861,7 @@ pkg_globmatch(char *pattern, const char *name)
		buf = strrchr(g.gl_pathv[i], '-');
		if (buf == NULL)
			continue;
-
		buf2 = strchr(g.gl_pathv[i], '/');
+
		buf2 = strrchr(g.gl_pathv[i], '/');
		if (buf2 == NULL)
			buf2 = g.gl_pathv[i];
		else
@@ -863,7 +876,8 @@ pkg_globmatch(char *pattern, const char *name)
		if (pkg_version_cmp(path, g.gl_pathv[i]) == 1)
			path = g.gl_pathv[i];
	}
-
	path = xstrdup(path);
+
	if (path)
+
		path = xstrdup(path);
	globfree(&g);

	return (path);
@@ -925,7 +939,7 @@ pkg_add_check_pkg_archive(struct pkgdb *db, struct pkg *pkg,
	fromstdin = (strcmp(path, "-") == 0);
	strlcpy(bd, path, sizeof(bd));
	if (!fromstdin) {
-
		basedir = bsd_dirname(bd);
+
		basedir = get_dirname(bd);
		strlcpy(bd, basedir, sizeof(bd));
		if ((ext = strrchr(path, '.')) == NULL) {
			pkg_emit_error("%s has no extension", path);
@@ -1060,7 +1074,7 @@ pkg_add_common(struct pkgdb *db, const char *path, unsigned flags,
	struct archive		*a;
	struct archive_entry	*ae;
	struct pkg		*pkg = NULL;
-
	UT_string		*message;
+
	xstring			*message = NULL;
	struct pkg_message	*msg;
	struct pkg_file		*f;
	const char		*msgstr;
@@ -1179,11 +1193,13 @@ pkg_add_common(struct pkgdb *db, const char *path, unsigned flags,

			pkg_rollback_pkg(pkg);
			pkg_delete_dirs(db, pkg, NULL);
-
			goto cleanup_reg;
+
			pkgdb_register_finale(db, retcode, NULL);
+
			goto cleanup;
		}
	}

	if (local != NULL) {
+
		pkg_open_root_fd(local);
		pkg_debug(1, "Cleaning up old version");
		if (pkg_add_cleanup_old(db, local, pkg, flags) != EPKG_OK) {
			retcode = EPKG_FATAL;
@@ -1196,7 +1212,7 @@ pkg_add_common(struct pkgdb *db, const char *path, unsigned flags,
	pkgdb_update_config_file_content(pkg, db->sqlite);

	retcode = pkg_extract_finalize(pkg);
-
cleanup_reg:
+

	pkgdb_register_finale(db, retcode, NULL);
	/*
	 * Execute post install scripts
@@ -1225,8 +1241,6 @@ cleanup_reg:
			pkg_emit_install_finished(pkg, local);
	}

-
	if (pkg->message != NULL)
-
		utstring_new(message);
	LL_FOREACH(pkg->message, msg) {
		msgstr = NULL;
		if (msg->type == PKG_MESSAGE_ALWAYS) {
@@ -1253,21 +1267,21 @@ cleanup_reg:
			msgstr = msg->str;
		}
		if (msgstr != NULL) {
-
			if (utstring_len(message) == 0) {
-
				pkg_utstring_printf(message, "=====\nMessage from "
+
			if (message == NULL) {
+
				message = xstring_new();
+
				pkg_fprintf(message->fp, "=====\nMessage from "
				    "%n-%v:\n\n", pkg, pkg);
			}
-
			utstring_printf(message, "--\n%s\n", msgstr);
+
			fprintf(message->fp, "--\n%s\n", msgstr);
		}
	}
-
	if (pkg->message != NULL) {
-
		if (utstring_len(message) > 0) {
-
			pkg_emit_message(utstring_body(message));
-
		}
-
		utstring_free(message);
+
	if (pkg->message != NULL && message != NULL) {
+
		fflush(message->fp);
+
		pkg_emit_message(message->buf);
+
		xstring_free(message);
	}

-
	cleanup:
+
cleanup:
	if (a != NULL) {
		archive_read_close(a);
		archive_read_free(a);
modified libpkg/pkg_attributes.c
@@ -97,7 +97,8 @@ pkg_script_get(struct pkg const * const p, pkg_script i)
	if (p->scripts[i] == NULL)
		return (NULL);

-
	return (utstring_body(p->scripts[i]));
+
	fflush(p->scripts[i]->fp);
+
	return (p->scripts[i]->buf);
}

/*
modified libpkg/pkg_audit.c
@@ -36,68 +36,15 @@
#include <stdio.h>
#include <string.h>
#include <utlist.h>
+
#include <xstring.h>

#include <yxml.h>

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

-
#define EQ 1
-
#define LT 2
-
#define LTE 3
-
#define GT 4
-
#define GTE 5
-

-
static const char* vop_names[] = {
-
	[0] = "",
-
	[EQ] = "=",
-
	[LT] = "<",
-
	[LTE] = "<=",
-
	[GT] = ">",
-
	[GTE] = ">="
-
};
-

-
struct pkg_audit_version {
-
	char *version;
-
	int type;
-
};
-

-
struct pkg_audit_versions_range {
-
	struct pkg_audit_version v1;
-
	struct pkg_audit_version v2;
-
	struct pkg_audit_versions_range *next;
-
};
-

-
struct pkg_audit_cve {
-
	char *cvename;
-
	struct pkg_audit_cve *next;
-
};
-

-
struct pkg_audit_pkgname {
-
	char *pkgname;
-
	struct pkg_audit_pkgname *next;
-
};
-

-
struct pkg_audit_package {
-
	struct pkg_audit_pkgname *names;
-
	struct pkg_audit_versions_range *versions;
-
	struct pkg_audit_package *next;
-
};
-

-
struct pkg_audit_entry {
-
	const char *pkgname;
-
	struct pkg_audit_package *packages;
-
	struct pkg_audit_pkgname *names;
-
	struct pkg_audit_versions_range *versions;
-
	struct pkg_audit_cve *cve;
-
	char *url;
-
	char *desc;
-
	char *id;
-
	bool ref;
-
	struct pkg_audit_entry *next;
-
};
-

/*
 * The _sorted stuff.
 *
@@ -137,6 +84,8 @@ struct pkg_audit {
	struct pkg_audit_item *items;
	bool parsed;
	bool loaded;
+
	char **ignore_globs;
+
	char **ignore_regexp;
	void *map;
	size_t len;
};
@@ -379,7 +328,7 @@ struct vulnxml_userdata {
	struct pkg_audit_entry *cur_entry;
	struct pkg_audit *audit;
	enum vulnxml_parse_state state;
-
	UT_string *content;
+
	xstring *content;
	int range_num;
	enum vulnxml_parse_attribute_state attr;
};
@@ -448,18 +397,19 @@ vulnxml_end_element(struct vulnxml_userdata *ud, yxml_t *xml)
	struct pkg_audit_versions_range *vers;
	int range_type = -1;

+
	fflush(ud->content->fp);
	if (ud->state == VULNXML_PARSE_VULN && strcasecmp(xml->elem, "vuxml") == 0) {
		pkg_audit_expand_entry(ud->cur_entry, &ud->audit->entries);
		ud->state = VULNXML_PARSE_INIT;
	}
	else if (ud->state == VULNXML_PARSE_TOPIC && strcasecmp(xml->elem, "vuln") == 0) {
-
		ud->cur_entry->desc = xstrdup(utstring_body(ud->content));
+
		ud->cur_entry->desc = xstrdup(ud->content->buf);
		ud->state = VULNXML_PARSE_VULN;
	}
	else if (ud->state == VULNXML_PARSE_CVE && strcasecmp(xml->elem, "references") == 0) {
		entry = ud->cur_entry;
		cve = xmalloc(sizeof(struct pkg_audit_cve));
-
		cve->cvename = xstrdup(utstring_body(ud->content));
+
		cve->cvename = xstrdup(ud->content->buf);
		LL_PREPEND(entry->cve, cve);
		ud->state = VULNXML_PARSE_VULN;
	}
@@ -467,7 +417,7 @@ vulnxml_end_element(struct vulnxml_userdata *ud, yxml_t *xml)
		ud->state = VULNXML_PARSE_VULN;
	}
	else if (ud->state == VULNXML_PARSE_PACKAGE_NAME && strcasecmp(xml->elem, "package") == 0) {
-
		ud->cur_entry->packages->names->pkgname = xstrdup(utstring_body(ud->content));
+
		ud->cur_entry->packages->names->pkgname = xstrdup(ud->content->buf);
		ud->state = VULNXML_PARSE_PACKAGE;
	}
	else if (ud->state == VULNXML_PARSE_RANGE && strcasecmp(xml->elem, "package") == 0) {
@@ -497,15 +447,15 @@ vulnxml_end_element(struct vulnxml_userdata *ud, yxml_t *xml)
	if (range_type > 0) {
		vers = ud->cur_entry->packages->versions;
		if (ud->range_num == 1) {
-
			vers->v1.version = xstrdup(utstring_body(ud->content));
+
			vers->v1.version = xstrdup(ud->content->buf);
			vers->v1.type = range_type;
		}
		else if (ud->range_num == 2) {
-
			vers->v2.version = xstrdup(utstring_body(ud->content));
+
			vers->v2.version = xstrdup(ud->content->buf);
			vers->v2.type = range_type;
		}
	}
-
	utstring_clear(ud->content);
+
	xstring_reset(ud->content);
}

static void
@@ -521,18 +471,19 @@ vulnxml_start_attribute(struct vulnxml_userdata *ud, yxml_t *xml)
static void
vulnxml_end_attribute(struct vulnxml_userdata *ud, yxml_t *xml __unused)
{
+
	fflush(ud->content->fp);
	if (ud->state == VULNXML_PARSE_VULN && ud->attr == VULNXML_ATTR_VID) {
-
		ud->cur_entry->id = xstrdup(utstring_body(ud->content));
+
		ud->cur_entry->id = xstrdup(ud->content->buf);
		ud->attr = VULNXML_ATTR_NONE;
	}
-
	utstring_clear(ud->content);
+
	xstring_reset(ud->content);
}

static void
vulnxml_val_attribute(struct vulnxml_userdata *ud, yxml_t *xml)
{
	if (ud->state == VULNXML_PARSE_VULN && ud->attr == VULNXML_ATTR_VID) {
-
		utstring_printf(ud->content, "%s", xml->data);
+
		fputs(xml->data, ud->content->fp);
	}
}

@@ -555,7 +506,7 @@ vulnxml_handle_data(struct vulnxml_userdata *ud, yxml_t *xml)
	case VULNXML_PARSE_RANGE_LT:
	case VULNXML_PARSE_RANGE_LE:
	case VULNXML_PARSE_RANGE_EQ:
-
		utstring_printf(ud->content, "%s", xml->data);
+
		fputs(xml->data, ud->content->fp);
		break;
	}
}
@@ -575,7 +526,7 @@ pkg_audit_parse_vulnxml(struct pkg_audit *audit)
	ud.audit = audit;
	ud.range_num = 0;
	ud.state = VULNXML_PARSE_INIT;
-
	utstring_new(ud.content);
+
	ud.content = xstring_new();

	walk = audit->map;
	end = walk + audit->len;
@@ -622,7 +573,7 @@ pkg_audit_parse_vulnxml(struct pkg_audit *audit)
	else
		pkg_emit_error("Invalid end of XML");
out:
-
	utstring_free(ud.content);
+
	xstring_free(ud.content);

	return (ret);
}
@@ -768,78 +719,38 @@ pkg_audit_version_match(const char *pkgversion, struct pkg_audit_version *v)
}

static void
-
pkg_audit_print_versions(struct pkg_audit_entry *e, UT_string *sb)
-
{
-
	struct pkg_audit_versions_range *vers;
-

-
	utstring_printf(sb, "%s", "Affected versions:\n");
-
	LL_FOREACH(e->versions, vers) {
-
		if (vers->v1.type > 0 && vers->v2.type > 0)
-
			utstring_printf(sb, "%s %s : %s %s\n",
-
				vop_names[vers->v1.type], vers->v1.version,
-
				vop_names[vers->v2.type], vers->v2.version);
-
		else if (vers->v1.type > 0)
-
			utstring_printf(sb, "%s %s\n",
-
				vop_names[vers->v1.type], vers->v1.version);
-
		else
-
			utstring_printf(sb, "%s %s\n",
-
				vop_names[vers->v2.type], vers->v2.version);
-
	}
-
}
-

-
static void
-
pkg_audit_print_entry(struct pkg_audit_entry *e, UT_string *sb,
-
	const char *pkgname, const char *pkgversion, bool quiet)
+
pkg_audit_add_entry(struct pkg_audit_entry *e, struct pkg_audit_issues **ai)
{
-
	struct pkg_audit_cve *cve;
-

-
	if (quiet) {
-
		if (pkgversion != NULL)
-
			utstring_printf(sb, "%s-%s\n", pkgname, pkgversion);
-
		else
-
			utstring_printf(sb, "%s\n", pkgname);
-
	} else {
-
		if (pkgversion != NULL)
-
			utstring_printf(sb, "%s-%s is vulnerable:\n", pkgname, pkgversion);
-
		else {
-
			utstring_printf(sb, "%s is vulnerable:\n", pkgname);
-
			pkg_audit_print_versions(e, sb);
-
		}
-

-
		utstring_printf(sb, "%s\n", e->desc);
-
		/* XXX: for vulnxml we should use more clever approach indeed */
-
		if (e->cve) {
-
			cve = e->cve;
-
			while (cve) {
-
				utstring_printf(sb, "CVE: %s\n", cve->cvename);
-
				cve = cve->next;
-
			}
-
		}
-
		if (e->url)
-
			utstring_printf(sb, "WWW: %s\n\n", e->url);
-
		else if (e->id)
-
			utstring_printf(sb,
-
				"WWW: https://vuxml.FreeBSD.org/freebsd/%s.html\n\n",
-
				e->id);
-
	}
+
	struct pkg_audit_issue *issue;
+

+
	if (*ai == NULL)
+
		*ai = xcalloc(1, sizeof(**ai));
+
	issue = xcalloc(1, sizeof(*issue));
+
	issue->audit = e;
+
	(*ai)->count++;
+
	LL_APPEND((*ai)->issues, issue);
}

bool
pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
-
		bool quiet, UT_string **result, int *affected)
+
    struct pkg_audit_issues **ai, bool stop_quick)
{
	struct pkg_audit_entry *e;
	struct pkg_audit_versions_range *vers;
-
	UT_string *sb;
	struct pkg_audit_item *a;
	bool res = false, res1, res2;

	if (!audit->parsed)
		return false;

+
	/* check if we decided to ignore that package or not */
+
	if (match_ucl_lists(pkg->name,
+
	    pkg_config_get("AUDIT_IGNORE_GLOB"),
+
	    pkg_config_get("AUDIT_IGNORE_REGEX")))
+
		return (false);
+

	a = audit->items;
	a += audit_entry_first_byte_idx[(size_t)pkg->name[0]];
-
	utstring_new(sb);

	for (; (e = a->e) != NULL; a += a->next_pfx_incr) {
		int cmp;
@@ -866,10 +777,7 @@ pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,
				 * Assume that all versions should be checked
				 */
				res = true;
-
				pkg_audit_print_entry(e, sb, pkg->name, NULL, quiet);
-
				if (affected != NULL) {
-
					++*affected;
-
				}
+
				pkg_audit_add_entry(e, ai);
			}
			else {
				LL_FOREACH(e->versions, vers) {
@@ -878,27 +786,17 @@ pkg_audit_is_vulnerable(struct pkg_audit *audit, struct pkg *pkg,

					if (res1 && res2) {
						res = true;
-
						pkg_audit_print_entry(e, sb, pkg->name, pkg->version, quiet);
-
						if (affected != NULL) {
-
							++*affected;
-
						}
+
						pkg_audit_add_entry(e, ai);
						break;
					}
				}
			}

-
			if (res && quiet)
-
				goto out;
+
			if (res && stop_quick)
+
				return (res);
		}
	}

-
out:
-
	if (res) {
-
		*result = sb;
-
	} else {
-
		utstring_free(sb);
-
	}
-

	return (res);
}

@@ -979,3 +877,17 @@ pkg_audit_free (struct pkg_audit *audit)
		free(audit);
	}
}
+

+
void
+
pkg_audit_issues_free(struct pkg_audit_issues *issues)
+
{
+
	struct pkg_audit_issue *i, *issue;
+

+
	if (issues == NULL)
+
		return;
+

+
	LL_FOREACH_SAFE(issues->issues, issue, i) {
+
		LL_DELETE(issues->issues, issue);
+
		free(issue);
+
	}
+
}
modified libpkg/pkg_checksum.c
@@ -274,10 +274,12 @@ pkg_checksum_generate(struct pkg *pkg, char *dest, size_t destlen,

	if (inc_scripts) {
		for (int i = 0; i < PKG_NUM_SCRIPTS; i++) {
-
			if (pkg->scripts[i] != NULL)
+
			if (pkg->scripts[i] != NULL) {
+
				fflush(pkg->scripts[i]->fp);
				pkg_checksum_add_entry("script",
-
				    utstring_body(pkg->scripts[i]),
+
				    pkg->scripts[i]->buf,
				    &entries);
+
			}
		}
		for (int i = 0; i < PKG_NUM_LUA_SCRIPTS; i++) {
			if (pkg->lua_scripts[i] != NULL)
@@ -417,13 +419,19 @@ static void
pkg_checksum_hash_sha256_file(int fd, unsigned char **out, size_t *outlen)
{
	char buffer[8192];
-
	size_t r;
+
	ssize_t r;

	SHA256_CTX sign_ctx;
	*out = xmalloc(SHA256_BLOCK_SIZE);
	sha256_init(&sign_ctx);
	while ((r = read(fd, buffer, sizeof(buffer))) > 0)
		sha256_update(&sign_ctx, buffer, r);
+
	if (r < 0) {
+
		pkg_emit_errno(__func__, "read failed");
+
		free(*out);
+
		*out = NULL;
+
		return;
+
	}
	sha256_final(&sign_ctx, *out);
	*outlen = SHA256_BLOCK_SIZE;
}
@@ -459,14 +467,19 @@ static void
pkg_checksum_hash_blake2_file(int fd, unsigned char **out, size_t *outlen)
{
	char buffer[8192];
-
	size_t r;
+
	ssize_t r;

	blake2b_state st;
	blake2b_init(&st, BLAKE2B_OUTBYTES);

	while ((r = read(fd, buffer, sizeof(buffer))) > 0)
		blake2b_update(&st, buffer, r);
-

+
	if (r < 0) {
+
		pkg_emit_errno(__func__, "read failed");
+
		free(*out);
+
		*out = NULL;
+
		return;
+
	}
	*out = xmalloc(BLAKE2B_OUTBYTES);
	blake2b_final(&st, *out, BLAKE2B_OUTBYTES);
	*outlen = BLAKE2B_OUTBYTES;
@@ -503,14 +516,19 @@ static void
pkg_checksum_hash_blake2s_file(int fd, unsigned char **out, size_t *outlen)
{
	char buffer[8192];
-
	size_t r;
+
	ssize_t r;

	blake2s_state st;
	blake2s_init(&st, BLAKE2S_OUTBYTES);

	while ((r = read(fd, buffer, sizeof(buffer))) > 0)
		blake2s_update(&st, buffer, r);
-

+
	if (r < 0) {
+
		pkg_emit_errno(__func__, "read failed");
+
		free(*out);
+
		*out = NULL;
+
		return;
+
	}
	*out = xmalloc(BLAKE2S_OUTBYTES);
	blake2s_final(&st, *out, BLAKE2S_OUTBYTES);
	*outlen = BLAKE2S_OUTBYTES;
modified libpkg/pkg_config.c
@@ -1,5 +1,5 @@
/*
-
 * Copyright (c) 2011-2015 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
 * Copyright (c) 2016 Vsevolod Stakhov <vsevolod@FreeBSD.org>
@@ -41,7 +41,6 @@
#include <osreldate.h>
#endif
#include <ucl.h>
-
#include <sysexits.h>

#include "pkg.h"
#include "private/pkg.h"
@@ -75,6 +74,7 @@ struct pkg_ctx ctx = {
	.pkg_dbdirfd = -1,
	.osversion = 0,
	.backup_libraries = false,
+
	.triggers = true,
};

struct config_entry {
@@ -382,18 +382,6 @@ static struct config_entry c[] = {
	},
	{
		PKG_BOOL,
-
		"PKG_REPO_HASH",
-
		"NO",
-
		"Rename packages with the short hash of their contents",
-
	},
-
	{
-
		PKG_BOOL,
-
		"PKG_REPO_SYMLINK",
-
		"NO",
-
		"Create symlinks from the hashed filename to the regular filename",
-
	},
-
	{
-
		PKG_BOOL,
		"AUTOCLEAN",
		"NO",
		"Always cleanup the cache directory after install/upgrade",
@@ -459,6 +447,30 @@ static struct config_entry c[] = {
		PREFIX "/lib/compat/pkg",
		"Path where pkg will backup libraries",
	},
+
	{
+
		PKG_STRING,
+
		"PKG_TRIGGERS_DIR",
+
		PREFIX "/share/pkg/triggers",
+
		"Path where the triggers should be installed",
+
	},
+
	{
+
		PKG_BOOL,
+
		"PKG_TRIGGERS_ENABLE",
+
		"YES",
+
		"Disable triggers",
+
	},
+
	{
+
		PKG_ARRAY,
+
		"AUDIT_IGNORE_GLOB",
+
		NULL,
+
		"List of glob to ignore while autiditing for vulnerabilities",
+
	},
+
	{
+
		PKG_ARRAY,
+
		"AUDIT_IGNORE_REGEX",
+
		"NULL",
+
		"List of regex to ignore while autiditing for vulnerabilities",
+
	},
};

static bool parsed = false;
@@ -769,7 +781,7 @@ walk_repo_obj(const ucl_object_t *obj, const char *file, pkg_init_flags flags)

static void
load_repo_file(int dfd, const char *repodir, const char *repofile,
-
    pkg_init_flags flags)
+
    pkg_init_flags flags, struct os_info *oi)
{
	struct ucl_parser *p;
	ucl_object_t *obj = NULL;
@@ -784,6 +796,27 @@ load_repo_file(int dfd, const char *repodir, const char *repofile,

	myarch_legacy = pkg_object_string(pkg_config_get("ALTABI"));
	ucl_parser_register_variable (p, "ALTABI", myarch_legacy);
+
#ifdef __FreeBSD__
+
	ucl_parser_register_variable(p, "OSVERSION", myosversion);
+
#endif
+
	if (oi->name != NULL) {
+
		ucl_parser_register_variable(p, "OSNAME", oi->name);
+
	}
+
	if (oi->version != NULL) {
+
		ucl_parser_register_variable(p, "RELEASE", oi->version);
+
	}
+
	if (oi->version_major != NULL) {
+
		ucl_parser_register_variable(p, "VERSION_MAJOR", oi->version_major);
+
	}
+
	if (oi->version_minor != NULL) {
+
		ucl_parser_register_variable(p, "VERSION_MINOR", oi->version_minor);
+
	}
+
	if (oi->arch != NULL) {
+
		ucl_parser_register_variable(p, "ARCH", oi->arch);
+
	}
+

+
	errno = 0;
+
	obj = NULL;

	pkg_debug(1, "PKgConfig: loading %s/%s", repodir, repofile);
	fd = openat(dfd, repofile, O_RDONLY);
@@ -830,7 +863,7 @@ configfile(const struct dirent *dp)
}

static void
-
load_repo_files(const char *repodir, pkg_init_flags flags)
+
load_repo_files(const char *repodir, pkg_init_flags flags, struct os_info *oi)
{
	struct dirent **ent;
	int nents, i, fd;
@@ -841,7 +874,7 @@ load_repo_files(const char *repodir, pkg_init_flags flags)

	nents = scandir(repodir, &ent, configfile, alphasort);
	for (i = 0; i < nents; i++) {
-
		load_repo_file(fd, repodir, ent[i]->d_name, flags);
+
		load_repo_file(fd, repodir, ent[i]->d_name, flags, oi);
		free(ent[i]);
	}
	if (nents >= 0)
@@ -850,19 +883,19 @@ load_repo_files(const char *repodir, pkg_init_flags flags)
}

static void
-
load_repositories(const char *repodir, pkg_init_flags flags)
+
load_repositories(const char *repodir, pkg_init_flags flags, struct os_info *oi)
{
	const pkg_object *reposlist, *cur;
	pkg_iter it = NULL;

	if (repodir != NULL) {
-
		load_repo_files(repodir, flags);
+
		load_repo_files(repodir, flags, oi);
		return;
	}

	reposlist = pkg_config_get("REPOS_DIR");
	while ((cur = pkg_object_iterate(reposlist, &it)))
-
		load_repo_files(pkg_object_string(cur), flags);
+
		load_repo_files(pkg_object_string(cur), flags, oi);
}

bool
@@ -898,6 +931,21 @@ pkg_init(const char *path, const char *reposdir)
	return (pkg_ini(path, reposdir, 0));
}

+
static const char *
+
type_to_string(int type)
+
{
+
	if (type == UCL_ARRAY)
+
		return ("array");
+
	if (type == UCL_OBJECT)
+
		return ("object");
+
	if (type == UCL_STRING)
+
		return ("string");
+
	if (type == UCL_INT)
+
		return ("integer");
+
	if (type == UCL_BOOLEAN)
+
		return ("boolean");
+
	return ("unknown");
+
}
int
pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
{
@@ -915,11 +963,12 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	const ucl_object_t *cur, *object;
	ucl_object_t *obj = NULL, *o, *ncfg;
	ucl_object_iter_t it = NULL;
-
	UT_string *ukey = NULL;
+
	xstring *ukey = NULL;
	bool fatal_errors = false;
	int conffd = -1;
	char *tmp = NULL;
	struct os_info oi;
+
	size_t ukeylen;

	k = NULL;
	o = NULL;
@@ -1056,11 +1105,6 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	if (oi.arch != NULL) {
		ucl_parser_register_variable(p, "ARCH", oi.arch);
	}
-
	free(oi.name);
-
	free(oi.version);
-
	free(oi.version_major);
-
	free(oi.version_minor);
-
	free(oi.arch);

	errno = 0;
	obj = NULL;
@@ -1073,21 +1117,22 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)

	obj = ucl_parser_get_object(p);
	ncfg = NULL;
-
	utstring_new(ukey);
+
	ukey = NULL;
	while (obj != NULL && (cur = ucl_iterate_object(obj, &it, true))) {
-
		utstring_clear(ukey);
+
		xstring_renew(ukey);
		key = ucl_object_key(cur);
		for (i = 0; key[i] != '\0'; i++)
-
			utstring_printf(ukey, "%c", toupper(key[i]));
-
		object = ucl_object_find_keyl(config, utstring_body(ukey), utstring_len(ukey));
-

-
		if (strncasecmp(utstring_body(ukey), "PACKAGESITE", utstring_len(ukey))
-
		    == 0 || strncasecmp(utstring_body(ukey), "PUBKEY",
-
		    utstring_len(ukey)) == 0 || strncasecmp(utstring_body(ukey),
-
		    "MIRROR_TYPE", utstring_len(ukey)) == 0) {
+
			fputc(toupper(key[i]), ukey->fp);
+
		fflush(ukey->fp);
+
		ukeylen = strlen(ukey->buf);
+
		object = ucl_object_find_keyl(config, ukey->buf, ukeylen);
+

+
		if (strncasecmp(ukey->buf, "PACKAGESITE", ukeylen) == 0 ||
+
		    strncasecmp(ukey->buf, "PUBKEY", ukeylen) == 0 ||
+
		    strncasecmp(ukey->buf, "MIRROR_TYPE", ukeylen) == 0) {
			pkg_emit_error("%s in pkg.conf is no longer "
			    "supported.  Convert to the new repository style."
-
			    "  See pkg.conf(5)", utstring_body(ukey));
+
			    "  See pkg.conf(5)", ukey->buf);
			fatal_errors = true;
			continue;
		}
@@ -1097,15 +1142,19 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
			continue;

		if (object->type != cur->type) {
-
			pkg_emit_error("Malformed key %s, ignoring", key);
+
			pkg_emit_error("Malformed key %s, got '%s' expecting "
+
			    "'%s', ignoring", key,
+
			    type_to_string(cur->type),
+
			    type_to_string(object->type));
			continue;
		}

		if (ncfg == NULL)
			ncfg = ucl_object_typed_new(UCL_OBJECT);
-
		ucl_object_insert_key(ncfg, ucl_object_copy(cur), utstring_body(ukey), utstring_len(ukey), true);
+
		ucl_object_insert_key(ncfg, ucl_object_copy(cur), ukey->buf,
+
		    ukeylen, true);
	}
-
	utstring_free(ukey);
+
	xstring_free(ukey);

	if (fatal_errors) {
		ucl_object_unref(ncfg);
@@ -1242,6 +1291,8 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
	ctx.cachedir = pkg_object_string(pkg_config_get("PKG_CACHEDIR"));
	ctx.backup_libraries = pkg_object_bool(pkg_config_get("BACKUP_LIBRARIES"));
	ctx.backup_library_path = pkg_object_string(pkg_config_get("BACKUP_LIBRARY_PATH"));
+
	ctx.triggers = pkg_object_bool(pkg_config_get("PKG_TRIGGERS_ENABLE"));
+
	ctx.triggers_path = pkg_object_string(pkg_config_get("PKG_TRIGGERS_DIR"));

	it = NULL;
	object = ucl_object_find_key(config, "PKG_ENV");
@@ -1260,7 +1311,7 @@ pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
		setenv("HTTP_USER_AGENT", "pkg/"PKGVERSION, 1);

	/* load the repositories */
-
	load_repositories(reposdir, flags);
+
	load_repositories(reposdir, flags, &oi);

	object = ucl_object_find_key(config, "REPOSITORIES");
	while ((cur = ucl_iterate_object(object, &it, true))) {
@@ -1390,10 +1441,11 @@ pkg_shutdown(void)
{
	if (!parsed) {
		pkg_emit_error("pkg_shutdown() must be called after pkg_init()");
-
		_exit(EX_SOFTWARE);
+
		_exit(EXIT_FAILURE);
		/* NOTREACHED */
	}

+
	metalog_close();
	ucl_object_unref(config);
	HASH_FREE(repos, pkg_repo_free);

modified libpkg/pkg_create.c
@@ -43,7 +43,6 @@

static int load_metadata(struct pkg *pkg, const char *metadata, const char *plist,
    const char *rootdir);
-
static int pkg_create_from_dir(struct pkg *, const char *, struct packing *);
static void fixup_abi(struct pkg *pkg, const char *rootdir, bool testing);
static void counter_init(const char *what, int64_t max);
static void counter_count(void);
@@ -53,7 +52,7 @@ extern struct pkg_ctx ctx;

static int
pkg_create_from_dir(struct pkg *pkg, const char *root,
-
    struct packing *pkg_archive)
+
    struct pkg_create *pc, struct packing *pkg_archive)
{
	char		 fpath[MAXPATHLEN];
	struct pkg_file	*file = NULL;
@@ -95,6 +94,12 @@ pkg_create_from_dir(struct pkg *pkg, const char *root,
			return (EPKG_FATAL);
		}

+
		if (!(S_ISREG(st.st_mode) || S_ISLNK(st.st_mode))) {
+
			pkg_emit_error("file '%s' is not a regular file", fpath);
+
			kh_destroy_hardlinks(hardlinks);
+
			return (EPKG_FATAL);
+
		}
+

		if (file->size == 0)
			file->size = (int64_t)st.st_size;

@@ -125,15 +130,19 @@ pkg_create_from_dir(struct pkg *pkg, const char *root,
	 * Register shared libraries used by the package if
	 * SHLIBS enabled in conf.  Deletes shlib info if not.
	 */
-
	UT_string *b;
-
	utstring_new(b);
+
	xstring *b = xstring_new();

	pkg_emit_manifest_buf(pkg, b, PKG_MANIFEST_EMIT_COMPACT, NULL);
-
	packing_append_buffer(pkg_archive, utstring_body(b), "+COMPACT_MANIFEST", utstring_len(b));
-
	utstring_clear(b);
-
	pkg_emit_manifest_buf(pkg, b, 0, NULL);
-
	packing_append_buffer(pkg_archive, utstring_body(b), "+MANIFEST", utstring_len(b));
-
	utstring_free(b);
+
	fflush(b->fp);
+
	packing_append_buffer(pkg_archive, b->buf, "+COMPACT_MANIFEST", strlen(b->buf));
+
	xstring_reset(b);
+
	if (pc->expand_manifest)
+
		pkg_emit_manifest_buf(pkg, b, PKG_MANIFEST_EMIT_UCL, NULL);
+
	else
+
		pkg_emit_manifest_buf(pkg, b, 0, NULL);
+
	fflush(b->fp);
+
	packing_append_buffer(pkg_archive, b->buf, "+MANIFEST", strlen(b->buf));
+
	xstring_free(b);

	counter_init("packing files", nfiles);

@@ -234,6 +243,7 @@ pkg_create_new(void)
	pc->format = TXZ;
	pc->timestamp = (time_t) -1;
	pc->overwrite = true;
+
	pc->expand_manifest = false;

	return (pc);
}
@@ -269,6 +279,12 @@ pkg_create_set_compression_level(struct pkg_create *pc, int clevel)
}

void
+
pkg_create_set_expand_manifest(struct pkg_create *pc, bool expand)
+
{
+
	pc->expand_manifest = expand;
+
}
+

+
void
pkg_create_set_rootdir(struct pkg_create *pc, const char *rootdir)
{
	pc->rootdir = rootdir;
@@ -340,7 +356,7 @@ pkg_create_i(struct pkg_create *pc, struct pkg *pkg, bool hash)
		return (EPKG_FATAL);
	}

-
	if ((ret = pkg_create_from_dir(pkg, NULL, pkg_archive)) != EPKG_OK) {
+
	if ((ret = pkg_create_from_dir(pkg, NULL, pc, pkg_archive)) != EPKG_OK) {
		pkg_emit_error("package creation failed");
	}
	packing_finish(pkg_archive);
@@ -382,7 +398,7 @@ pkg_create(struct pkg_create *pc, const char *metadata, const char *plist,
		return (EPKG_FATAL);
	}

-
	if ((ret = pkg_create_from_dir(pkg, pc->rootdir, pkg_archive)) != EPKG_OK)
+
	if ((ret = pkg_create_from_dir(pkg, pc->rootdir, pc, pkg_archive)) != EPKG_OK)
		pkg_emit_error("package creation failed");

	packing_finish(pkg_archive);
modified libpkg/pkg_delete.c
@@ -38,7 +38,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
-
#include <utstring.h>

#include <bsd_compat.h>

@@ -58,11 +57,12 @@ int
pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)
{
	struct pkg_message	*msg;
-
	UT_string	*message = NULL;
+
	xstring		*message = NULL;
	int		 ret;
	bool		 handle_rc = false;
	const unsigned load_flags = PKG_LOAD_RDEPS|PKG_LOAD_FILES|PKG_LOAD_DIRS|
					PKG_LOAD_SCRIPTS|PKG_LOAD_ANNOTATIONS|PKG_LOAD_LUA_SCRIPTS;
+
	bool		head = true;

	assert(pkg != NULL);
	assert(db != NULL);
@@ -116,22 +116,22 @@ pkg_delete(struct pkg *pkg, struct pkgdb *db, unsigned flags)

	if ((flags & PKG_DELETE_UPGRADE) == 0) {
		pkg_emit_deinstall_finished(pkg);
-
		utstring_renew(message);
		LL_FOREACH(pkg->message, msg) {
			if (msg->type == PKG_MESSAGE_REMOVE) {
-
				if (utstring_len(message) == 0) {
-
					pkg_utstring_printf(message, "Message from "
+
				if (message == NULL) {
+
					message = xstring_new();
+
					pkg_fprintf(message->fp, "Message from "
					    "%n-%v:\n", pkg, pkg);
+
					head = false;
				}
-
				utstring_printf(message, "%s\n", msg->str);
+
				fprintf(message->fp, "%s\n", msg->str);
			}
		}
-
		if (pkg->message != NULL) {
-
			if (utstring_len(message) > 0) {
-
				pkg_emit_message(utstring_body(message));
-
			}
+
		if (pkg->message != NULL && message != NULL) {
+
			fflush(message->fp);
+
			pkg_emit_message(message->buf);
+
			xstring_free(message);
		}
-
		utstring_free(message);

	}

@@ -355,6 +355,7 @@ pkg_delete_files(struct pkg *pkg, unsigned force)
	pkg_emit_progress_start(NULL);

	while (pkg_files(pkg, &file) == EPKG_OK) {
+
		append_touched_file(file->path);
		pkg_emit_progress_tick(cur_file++, nfiles);
		pkg_delete_file(pkg, file, force);
	}
modified libpkg/pkg_elf.c
@@ -284,7 +284,7 @@ analyse_elf(struct pkg *pkg, const char *fpath)

	if ((e = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
		ret = EPKG_FATAL;
-
		pkg_emit_error("elf_begin() for %s failed: %s", fpath,
+
		pkg_debug(1, "elf_begin() for %s failed: %s", fpath,
		    elf_errmsg(-1));
		goto cleanup;
	}
@@ -410,8 +410,11 @@ analyse_elf(struct pkg *pkg, const char *fpath)
		    rpath == NULL)
			rpath = elf_strptr(e, sh_link, dyn->d_un.d_val);
	}
-
	if (rpath != NULL)
-
		shlib_list_from_rpath(rpath, bsd_dirname(fpath));
+
	if (rpath != NULL) {
+
		char *p = xstrdup(fpath);
+
		shlib_list_from_rpath(rpath, get_dirname(p));
+
		free(p);
+
	}

	/* Now find all of the NEEDED shared libraries. */

modified libpkg/pkg_event.c
@@ -29,7 +29,7 @@
#include <errno.h>
#include <string.h>
#include <syslog.h>
-
#include <utstring.h>
+
#include <xstring.h>

#include "pkg.h"
#include "private/pkg.h"
@@ -39,17 +39,18 @@ static pkg_event_cb _cb = NULL;
static void *_data = NULL;

static char *
-
buf_json_escape(UT_string *buf, const char *str)
+
buf_json_escape(const char *str)
{
-
	utstring_clear(buf);
+
	xstring *buf = xstring_new();
+

	while (str != NULL && *str != '\0') {
		if (*str == '"' || *str == '\\')
-
			utstring_printf(buf, "%c", '\\');
-
		utstring_printf(buf, "%c", *str);
+
			fputc('\\', buf->fp);
+
		fputc(*str, buf->fp);
		str++;
	}

-
	return (utstring_body(buf));
+
	return (xstring_get(buf));
}

static void
@@ -57,42 +58,41 @@ pipeevent(struct pkg_event *ev)
{
	int i;
	struct pkg_dep *dep = NULL;
-
	UT_string *msg, *buf;
+
	xstring *msg;
	struct pkg_event_conflict *cur_conflict;
	if (ctx.eventpipe < 0)
		return;

-
	utstring_new(msg);
-
	utstring_new(buf);
+
	msg = xstring_new();

	switch(ev->type) {
	case PKG_EVENT_ERRNO:
-
		utstring_printf(msg, "{ \"type\": \"ERROR\", "
+
		fprintf(msg->fp, "{ \"type\": \"ERROR\", "
		    "\"data\": {"
		    "\"msg\": \"%s(%s): %s\","
		    "\"errno\": %d}}",
-
		    buf_json_escape(buf, ev->e_errno.func),
-
		    buf_json_escape(buf, ev->e_errno.arg),
-
		    buf_json_escape(buf, strerror(ev->e_errno.no)),
+
		    buf_json_escape(ev->e_errno.func),
+
		    buf_json_escape(ev->e_errno.arg),
+
		    buf_json_escape(strerror(ev->e_errno.no)),
		    ev->e_errno.no);
		break;
	case PKG_EVENT_ERROR:
-
		utstring_printf(msg, "{ \"type\": \"ERROR\", "
+
		fprintf(msg->fp, "{ \"type\": \"ERROR\", "
		    "\"data\": {\"msg\": \"%s\"}}",
-
		    buf_json_escape(buf, ev->e_pkg_error.msg));
+
		    buf_json_escape(ev->e_pkg_error.msg));
		break;
	case PKG_EVENT_NOTICE:
-
		utstring_printf(msg, "{ \"type\": \"NOTICE\", "
+
		fprintf(msg->fp, "{ \"type\": \"NOTICE\", "
		    "\"data\": {\"msg\": \"%s\"}}",
-
		    buf_json_escape(buf, ev->e_pkg_notice.msg));
+
		    buf_json_escape(ev->e_pkg_notice.msg));
		break;
	case PKG_EVENT_DEVELOPER_MODE:
-
		utstring_printf(msg, "{ \"type\": \"ERROR\", "
+
		fprintf(msg->fp, "{ \"type\": \"ERROR\", "
		    "\"data\": {\"msg\": \"DEVELOPER_MODE: %s\"}}",
-
		    buf_json_escape(buf, ev->e_pkg_error.msg));
+
		    buf_json_escape(ev->e_pkg_error.msg));
		break;
	case PKG_EVENT_UPDATE_ADD:
-
		utstring_printf(msg, "{ \"type\": \"INFO_UPDATE_ADD\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_UPDATE_ADD\", "
		    "\"data\": { "
		    "\"fetched\": %d, "
		    "\"total\": %d"
@@ -102,7 +102,7 @@ pipeevent(struct pkg_event *ev)
		    );
		break;
	case PKG_EVENT_UPDATE_REMOVE:
-
		utstring_printf(msg, "{ \"type\": \"INFO_UPDATE_REMOVE\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_UPDATE_REMOVE\", "
		    "\"data\": { "
		    "\"fetched\": %d, "
		    "\"total\": %d"
@@ -112,44 +112,44 @@ pipeevent(struct pkg_event *ev)
		    );
		break;
	case PKG_EVENT_FETCH_BEGIN:
-
		utstring_printf(msg, "{ \"type\": \"INFO_FETCH_BEGIN\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_FETCH_BEGIN\", "
		    "\"data\": { "
		    "\"url\": \"%s\" "
		    "}}",
-
		    buf_json_escape(buf, ev->e_fetching.url)
+
		    buf_json_escape(ev->e_fetching.url)
		    );
		break;
	case PKG_EVENT_FETCH_FINISHED:
-
		utstring_printf(msg, "{ \"type\": \"INFO_FETCH_FINISHED\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_FETCH_FINISHED\", "
		    "\"data\": { "
		    "\"url\": \"%s\" "
		    "}}",
-
		    buf_json_escape(buf, ev->e_fetching.url)
+
		    buf_json_escape(ev->e_fetching.url)
		    );
		break;
	case PKG_EVENT_INSTALL_BEGIN:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_INSTALL_BEGIN\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_INSTALL_BEGIN\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\""
		    "}}", ev->e_install_begin.pkg, ev->e_install_begin.pkg);
		break;
	case PKG_EVENT_EXTRACT_BEGIN:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_EXTRACT_BEGIN\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_EXTRACT_BEGIN\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\""
		    "}}", ev->e_extract_begin.pkg, ev->e_extract_begin.pkg);
		break;
	case PKG_EVENT_EXTRACT_FINISHED:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_EXTRACT_FINISHED\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_EXTRACT_FINISHED\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\""
		    "}}", ev->e_extract_finished.pkg, ev->e_extract_finished.pkg);
		break;
	case PKG_EVENT_INSTALL_FINISHED:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_INSTALL_FINISHED\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_INSTALL_FINISHED\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\", "
@@ -158,15 +158,15 @@ pipeevent(struct pkg_event *ev)
		    ev->e_install_finished.pkg,
		    ev->e_install_finished.pkg,
			ev->e_install_finished.pkg->message ?
-
				buf_json_escape(buf, ev->e_install_finished.pkg->message->str) :
+
				buf_json_escape(ev->e_install_finished.pkg->message->str) :
				"");
		break;
	case PKG_EVENT_INTEGRITYCHECK_BEGIN:
-
		utstring_printf(msg, "{ \"type\": \"INFO_INTEGRITYCHECK_BEGIN\", "
-
		    "\"data\": {}}");
+
		fputs("{ \"type\": \"INFO_INTEGRITYCHECK_BEGIN\", "
+
		    "\"data\": {}}", msg->fp);
		break;
	case PKG_EVENT_INTEGRITYCHECK_CONFLICT:
-
		utstring_printf(msg, "{ \"type\": \"INFO_INTEGRITYCHECK_CONFLICT\","
+
		fprintf(msg->fp, "{ \"type\": \"INFO_INTEGRITYCHECK_CONFLICT\","
			"\"data\": { "
			"\"pkguid\": \"%s\", "
			"\"pkgpath\": \"%s\", "
@@ -176,25 +176,25 @@ pipeevent(struct pkg_event *ev)
		cur_conflict = ev->e_integrity_conflict.conflicts;
		while (cur_conflict != NULL) {
			if (cur_conflict->next != NULL) {
-
				utstring_printf(msg, "{\"uid\":\"%s\"},",
+
				fprintf(msg->fp, "{\"uid\":\"%s\"},",
						cur_conflict->uid);
			}
			else {
-
				utstring_printf(msg, "{\"uid\":\"%s\"}",
+
				fprintf(msg->fp, "{\"uid\":\"%s\"}",
						cur_conflict->uid);
				break;
			}
			cur_conflict = cur_conflict->next;
		}
-
		utstring_printf(msg, "%s", "]}}");
+
		fputs("]}}", msg->fp);
		break;
	case PKG_EVENT_INTEGRITYCHECK_FINISHED:
-
		utstring_printf(msg, "{ \"type\": \"INFO_INTEGRITYCHECK_FINISHED\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_INTEGRITYCHECK_FINISHED\", "
		    "\"data\": {\"conflicting\": %d}}",
		    ev->e_integrity_finished.conflicting);
		break;
	case PKG_EVENT_DEINSTALL_BEGIN:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_DEINSTALL_BEGIN\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_DEINSTALL_BEGIN\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\""
@@ -203,7 +203,7 @@ pipeevent(struct pkg_event *ev)
		    ev->e_deinstall_begin.pkg);
		break;
	case PKG_EVENT_DEINSTALL_FINISHED:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_DEINSTALL_FINISHED\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_DEINSTALL_FINISHED\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\""
@@ -212,7 +212,7 @@ pipeevent(struct pkg_event *ev)
		    ev->e_deinstall_finished.pkg);
		break;
	case PKG_EVENT_UPGRADE_BEGIN:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_UPGRADE_BEGIN\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_UPGRADE_BEGIN\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\" ,"
@@ -223,7 +223,7 @@ pipeevent(struct pkg_event *ev)
		    ev->e_upgrade_begin.n);
		break;
	case PKG_EVENT_UPGRADE_FINISHED:
-
		pkg_utstring_printf(msg, "{ \"type\": \"INFO_UPGRADE_FINISHED\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"INFO_UPGRADE_FINISHED\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\" ,"
@@ -234,7 +234,7 @@ pipeevent(struct pkg_event *ev)
		    ev->e_upgrade_finished.n);
		break;
	case PKG_EVENT_LOCKED:
-
		pkg_utstring_printf(msg, "{ \"type\": \"ERROR_LOCKED\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"ERROR_LOCKED\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%n\""
@@ -243,7 +243,7 @@ pipeevent(struct pkg_event *ev)
		    ev->e_locked.pkg);
		break;
	case PKG_EVENT_REQUIRED:
-
		pkg_utstring_printf(msg, "{ \"type\": \"ERROR_REQUIRED\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"ERROR_REQUIRED\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\", "
@@ -253,15 +253,16 @@ pipeevent(struct pkg_event *ev)
		    ev->e_required.pkg,
		    ev->e_required.force == 1 ? "true": "false");
		while (pkg_rdeps(ev->e_required.pkg, &dep) == EPKG_OK)
-
			utstring_printf(msg, "{ \"pkgname\": \"%s\", "
+
			fprintf(msg->fp, "{ \"pkgname\": \"%s\", "
			    "\"pkgversion\": \"%s\" }, ",
			    dep->name, dep->version);
-
		msg->i -= 2;
-
		msg->d[msg->i] = '\0';
-
		utstring_printf(msg, "%s", "]}}");
+
		int c = 0;
+
		ungetc(c, msg->fp);
+
		ungetc(c, msg->fp);
+
		fputs("]}}", msg->fp);
		break;
	case PKG_EVENT_ALREADY_INSTALLED:
-
		pkg_utstring_printf(msg, "{ \"type\": \"ERROR_ALREADY_INSTALLED\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"ERROR_ALREADY_INSTALLED\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\""
@@ -270,7 +271,7 @@ pipeevent(struct pkg_event *ev)
		    ev->e_already_installed.pkg);
		break;
	case PKG_EVENT_MISSING_DEP:
-
		utstring_printf(msg, "{ \"type\": \"ERROR_MISSING_DEP\", "
+
		fprintf(msg->fp, "{ \"type\": \"ERROR_MISSING_DEP\", "
		    "\"data\": { "
		    "\"depname\": \"%s\", "
		    "\"depversion\": \"%s\""
@@ -279,22 +280,22 @@ pipeevent(struct pkg_event *ev)
		    ev->e_missing_dep.dep->version);
		break;
	case PKG_EVENT_NOREMOTEDB:
-
		utstring_printf(msg, "{ \"type\": \"ERROR_NOREMOTEDB\", "
+
		fprintf(msg->fp, "{ \"type\": \"ERROR_NOREMOTEDB\", "
		    "\"data\": { "
		    "\"url\": \"%s\" "
		    "}}" ,
		    ev->e_remotedb.repo);
		break;
	case PKG_EVENT_NOLOCALDB:
-
		utstring_printf(msg, "{ \"type\": \"ERROR_NOLOCALDB\", "
-
		    "\"data\": {}} ");
+
		fputs("{ \"type\": \"ERROR_NOLOCALDB\", \"data\": {}} ",
+
		    msg->fp);
		break;
	case PKG_EVENT_NEWPKGVERSION:
-
		utstring_printf(msg, "{ \"type\": \"INFO_NEWPKGVERSION\", "
-
		    "\"data\": {}} ");
+
		fputs("{ \"type\": \"INFO_NEWPKGVERSION\", \"data\": {}} ",
+
		    msg->fp);
		break;
	case PKG_EVENT_FILE_MISMATCH:
-
		pkg_utstring_printf(msg, "{ \"type\": \"ERROR_FILE_MISMATCH\", "
+
		pkg_fprintf(msg->fp, "{ \"type\": \"ERROR_FILE_MISMATCH\", "
		    "\"data\": { "
		    "\"pkgname\": \"%n\", "
		    "\"pkgversion\": \"%v\", "
@@ -302,41 +303,41 @@ pipeevent(struct pkg_event *ev)
		    "}}",
		    ev->e_file_mismatch.pkg,
		    ev->e_file_mismatch.pkg,
-
		    buf_json_escape(buf, ev->e_file_mismatch.file->path));
+
		    buf_json_escape(ev->e_file_mismatch.file->path));
		break;
	case PKG_EVENT_PLUGIN_ERRNO:
-
		utstring_printf(msg, "{ \"type\": \"ERROR_PLUGIN\", "
+
		fprintf(msg->fp, "{ \"type\": \"ERROR_PLUGIN\", "
		    "\"data\": {"
		    "\"plugin\": \"%s\", "
		    "\"msg\": \"%s(%s): %s\","
		    "\"errno\": %d"
		    "}}",
		    pkg_plugin_get(ev->e_plugin_errno.plugin, PKG_PLUGIN_NAME),
-
		    buf_json_escape(buf, ev->e_plugin_errno.func),
-
		    buf_json_escape(buf, ev->e_plugin_errno.arg),
-
		    buf_json_escape(buf, strerror(ev->e_plugin_errno.no)),
+
		    buf_json_escape(ev->e_plugin_errno.func),
+
		    buf_json_escape(ev->e_plugin_errno.arg),
+
		    buf_json_escape(strerror(ev->e_plugin_errno.no)),
		    ev->e_plugin_errno.no);
		break;
	case PKG_EVENT_PLUGIN_ERROR:
-
		utstring_printf(msg, "{ \"type\": \"ERROR_PLUGIN\", "
+
		fprintf(msg->fp, "{ \"type\": \"ERROR_PLUGIN\", "
		    "\"data\": {"
		    "\"plugin\": \"%s\", "
		    "\"msg\": \"%s\""
		    "}}",
		    pkg_plugin_get(ev->e_plugin_error.plugin, PKG_PLUGIN_NAME),
-
		    buf_json_escape(buf, ev->e_plugin_error.msg));
+
		    buf_json_escape(ev->e_plugin_error.msg));
		break;
	case PKG_EVENT_PLUGIN_INFO:
-
		utstring_printf(msg, "{ \"type\": \"INFO_PLUGIN\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_PLUGIN\", "
		    "\"data\": {"
		    "\"plugin\": \"%s\", "
		    "\"msg\": \"%s\""
		    "}}",
		    pkg_plugin_get(ev->e_plugin_info.plugin, PKG_PLUGIN_NAME),
-
		    buf_json_escape(buf, ev->e_plugin_info.msg));
+
		    buf_json_escape(ev->e_plugin_info.msg));
		break;
	case PKG_EVENT_INCREMENTAL_UPDATE:
-
		utstring_printf(msg, "{ \"type\": \"INFO_INCREMENTAL_UPDATE\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_INCREMENTAL_UPDATE\", "
		    "\"data\": {"
		        "\"name\": \"%s\", "
			"\"processed\": %d"
@@ -344,7 +345,7 @@ pipeevent(struct pkg_event *ev)
			ev->e_incremental_update.processed);
		break;
	case PKG_EVENT_QUERY_YESNO:
-
		utstring_printf(msg, "{ \"type\": \"QUERY_YESNO\", "
+
		fprintf(msg->fp, "{ \"type\": \"QUERY_YESNO\", "
		    "\"data\": {"
			"\"msg\": \"%s\","
			"\"default\": \"%d\""
@@ -352,7 +353,7 @@ pipeevent(struct pkg_event *ev)
			ev->e_query_yesno.deft);
		break;
	case PKG_EVENT_QUERY_SELECT:
-
		utstring_printf(msg, "{ \"type\": \"QUERY_SELECT\", "
+
		fprintf(msg->fp, "{ \"type\": \"QUERY_SELECT\", "
		    "\"data\": {"
			"\"msg\": \"%s\","
			"\"ncnt\": \"%d\","
@@ -363,31 +364,44 @@ pipeevent(struct pkg_event *ev)
			ev->e_query_select.deft);
		for (i = 0; i < ev->e_query_select.ncnt - 1; i++)
		{
-
			utstring_printf(msg, "{ \"text\": \"%s\" },",
+
			fprintf(msg->fp, "{ \"text\": \"%s\" },",
				ev->e_query_select.items[i]);
		}
-
		utstring_printf(msg, "{ \"text\": \"%s\" } ] }}",
+
		fprintf(msg->fp, "{ \"text\": \"%s\" } ] }}",
			ev->e_query_select.items[i]);
		break;
	case PKG_EVENT_PROGRESS_START:
-
		utstring_printf(msg, "{ \"type\": \"INFO_PROGRESS_START\", "
-
		  "\"data\": {}}");
+
		fputs("{ \"type\": \"INFO_PROGRESS_START\", \"data\": {}}",
+
		    msg->fp);
		break;
	case PKG_EVENT_PROGRESS_TICK:
-
		utstring_printf(msg, "{ \"type\": \"INFO_PROGRESS_TICK\", "
+
		fprintf(msg->fp, "{ \"type\": \"INFO_PROGRESS_TICK\", "
		  "\"data\": { \"current\": %jd, \"total\" : %jd}}",
		  (intmax_t)ev->e_progress_tick.current,
		  (intmax_t)ev->e_progress_tick.total);
		break;
+
	case PKG_EVENT_TRIGGERS_BEGIN:
+
		fputs("{ \"type\": \"INFO_TRIGGERS_BEGIN\", \"data\": {}}",
+
		    msg->fp);
+
		break;
+
	case PKG_EVENT_TRIGGERS_FINISHED:
+
		fputs("{ \"type\": \"INFO_TRIGGERS_FINISHED\", \"data\": {}}",
+
		    msg->fp);
+
		break;
+
	case PKG_EVENT_TRIGGER:
+
		fprintf(msg->fp, "{ \"type\": \"INFO_TRIGGER\", \"data\": { "
+
		    "\"cleanup\": %s, \"name\": \"%s\" }}",
+
		    ev->e_trigger.cleanup ? "true" : "false",
+
		    ev->e_trigger.name);
	case PKG_EVENT_BACKUP:
	case PKG_EVENT_RESTORE:
		break;
	default:
		break;
	}
-
	dprintf(ctx.eventpipe, "%s\n", utstring_body(msg));
-
	utstring_free(msg);
-
	utstring_free(buf);
+
	fflush(msg->fp);
+
	dprintf(ctx.eventpipe, "%s\n", msg->buf);
+
	xstring_free(msg);
}

void
@@ -1071,3 +1085,34 @@ pkg_emit_conflicts(struct pkg *p1, struct pkg *p2, const char *path)
	ev.e_conflicts.path = path;
	pkg_emit_event(&ev);
}
+

+
void
+
pkg_emit_triggers_begin(void)
+
{
+
	struct pkg_event ev;
+

+
	ev.type = PKG_EVENT_TRIGGERS_BEGIN;
+

+
	pkg_emit_event(&ev);
+
}
+

+
void
+
pkg_emit_triggers_finished(void)
+
{
+
	struct pkg_event ev;
+

+
	ev.type = PKG_EVENT_TRIGGERS_FINISHED;
+

+
	pkg_emit_event(&ev);
+
}
+

+
void
+
pkg_emit_trigger(const char *name, bool cleanup)
+
{
+
	struct pkg_event ev;
+

+
	ev.type = PKG_EVENT_TRIGGER;
+
	ev.e_trigger.name = name;
+
	ev.e_trigger.cleanup = cleanup;
+

+
}
modified libpkg/pkg_jobs.c
@@ -809,23 +809,23 @@ pkg_jobs_try_remote_candidate(struct pkg_jobs *j, const char *pattern,
				PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_SHLIBS_PROVIDED|
				PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
	int rc = EPKG_FATAL;
-
	UT_string *qmsg;
+
	xstring *qmsg = NULL;
	struct pkg_job_universe_item *unit;

	if ((it = pkgdb_repo_query(j->db, pattern, m, j->reponame)) == NULL)
		return (EPKG_FATAL);

-
	utstring_new(qmsg);
-

	while (it != NULL && pkgdb_it_next(it, &p, flags) == EPKG_OK) {
+
		xstring_renew(qmsg);
		if (pkg_jobs_has_replacement(j, p->uid)) {
			pkg_debug(1, "replacement %s is already used", p->uid);
			continue;
		}

-
		utstring_printf(qmsg, "%s has no direct installation candidates, change it to "
+
		fprintf(qmsg->fp, "%s has no direct installation candidates, change it to "
				"%s? ", uid, p->uid);
-
		if (pkg_emit_query_yesno(true, utstring_body(qmsg))) {
+
		fflush(qmsg->fp);
+
		if (pkg_emit_query_yesno(true, qmsg->buf)) {
			/* Change the origin of the local package */
			pkg_validate(p, j->db);
			unit = pkg_jobs_universe_find(j->universe, uid);
@@ -843,13 +843,12 @@ pkg_jobs_try_remote_candidate(struct pkg_jobs *j, const char *pattern,
			}
			break;
		}
-
		utstring_clear(qmsg);
	}


	pkg_free(p);

-
	utstring_free(qmsg);
+
	xstring_free(qmsg);
	pkgdb_it_free(it);

	return (rc);
@@ -1542,21 +1541,107 @@ pkg_jobs_find_install_candidates(struct pkg_jobs *j, size_t *count)
}

static int
-
jobs_solve_install_upgrade(struct pkg_jobs *j)
+
jobs_solve_full_upgrade(struct pkg_jobs *j)
{
	struct pkg *pkg = NULL;
-
	struct pkgdb_it *it;
-
	char sqlbuf[256];
	size_t jcount = 0;
-
	struct job_pattern *jp;
+
	size_t elt_num = 0;
+
	char sqlbuf[256];
+
	struct pkg_jobs_install_candidate *candidates, *c;
	struct pkg_job_request *req, *rtmp;
+
	struct pkgdb_it *it;
	unsigned flags = PKG_LOAD_BASIC|PKG_LOAD_OPTIONS|PKG_LOAD_DEPS|PKG_LOAD_REQUIRES|
			PKG_LOAD_SHLIBS_REQUIRED|PKG_LOAD_ANNOTATIONS|PKG_LOAD_CONFLICTS;
-
	struct pkg_jobs_install_candidate *candidates, *c;
+

+
	candidates = pkg_jobs_find_install_candidates(j, &jcount);
+

+
	pkg_emit_progress_start("Checking for upgrades (%zd candidates)",
+
			jcount);
+

+
	LL_FOREACH(candidates, c) {
+
		pkg_emit_progress_tick(++elt_num, jcount);
+
		sqlite3_snprintf(sizeof(sqlbuf), sqlbuf, " WHERE id=%" PRId64,
+
		    c->id);
+
		if ((it = pkgdb_query(j->db, sqlbuf, MATCH_CONDITION)) == NULL)
+
			return (EPKG_FATAL);
+

+
		pkg = NULL;
+
		while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
+
			/* Do not test we ignore what doesn't exists remotely */
+
			pkg_jobs_find_upgrade(j, pkg->uid, MATCH_EXACT);
+
		}
+
		pkg_free(pkg);
+
		pkgdb_it_free(it);
+
	}
+
	pkg_emit_progress_tick(jcount, jcount);
+
	LL_FREE(candidates, free);
+

+
	pkg_emit_progress_start("Processing candidates (%zd candidates)",
+
			jcount);
+
	elt_num = 0;
+

+
	HASH_ITER(hh, j->request_add, req, rtmp) {
+
		pkg_emit_progress_tick(++elt_num, jcount);
+
		pkg_jobs_universe_process(j->universe, req->item->pkg);
+
	}
+
	pkg_emit_progress_tick(jcount, jcount);
+

+
	pkg_jobs_universe_process_upgrade_chains(j);
+

+
	return (EPKG_OK);
+
}
+

+
static int
+
jobs_solve_partial_upgrade(struct pkg_jobs *j)
+
{
+
	struct job_pattern *jp;
+
	struct pkg_job_request *req, *rtmp;
+
	bool error_found = false;
+
	int retcode;
+

+
	LL_FOREACH(j->patterns, jp) {
+
		retcode = pkg_jobs_find_remote_pattern(j, jp);
+
		if (retcode == EPKG_FATAL) {
+
			pkg_emit_error("No packages available to %s matching '%s' "
+
					"have been found in the "
+
					"repositories",
+
					(j->type == PKG_JOBS_UPGRADE) ? "upgrade" : "install",
+
					jp->pattern);
+
			/* delay the return to be sure we print a message for all issues */
+
			if ((j->flags & PKG_FLAG_UPGRADE_VULNERABLE) == 0)
+
				error_found = true;
+
		}
+
		if (retcode == EPKG_LOCKED) {
+
			return (retcode);
+
		}
+
	}
+
	if (error_found)
+
		return (EPKG_FATAL);
+
	/*
+
	 * Here we have not selected the proper candidate among all
+
	 * possible choices.
+
	 * Hence, we want to perform this procedure now to ensure that
+
	 * we are processing the correct packages.
+
	 */
+
	pkg_jobs_universe_process_upgrade_chains(j);
+
	/*
+
	 * Need to iterate request one more time to recurse depends
+
	 */
+
	HASH_ITER(hh, j->request_add, req, rtmp) {
+
		pkg_jobs_universe_process(j->universe, req->item->pkg);
+
	}
+
	return (EPKG_OK);
+
}
+

+
static int
+
jobs_solve_install_upgrade(struct pkg_jobs *j)
+
{
+
	struct pkg_job_request *req, *rtmp;
	int retcode = 0;

	/* Check for new pkg. Skip for 'upgrade -F'. */
-
	if ((j->flags & PKG_FLAG_SKIP_INSTALL) == 0 &&
+
	if (((j->flags & PKG_FLAG_SKIP_INSTALL) == 0 &&
+
	    (j->flags & PKG_FLAG_DRY_RUN) == 0) &&
	    (j->flags & PKG_FLAG_PKG_VERSION_TEST) == PKG_FLAG_PKG_VERSION_TEST)
		if (new_pkg_version(j)) {
			j->flags &= ~PKG_FLAG_PKG_VERSION_TEST;
@@ -1573,72 +1658,13 @@ jobs_solve_install_upgrade(struct pkg_jobs *j)

	if (j->solved == 0) {
		if (j->patterns == NULL) {
-
			size_t elt_num = 0;
-

-
			candidates = pkg_jobs_find_install_candidates(j, &jcount);
-

-
			pkg_emit_progress_start("Checking for upgrades (%zd candidates)",
-
				jcount);
-

-
			LL_FOREACH(candidates, c) {
-
				pkg_emit_progress_tick(++elt_num, jcount);
-
				sqlite3_snprintf(sizeof(sqlbuf), sqlbuf, " WHERE id=%" PRId64,
-
						c->id);
-
				if ((it = pkgdb_query(j->db, sqlbuf, MATCH_CONDITION)) == NULL)
-
					return (EPKG_FATAL);
-

-
				pkg = NULL;
-
				while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
-
					/* Do not test we ignore what doesn't exists remotely */
-
					pkg_jobs_find_upgrade(j, pkg->uid, MATCH_EXACT);
-
				}
-
				pkg_free(pkg);
-
				pkgdb_it_free(it);
-
			}
-
			pkg_emit_progress_tick(jcount, jcount);
-
			LL_FREE(candidates, free);
-

-
			pkg_emit_progress_start("Processing candidates (%zd candidates)",
-
				jcount);
-
			elt_num = 0;
-

-
			HASH_ITER(hh, j->request_add, req, rtmp) {
-
				pkg_emit_progress_tick(++elt_num, jcount);
-
				pkg_jobs_universe_process(j->universe, req->item->pkg);
-
			}
-
			pkg_emit_progress_tick(jcount, jcount);
-

-
			pkg_jobs_universe_process_upgrade_chains(j);
-
		}
-
		else {
-
			LL_FOREACH(j->patterns, jp) {
-
				retcode = pkg_jobs_find_remote_pattern(j, jp);
-
				if (retcode == EPKG_FATAL) {
-
					pkg_emit_error("No packages available to %s matching '%s' "
-
							"have been found in the "
-
							"repositories",
-
							(j->type == PKG_JOBS_UPGRADE) ? "upgrade" : "install",
-
							jp->pattern);
-
					if ((j->flags & PKG_FLAG_UPGRADE_VULNERABLE) == 0)
-
						return (retcode);
-
				}
-
				if (retcode == EPKG_LOCKED) {
-
					return (retcode);
-
				}
-
			}
-
			/*
-
			 * Here we have not selected the proper candidate among all
-
			 * possible choices.
-
			 * Hence, we want to perform this procedure now to ensure that
-
			 * we are processing the correct packages.
-
			 */
-
			pkg_jobs_universe_process_upgrade_chains(j);
-
			/*
-
			 * Need to iterate request one more time to recurse depends
-
			 */
-
			HASH_ITER(hh, j->request_add, req, rtmp) {
-
				pkg_jobs_universe_process(j->universe, req->item->pkg);
-
			}
+
			retcode = jobs_solve_full_upgrade(j);
+
			if (retcode != EPKG_OK)
+
				return (retcode);
+
		} else {
+
			retcode = jobs_solve_partial_upgrade(j);
+
			if (retcode != EPKG_OK)
+
				return (retcode);
		}
	}
	else {
@@ -1739,15 +1765,117 @@ pkg_jobs_apply_replacements(struct pkg_jobs *j)
	sqlite3_finalize(stmt);
}

-
int
-
pkg_jobs_solve(struct pkg_jobs *j)
+
static int
+
solve_with_external_cudf_solver(struct pkg_jobs *j, const char *solver)
+
{
+
	int ret, pstatus;
+
	FILE *spipe[2];
+
	pid_t pchild;
+

+
	pchild = process_spawn_pipe(spipe, solver);
+
	if (pchild == -1)
+
		return (EPKG_FATAL);
+

+
	ret = pkg_jobs_cudf_emit_file(j, j->type, spipe[1]);
+
	fclose(spipe[1]);
+

+
	if (ret == EPKG_OK)
+
		ret = pkg_jobs_cudf_parse_output(j, spipe[0]);
+

+
	fclose(spipe[0]);
+
	waitpid(pchild, &pstatus, WNOHANG);
+

+
	return (ret);
+
}
+

+
static int
+
solve_with_external_sat_solver(struct pkg_solve_problem *pb, const char *solver)
{
	int ret, pstatus;
+
	FILE *spipe[2];
+
	pid_t pchild;
+

+
	pchild = process_spawn_pipe(spipe, solver);
+
	if (pchild == -1)
+
		return (EPKG_FATAL);
+

+
	ret = pkg_solve_dimacs_export(pb, spipe[1]);
+
	fclose(spipe[1]);
+

+
	if (ret == EPKG_OK)
+
		ret = pkg_solve_parse_sat_output(spipe[0], pb);
+

+
	fclose(spipe[0]);
+
	waitpid(pchild, &pstatus, WNOHANG);
+

+
	return (ret);
+
}
+

+
static int
+
solve_with_sat_solver(struct pkg_jobs *j)
+
{
+
	const char *sat_solver = pkg_object_string(pkg_config_get("SAT_SOLVER"));
	struct pkg_solve_problem *problem;
+
	const char *dotfile;
+
	FILE *dot = NULL;
+
	int ret;
+

+
	pkg_jobs_universe_process_upgrade_chains(j);
+
	problem = pkg_solve_jobs_to_sat(j);
+
	if (problem == NULL) {
+
		pkg_emit_error("cannot convert job to SAT problem");
+
		j->solved = 0;
+
		return (EPKG_FATAL);
+
	}
+

+
	if (sat_solver != NULL)
+
		return (solve_with_external_sat_solver(problem, sat_solver));
+

+
	if ((dotfile = pkg_object_string(pkg_config_get("DOT_FILE")))
+
		!= NULL) {
+
		dot = fopen(dotfile, "we");
+

+
		if (dot == NULL) {
+
			pkg_emit_errno("fopen", dotfile);
+
		}
+
	}
+

+
	ret = pkg_solve_sat_problem(problem);
+
	if (ret == EPKG_AGAIN) {
+
		pkg_solve_problem_free(problem);
+
		return (solve_with_sat_solver(j));
+
	}
+

+
	if (ret == EPKG_FATAL) {
+
		pkg_emit_error("cannot solve job using SAT solver");
+
		pkg_solve_problem_free(problem);
+
		j->solved = 0;
+
	} else {
+
		ret = pkg_solve_sat_to_jobs(problem);
+
	}
+

+
	if ((dotfile = pkg_object_string(pkg_config_get("DOT_FILE")))
+
		!= NULL) {
+
		dot = fopen(dotfile, "we");
+

+
		if (dot == NULL) {
+
			pkg_emit_errno("fopen", dotfile);
+
		} else {
+
			pkg_solve_dot_export(problem, dot);
+
			fclose(dot);
+
		}
+
	}
+
	pkg_solve_problem_free(problem);
+

+
	return (ret);
+
}
+

+
int
+
pkg_jobs_solve(struct pkg_jobs *j)
+
{
+
	int ret;
	struct pkg_solved *job;
-
	const char *solver, *dotfile;
-
	FILE *spipe[2], *dot = NULL;
-
	pid_t pchild;
+
	const char *cudf_solver;

	pkgdb_begin_solver(j->db);

@@ -1770,86 +1898,13 @@ pkg_jobs_solve(struct pkg_jobs *j)
		return (EPKG_FATAL);
	}

-
	if (ret == EPKG_OK) {
-
		if ((solver = pkg_object_string(pkg_config_get("CUDF_SOLVER"))) != NULL) {
-
			pchild = process_spawn_pipe(spipe, solver);
-
			if (pchild == -1)
-
				return (EPKG_FATAL);
-

-
			ret = pkg_jobs_cudf_emit_file(j, j->type, spipe[1]);
-
			fclose(spipe[1]);
-

-
			if (ret == EPKG_OK)
-
				ret = pkg_jobs_cudf_parse_output(j, spipe[0]);
-

-
			fclose(spipe[0]);
-
			waitpid(pchild, &pstatus, WNOHANG);
-
		}
-
		else {
-
again:
-

-
			pkg_jobs_universe_process_upgrade_chains(j);
-
			problem = pkg_solve_jobs_to_sat(j);
-
			if (problem != NULL) {
-
				if ((solver = pkg_object_string(pkg_config_get("SAT_SOLVER"))) != NULL) {
-
					pchild = process_spawn_pipe(spipe, solver);
-
					if (pchild == -1)
-
						return (EPKG_FATAL);
-

-
					ret = pkg_solve_dimacs_export(problem, spipe[1]);
-
					fclose(spipe[1]);
-

-
					if (ret == EPKG_OK) {
-
						ret = pkg_solve_parse_sat_output(spipe[0], problem);
-
					}
-

-
					fclose(spipe[0]);
-
					waitpid(pchild, &pstatus, WNOHANG);
-
				}
-
				else {
-
					if ((dotfile = pkg_object_string(pkg_config_get("DOT_FILE")))
-
							!= NULL) {
-
						dot = fopen(dotfile, "w");
-

-
						if (dot == NULL) {
-
							pkg_emit_errno("fopen", dotfile);
-
						}
-
					}
-

-
					ret = pkg_solve_sat_problem(problem);
-
					if (ret == EPKG_FATAL) {
-
						pkg_emit_error("cannot solve job using SAT solver");
-
						ret = EPKG_FATAL;
-

-
						if (dot) {
-
							pkg_solve_dot_export(problem, dot);
-
							fclose(dot);
-
						}
-

-
						pkg_solve_problem_free(problem);
-
						j->solved = 0;
-
					}
-
					else if (ret == EPKG_AGAIN) {
-
						pkg_solve_problem_free(problem);
-
						goto again;
-
					}
-
					else {
-
						ret = pkg_solve_sat_to_jobs(problem);
-

-
						if (dot) {
-
							pkg_solve_dot_export(problem, dot);
-
							fclose(dot);
-
						}
+
	cudf_solver = pkg_object_string(pkg_config_get("CUDF_SOLVER"));

-
						pkg_solve_problem_free(problem);
-
					}
-
				}
-
			}
-
			else {
-
				pkg_emit_error("cannot convert job to SAT problem");
-
				ret = EPKG_FATAL;
-
				j->solved = 0;
-
			}
+
	if (ret == EPKG_OK) {
+
		if (cudf_solver != NULL) {
+
			ret = solve_with_external_cudf_solver(j, cudf_solver);
+
		} else {
+
			ret = solve_with_sat_solver(j);
		}
	}

@@ -1987,7 +2042,9 @@ pkg_jobs_execute(struct pkg_jobs *j)
	int flags = 0;
	int retcode = EPKG_FATAL;
	pkg_plugin_hook_t pre, post;
+
	struct trigger *cleanup_triggers;

+
	cleanup_triggers = triggers_load(true);
	if (j->type == PKG_JOBS_INSTALL) {
		pre = PKG_PLUGIN_HOOK_PRE_INSTALL;
		post = PKG_PLUGIN_HOOK_POST_INSTALL;
@@ -2082,6 +2139,7 @@ pkg_jobs_execute(struct pkg_jobs *j)
	}

	pkg_plugins_hook_run(post, j, j->db);
+
	triggers_execute(cleanup_triggers);

cleanup:
	pkgdb_release_lock(j->db, PKGDB_LOCK_EXCLUSIVE);
modified libpkg/pkg_jobs_universe.c
@@ -240,20 +240,28 @@ pkg_jobs_universe_process_deps(struct pkg_jobs_universe *universe,
	int (*deps_func)(const struct pkg *pkg, struct pkg_dep **d);
	int rc;
	struct pkg_job_universe_item *unit;
-
	struct pkg *npkg, *rpkg;
+
	struct pkg *npkg, *rpkg, *lpkg;
	pkg_chain_t *rpkgs = NULL;
	bool found = false;

	rpkg = NULL;

	if (flags & DEPS_FLAG_REVERSE) {
+
		pkg_debug(4, "Processing rdeps for %s (%s)", pkg->uid, pkg->type == PKG_INSTALLED ? "installed" : "remote");
+
		if (pkg->type != PKG_INSTALLED) {
+
			lpkg = pkg_jobs_universe_get_local(universe, pkg->uid, 0);
+
			if (lpkg != NULL)
+
				return (pkg_jobs_universe_process_deps(universe, lpkg, flags));
+
		}
		deps_func = pkg_rdeps;
	}
	else {
+
		pkg_debug(4, "Processing deps for %s", pkg->uid);
		deps_func = pkg_deps;
	}

	while (deps_func(pkg, &d) == EPKG_OK) {
+
		pkg_debug(4, "Processing *deps for %s: %s", pkg->uid, d->uid);
		HASH_FIND_STR(universe->items, d->uid, unit);
		if (unit != NULL) {
			continue;
@@ -564,6 +572,8 @@ pkg_jobs_universe_process_item(struct pkg_jobs_universe *universe, struct pkg *p
	pkg_jobs_t type = universe->j->type;
	struct pkg_job_universe_item *found;

+
	pkg_debug(4, "Processing item %s\n", pkg->uid);
+

	job_flags = universe->j->flags;

	/*
modified libpkg/pkg_manifest.c
@@ -256,39 +256,40 @@ pkg_manifest_keys_free(struct pkg_manifest_key *key)
}

static int
-
urlencode(const char *src, UT_string **dest)
+
urlencode(const char *src, xstring **dest)
{
	size_t len;
	size_t i;

-
	utstring_renew(*dest);
+
	xstring_renew(*dest);

	len = strlen(src);
	for (i = 0; i < len; i++) {
		if (!isascii(src[i]) || src[i] == '%')
-
			utstring_printf(*dest, "%%%.2x", (unsigned char)src[i]);
+
			fprintf((*dest)->fp, "%%%.2x", (unsigned char)src[i]);
		else
-
			utstring_printf(*dest, "%c", src[i]);
+
			fputc(src[i], (*dest)->fp);
	}

+
	fflush((*dest)->fp);
	return (EPKG_OK);
}


static int
-
urldecode(const char *src, UT_string **dest)
+
urldecode(const char *src, xstring **dest)
{
	size_t len;
	size_t i;
	char c;
	char hex[] = {'\0', '\0', '\0'};

-
	utstring_renew(*dest);
+
	xstring_renew(*dest);

	len = strlen(src);
	for (i = 0; i < len; i++) {
		if (src[i] != '%') {
-
			utstring_printf(*dest, "%c", src[i]);
+
			fputc(src[i], (*dest)->fp);
		} else {
			if (i + 2 > len) {
				pkg_emit_error("unexpected end of string");
@@ -304,13 +305,14 @@ urldecode(const char *src, UT_string **dest)
				 * if it fails consider this is not a urlencoded
				 * information
				 */
-
				utstring_printf(*dest, "%%%s", hex);
+
				fprintf((*dest)->fp, "%%%s", hex);
			} else {
-
				utstring_printf(*dest, "%c", c);
+
				fputc(c,(*dest)->fp);
			}
		}
	}

+
	fflush((*dest)->fp);
	return (EPKG_OK);
}

@@ -351,7 +353,7 @@ pkg_string(struct pkg *pkg, const ucl_object_t *obj, uint32_t offset)
{
	const char *str;
	char **dest;
-
	UT_string *buf = NULL;
+
	xstring *buf = NULL;

	str = ucl_object_tostring_forced(obj);

@@ -373,7 +375,7 @@ pkg_string(struct pkg *pkg, const ucl_object_t *obj, uint32_t offset)

		if (offset & STRING_FLAG_URLDECODE) {
			urldecode(str, &buf);
-
			str = utstring_body(buf);
+
			str = buf->buf;
		}

		/* Remove flags from the offset */
@@ -381,9 +383,7 @@ pkg_string(struct pkg *pkg, const ucl_object_t *obj, uint32_t offset)
		dest = (char **) ((unsigned char *)pkg + offset);
		*dest = xstrdup(str);

-
		if (buf) {
-
			utstring_free(buf);
-
		}
+
		xstring_free(buf);
	}

	return (EPKG_OK);
@@ -503,7 +503,7 @@ pkg_array(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
static int
pkg_obj(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
{
-
	UT_string *tmp = NULL;
+
	xstring *tmp = NULL;
	const ucl_object_t *cur;
	ucl_object_iter_t it = NULL;
	pkg_script script_type;
@@ -534,12 +534,12 @@ pkg_obj(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
		case PKG_DIRECTORIES:
			if (cur->type == UCL_BOOLEAN) {
				urldecode(key, &tmp);
-
				pkg_adddir(pkg, utstring_body(tmp), false);
+
				pkg_adddir(pkg, tmp->buf, false);
			} else if (cur->type == UCL_OBJECT) {
				pkg_set_dirs_from_object(pkg, cur);
			} else if (cur->type == UCL_STRING) {
				urldecode(key, &tmp);
-
				pkg_adddir(pkg, utstring_body(tmp), false);
+
				pkg_adddir(pkg, tmp->buf, false);
			} else {
				pkg_emit_error("Skipping malformed directories %s",
				    key);
@@ -549,7 +549,7 @@ pkg_obj(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
			if (cur->type == UCL_STRING) {
				buf = ucl_object_tolstring(cur, &len);
				urldecode(key, &tmp);
-
				pkg_addfile(pkg, utstring_body(tmp), len >= 2 ? buf : NULL, false);
+
				pkg_addfile(pkg, tmp->buf, len >= 2 ? buf : NULL, false);
			} else if (cur->type == UCL_OBJECT)
				pkg_set_files_from_object(pkg, cur);
			else
@@ -595,7 +595,7 @@ pkg_obj(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
				}

				urldecode(ucl_object_tostring(cur), &tmp);
-
				pkg_addscript(pkg, utstring_body(tmp), script_type);
+
				pkg_addscript(pkg, tmp->buf, script_type);
			}
			break;
		case PKG_LUA_SCRIPTS:
@@ -622,8 +622,7 @@ pkg_obj(struct pkg *pkg, const ucl_object_t *obj, uint32_t attr)
		}
	}

-
	if (tmp)
-
		utstring_free(tmp);
+
	xstring_free(tmp);

	return (EPKG_OK);
}
@@ -644,7 +643,7 @@ pkg_set_files_from_object(struct pkg *pkg, const ucl_object_t *obj)
	const char *gname = NULL;
	void *set = NULL;
	mode_t perm = 0;
-
	UT_string *fname = NULL;
+
	xstring *fname = NULL;
	const char *key, *okey;

	okey = ucl_object_key(obj);
@@ -671,13 +670,13 @@ pkg_set_files_from_object(struct pkg *pkg, const ucl_object_t *obj)
				perm = getmode(set, 0);
		} else {
			pkg_debug(1, "Skipping unknown key for file(%s): %s",
-
			    utstring_body(fname), key);
+
			    fname->buf, key);
		}
	}

-
	pkg_addfile_attr(pkg, utstring_body(fname), sum, uname, gname, perm, 0,
+
	pkg_addfile_attr(pkg, fname->buf, sum, uname, gname, perm, 0,
	    false);
-
	utstring_free(fname);
+
	xstring_free(fname);

	return (EPKG_OK);
}
@@ -691,7 +690,7 @@ pkg_set_dirs_from_object(struct pkg *pkg, const ucl_object_t *obj)
	const char *gname = NULL;
	void *set;
	mode_t perm = 0;
-
	UT_string *dirname = NULL;
+
	xstring *dirname = NULL;
	const char *key, *okey;

	okey = ucl_object_key(obj);
@@ -717,12 +716,12 @@ pkg_set_dirs_from_object(struct pkg *pkg, const ucl_object_t *obj)
			/* ignore on purpose : compatibility*/
		} else {
			pkg_debug(1, "Skipping unknown key for dir(%s): %s",
-
			    utstring_body(dirname), key);
+
			    dirname->buf, key);
		}
	}

-
	pkg_adddir_attr(pkg, utstring_body(dirname), uname, gname, perm, 0, false);
-
	utstring_free(dirname);
+
	pkg_adddir_attr(pkg, dirname->buf, uname, gname, perm, 0, false);
+
	xstring_free(dirname);

	return (EPKG_OK);
}
@@ -944,7 +943,7 @@ pkg_emit_filelist(struct pkg *pkg, FILE *f)
{
	ucl_object_t *obj = NULL, *seq;
	struct pkg_file *file = NULL;
-
	UT_string *b = NULL;
+
	xstring *b = NULL;

	obj = ucl_object_typed_new(UCL_OBJECT);
	ucl_object_insert_key(obj, ucl_object_fromstring(pkg->origin), "origin", 6, false);
@@ -956,16 +955,14 @@ pkg_emit_filelist(struct pkg *pkg, FILE *f)
		urlencode(file->path, &b);
		if (seq == NULL)
			seq = ucl_object_typed_new(UCL_ARRAY);
-
		ucl_array_append(seq, ucl_object_fromlstring(utstring_body(b), utstring_len(b)));
+
		ucl_array_append(seq, ucl_object_fromlstring(b->buf, strlen(b->buf)));
	}
	if (seq != NULL)
		ucl_object_insert_key(obj, seq, "files", 5, false);

	ucl_object_emit_file(obj, UCL_EMIT_JSON_COMPACT, f);

-
	if (b != NULL)
-
		utstring_free(b);
-

+
	xstring_free(b);
	ucl_object_unref(obj);

	return (EPKG_OK);
@@ -981,7 +978,7 @@ pkg_emit_object(struct pkg *pkg, short flags)
	struct pkg_dir		*dir      = NULL;
	struct pkg_conflict	*conflict = NULL;
	struct pkg_config_file	*cf       = NULL;
-
	UT_string		*tmpsbuf  = NULL;
+
	xstring		*tmpsbuf  = NULL;
	char			*buf;
	int i;
	const char *script_types = NULL;
@@ -1019,6 +1016,11 @@ pkg_emit_object(struct pkg *pkg, short flags)
		ucl_object_insert_key(top, ucl_object_fromstring_common(pkg->dep_formula, 0,
		    UCL_STRING_TRIM), "dep_formula", 11, false);
	}
+
	if (pkg->type == PKG_INSTALLED &&
+
	    (flags & PKG_MANIFEST_EMIT_LOCAL_METADATA) == PKG_MANIFEST_EMIT_LOCAL_METADATA) {
+
		ucl_object_insert_key(top, ucl_object_fromint(pkg->timestamp), "timestamp", 9, false);
+
	}
+

	/*
	 * XXX: dirty hack to be compatible with pkg 1.2
	 */
@@ -1060,7 +1062,7 @@ pkg_emit_object(struct pkg *pkg, short flags)
	if (pkg->desc != NULL) {
		urlencode(pkg->desc, &tmpsbuf);
		ucl_object_insert_key(top,
-
			ucl_object_fromstring_common(utstring_body(tmpsbuf), utstring_len(tmpsbuf), UCL_STRING_TRIM),
+
			ucl_object_fromstring_common(tmpsbuf->buf, strlen(tmpsbuf->buf), UCL_STRING_TRIM),
			"desc", 4, false);
	}

@@ -1182,8 +1184,9 @@ pkg_emit_object(struct pkg *pkg, short flags)
		if (map == NULL)
			map = ucl_object_typed_new(UCL_OBJECT);
		/* Add annotations except for internal ones. */
-
		if (strcmp(kv->key, "repository") == 0 ||
-
		    strcmp(kv->key, "relocated") == 0)
+
		if ((strcmp(kv->key, "repository") == 0 ||
+
		    strcmp(kv->key, "relocated") == 0) &&
+
		    (flags & PKG_MANIFEST_EMIT_LOCAL_METADATA) == 0)
			continue;
		ucl_object_insert_key(map, ucl_object_fromstring(kv->value),
		    kv->key, strlen(kv->key), true);
@@ -1204,7 +1207,7 @@ pkg_emit_object(struct pkg *pkg, short flags)
					map = ucl_object_typed_new(UCL_OBJECT);
				ucl_object_insert_key(map,
				    ucl_object_fromstring(file->sum),
-
				    utstring_body(tmpsbuf), utstring_len(tmpsbuf), true);
+
				    tmpsbuf->buf, strlen(tmpsbuf->buf), true);
			}
			if (map)
				ucl_object_insert_key(top, map, "files", 5, false);
@@ -1215,7 +1218,7 @@ pkg_emit_object(struct pkg *pkg, short flags)
				urlencode(cf->path, &tmpsbuf);
				if (seq == NULL)
					seq = ucl_object_typed_new(UCL_ARRAY);
-
				ucl_array_append(seq, ucl_object_fromstring(utstring_body(tmpsbuf)));
+
				ucl_array_append(seq, ucl_object_fromstring(tmpsbuf->buf));
			}
			if (seq)
				ucl_object_insert_key(top, seq, "config", 6, false);
@@ -1228,7 +1231,7 @@ pkg_emit_object(struct pkg *pkg, short flags)
					map = ucl_object_typed_new(UCL_OBJECT);
				ucl_object_insert_key(map,
				    ucl_object_fromstring("y"),
-
				    utstring_body(tmpsbuf), utstring_len(tmpsbuf), true);
+
				    tmpsbuf->buf, strlen(tmpsbuf->buf), true);
			}
			if (map)
				ucl_object_insert_key(top, map, "directories", 11, false);
@@ -1264,8 +1267,8 @@ pkg_emit_object(struct pkg *pkg, short flags)
			if (map == NULL)
				map = ucl_object_typed_new(UCL_OBJECT);
			ucl_object_insert_key(map,
-
			    ucl_object_fromstring_common(utstring_body(tmpsbuf),
-
			        utstring_len(tmpsbuf), UCL_STRING_TRIM),
+
			    ucl_object_fromstring_common(tmpsbuf->buf,
+
			        strlen(tmpsbuf->buf), UCL_STRING_TRIM),
			    script_types, 0, true);
		}
		if (map)
@@ -1307,15 +1310,14 @@ pkg_emit_object(struct pkg *pkg, short flags)
			"messages", sizeof("messages") - 1, false);
	}

-
	if (tmpsbuf != NULL)
-
		utstring_free(tmpsbuf);
+
	xstring_free(tmpsbuf);

	return (top);
}


static int
-
emit_manifest(struct pkg *pkg, UT_string **out, short flags)
+
emit_manifest(struct pkg *pkg, xstring **out, short flags)
{
	ucl_object_t *top;

@@ -1355,7 +1357,7 @@ static int
pkg_emit_manifest_generic(struct pkg *pkg, void *out, short flags,
	    char **pdigest, bool out_is_a_buf)
{
-
	UT_string *output = NULL;
+
	xstring *output = NULL;
	unsigned char digest[SHA256_BLOCK_SIZE];
	SHA256_CTX *sign_ctx = NULL;
	int rc;
@@ -1371,11 +1373,12 @@ pkg_emit_manifest_generic(struct pkg *pkg, void *out, short flags,

	rc = emit_manifest(pkg, &output, flags);

+
	fflush(output->fp);
	if (sign_ctx != NULL)
-
		sha256_update(sign_ctx, utstring_body(output), utstring_len(output));
+
		sha256_update(sign_ctx, output->buf, strlen(output->buf));

	if (!out_is_a_buf)
-
		fprintf(out, "%s\n", utstring_body(output));
+
		fprintf(out, "%s\n", output->buf);

	if (pdigest != NULL) {
		sha256_final(sign_ctx, digest);
@@ -1384,7 +1387,7 @@ pkg_emit_manifest_generic(struct pkg *pkg, void *out, short flags,
	}

	if (!out_is_a_buf)
-
		utstring_free(output);
+
		xstring_free(output);

	return (rc);
}
@@ -1397,7 +1400,7 @@ pkg_emit_manifest_file(struct pkg *pkg, FILE *f, short flags, char **pdigest)
}

int
-
pkg_emit_manifest_buf(struct pkg *pkg, UT_string *b, short flags, char **pdigest)
+
pkg_emit_manifest_buf(struct pkg *pkg, xstring *b, short flags, char **pdigest)
{

	return (pkg_emit_manifest_generic(pkg, b, flags, pdigest, true));
@@ -1406,19 +1409,18 @@ pkg_emit_manifest_buf(struct pkg *pkg, UT_string *b, short flags, char **pdigest
int
pkg_emit_manifest(struct pkg *pkg, char **dest, short flags, char **pdigest)
{
-
	UT_string *b;
+
	xstring *b;
	int rc;

-
	utstring_new(b);
+
	b = xstring_new();
	rc = pkg_emit_manifest_buf(pkg, b, flags, pdigest);

	if (rc != EPKG_OK) {
-
		utstring_free(b);
+
		xstring_free(b);
		return (rc);
	}

-
	*dest = xstrdup(utstring_body(b));
-
	utstring_free(b);
+
	*dest = xstring_get(b);

	return (rc);
}
modified libpkg/pkg_ports.c
@@ -1,5 +1,5 @@
/*-
-
 * Copyright (c) 2011-2013 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2012-2013 Bryan Drewery <bdrewery@FreeBSD.org>
 * All rights reserved.
@@ -26,7 +26,15 @@
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

+
#include "pkg_config.h"
+

+
#ifdef HAVE_CAPSICUM
+
#include <sys/capsicum.h>
+
#endif
+

#include <sys/stat.h>
+
#include <sys/types.h>
+
#include <sys/wait.h>

#include <assert.h>
#include <ctype.h>
@@ -39,11 +47,13 @@
#include <fcntl.h>
#include <unistd.h>
#include <uthash.h>
+
#include <err.h>

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

static ucl_object_t *keyword_schema = NULL;

@@ -60,6 +70,7 @@ static int config(struct plist *, char *, struct file_attr *);
/* compat with old packages */
static int name_key(struct plist *, char *, struct file_attr *);
static int pkgdep(struct plist *, char *, struct file_attr *);
+
static int include_plist(struct plist *, char *, struct file_attr *);

static struct action_cmd {
	const char *name;
@@ -96,6 +107,10 @@ keyword_open_schema(void)
		"      items = { type = string }; "
		"      uniqueItems: true "
		"    }; "
+
		"    actions_script = { type = string }; "
+
		"    validation = { type = string }; "
+
		"    deprecated = { type = boolean }; "
+
		"    deprecation_message = { type = string }; "
		"    attributes = { "
		"      type = object; "
		"      properties { "
@@ -158,8 +173,7 @@ parse_mode(const char *str)
	return (setmode(str));
}

-

-
static void
+
void
free_file_attr(struct file_attr *a)
{
	if (a == NULL)
@@ -184,9 +198,9 @@ setprefix(struct plist *p, char *line, struct file_attr *a __unused)

	p->slash = p->prefix[strlen(p->prefix) -1] == '/' ? "" : "/";

-
	utstring_printf(p->post_install_buf, "cd %s\n", p->prefix);
-
	utstring_printf(p->pre_deinstall_buf, "cd %s\n", p->prefix);
-
	utstring_printf(p->post_deinstall_buf, "cd %s\n", p->prefix);
+
	fprintf(p->post_install_buf->fp, "cd %s\n", p->prefix);
+
	fprintf(p->pre_deinstall_buf->fp, "cd %s\n", p->prefix);
+
	fprintf(p->post_deinstall_buf->fp, "cd %s\n", p->prefix);

	return (EPKG_OK);
}
@@ -219,6 +233,43 @@ pkgdep(struct plist *p, char *line, struct file_attr *a __unused)
}

static int
+
lua_meta(lua_State *L,
+
    int (*perform)(struct plist *, char *, struct file_attr *))
+
{
+
	int n = lua_gettop(L);
+
	int ret;
+
	luaL_argcheck(L, n == 1, n > 1 ? 2 : n,
+
	    "takes exactly one argument");
+
	char *str = strdup(luaL_checkstring(L, 1));
+
	lua_getglobal(L, "plist");
+
	struct plist *p = lua_touserdata(L, -1);
+
	lua_getglobal(L, "attrs");
+
	struct file_attr *a = lua_touserdata(L, -1);
+

+
	ret = perform(p, str, a);
+
	free(str);
+
	lua_pushboolean(L, ret == EPKG_OK);
+
	return (1);
+
}
+

+
static int
+
lua_dir(lua_State *L)
+
{
+
	return (lua_meta(L, dir));
+
}
+

+
static int
+
lua_config(lua_State *L) {
+
	return (lua_meta(L, config));
+
}
+

+
static int
+
lua_file(lua_State *L) {
+
	return (lua_meta(L, file));
+
}
+

+

+
static int
dir(struct plist *p, char *line, struct file_attr *a)
{
	char path[MAXPATHLEN+1];
@@ -541,22 +592,22 @@ meta_exec(struct plist *p, char *line, struct file_attr *a, exec_t type)
	int ret;

	ret = format_exec_cmd(&cmd, line, p->prefix, p->last_file, NULL, 0,
-
	    NULL);
+
	    NULL, false);
	if (ret != EPKG_OK)
		return (EPKG_OK);

	switch (type) {
	case PREEXEC:
-
		utstring_printf(p->pre_install_buf, "%s\n", cmd);
+
		fprintf(p->pre_install_buf->fp, "%s\n", cmd);
		break;
	case POSTEXEC:
-
		utstring_printf(p->post_install_buf, "%s\n", cmd);
+
		fprintf(p->post_install_buf->fp, "%s\n", cmd);
		break;
	case PREUNEXEC:
-
		utstring_printf(p->pre_deinstall_buf, "%s\n", cmd);
+
		fprintf(p->pre_deinstall_buf->fp, "%s\n", cmd);
		break;
	case POSTUNEXEC:
-
		utstring_printf(p->post_deinstall_buf, "%s\n", cmd);
+
		fprintf(p->post_deinstall_buf->fp, "%s\n", cmd);
		break;
	case UNEXEC:
		comment[0] = '\0';
@@ -586,10 +637,10 @@ meta_exec(struct plist *p, char *line, struct file_attr *a, exec_t type)

		if (should_be_post(cmd, p)) {
			if (comment[0] != '#')
-
				utstring_printf(p->post_deinstall_buf,
+
				fprintf(p->post_deinstall_buf->fp,
				    "%s%s\n", comment, cmd);
		} else {
-
			utstring_printf(p->pre_deinstall_buf, "%s%s\n", comment, cmd);
+
			fprintf(p->pre_deinstall_buf->fp, "%s%s\n", comment, cmd);
		}
		if (comment[0] == '#') {
			buf = cmd;
@@ -633,7 +684,7 @@ meta_exec(struct plist *p, char *line, struct file_attr *a, exec_t type)
		}
		break;
	case EXEC:
-
		utstring_printf(p->post_install_buf, "%s\n", cmd);
+
		fprintf(p->post_install_buf->fp, "%s\n", cmd);
		break;
	}

@@ -702,6 +753,7 @@ static struct keyact {
	{ "dir", dir },
	{ "dirrm", dirrm },
	{ "dirrmtry", dirrm },
+
	{ "include", include_plist },
	{ "mode", setmod },
	{ "owner", setowner },
	{ "group", setgroup },
@@ -773,6 +825,7 @@ parse_actions(const ucl_object_t *o, struct plist *p,
	const char *actname;
	ucl_object_iter_t it = NULL;
	int i, j = 0;
+
	int r, rc = EPKG_OK;

	while ((cur = ucl_iterate_object(o, &it, true))) {
		actname = ucl_object_tostring(cur);
@@ -799,13 +852,15 @@ parse_actions(const ucl_object_t *o, struct plist *p,
						return (EPKG_FATAL);
					}
				}
-
				list_actions[i].perform(p, j > 0 ? argv[j - 1] : line, a);
+
				r = list_actions[i].perform(p, j > 0 ? argv[j - 1] : line, a);
+
				if (r != EPKG_OK && rc == EPKG_OK)
+
					rc = r;
				break;
			}
		}
	}

-
	return (EPKG_OK);
+
	return (rc);
}

static void
@@ -853,16 +908,16 @@ append_script(struct plist *p, pkg_script t, const char *cmd)
{
	switch (t) {
	case PKG_SCRIPT_PRE_INSTALL:
-
		utstring_printf(p->pre_install_buf, "%s\n", cmd);
+
		fprintf(p->pre_install_buf->fp, "%s\n", cmd);
		break;
	case PKG_SCRIPT_POST_INSTALL:
-
		utstring_printf(p->post_install_buf, "%s\n", cmd);
+
		fprintf(p->post_install_buf->fp, "%s\n", cmd);
		break;
	case PKG_SCRIPT_PRE_DEINSTALL:
-
		utstring_printf(p->pre_deinstall_buf, "%s\n", cmd);
+
		fprintf(p->pre_deinstall_buf->fp, "%s\n", cmd);
		break;
	case PKG_SCRIPT_POST_DEINSTALL:
-
		utstring_printf(p->post_deinstall_buf, "%s\n", cmd);
+
		fprintf(p->post_deinstall_buf->fp, "%s\n", cmd);
		break;
	}
}
@@ -896,7 +951,7 @@ apply_keyword_file(ucl_object_t *obj, struct plist *p, char *line, struct file_a
	for (int i = 0; i < nitems(script_mapping); i++) {
		if ((o = ucl_object_find_key(obj, script_mapping[i].key))) {
			if (format_exec_cmd(&cmd, ucl_object_tostring(o), p->prefix,
-
			    p->last_file, line, argc, args) != EPKG_OK)
+
			    p->last_file, line, argc, args, false) != EPKG_OK)
				goto keywords_cleanup;
			append_script(p, script_mapping[i].type, cmd);
			free(cmd);
@@ -907,7 +962,7 @@ apply_keyword_file(ucl_object_t *obj, struct plist *p, char *line, struct file_a
	for (int i = 0; i < nitems(lua_mapping); i++) {
		if ((o = ucl_object_find_key(obj, lua_mapping[i].key))) {
			if (format_exec_cmd(&cmd, ucl_object_tostring(o), p->prefix,
-
			    p->last_file, line, argc, args) != EPKG_OK)
+
			    p->last_file, line, argc, args, true) != EPKG_OK)
				goto keywords_cleanup;
			pkg_add_lua_script(p->pkg, cmd, lua_mapping[i].type);
			free(cmd);
@@ -937,6 +992,38 @@ apply_keyword_file(ucl_object_t *obj, struct plist *p, char *line, struct file_a
	if ((o = ucl_object_find_key(obj,  "actions")))
		ret = parse_actions(o, p, line, attr, argc, args);

+
	if (ret == EPKG_OK && (o = ucl_object_find_key(obj, "prepackaging"))) {
+
		lua_State *L = luaL_newstate();
+
		static const luaL_Reg plist_lib[] = {
+
			{ "config", lua_config },
+
			{ "dir", lua_dir },
+
			{ "file", lua_file },
+
			{ NULL, NULL },
+
		};
+
		luaL_openlibs(L);
+
		lua_pushlightuserdata(L, p);
+
		lua_setglobal(L, "plist");
+
		lua_pushlightuserdata(L, attr);
+
		lua_setglobal(L, "attrs");
+
		lua_pushstring(L, line);
+
		lua_setglobal(L, "line");
+
		lua_args_table(L, args, argc);
+
		luaL_newlib(L, plist_lib);
+
		lua_setglobal(L, "pkg");
+
		lua_override_ios(L);
+
		pkg_debug(3, "Scripts: executing lua\n--- BEGIN ---"
+
		    "\n%s\nScripts: --- END ---", ucl_object_tostring(o));
+
		if (luaL_dostring(L, ucl_object_tostring(o))) {
+
			pkg_emit_error("Failed to execute lua script: "
+
			    "%s", lua_tostring(L, -1));
+
			ret = EPKG_FATAL;
+
		}
+
		if (lua_tonumber(L, -1) != 0) {
+
			ret = EPKG_FATAL;
+
		}
+
		lua_close(L);
+
	}
+

keywords_cleanup:
	free(args);
	free(tofree);
@@ -951,6 +1038,7 @@ external_keyword(struct plist *plist, char *keyword, char *line, struct file_att
	char keyfile_path[MAXPATHLEN];
	int ret = EPKG_UNKNOWN, fd;
	ucl_object_t *o, *schema;
+
	const ucl_object_t *obj;
	struct ucl_schema_error err;

	keyword_dir = pkg_object_string(pkg_config_get("PLIST_KEYWORDS_DIR"));
@@ -993,12 +1081,26 @@ external_keyword(struct plist *plist, char *keyword, char *line, struct file_att
		}
	}

+
	if ((obj = ucl_object_find_key(o, "deprecated")) &&
+
	    ucl_object_toboolean(obj)) {
+
		obj = ucl_object_find_key(o, "deprecation_message");
+
		pkg_emit_error("Use of '@%s' is deprecated%s%s", keyword,
+
		   obj != NULL ? ": " : "",
+
		   obj != NULL ? ucl_object_tostring(obj) : "");
+
		if (ctx.developer_mode) {
+
			ucl_object_unref(o);
+
			return (EPKG_FATAL);
+
		}
+
	}
	ret = apply_keyword_file(o, plist, line, attr);
+
	if (ret != EPKG_OK) {
+
		pkg_emit_error("Fail to apply keyword '%s'", keyword);
+
	}

	return (ret);
}

-
static struct file_attr *
+
 struct file_attr *
parse_keyword_args(char *args, char *keyword)
{
	struct file_attr *attr;
@@ -1014,6 +1116,8 @@ parse_keyword_args(char *args, char *keyword)
	do {
		args[0] = '\0';
		args++;
+
		while (isspace(*args))
+
			args++;
		if (*args == '\0')
			break;
		if (owner == NULL) {
@@ -1026,9 +1130,6 @@ parse_keyword_args(char *args, char *keyword)
			fflags = args;
			break;
		} else {
-
			pkg_emit_error("Malformed keyword '%s', expecting "
-
			    "keyword or keyword(owner,group,mode,fflags...)",
-
			    keyword);
			return (NULL);
		}
	} while ((args = strchr(args, ',')) != NULL);
@@ -1053,12 +1154,14 @@ parse_keyword_args(char *args, char *keyword)
			return (NULL);
		}
	}
+
	if (owner == NULL && group == NULL && set == NULL)
+
		return (NULL);

	attr = xcalloc(1, sizeof(struct file_attr));
	if (owner != NULL && *owner != '\0')
-
		attr->owner = xstrdup(owner);
+
		attr->owner = xstrdup(rtrimspace(owner));
	if (group != NULL && *group != '\0')
-
		attr->group = xstrdup(group);
+
		attr->group = xstrdup(rtrimspace(group));
	if (set != NULL) {
		attr->mode = getmode(set, 0);
		free(set);
@@ -1069,27 +1172,13 @@ parse_keyword_args(char *args, char *keyword)
}

static int
-
parse_keywords(struct plist *plist, char *keyword, char *line)
+
parse_keywords(struct plist *plist, char *keyword,
+
    char *line, struct file_attr *attr)
{
	struct keyword *k;
	struct action *a;
-
	struct file_attr *attr = NULL;
-
	char *tmp;
	int ret = EPKG_FATAL;

-
	if ((tmp = strchr(keyword, '(')) != NULL &&
-
	    keyword[strlen(keyword) -1] != ')') {
-
		pkg_emit_error("Malformed keyword %s, expecting @keyword "
-
		    "or @keyword(owner,group,mode)", keyword);
-
		return (ret);
-
	}
-

-
	if (tmp != NULL) {
-
		attr = parse_keyword_args(tmp, keyword);
-
		if (attr == NULL)
-
			return (ret);
-
	}
-

	/* if keyword is empty consider it as a file */
	if (*keyword == '\0')
		return (file(plist, line, attr));
@@ -1099,9 +1188,9 @@ parse_keywords(struct plist *plist, char *keyword, char *line)
		LL_FOREACH(k->actions, a) {
			ret = a->perform(plist, line, attr);
			if (ret != EPKG_OK)
-
				goto end;
+
				break;
		}
-
		goto end;
+
		return (ret);
	}

	/*
@@ -1109,24 +1198,61 @@ parse_keywords(struct plist *plist, char *keyword, char *line)
	 * maybe it is defined externally
	 * let's try to find it
	 */
-
	ret = external_keyword(plist, keyword, line, attr);
-
end:
-
	free_file_attr(attr);
-
	return (ret);
+
	return (external_keyword(plist, keyword, line, attr));
+
}
+

+
char *
+
extract_keywords(char *line, char **keyword, struct file_attr **attr)
+
{
+
	char *k, *buf, *tmp;
+
	struct file_attr *a = NULL;
+

+
	buf = k = line;
+
	while (!(isspace(buf[0]) || buf[0] == '\0')) {
+
		if (buf[0] == '(' && (buf = strchr(buf, ')')) == NULL)
+
			return (NULL);
+
		buf++;
+
	}
+
	if (buf[0] != '\0') {
+
		buf[0] = '\0';
+
		buf++;
+
	}
+

+
	/* trim spaces after the keyword */
+
	while (isspace(buf[0]))
+
		buf++;
+

+
	pkg_debug(1, "Parsing plist, found keyword: '%s", k);
+

+
	if ((tmp = strchr(k, '(')) != NULL && k[strlen(k) -1] != ')')
+
		return (NULL);
+

+
	if (tmp != NULL) {
+
		a = parse_keyword_args(tmp, k);
+
		if (a == NULL)
+
			return (NULL);
+
	}
+

+
	*attr = a;
+
	*keyword = k;
+

+
	return (buf);
}

static void
-
flush_script_buffer(UT_string *buf, struct pkg *p, int type)
+
flush_script_buffer(xstring *buf, struct pkg *p, int type)
{
-
	if (utstring_len(buf) > 0) {
-
		pkg_appendscript(p, utstring_body(buf), type);
+
	fflush(buf->fp);
+
	if (buf->buf[0] != '\0') {
+
		pkg_appendscript(p, buf->buf, type);
	}
}

int
plist_parse_line(struct plist *plist, char *line)
{
-
	char *keyword, *buf;
+
	char *keyword, *buf, *bkpline;
+
	struct file_attr *a;

	if (plist->ignore_next) {
		plist->ignore_next = false;
@@ -1137,24 +1263,19 @@ plist_parse_line(struct plist *plist, char *line)
		return (EPKG_OK);

	pkg_debug(1, "Parsing plist line: '%s'", line);
+
	bkpline = xstrdup(line);

	if (line[0] == '@') {
-
		keyword = line;
-
		keyword++; /* skip the @ */
-
		buf = keyword;
-
		while (!(isspace(buf[0]) || buf[0] == '\0'))
-
			buf++;
-

-
		if (buf[0] != '\0') {
-
			buf[0] = '\0';
-
			buf++;
+
		keyword = NULL;
+
		a = NULL;
+
		buf = extract_keywords(line + 1, &keyword, &a);
+
		if (buf == NULL) {
+
			pkg_emit_error("Malformed keyword %s, expecting @keyword "
+
			    "or @keyword(owner,group,mode)", bkpline);
+
			return (EPKG_FATAL);
		}
-
		/* trim write spaces */
-
		while (isspace(buf[0]))
-
			buf++;
-
		pkg_debug(1, "Parsing plist, found keyword: '%s", keyword);

-
		switch (parse_keywords(plist, keyword, buf)) {
+
		switch (parse_keywords(plist, keyword, buf, a)) {
		case EPKG_UNKNOWN:
			pkg_emit_error("unknown keyword %s: %s",
			    keyword, line);
@@ -1186,6 +1307,7 @@ plist_new(struct pkg *pkg, const char *stage)
	if (p == NULL)
		return (NULL);

+
	p->plistdirfd = -1;
	p->stagefd = open(stage ? stage : "/", O_DIRECTORY | O_CLOEXEC);
	if (p->stagefd == -1) {
		free(p);
@@ -1201,10 +1323,10 @@ plist_new(struct pkg *pkg, const char *stage)
	p->uname = xstrdup("root");
	p->gname = xstrdup("wheel");

-
	utstring_new(p->pre_install_buf);
-
	utstring_new(p->post_install_buf);
-
	utstring_new(p->pre_deinstall_buf);
-
	utstring_new(p->post_deinstall_buf);
+
	p->pre_install_buf = xstring_new();
+
	p->post_install_buf = xstring_new();
+
	p->pre_deinstall_buf = xstring_new();
+
	p->post_deinstall_buf = xstring_new();
	p->hardlinks = kh_init_hardlinks();

	populate_keywords(p);
@@ -1220,6 +1342,8 @@ plist_free(struct plist *p)

	if (p->stagefd != -1)
		close(p->stagefd);
+
	if (p->plistdirfd != -1)
+
		close(p->plistdirfd);

	HASH_FREE(p->keywords, keyword_free);

@@ -1230,23 +1354,90 @@ plist_free(struct plist *p)
	free(p->post_patterns.patterns);
	kh_destroy_hardlinks(p->hardlinks);

-
	utstring_free(p->post_deinstall_buf);
-
	utstring_free(p->post_install_buf);
-
	utstring_free(p->pre_deinstall_buf);
-
	utstring_free(p->pre_install_buf);
+
	xstring_free(p->post_deinstall_buf);
+
	xstring_free(p->post_install_buf);
+
	xstring_free(p->pre_deinstall_buf);
+
	xstring_free(p->pre_install_buf);

	free(p);
}

+
static int
+
plist_parse(struct plist *pplist, FILE *f)
+
{
+
	int ret, rc = EPKG_OK;
+
	size_t linecap = 0;
+
	ssize_t linelen;
+
	char *line = NULL;
+

+
	while ((linelen = getline(&line, &linecap, f)) > 0) {
+
		if (line[linelen - 1] == '\n')
+
			line[linelen - 1] = '\0';
+
		ret = plist_parse_line(pplist, line);
+
		if (ret != EPKG_OK && rc == EPKG_OK)
+
			rc = ret;
+
	}
+
	free(line);
+

+
	return (rc);
+
}
+

+
static int
+
open_directory_of(const char *file)
+
{
+
	char path[MAXPATHLEN];
+
	char *walk;
+

+
	if (strchr(file, '/') == NULL) {
+
		if (getcwd(path, MAXPATHLEN) == NULL) {
+
			pkg_emit_error("Unable to determine current location");
+
			return (-1);
+
		}
+
		return (open(path, O_DIRECTORY));
+
	}
+
	strlcpy(path, file, sizeof(path));
+
	walk = strrchr(path, '/');
+
	*walk = '\0';
+
	return (open(path, O_DIRECTORY));
+
}
+

+
int
+
include_plist(struct plist *p, char *name, struct file_attr *a __unused)
+
{
+
	FILE *f;
+
	int fd;
+
	int rc;
+

+
	if (p->in_include) {
+
		pkg_emit_error("Inside in @include it is not allowed to reuse @include");
+
		return (EPKG_FATAL);
+
	}
+
	p->in_include = true;
+

+
	fd = openat(p->plistdirfd, name, O_RDONLY);
+
	if (fd == -1) {
+
		pkg_emit_errno("Inpossible to include", name);
+
		return (EPKG_FATAL);
+
	}
+
	f = fdopen(fd, "r");
+
	if (f == NULL) {
+
		pkg_emit_errno("Inpossible to include", name);
+
		close(fd);
+
		return (EPKG_FATAL);
+
	}
+

+
	rc = plist_parse(p, f);
+

+
	fclose(f);
+
	return (rc);
+
}
+

int
ports_parse_plist(struct pkg *pkg, const char *plist, const char *stage)
{
-
	char *line = NULL;
-
	int ret, rc = EPKG_OK;
+
	int rc = EPKG_OK;
	struct plist *pplist;
	FILE *plist_f;
-
	size_t linecap = 0;
-
	ssize_t linelen;

	assert(pkg != NULL);
	assert(plist != NULL);
@@ -1254,21 +1445,19 @@ ports_parse_plist(struct pkg *pkg, const char *plist, const char *stage)
	if ((pplist = plist_new(pkg, stage)) == NULL)
		return (EPKG_FATAL);

-
	if ((plist_f = fopen(plist, "r")) == NULL) {
-
		pkg_emit_error("Unable to open plist file: %s", plist);
+
	pplist->plistdirfd = open_directory_of(plist);
+
	if (pplist->plistdirfd == -1) {
+
		pkg_emit_error("impossible to open the directory where the plist is", plist);
		plist_free(pplist);
		return (EPKG_FATAL);
	}
-

-
	while ((linelen = getline(&line, &linecap, plist_f)) > 0) {
-
		if (line[linelen - 1] == '\n')
-
			line[linelen - 1] = '\0';
-
		ret = plist_parse_line(pplist, line);
-
		if (rc == EPKG_OK)
-
			rc = ret;
+
	if ((plist_f = fopen(plist, "re")) == NULL) {
+
		pkg_emit_error("Unable to open plist file: %s", plist);
+
		plist_free(pplist);
+
		return (EPKG_FATAL);
	}

-
	free(line);
+
	rc = plist_parse(pplist, plist_f);

	pkg->flatsize = pplist->flatsize;

@@ -1294,7 +1483,7 @@ pkg_add_port(struct pkgdb *db, struct pkg *pkg, const char *input_path,
{
	const char *location;
	int rc = EPKG_OK;
-
	UT_string *message;
+
	xstring *message;
	struct pkg_message *msg;

	if (pkg_is_installed(db, pkg->name) != EPKG_END) {
@@ -1338,18 +1527,19 @@ pkg_add_port(struct pkgdb *db, struct pkg *pkg, const char *input_path,
	if (rc == EPKG_OK) {
		pkg_emit_install_finished(pkg, NULL);
		if (pkg->message != NULL)
-
			utstring_new(message);
+
			message = xstring_new();
		LL_FOREACH(pkg->message, msg) {
			if (msg->type == PKG_MESSAGE_ALWAYS ||
			    msg->type == PKG_MESSAGE_INSTALL) {
-
				utstring_printf(message, "%s\n", msg->str);
+
				fprintf(message->fp, "%s\n", msg->str);
			}
		}
		if (pkg->message != NULL) {
-
			if (utstring_len(message) > 0) {
-
				pkg_emit_message(utstring_body(message));
+
			fflush(message->fp);
+
			if (message->buf[0] != '\0') {
+
				pkg_emit_message(message->buf);
			}
-
			utstring_free(message);
+
			xstring_free(message);
		}
	}

modified libpkg/pkg_printf.c
@@ -1,6 +1,6 @@
/*
 * Copyright (c) 2012-2015 Matthew Seaman <matthew@FreeBSD.org>
-
 * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2014-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
@@ -42,9 +42,9 @@
#include <string.h>
#include <time.h>
#include <utlist.h>
-
#include <utstring.h>

#include "pkg.h"
+
#include <xstring.h>
#include <private/pkg_printf.h>
#include <private/pkg.h>

@@ -165,6 +165,7 @@
 *
 * z  pkg          short checksum
 */
+
static xstring *pkg_xstring_vprintf(xstring * restrict buf, const char * restrict format, va_list ap);

struct pkg_printf_fmt {
	char	         fmt_main;
@@ -172,7 +173,7 @@ struct pkg_printf_fmt {
	bool		 has_trailer;
	bool		 struct_pkg; /* or else a sub-type? */
	unsigned	 context;
-
	UT_string	*(*fmt_handler)(UT_string *, const void *,
+
	xstring	*(*fmt_handler)(xstring *, const void *,
					struct percent_esc *);
};

@@ -852,8 +853,8 @@ static const struct pkg_printf_fmt fmt[] = {
 * packages.  Optionally accepts per-field format in %{ %| %} Default
 * %{%An: %Av\n%|%}
 */  
-
UT_string *
-
format_annotations(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_annotations(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;
	struct pkg_kv		*kv;
@@ -866,12 +867,14 @@ format_annotations(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%An: %Av\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		LL_FOREACH(pkg->annotations, kv) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     kv, count, PP_A);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     kv, count, PP_A);
			count++;
		}
@@ -882,8 +885,8 @@ format_annotations(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %An -- Annotation tag name.
 */
-
UT_string *
-
format_annotation_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_annotation_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_kv	*kv = data;

@@ -893,8 +896,8 @@ format_annotation_name(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Av -- Annotation value.
 */
-
UT_string *
-
format_annotation_value(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_annotation_value(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_kv	*kv = data;

@@ -906,8 +909,8 @@ format_annotation_value(UT_string *buf, const void *data, struct percent_esc *p)
 * binaries in the pkg.  Optionally accepts per-field format in %{ %|
 * %}.  Default %{%Bn\n%|%}
 */
-
UT_string *
-
format_shlibs_required(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_shlibs_required(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -920,12 +923,14 @@ format_shlibs_required(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Bn\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_shlibs_required(pkg, &buffer) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     buffer, count, PP_B);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     buffer, count, PP_B);
			count++;
		}
@@ -937,8 +942,8 @@ format_shlibs_required(UT_string *buf, const void *data, struct percent_esc *p)
 * %Bn -- Required Shared Library name or %bn -- Provided Shared
 * Library name
 */
-
UT_string *
-
format_shlib_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_shlib_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const char	*shlib = data;

@@ -951,8 +956,8 @@ format_shlib_name(UT_string *buf, const void *data, struct percent_esc *p)
 * Optionally accepts per-field format in %{ %| %}, where %n is
 * replaced by the category name.  Default %{%Cn%|, %}
 */
-
UT_string *
-
format_categories(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_categories(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;
	int			 count = 0;
@@ -964,12 +969,14 @@ format_categories(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Cn", ", ");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		kh_each_value(pkg->categories, cat, {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
				    cat, count, PP_C);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt), cat,
+
			iterate_item(buf, pkg, p->item_fmt->buf, cat,
			    count, PP_C);
			count++;
		});
@@ -980,8 +987,8 @@ format_categories(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Cn -- Category name.
 */
-
UT_string *
-
format_category_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_category_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const char *cat = data;

@@ -994,8 +1001,8 @@ format_category_name(UT_string *buf, const void *data, struct percent_esc *p)
 * %{ %| %}, where %Dn is replaced by the directory name.  Default
 * %{%Dn\n%|%}
 */
-
UT_string *
-
format_directories(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_directories(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1008,12 +1015,14 @@ format_directories(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Dn\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_dirs(pkg, &dir) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     dir, count, PP_D);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     dir, count, PP_D);
			count++;
		}
@@ -1024,8 +1033,8 @@ format_directories(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Dg -- Directory group. TODO: numeric gid
 */
-
UT_string *
-
format_directory_group(UT_string *buf, const void *data,
+
xstring *
+
format_directory_group(xstring *buf, const void *data,
		       struct percent_esc *p)
{
	const struct pkg_dir	*dir = data;
@@ -1036,8 +1045,8 @@ format_directory_group(UT_string *buf, const void *data,
/*
 * %Dn -- Directory path name.
 */
-
UT_string *
-
format_directory_path(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_directory_path(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_dir	*dir = data;

@@ -1047,8 +1056,8 @@ format_directory_path(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Dp -- Directory permissions.
 */
-
UT_string *
-
format_directory_perms(UT_string *buf, const void *data,
+
xstring *
+
format_directory_perms(xstring *buf, const void *data,
		       struct percent_esc *p)
{
	const struct pkg_dir	*dir = data;
@@ -1059,8 +1068,8 @@ format_directory_perms(UT_string *buf, const void *data,
/*
 * %Du -- Directory user. TODO: numeric UID
 */
-
UT_string *
-
format_directory_user(UT_string *buf, const void *data,
+
xstring *
+
format_directory_user(xstring *buf, const void *data,
		      struct percent_esc *p)
{
	const struct pkg_dir	*dir = data;
@@ -1074,8 +1083,8 @@ format_directory_user(UT_string *buf, const void *data,
 * %}, where %n is replaced by the filename, %s by the checksum, etc.
 * Default %{%Fn\n%|%}
 */
-
UT_string *
-
format_files(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_files(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1088,12 +1097,14 @@ format_files(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Fn\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		LL_FOREACH(pkg->files, file) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     file, count, PP_F);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     file, count, PP_F);
			count++;
		}
@@ -1104,8 +1115,8 @@ format_files(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Fg -- File group.
 */
-
UT_string *
-
format_file_group(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_file_group(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_file	*file = data;

@@ -1115,8 +1126,8 @@ format_file_group(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Fn -- File path name.
 */
-
UT_string *
-
format_file_path(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_file_path(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_file	*file = data;

@@ -1126,8 +1137,8 @@ format_file_path(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Fp -- File permissions.
 */
-
UT_string *
-
format_file_perms(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_file_perms(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_file	*file = data;

@@ -1137,8 +1148,8 @@ format_file_perms(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Fs -- File SHA256 Checksum.
 */
-
UT_string *
-
format_file_sha256(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_file_sha256(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_file	*file = data;

@@ -1148,8 +1159,8 @@ format_file_sha256(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Fu -- File user.
 */
-
UT_string *
-
format_file_user(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_file_user(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_file	*file = data;

@@ -1162,8 +1173,8 @@ format_file_user(UT_string *buf, const void *data, struct percent_esc *p)
 * groupname or %#Gn by the gid -- a line from
 * /etc/group. Default %{%Gn\n%|%}
 */
-
UT_string *
-
format_groups(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_groups(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1176,12 +1187,14 @@ format_groups(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Gn\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while(pkg_groups(pkg, &group) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     group, count, PP_G);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg,p->item_fmt->buf,
				     group, count, PP_G);
			count++;
		}
@@ -1192,8 +1205,8 @@ format_groups(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Gn -- Group name.
 */
-
UT_string *
-
format_group_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_group_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const char	*group = data;

@@ -1203,8 +1216,8 @@ format_group_name(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %I -- Row counter (integer*). Usually used only in per-field format.
 */
-
UT_string *
-
format_row_counter(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_row_counter(xstring *buf, const void *data, struct percent_esc *p)
{
	const int *counter = data;

@@ -1216,8 +1229,8 @@ format_row_counter(UT_string *buf, const void *data, struct percent_esc *p)
 * following per-field format in %{ %| %} where %Ln is replaced by the
 * license name and %l by the license logic.  Default %{%n%| %l %}
 */
-
UT_string *
-
format_licenses(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_licenses(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;
	char			*lic;
@@ -1229,12 +1242,14 @@ format_licenses(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Ln", " %l ");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		kh_each_value(pkg->licenses, lic, {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
				    lic, count, PP_L);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt), lic,
+
			iterate_item(buf, pkg, p->item_fmt->buf, lic,
			    count, PP_L);
			count++;
		});
@@ -1245,8 +1260,8 @@ format_licenses(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Ln -- License name.
 */
-
UT_string *
-
format_license_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_license_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const char *lic = data;

@@ -1256,52 +1271,56 @@ format_license_name(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %M -- Pkg message. string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_message(UT_string *buffer, const void *data, struct percent_esc *p)
+
xstring *
+
format_message(xstring *buffer, const void *data, struct percent_esc *p)
{
-
	UT_string		*buf, *bufmsg;
+
	xstring		*buf, *bufmsg = NULL;
	const struct pkg	*pkg = data;
	struct pkg_message	*msg;
	char			*message;

-
	utstring_new(bufmsg);
	LL_FOREACH(pkg->message, msg) {
-
		if (utstring_len(bufmsg) > 0)
-
			utstring_printf(bufmsg, "%c", '\n');
+
		if (bufmsg == NULL) {
+
			bufmsg = xstring_new();
+
		} else {
+
			fputc('\n', bufmsg->fp);
+
		}
		switch(msg->type) {
		case PKG_MESSAGE_ALWAYS:
-
			utstring_printf(bufmsg, "Always:\n");
+
			fprintf(bufmsg->fp, "Always:\n");
			break;
		case PKG_MESSAGE_UPGRADE:
-
			utstring_printf(bufmsg, "On upgrade");
+
			fprintf(bufmsg->fp, "On upgrade");
			if (msg->minimum_version != NULL ||
			    msg->maximum_version != NULL) {
-
				utstring_printf(bufmsg, " from %s", pkg->name);
+
				fprintf(bufmsg->fp, " from %s", pkg->name);
			}
			if (msg->minimum_version != NULL) {
-
				utstring_printf(bufmsg, ">%s", msg->minimum_version);
+
				fprintf(bufmsg->fp, ">%s", msg->minimum_version);
			}
			if (msg->maximum_version != NULL) {
-
				utstring_printf(bufmsg, "<%s", msg->maximum_version);
+
				fprintf(bufmsg->fp, "<%s", msg->maximum_version);
			}
-
			utstring_printf(bufmsg, ":\n");
+
			fprintf(bufmsg->fp, ":\n");
			break;
		case PKG_MESSAGE_INSTALL:
-
			utstring_printf(bufmsg, "On install:\n");
+
			fprintf(bufmsg->fp, "On install:\n");
			break;
		case PKG_MESSAGE_REMOVE:
-
			utstring_printf(bufmsg, "On remove:\n");
+
			fprintf(bufmsg->fp, "On remove:\n");
			break;
		}
-
		utstring_printf(bufmsg, "%s\n", msg->str);
+
		fprintf(bufmsg->fp, "%s\n", msg->str);
	}
-
	if (utstring_len(bufmsg) == 0)
+
	if (bufmsg == NULL)
		message = NULL;
-
	else
-
		message = utstring_body(bufmsg);
+
	else {
+
		fflush(bufmsg->fp);
+
		message = bufmsg->buf;
+
	}

	buf = string_val(buffer, message, p);
-
	utstring_free(bufmsg);
+
	xstring_free(bufmsg);

	return (buf);
}
@@ -1309,8 +1328,8 @@ format_message(UT_string *buffer, const void *data, struct percent_esc *p)
/*
 * %N -- Repository identity. string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_repo_ident(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_repo_ident(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;
	const char		*reponame;
@@ -1329,8 +1348,8 @@ format_repo_ident(UT_string *buf, const void *data, struct percent_esc *p)
 * following per-field format in %{ %| %}, where %On is replaced by the
 * option name and %Ov by the value.  Default %{%On %Ov\n%|%}
 */ 
-
UT_string *
-
format_options(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_options(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1343,12 +1362,14 @@ format_options(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%On %Ov\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_options(pkg, &opt) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     opt, count, PP_O);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     opt, count, PP_O);
			count++;
		}
@@ -1359,8 +1380,8 @@ format_options(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %On -- Option name.
 */
-
UT_string *
-
format_option_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_option_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_option	*option = data;

@@ -1370,8 +1391,8 @@ format_option_name(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Ov -- Option value.
 */
-
UT_string *
-
format_option_value(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_option_value(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_option	*option = data;

@@ -1381,8 +1402,8 @@ format_option_value(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Od -- Option default value.
 */
-
UT_string *
-
format_option_default(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_option_default(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_option	*option = data;

@@ -1392,8 +1413,8 @@ format_option_default(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %OD -- Option description
 */
-
UT_string *
-
format_option_description(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_option_description(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg_option	*option = data;

@@ -1403,8 +1424,8 @@ format_option_description(UT_string *buf, const void *data, struct percent_esc *
/*
 * %Q -- pkg architecture a.k.a ABI string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_altabi(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_altabi(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1414,8 +1435,8 @@ format_altabi(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %R -- Repo path. string.
 */
-
UT_string *
-
format_repo_path(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_repo_path(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1425,8 +1446,8 @@ format_repo_path(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %S -- Character string.
 */
-
UT_string *
-
format_char_string(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_char_string(xstring *buf, const void *data, struct percent_esc *p)
{
	const char	*charstring = data;

@@ -1439,8 +1460,8 @@ format_char_string(UT_string *buf, const void *data, struct percent_esc *p)
 * username or %#Un by the uid -- a line from
 * /etc/passwd. Default %{%Un\n%|%}
 */
-
UT_string *
-
format_users(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_users(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1453,12 +1474,14 @@ format_users(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Un\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_users(pkg, &user) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     user, count, PP_U);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     user, count, PP_U);
			count++;
		}
@@ -1469,8 +1492,8 @@ format_users(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Un -- User name.
 */
-
UT_string *
-
format_user_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_user_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const char	*user = data;

@@ -1480,8 +1503,8 @@ format_user_name(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %V -- Old package version. string. Accepts field width, left align
 */
-
UT_string *
-
format_old_version(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_old_version(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1491,8 +1514,8 @@ format_old_version(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %X -- Package checksum. string. Accepts field width, left align
 */
-
UT_string *
-
format_int_checksum(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_int_checksum(xstring *buf, const void *data, struct percent_esc *p)
{
	struct pkg	*pkg = (struct pkg *)data;

@@ -1505,8 +1528,8 @@ format_int_checksum(UT_string *buf, const void *data, struct percent_esc *p)
 * binaries in the pkg.  Optionally accepts per-field format in %{ %|
 * %}.  Default %{%Yn\n%|%}
 */
-
UT_string *
-
format_required(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_required(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1519,12 +1542,14 @@ format_required(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%Yn\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_requires(pkg, &provide) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     provide, count, PP_Y);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     provide, count, PP_Y);
			count++;
		}
@@ -1535,8 +1560,8 @@ format_required(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %Yn -- Required name or %yn -- Provided name
 */
-
UT_string *
-
format_provide_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_provide_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const char	*provide = data;

@@ -1547,8 +1572,8 @@ format_provide_name(UT_string *buf, const void *data, struct percent_esc *p)
 * Standard form: 0, 1.  Alternate form1: no, yes.  Alternate form2:
 * false, true
 */
-
UT_string *
-
format_autoremove(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_autoremove(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1561,8 +1586,8 @@ format_autoremove(UT_string *buf, const void *data, struct percent_esc *p)
 * binaries in the pkg.  Optionally accepts per-field format in %{ %|
 * %}, where %n is replaced by the shlib name.  Default %{%bn\n%|%}
 */
-
UT_string *
-
format_shlibs_provided(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_shlibs_provided(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1575,12 +1600,14 @@ format_shlibs_provided(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%bn\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_shlibs_provided(pkg, &shlib) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     shlib, count, PP_b);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     shlib, count, PP_b);
			count++;
		}
@@ -1591,8 +1618,8 @@ format_shlibs_provided(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %c -- Comment. string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_comment(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_comment(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1604,8 +1631,8 @@ format_comment(UT_string *buf, const void *data, struct percent_esc *p)
 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
 * formats. Defaults to printing "%dn-%dv\n" for each dependency.
 */
-
UT_string *
-
format_dependencies(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_dependencies(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1618,12 +1645,14 @@ format_dependencies(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%dn-%dv\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_deps(pkg, &dep) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     dep, count, PP_d);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     dep, count, PP_d);
			count++;
		}
@@ -1634,8 +1663,8 @@ format_dependencies(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %dk -- Dependency lock status or %rk -- Requirement lock status.
 */
-
UT_string *
-
format_dependency_lock(UT_string *buf, const void *data,
+
xstring *
+
format_dependency_lock(xstring *buf, const void *data,
		       struct percent_esc *p)
{
	const struct pkg_dep	*dep = data;
@@ -1646,8 +1675,8 @@ format_dependency_lock(UT_string *buf, const void *data,
/*
 * %dn -- Dependency name or %rn -- Requirement name.
 */
-
UT_string *
-
format_dependency_name(UT_string *buf, const void *data,
+
xstring *
+
format_dependency_name(xstring *buf, const void *data,
		       struct percent_esc *p)
{
	const struct pkg_dep	*dep = data;
@@ -1658,8 +1687,8 @@ format_dependency_name(UT_string *buf, const void *data,
/*
 * %do -- Dependency origin or %ro -- Requirement origin.
 */
-
UT_string *
-
format_dependency_origin(UT_string *buf, const void *data,
+
xstring *
+
format_dependency_origin(xstring *buf, const void *data,
			 struct percent_esc *p)
{
	const struct pkg_dep	*dep = data;
@@ -1670,8 +1699,8 @@ format_dependency_origin(UT_string *buf, const void *data,
/*
 * %dv -- Dependency version or %rv -- Requirement version.
 */
-
UT_string *
-
format_dependency_version(UT_string *buf, const void *data,
+
xstring *
+
format_dependency_version(xstring *buf, const void *data,
			  struct percent_esc *p)
{
	const struct pkg_dep	*dep = data;
@@ -1682,8 +1711,8 @@ format_dependency_version(UT_string *buf, const void *data,
/*
 * %e -- Description. string. Accepts field-width, left-align
 */
-
UT_string *
-
format_description(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_description(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1695,8 +1724,8 @@ format_description(UT_string *buf, const void *data, struct percent_esc *p)
 * Standard form: 0, 1.  Alternate form1: no, yes.  Alternate form2:
 * false, true
 */
-
UT_string *
-
format_lock_status(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_lock_status(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1708,8 +1737,8 @@ format_lock_status(UT_string *buf, const void *data, struct percent_esc *p)
 * Standard form: and, or, single. Alternate form 1: &, |, ''.
 * Alternate form 2: &&, ||, ==
 */
-
UT_string *
-
format_license_logic(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_license_logic(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1719,8 +1748,8 @@ format_license_logic(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %m -- Maintainer e-mail address. string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_maintainer(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_maintainer(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1730,8 +1759,8 @@ format_maintainer(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %n -- Package name. string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_name(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_name(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1741,8 +1770,8 @@ format_name(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %o -- Package origin. string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_origin(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_origin(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1752,8 +1781,8 @@ format_origin(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %p -- Installation prefix. string. Accepts field-width, left-align
 */
-
UT_string *
-
format_prefix(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_prefix(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1763,8 +1792,8 @@ format_prefix(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %q -- pkg architecture a.k.a ABI string.  Accepts field-width, left-align
 */
-
UT_string *
-
format_architecture(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_architecture(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1776,8 +1805,8 @@ format_architecture(UT_string *buf, const void *data, struct percent_esc *p)
 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
 * formats. Defaults to printing "%{%rn-%rv\n%|%}" for each dependency.
 */
-
UT_string *
-
format_requirements(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_requirements(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1790,12 +1819,14 @@ format_requirements(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%rn-%rv\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_rdeps(pkg, &req) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     req, count, PP_r);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     req, count, PP_r);
			count++;
		}
@@ -1810,8 +1841,8 @@ format_requirements(UT_string *buf, const void *data, struct percent_esc *p)
 * exponents (k, M, G).  Alternate form 2, ditto, but using binary
 * scale prefixes (ki, Mi, Gi etc.)
 */
-
UT_string *
-
format_flatsize(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_flatsize(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1824,21 +1855,22 @@ format_flatsize(UT_string *buf, const void *data, struct percent_esc *p)
 * format string in %{ %}.  Default is to print seconds-since-epoch as
 * an integer applying our integer format modifiers.
 */
-
UT_string *
-
format_install_tstamp(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_install_tstamp(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

-
	if (utstring_len(p->item_fmt) == 0)
+
	fflush(p->item_fmt->fp);
+
	if (strlen(p->item_fmt->buf) == 0)
		return (int_val(buf, pkg->timestamp, p));
	else {
		char	 buffer[1024];
		time_t	 tsv;

		tsv = (time_t)pkg->timestamp;
-
		strftime(buffer, sizeof(buffer), utstring_body(p->item_fmt),
+
		strftime(buffer, sizeof(buffer), p->item_fmt->buf,
			 localtime(&tsv));
-
		utstring_printf(buf, "%s", buffer);
+
		fprintf(buf->fp, "%s", buffer);
	}
	return (buf);
}
@@ -1846,8 +1878,8 @@ format_install_tstamp(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %v -- Package version. string. Accepts field width, left align
 */
-
UT_string *
-
format_version(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_version(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1857,8 +1889,8 @@ format_version(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %u -- Package checksum. string. Accepts field width, left align
 */
-
UT_string *
-
format_checksum(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_checksum(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1868,8 +1900,8 @@ format_checksum(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %w -- Home page URL.  string.  Accepts field width, left align
 */
-
UT_string *
-
format_home_url(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_home_url(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1883,8 +1915,8 @@ format_home_url(UT_string *buf, const void *data, struct percent_esc *p)
 * exponents (k, M, G).  Alternate form 2, ditto, but using binary
 * scale prefixes (ki, Mi, Gi etc.)
 */
-
UT_string *
-
format_pkgsize(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_pkgsize(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1896,8 +1928,8 @@ format_pkgsize(UT_string *buf, const void *data, struct percent_esc *p)
 * binaries in the pkg.  Optionally accepts per-field format in %{ %|
 * %}.  Default %{%yn\n%|%}
 */
-
UT_string *
-
format_provided(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_provided(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

@@ -1910,12 +1942,14 @@ format_provided(UT_string *buf, const void *data, struct percent_esc *p)
		set_list_defaults(p, "%yn\n", "");

		count = 1;
+
		fflush(p->sep_fmt->fp);
+
		fflush(p->item_fmt->fp);
		while (pkg_provides(pkg, &provide) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(buf, pkg, utstring_body(p->sep_fmt),
+
				iterate_item(buf, pkg, p->sep_fmt->buf,
					     provide, count, PP_y);

-
			iterate_item(buf, pkg, utstring_body(p->item_fmt),
+
			iterate_item(buf, pkg, p->item_fmt->buf,
				     provide, count, PP_y);
			count++;
		}
@@ -1926,8 +1960,8 @@ format_provided(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %z -- Package short checksum. string. Accepts field width, left align
 */
-
UT_string *
-
format_short_checksum(UT_string *buf, const void *data, struct percent_esc *p)
+
xstring *
+
format_short_checksum(xstring *buf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;
	char	 csum[PKG_FILE_CKSUM_CHARS + 1];
@@ -1945,11 +1979,11 @@ format_short_checksum(UT_string *buf, const void *data, struct percent_esc *p)
/*
 * %% -- Output a literal '%' character
 */
-
UT_string *
-
format_literal_percent(UT_string *buf, __unused const void *data,
+
xstring *
+
format_literal_percent(xstring *buf, __unused const void *data,
		       __unused struct percent_esc *p)
{
-
	utstring_printf(buf, "%c", '%');
+
	fputc('%', buf->fp);
	return (buf);
}

@@ -1957,11 +1991,11 @@ format_literal_percent(UT_string *buf, __unused const void *data,
 * Unknown format code -- return NULL to signal upper layers to pass
 * the text through unchanged.
 */
-
UT_string *
-
format_unknown(UT_string *buf, __unused const void *data,
+
xstring *
+
format_unknown(xstring *buf, __unused const void *data,
		       __unused struct percent_esc *p)
{
-
	utstring_printf(buf, "%c", '%');
+
	fputc('%', buf->fp);
	return (NULL);
}

@@ -1973,8 +2007,8 @@ new_percent_esc(void)
	struct percent_esc	*p;

	p = xcalloc(1, sizeof(struct percent_esc));
-
	utstring_new(p->item_fmt);
-
	utstring_new(p->sep_fmt);
+
	p->item_fmt = xstring_new();
+
	p->sep_fmt = xstring_new();
	return (p);
}

@@ -1984,8 +2018,8 @@ clear_percent_esc(struct percent_esc *p)
	p->flags = 0;
	p->width = 0;
	p->trailer_status = 0;
-
	utstring_clear(p->item_fmt);
-
	utstring_clear(p->sep_fmt);
+
	xstring_reset(p->item_fmt);
+
	xstring_reset(p->sep_fmt);

	p->fmt_code = '\0';

@@ -1997,9 +2031,9 @@ free_percent_esc(struct percent_esc *p)
{
	if (p) {
		if (p->item_fmt)
-
			utstring_free(p->item_fmt);
+
			xstring_free(p->item_fmt);
		if (p->sep_fmt)
-
			utstring_free(p->sep_fmt);
+
			xstring_free(p->sep_fmt);
		free(p);
	}
	return;
@@ -2074,8 +2108,8 @@ gen_format(char *buf, size_t buflen, unsigned flags, const char *tail)
}


-
UT_string *
-
human_number(UT_string *buf, int64_t number, struct percent_esc *p)
+
xstring *
+
human_number(xstring *buf, int64_t number, struct percent_esc *p)
{
	double		 num;
	int		 sign;
@@ -2150,17 +2184,17 @@ human_number(UT_string *buf, int64_t number, struct percent_esc *p)
			precision = 0;
	}

-
	utstring_printf(buf, format, width, precision, num * sign);
+
	fprintf(buf->fp, format, width, precision, num * sign);

	if (scale > 0)
-
		utstring_printf(buf, "%s",
+
		fprintf(buf->fp, "%s",
		    bin_scale ? bin_pfx[scale] : si_pfx[scale]);

	return (buf);
}

-
UT_string *
-
string_val(UT_string *buf, const char *str, struct percent_esc *p)
+
xstring *
+
string_val(xstring *buf, const char *str, struct percent_esc *p)
{
	char	format[16];

@@ -2177,12 +2211,12 @@ string_val(UT_string *buf, const char *str, struct percent_esc *p)
	if (gen_format(format, sizeof(format), p->flags, "s") == NULL)
		return (NULL);

-
	utstring_printf(buf, format, p->width, str);
+
	fprintf(buf->fp, format, p->width, str);
	return (buf);
}

-
UT_string *
-
int_val(UT_string *buf, int64_t value, struct percent_esc *p)
+
xstring *
+
int_val(xstring *buf, int64_t value, struct percent_esc *p)
{
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
		return (human_number(buf, value, p));
@@ -2193,13 +2227,13 @@ int_val(UT_string *buf, int64_t value, struct percent_esc *p)
		    == NULL)
			return (NULL);

-
		utstring_printf(buf, format, p->width, value);
+
		fprintf(buf->fp, format, p->width, value);
	}
	return (buf);
}

-
UT_string *
-
bool_val(UT_string *buf, bool value, struct percent_esc *p)
+
xstring *
+
bool_val(xstring *buf, bool value, struct percent_esc *p)
{
	static const char	*boolean_str[2][3] = {
		[false]	= { "false", "no",  ""    },
@@ -2219,8 +2253,8 @@ bool_val(UT_string *buf, bool value, struct percent_esc *p)
	return (string_val(buf, boolean_str[value][alternate], p));
}

-
UT_string *
-
mode_val(UT_string *buf, mode_t mode, struct percent_esc *p)
+
xstring *
+
mode_val(xstring *buf, mode_t mode, struct percent_esc *p)
{
	/*
         * Print mode as an octal integer '%o' by default.
@@ -2256,13 +2290,13 @@ mode_val(UT_string *buf, mode_t mode, struct percent_esc *p)
		    == NULL)
			return (NULL);

-
		utstring_printf(buf, format, p->width, mode);
+
		fprintf(buf->fp, format, p->width, mode);
	}
	return (buf);
}

-
UT_string *
-
liclog_val(UT_string *buf, lic_t licenselogic, struct percent_esc *p)
+
xstring *
+
liclog_val(xstring *buf, lic_t licenselogic, struct percent_esc *p)
{
	int			 alternate;
	int			 llogic = PP_LIC_SINGLE;
@@ -2297,8 +2331,8 @@ liclog_val(UT_string *buf, lic_t licenselogic, struct percent_esc *p)
	return (string_val(buf, liclog_str[llogic][alternate], p));
}

-
UT_string *
-
list_count(UT_string *buf, int64_t count, struct percent_esc *p)
+
xstring *
+
list_count(xstring *buf, int64_t count, struct percent_esc *p)
{
	/* Convert to 0 or 1 for %?X */
	if (p->flags & PP_ALTERNATE_FORM1)
@@ -2315,18 +2349,18 @@ set_list_defaults(struct percent_esc *p, const char *item_fmt,
		  const char *sep_fmt)
{
	if ((p->trailer_status & ITEM_FMT_SET) != ITEM_FMT_SET) {
-
		utstring_printf(p->item_fmt, "%s", item_fmt);
+
		fprintf(p->item_fmt->fp, "%s", item_fmt);
		p->trailer_status |= ITEM_FMT_SET;
	}
	if ((p->trailer_status & SEP_FMT_SET) != SEP_FMT_SET) {
-
		utstring_printf(p->sep_fmt, "%s", sep_fmt);
+
		fprintf(p->sep_fmt->fp, "%s", sep_fmt);
		p->trailer_status |= SEP_FMT_SET;
	}
	return (p);
}

-
UT_string *
-
iterate_item(UT_string *buf, const struct pkg *pkg, const char *format,
+
xstring *
+
iterate_item(xstring *buf, const struct pkg *pkg, const char *format,
	     const void *data, int count, unsigned context)
{
	const char		*f;
@@ -2338,7 +2372,7 @@ iterate_item(UT_string *buf, const struct pkg *pkg, const char *format,
	p = new_percent_esc();

	if (p == NULL) {
-
		utstring_clear(buf);
+
		xstring_reset(buf);
		return (buf);	/* Out of memory */
	}

@@ -2351,12 +2385,12 @@ iterate_item(UT_string *buf, const struct pkg *pkg, const char *format,
			f = process_escape(buf, f);
			break;
		default:
-
			utstring_printf(buf, "%c", *f);
+
			fprintf(buf->fp, "%c", *f);
			f++;
			break;
		}
		if (f == NULL) {
-
			utstring_clear(buf);
+
			xstring_reset(buf);
			break;	/* Out of memory */
		}
	}
@@ -2491,7 +2525,8 @@ format_trailer(const char *f, struct percent_esc *p)
				f1 = f2 + 2;
				break;
			}
-
			utstring_printf(p->item_fmt, "%c", *f2);
+
			fputc(*f2, p->item_fmt->fp);
+
			fflush(p->item_fmt->fp);
		}


@@ -2505,7 +2540,8 @@ format_trailer(const char *f, struct percent_esc *p)
					f1 = f2 + 2;
					break;
				}
-
				utstring_printf(p->sep_fmt, "%c", *f2);
+
				fputc(*f2, p->sep_fmt->fp);
+
				fflush(p->sep_fmt->fp);
			}
			
		}
@@ -2513,8 +2549,8 @@ format_trailer(const char *f, struct percent_esc *p)
		if (done) {
			f = f1;
		} else {
-
			utstring_clear(p->item_fmt);
-
			utstring_clear(p->sep_fmt);
+
			xstring_reset(p->item_fmt);
+
			xstring_reset(p->sep_fmt);
		}
	}

@@ -2579,7 +2615,7 @@ parse_format(const char *f, unsigned context, struct percent_esc *p)
}

const char*
-
maybe_read_hex_byte(UT_string *buf, const char *f)
+
maybe_read_hex_byte(xstring *buf, const char *f)
{
	/* Hex escapes are of the form \xNN -- always two hex digits */

@@ -2711,19 +2747,19 @@ maybe_read_hex_byte(UT_string *buf, const char *f)
			break;
		}

-
		utstring_printf(buf, "%c", val);
+
		fputc(val, buf->fp);
		f++;
	} else {
		/* Pass through unchanged if it's not a recognizable
		   hex byte. */
-
		utstring_printf(buf, "%c", '\\');
-
		utstring_printf(buf, "%c", 'x');
+
		fputc('\\', buf->fp);
+
		fputc('x', buf->fp);
	}
	return (f);
}

const char*
-
read_oct_byte(UT_string *buf, const char *f)
+
read_oct_byte(xstring *buf, const char *f)
{
	int	val = 0;
	int	count = 0;
@@ -2765,51 +2801,51 @@ read_oct_byte(UT_string *buf, const char *f)
		f++;
	} 
done:
-
	utstring_printf(buf, "%c", val);
+
	fputc(val, buf->fp);

	return (f);
}

const char *
-
process_escape(UT_string *buf, const char *f)
+
process_escape(xstring *buf, const char *f)
{
	f++;			/* Eat the \ */

	switch (*f) {
	case 'a':
-
		utstring_printf(buf, "%c", '\a');
+
		fputc('\a', buf->fp);
		f++;
		break;
	case 'b':
-
		utstring_printf(buf, "%c", '\b');
+
		fputc('\b', buf->fp);
		f++;
		break;
	case 'f':
-
		utstring_printf(buf, "%c", '\f');
+
		fputc('\f', buf->fp);
		f++;
		break;
	case 'n':
-
		utstring_printf(buf, "%c", '\n');
+
		fputc('\n', buf->fp);
		f++;
		break;
	case 't':
-
		utstring_printf(buf, "%c", '\t');
+
		fputc('\t', buf->fp);
		f++;
		break;
	case 'v':
-
		utstring_printf(buf, "%c", '\v');
+
		fputc('\v', buf->fp);
		f++;
		break;
	case '\'':
-
		utstring_printf(buf, "%c", '\'');
+
		fputc('\'', buf->fp);
		f++;
		break;
	case '"':
-
		utstring_printf(buf, "%c", '"');
+
		fputc('"', buf->fp);
		f++;
		break;
	case '\\':
-
		utstring_printf(buf, "%c", '\\');
+
		fputc('\\', buf->fp);
		f++;
		break;
	case 'x':		/* Hex escape: \xNN */
@@ -2828,7 +2864,7 @@ process_escape(UT_string *buf, const char *f)
	default:		/* If it's not a recognised escape,
				   leave f pointing at the escaped
				   character */
-
		utstring_printf(buf, "%c", '\\');
+
		fputc('\\', buf->fp);
		break;
	}

@@ -2836,12 +2872,12 @@ process_escape(UT_string *buf, const char *f)
}

const char *
-
process_format_trailer(UT_string *buf, struct percent_esc *p,
+
process_format_trailer(xstring *buf, struct percent_esc *p,
		       const char *f, const struct pkg *pkg, 
		       const void *data, int count, unsigned context)
{
	const char		*fstart;
-
	UT_string		*s;
+
	xstring		*s;

	fstart = f;
	f = parse_format(f, context, p);
@@ -2866,10 +2902,10 @@ process_format_trailer(UT_string *buf, struct percent_esc *p,
}

const char *
-
process_format_main(UT_string *buf, struct percent_esc *p,
+
process_format_main(xstring *buf, struct percent_esc *p,
		const char *fstart, const char *fend, void *data)
{
-
	UT_string		*s;
+
	xstring		*s;

	s = fmt[p->fmt_code].fmt_handler(buf, data, p);

@@ -2907,19 +2943,20 @@ pkg_printf(const char * restrict format, ...)
int
pkg_vprintf(const char * restrict format, va_list ap)
{
-
	UT_string	*buf;
+
	xstring	*buf;
	int		 count;

-
	utstring_new(buf);
+
	buf = xstring_new();

	if (buf)
-
		buf = pkg_utstring_vprintf(buf, format, ap);
-
	if (buf && utstring_len(buf) > 0) {
-
		count = printf("%s", utstring_body(buf));
+
		buf = pkg_xstring_vprintf(buf, format, ap);
+
	fflush(buf->fp);
+
	if (buf && strlen(buf->buf) > 0) {
+
		count = printf("%s", buf->buf);
	} else
		count = -1;
	if (buf)
-
		utstring_free(buf);
+
		xstring_free(buf);
	return (count);
}

@@ -2951,19 +2988,20 @@ pkg_fprintf(FILE * restrict stream, const char * restrict format, ...)
int
pkg_vfprintf(FILE * restrict stream, const char * restrict format, va_list ap)
{
-
	UT_string	*buf;
+
	xstring	*buf;
	int		 count;

-
	utstring_new(buf);
+
	buf = xstring_new();

	if (buf)
-
		buf = pkg_utstring_vprintf(buf, format, ap);
-
	if (buf && utstring_len(buf) > 0) {
-
		count = fprintf(stream, "%s", utstring_body(buf));
+
		buf = pkg_xstring_vprintf(buf, format, ap);
+
	fflush(buf->fp);
+
	if (buf && strlen(buf->buf) > 0) {
+
		count = fprintf(stream, "%s", buf->buf);
	} else
		count = -1;
	if (buf)
-
		utstring_free(buf);
+
		xstring_free(buf);
	return (count);
}

@@ -2999,19 +3037,20 @@ pkg_dprintf(int fd, const char * restrict format, ...)
int
pkg_vdprintf(int fd, const char * restrict format, va_list ap)
{
-
	UT_string	*buf;
+
	xstring	*buf;
	int		 count;

-
	utstring_new(buf);
+
	buf = xstring_new();

	if (buf)
-
		buf = pkg_utstring_vprintf(buf, format, ap);
-
	if (buf && utstring_len(buf) > 0) {
-
		count = dprintf(fd, "%s", utstring_body(buf));
+
		buf = pkg_xstring_vprintf(buf, format, ap);
+
	fflush(buf->fp);
+
	if (buf && strlen(buf->buf) > 0) {
+
		count = dprintf(fd, "%s", buf->buf);
	} else 
		count = -1;
	if (buf)
-
		utstring_free(buf);
+
		xstring_free(buf);
	return (count);
}

@@ -3052,19 +3091,20 @@ int
pkg_vsnprintf(char * restrict str, size_t size, const char * restrict format,
	     va_list ap)
{
-
	UT_string	*buf;
+
	xstring	*buf;
	int		 count;

-
	utstring_new(buf);
+
	buf = xstring_new();

	if (buf)
-
		buf = pkg_utstring_vprintf(buf, format, ap);
-
	if (buf && utstring_len(buf) > 0) {
-
		count = snprintf(str, size, "%s", utstring_body(buf));
+
		buf = pkg_xstring_vprintf(buf, format, ap);
+
	fflush(buf->fp);
+
	if (buf && strlen(buf->buf) > 0) {
+
		count = snprintf(str, size, "%s", buf->buf);
	} else
		count = -1;
	if (buf)
-
		utstring_free(buf);
+
		xstring_free(buf);

	return (count);
}
@@ -3103,54 +3143,36 @@ pkg_asprintf(char **ret, const char * restrict format, ...)
int
pkg_vasprintf(char **ret, const char * restrict format, va_list ap)
{
-
	UT_string	*buf;
+
	xstring	*buf;
	int		 count;

-
	utstring_new(buf);
+
	buf = xstring_new();

	if (buf)
-
		buf = pkg_utstring_vprintf(buf, format, ap);
-
	if (buf && utstring_len(buf) > 0) {
-
		count = xasprintf(ret, "%s", utstring_body(buf));
+
		buf = pkg_xstring_vprintf(buf, format, ap);
+
	fflush(buf->fp);
+
	if (buf && strlen(buf->buf) > 0) {
+
		count = xasprintf(ret, "%s", buf->buf);
	} else {
		count = -1;
		*ret = NULL;
	}
	if (buf)
-
		utstring_free(buf);
+
		xstring_free(buf);
	return (count);
}

/**
 * store data from pkg into buf as indicated by the format code format.
-
 * @param buf contains the result
-
 * @param ... Varargs list of struct pkg etc. supplying the data
-
 * @param format String with embedded %-escapes indicating what to output
-
 * @return count of the number of characters in the result
-
 */
-
UT_string *
-
pkg_utstring_printf(UT_string * restrict buf, const char *restrict format, ...)
-
{
-
	va_list		 ap;
-

-
	va_start(ap, format);
-
	buf = pkg_utstring_vprintf(buf, format, ap);
-
	va_end(ap);
-

-
	return (buf);
-
}
-

-
/**
-
 * store data from pkg into buf as indicated by the format code format.
 * This is the core function called by all the other pkg_printf() family.
 * @param buf contains the result
 * @param ap Arglist with struct pkg etc. supplying the data
 * @param format String with embedded %-escapes indicating what to output
 * @return count of the number of characters in the result
 */
-
UT_string *
-
pkg_utstring_vprintf(UT_string * restrict buf, const char * restrict format,
-
		 va_list ap)
+
static xstring *
+
pkg_xstring_vprintf(xstring * restrict buf, const char * restrict format,
+
  va_list ap)
{
	const char		*f, *fend;
	struct percent_esc	*p;
@@ -3163,7 +3185,7 @@ pkg_utstring_vprintf(UT_string * restrict buf, const char * restrict format,
	p = new_percent_esc();

	if (p == NULL) {
-
		utstring_clear(buf);
+
		xstring_reset(buf);
		return (buf);	/* Out of memory */
	}

@@ -3182,12 +3204,12 @@ pkg_utstring_vprintf(UT_string * restrict buf, const char * restrict format,
			f = process_escape(buf, f);
			break;
		default:
-
			utstring_printf(buf, "%c", *f);
+
			fputc(*f, buf->fp);
			f++;
			break;
		}
		if (f == NULL) {
-
			utstring_clear(buf);
+
			xstring_reset(buf);
			break;	/* Error: out of memory */
		}
	}
modified libpkg/pkg_repo.c
@@ -43,7 +43,6 @@
#define _WITH_GETLINE
#include <stdio.h>
#include <stdbool.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
@@ -421,7 +420,7 @@ pkg_repo_parse_sigkeys(const char *in, int inlen, struct sig_cert **sc)
			if (type != 0 && type != 1) {
				/* Invalid type */
				pkg_emit_error("%d is not a valid type for signature_fingerprints"
-
						"output", type);
+
						" output", type);
				return (EPKG_FATAL);
			}
			state = fp_parse_flen;
@@ -431,7 +430,7 @@ pkg_repo_parse_sigkeys(const char *in, int inlen, struct sig_cert **sc)
		case fp_parse_flen:
			if (end - p < sizeof (int)) {
				pkg_emit_error("truncated reply for signature_fingerprints"
-
						"output");
+
						" output");
				return (EPKG_FATAL);
			}
			memcpy(&len, p, sizeof(int));
@@ -442,12 +441,12 @@ pkg_repo_parse_sigkeys(const char *in, int inlen, struct sig_cert **sc)
		case fp_parse_file:
			if (end - p < len || len <= 0) {
				pkg_emit_error("truncated reply for signature_fingerprints"
-
						"output, wanted %d bytes", len);
+
						" output, wanted %d bytes", len);
				return (EPKG_FATAL);
			}
			else if (len >= MAXPATHLEN) {
				pkg_emit_error("filename is incorrect for signature_fingerprints"
-
						"output: %d, wanted 5..%d bytes", type, len);
+
						" output: %d, wanted 5..%d bytes", type, len);
				free(s);
				return (EPKG_FATAL);
			}
@@ -794,6 +793,8 @@ pkg_repo_fetch_meta(struct pkg_repo *repo, time_t *t)
			return (EPKG_FATAL);
		}
		goto load_meta;
+
	} else if (rc == EPKG_UPTODATE) {
+
		return (EPKG_UPTODATE);
	}

	/* TODO: remove this backward compatibility some day */
modified libpkg/pkg_repo_create.c
@@ -45,7 +45,6 @@
#include <string.h>
#include <stdio.h>
#include <stdbool.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
@@ -102,9 +101,9 @@ hash_file(struct pkg_repo_meta *meta, struct pkg *pkg, char *path)
	ext = strrchr(path, '.');

	strlcpy(tmp_name, path, sizeof(tmp_name));
-
	rel_dir = dirname(tmp_name);
+
	rel_dir = get_dirname(tmp_name);
	while (strstr(rel_dir, "/Hashed") != NULL) {
-
		rel_dir = dirname(rel_dir);
+
		rel_dir = get_dirname(rel_dir);
	}
	strlcpy(tmp_name, rel_dir, sizeof(tmp_name));
	rel_dir = (char *)&tmp_name;
@@ -116,9 +115,9 @@ hash_file(struct pkg_repo_meta *meta, struct pkg *pkg, char *path)
			rel_repo++;
	}
	strlcpy(tmp_repo, rel_repo, sizeof(tmp_repo));
-
	rel_repo = dirname(tmp_repo);
+
	rel_repo = get_dirname(tmp_repo);
	while (strstr(rel_repo, "/Hashed") != NULL) {
-
		rel_repo = dirname(rel_repo);
+
		rel_repo = get_dirname(rel_repo);
	}
	strlcpy(tmp_repo, rel_repo, sizeof(tmp_repo));
	rel_repo = (char *)&tmp_repo;
@@ -329,25 +328,25 @@ pkg_create_repo_worker(struct pkg_fts_item *start, size_t nelts,
	struct pkg_manifest_key *keys = NULL;
	char *mdigest = NULL;
	char digestbuf[1024];
-
	UT_string *b;
+
	xstring *b;
	struct iovec iov[2];
	char buf[1024];
	char *w;

-
	utstring_new(b);
+
	b = xstring_new();

	pid = fork();
	switch(pid) {
	case -1:
		pkg_emit_errno("pkg_create_repo_worker", "fork");
-
		utstring_free(b);
+
		xstring_free(b);
		return (EPKG_FATAL);
		break;
	case 0:
		break;
	default:
		/* Parent */
-
		utstring_free(b);
+
		xstring_free(b);
		return (EPKG_OK);
		break;
	}
@@ -388,7 +387,7 @@ pkg_create_repo_worker(struct pkg_fts_item *start, size_t nelts,
			/*
			 * TODO: use pkg_checksum for new manifests
			 */
-
			utstring_clear(b);
+
			xstring_reset(b);
			mdigest = xmalloc(pkg_checksum_type_size(meta->digest_format));

			pkg_emit_manifest_buf(pkg, b, PKG_MANIFEST_EMIT_COMPACT, NULL);
@@ -403,7 +402,8 @@ pkg_create_repo_worker(struct pkg_fts_item *start, size_t nelts,
					goto cleanup;
				}
			}
-
			mlen = utstring_len(b);
+
			fflush(b->fp);
+
			mlen = strlen(b->buf);

			if (flock(mfd, LOCK_EX) == -1) {
				pkg_emit_errno("pkg_create_repo_worker", "flock");
@@ -413,8 +413,8 @@ pkg_create_repo_worker(struct pkg_fts_item *start, size_t nelts,

			mpos = lseek(mfd, 0, SEEK_END);

-
			iov[0].iov_base = utstring_body(b);
-
			iov[0].iov_len = utstring_len(b);
+
			iov[0].iov_base = b->buf;
+
			iov[0].iov_len = mlen;
			iov[1].iov_base = (void *)"\n";
			iov[1].iov_len = 1;

@@ -466,12 +466,12 @@ pkg_create_repo_worker(struct pkg_fts_item *start, size_t nelts,

cleanup:
	pkg_manifest_keys_free(keys);
-
	utstring_free(b);
+
	xstring_free(b);
	close(pip);
	free(mdigest);

	pkg_debug(1, "worker done");
-
	exit(ret);
+
	_exit(ret);
}

static int
@@ -779,12 +779,12 @@ pkg_create_repo(char *path, const char *output_dir, bool filelist,
	/* Write metafile */
	snprintf(repodb, sizeof(repodb), "%s/%s", output_dir,
		"meta");
-
	if ((mfile = fopen(repodb, "w")) != NULL) {
+
	if ((mfile = fopen(repodb, "we")) != NULL) {
		meta_dump = pkg_repo_meta_to_ucl(meta);
		ucl_object_emit_file(meta_dump, UCL_EMIT_CONFIG, mfile);
		fclose(mfile);
		strlcat(repodb, ".conf", sizeof(repodb));
-
		if ((mfile = fopen(repodb, "w")) != NULL) {
+
		if ((mfile = fopen(repodb, "we")) != NULL) {
			ucl_object_emit_file(meta_dump, UCL_EMIT_CONFIG, mfile);
			fclose(mfile);
		} else {
@@ -841,14 +841,16 @@ cleanup:
	return (retcode);
}

-

static int
-
pkg_repo_sign(char *path, char **argv, int argc, UT_string **sig, UT_string **cert)
+
pkg_repo_sign(char *path, char **argv, int argc, char **sig, size_t *siglen,
+
    char **cert)
{
	FILE *fp;
	char *sha256;
-
	UT_string *cmd = NULL;
-
	UT_string *buf = NULL;
+
	xstring *cmd = NULL;
+
	xstring *buf = NULL;
+
	xstring *sigstr = NULL;
+
	xstring *certstr = NULL;
	char *line = NULL;
	size_t linecap = 0;
	ssize_t linelen;
@@ -858,55 +860,58 @@ pkg_repo_sign(char *path, char **argv, int argc, UT_string **sig, UT_string **ce
	if (!sha256)
		return (EPKG_FATAL);

-
	utstring_new(cmd);
+
	cmd = xstring_new();

	for (i = 0; i < argc; i++) {
		if (strspn(argv[i], " \t\n") > 0)
-
			utstring_printf(cmd, " \"%s\" ", argv[i]);
+
			fprintf(cmd->fp, " \"%s\" ", argv[i]);
		else
-
			utstring_printf(cmd, " %s ", argv[i]);
+
			fprintf(cmd->fp, " %s ", argv[i]);
	}

-
	if ((fp = popen(utstring_body(cmd), "r+")) == NULL) {
+
	fflush(cmd->fp);
+
	if ((fp = popen(cmd->buf, "r+")) == NULL) {
		ret = EPKG_FATAL;
		goto done;
	}

	fprintf(fp, "%s\n", sha256);

-
	if (*sig == NULL)
-
		utstring_new(*sig);
-
	if (*cert == NULL)
-
		utstring_new(*cert);
+
	sigstr = xstring_new();
+
	certstr = xstring_new();

	while ((linelen = getline(&line, &linecap, fp)) > 0 ) {
		if (strcmp(line, "SIGNATURE\n") == 0) {
-
			buf = *sig;
+
			buf = sigstr;
			continue;
		} else if (strcmp(line, "CERT\n") == 0) {
-
			buf = *cert;
+
			buf = certstr;
			continue;
		} else if (strcmp(line, "END\n") == 0) {
			break;
		}
-
		if (buf != NULL)
-
			utstring_bincpy(buf, line, linelen);
+
		if (buf != NULL) {
+
			fwrite(line, linelen, 1, buf->fp);
+
		}
	}

+
	*cert = xstring_get(certstr);
+
	fclose(sigstr->fp);
+
	sigstr->size--;
+
	*siglen = sigstr->size;
+
	*sig = sigstr->buf;
+
	free(sigstr);
+

+
	/* remove the latest \n */
+

	if (pclose(fp) != 0) {
		ret = EPKG_FATAL;
		goto done;
	}

-
	if (utstring_body(*sig)[utstring_len(*sig) -1 ] == '\n') {
-
		(*sig)->i--;
-
		(*sig)->d[(*sig)->i] = '\0';
-
	}
-

done:
	free(sha256);
-
	if (cmd)
-
		utstring_free(cmd);
+
	xstring_free(cmd);

	return (ret);
}
@@ -919,8 +924,9 @@ pkg_repo_pack_db(const char *name, const char *archive, char *path,
	struct packing *pack;
	unsigned char *sigret = NULL;
	unsigned int siglen = 0;
+
	size_t signature_len = 0;
	char fname[MAXPATHLEN];
-
	UT_string *sig, *pub;
+
	char *sig, *pub;
	int ret = EPKG_OK;

	sig = NULL;
@@ -940,19 +946,19 @@ pkg_repo_pack_db(const char *name, const char *archive, char *path,
			goto out;
		}
	} else if (argc >= 1) {
-
		if (pkg_repo_sign(path, argv, argc, &sig, &pub) != EPKG_OK) {
+
		if (pkg_repo_sign(path, argv, argc, &sig, &signature_len, &pub) != EPKG_OK) {
			ret = EPKG_FATAL;
			goto out;
		}

		snprintf(fname, sizeof(fname), "%s.sig", name);
-
		if (packing_append_buffer(pack, utstring_body(sig), fname, utstring_len(sig)) != EPKG_OK) {
+
		if (packing_append_buffer(pack, sig, fname, signature_len) != EPKG_OK) {
			ret = EPKG_FATAL;
			goto out;
		}

		snprintf(fname, sizeof(fname), "%s.pub", name);
-
		if (packing_append_buffer(pack, utstring_body(pub), fname, utstring_len(pub)) != EPKG_OK) {
+
		if (packing_append_buffer(pack, pub, fname, strlen(pub)) != EPKG_OK) {
			ret = EPKG_FATAL;
			goto out;
		}
@@ -964,10 +970,8 @@ out:
	packing_finish(pack);
	unlink(path);
	free(sigret);
-
	if (sig != NULL)
-
		utstring_free(sig);
-
	if (pub != NULL)
-
		utstring_free(pub);
+
	free(sig);
+
	free(pub);

	return (ret);
}
modified libpkg/pkg_solve.c
@@ -194,11 +194,11 @@ pkg_solve_problem_free(struct pkg_solve_problem *problem)
} while (0)

static void
-
pkg_print_rule_buf(struct pkg_solve_rule *rule, UT_string *sb)
+
pkg_print_rule_buf(struct pkg_solve_rule *rule, xstring *sb)
{
	struct pkg_solve_item *it = rule->items, *key_elt = NULL;

-
	utstring_printf(sb, "%s rule: ", rule_reasons[rule->reason]);
+
	fprintf(sb->fp, "%s rule: ", rule_reasons[rule->reason]);
	switch(rule->reason) {
	case PKG_RULE_DEPEND:
		LL_FOREACH(rule->items, it) {
@@ -208,25 +208,25 @@ pkg_print_rule_buf(struct pkg_solve_rule *rule, UT_string *sb)
			}
		}
		if (key_elt) {
-
			utstring_printf(sb, "package %s%s depends on: ", key_elt->var->uid,
+
			fprintf(sb->fp, "package %s%s depends on: ", key_elt->var->uid,
				(key_elt->var->unit->pkg->type == PKG_INSTALLED) ? "(l)" : "(r)");
		}
		LL_FOREACH(rule->items, it) {
			if (it != key_elt) {
-
				utstring_printf(sb, "%s%s", it->var->uid,
+
				fprintf(sb->fp, "%s%s", it->var->uid,
					(it->var->unit->pkg->type == PKG_INSTALLED) ? "(l)" : "(r)");
			}
		}
		break;
	case PKG_RULE_UPGRADE_CONFLICT:
-
		utstring_printf(sb, "upgrade local %s-%s to remote %s-%s",
+
		fprintf(sb->fp, "upgrade local %s-%s to remote %s-%s",
			it->var->uid, it->var->unit->pkg->version,
			it->next->var->uid, it->next->var->unit->pkg->version);
		break;
	case PKG_RULE_EXPLICIT_CONFLICT:
-
		utstring_printf(sb, "The following packages conflict with each other: ");
+
		fprintf(sb->fp, "The following packages conflict with each other: ");
		LL_FOREACH(rule->items, it) {
-
			utstring_printf(sb, "%s-%s%s%s", it->var->unit->pkg->uid, it->var->unit->pkg->version,
+
			fprintf(sb->fp, "%s-%s%s%s", it->var->unit->pkg->uid, it->var->unit->pkg->version,
				(it->var->unit->pkg->type == PKG_INSTALLED) ? "(l)" : "(r)",
				it->next ? ", " : "");
		}
@@ -239,21 +239,21 @@ pkg_print_rule_buf(struct pkg_solve_rule *rule, UT_string *sb)
			}
		}
		if (key_elt) {
-
			utstring_printf(sb, "package %s%s depends on a requirement provided by: ",
+
			fprintf(sb->fp, "package %s%s depends on a requirement provided by: ",
				key_elt->var->uid,
				(key_elt->var->unit->pkg->type == PKG_INSTALLED) ? "(l)" : "(r)");
		}
		LL_FOREACH(rule->items, it) {
			if (it != key_elt) {
-
				utstring_printf(sb, "%s%s", it->var->uid,
+
				fprintf(sb->fp, "%s%s", it->var->uid,
					(it->var->unit->pkg->type == PKG_INSTALLED) ? "(l)" : "(r)");
			}
		}
		break;
	case PKG_RULE_REQUEST_CONFLICT:
-
		utstring_printf(sb, "The following packages in request are candidates for installation: ");
+
		fprintf(sb->fp, "The following packages in request are candidates for installation: ");
		LL_FOREACH(rule->items, it) {
-
			utstring_printf(sb, "%s-%s%s", it->var->uid, it->var->unit->pkg->version,
+
			fprintf(sb->fp, "%s-%s%s", it->var->uid, it->var->unit->pkg->version,
					it->next ? ", " : "");
		}
		break;
@@ -265,17 +265,18 @@ pkg_print_rule_buf(struct pkg_solve_rule *rule, UT_string *sb)
static void
pkg_debug_print_rule(struct pkg_solve_rule *rule)
{
-
	UT_string *sb;
+
	xstring *sb;

	if (ctx.debug_level < 3)
		return;

-
	utstring_new(sb);
+
	sb = xstring_new();

	pkg_print_rule_buf(rule, sb);

-
	pkg_debug(2, "%s", utstring_body(sb));
-
	utstring_free(sb);
+
	fflush(sb->fp);
+
	pkg_debug(2, "%s", sb->buf);
+
	xstring_free(sb);
}

static int
@@ -1117,8 +1118,7 @@ reiterate:

		if (attempt >= 10) {
			pkg_emit_error("Cannot solve problem using SAT solver");
-
			UT_string *sb;
-
			utstring_new(sb);
+
			xstring *sb = xstring_new();

			while (*failed) {
				var = &problem->variables[abs(*failed) - 1];
@@ -1129,24 +1129,25 @@ reiterate:
						LL_FOREACH(rule->items, item) {
							if (item->var == var) {
								pkg_print_rule_buf(rule, sb);
-
								utstring_printf(sb, "%c", '\n');
+
								fputc('\n', sb->fp);
								break;
							}
						}
					}
				}

-
				utstring_printf(sb, "cannot %s package %s, remove it from request? ",
+
				fprintf(sb->fp, "cannot %s package %s, remove it from request? ",
						var->flags & PKG_VAR_INSTALL ? "install" : "remove", var->uid);

-
				if (pkg_emit_query_yesno(true, utstring_body(sb))) {
+
				fflush(sb->fp);
+
				if (pkg_emit_query_yesno(true, sb->buf)) {
					var->flags |= PKG_VAR_FAILED;
				}

				failed++;
				need_reiterate = true;
			}
-
			utstring_clear(sb);
+
			xstring_free(sb);
		} else {
			pkg_emit_notice("Cannot solve problem using SAT solver, trying another plan");
			var = &problem->variables[abs(*failed) - 1];
modified libpkg/pkgdb.c
@@ -101,9 +101,23 @@ static void prstmt_finalize(struct pkgdb *db);
static int pkgdb_insert_scripts(struct pkg *pkg, int64_t package_id, sqlite3 *s);
static int pkgdb_insert_lua_scripts(struct pkg *pkg, int64_t package_id, sqlite3 *s);

-

extern int sqlite3_shell(int, char**);

+
struct sqlite3_stmt *
+
prepare_sql(sqlite3 *s, const char *sql)
+
{
+
	int ret;
+
	sqlite3_stmt *stmt;
+

+
	ret = sqlite3_prepare_v2(s, sql, strlen(sql), &stmt,
+
	    NULL);
+
	if (ret != SQLITE_OK) {
+
		ERROR_SQLITE(s, sql);
+
		return (NULL);
+
	}
+
	return (stmt);
+
}
+

void
pkgdb_regex(sqlite3_context *ctx, int argc, sqlite3_value **argv)
{
@@ -1650,21 +1664,17 @@ prstmt_initialize(struct pkgdb *db)
{
	sql_prstmt_index	 i;
	sqlite3			*sqlite;
-
	int			 ret;

	assert(db != NULL);

	if (!db->prstmt_initialized) {
		sqlite = db->sqlite;

-
		for (i = 0; i < PRSTMT_LAST; i++)
-
		{
+
		for (i = 0; i < PRSTMT_LAST; i++) {
			pkg_debug(4, "Pkgdb: preparing statement '%s'", SQL(i));
-
			ret = sqlite3_prepare_v2(sqlite, SQL(i), -1, &STMT(i), NULL);
-
			if (ret != SQLITE_OK) {
-
				ERROR_SQLITE(sqlite, SQL(i));
+
			STMT(i) = prepare_sql(sqlite, SQL(i));
+
			if (STMT(i) == NULL)
				return (EPKG_FATAL);
-
			}
		}
		db->prstmt_initialized = true;
	}
@@ -2222,12 +2232,9 @@ pkgdb_reanalyse_shlibs(struct pkgdb *db, struct pkg *pkg)
		for (i = 0; i < 2; i++) {
			/* Clean out old shlibs first */
			pkg_debug(4, "Pkgdb: running '%s'", sql[i]);
-
			if (sqlite3_prepare_v2(db->sqlite, sql[i], -1,
-
					       &stmt_del, NULL)
-
			    != SQLITE_OK) {
-
				ERROR_SQLITE(db->sqlite, sql[i]);
+
			stmt_del = prepare_sql(db->sqlite, sql[i]);
+
			if (stmt_del == NULL)
				return (EPKG_FATAL);
-
			}

			sqlite3_bind_int64(stmt_del, 1, package_id);

@@ -2434,11 +2441,9 @@ pkgdb_unregister_pkg(struct pkgdb *db, int64_t id)
	assert(db != NULL);

	pkg_debug(4, "Pkgdb: running '%s'", sql);
-
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt_del, NULL)
-
	    != SQLITE_OK){
-
		ERROR_SQLITE(db->sqlite, sql);
+
	stmt_del = prepare_sql(db->sqlite, sql);
+
	if (stmt_del == NULL)
		return (EPKG_FATAL);
-
	}

	sqlite3_bind_int64(stmt_del, 1, id);

@@ -2528,41 +2533,6 @@ get_pragma(sqlite3 *s, const char *sql, int64_t *res, bool silence)
}

int
-
get_sql_string(sqlite3 *s, const char *sql, char **res)
-
{
-
	sqlite3_stmt	*stmt;
-
	int		 ret;
-

-
	assert(s != NULL && sql != NULL);
-

-
	pkg_debug(4, "Pkgdb: running '%s'", sql);
-
	if (sqlite3_prepare_v2(s, sql, -1, &stmt, NULL) != SQLITE_OK) {
-
		ERROR_SQLITE(s, sql);
-
		return (EPKG_OK);
-
	}
-

-
	ret = sqlite3_step(stmt);
-

-
	if (ret == SQLITE_ROW) {
-
		const unsigned char *tmp;
-
		tmp = sqlite3_column_text(stmt, 0);
-
		*res = (tmp == NULL ? NULL : xstrdup(tmp));
-
	}
-

-
	if (ret == SQLITE_DONE)
-
		*res = NULL;
-

-
	sqlite3_finalize(stmt);
-

-
	if (ret != SQLITE_ROW && ret != SQLITE_DONE) {
-
		ERROR_SQLITE(s, sql);
-
		return (EPKG_FATAL);
-
	}
-

-
	return (EPKG_OK);
-
}
-

-
int
pkgdb_compact(struct pkgdb *db)
{
	int64_t	page_count = 0;
@@ -2628,11 +2598,9 @@ pkgdb_vset(struct pkgdb *db, int64_t id, va_list ap)

	while ((attr = va_arg(ap, int)) > 0) {
		pkg_debug(4, "Pkgdb: running '%s'", sql[attr]);
-
		if (sqlite3_prepare_v2(db->sqlite, sql[attr], -1, &stmt, NULL)
-
		    != SQLITE_OK) {
-
			ERROR_SQLITE(db->sqlite, sql[attr]);
+
		stmt = prepare_sql(db->sqlite, sql[attr]);
+
		if (stmt == NULL)
			return (EPKG_FATAL);
-
		}

		switch (attr) {
		case PKG_SET_FLATSIZE:
@@ -2704,14 +2672,11 @@ pkgdb_file_set_cksum(struct pkgdb *db, struct pkg_file *file,
	sqlite3_stmt	*stmt = NULL;
	const char	 sql_file_update[] = ""
		"UPDATE files SET sha256 = ?1 WHERE path = ?2";
-
	int		 ret;

	pkg_debug(4, "Pkgdb: running '%s'", sql_file_update);
-
	ret = sqlite3_prepare_v2(db->sqlite, sql_file_update, -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite, sql_file_update);
+
	stmt = prepare_sql(db->sqlite, sql_file_update);
+
	if (stmt == NULL)
		return (EPKG_FATAL);
-
	}
	sqlite3_bind_text(stmt, 1, sum, -1, SQLITE_STATIC);
	sqlite3_bind_text(stmt, 2, file->path, -1, SQLITE_STATIC);

@@ -2775,13 +2740,10 @@ pkgdb_write_lock_pid(struct pkgdb *db)
	const char lock_pid_sql[] = ""
			"INSERT INTO pkg_lock_pid VALUES (?1);";
	sqlite3_stmt	*stmt = NULL;
-
	int ret;

-
	ret = sqlite3_prepare_v2(db->sqlite, lock_pid_sql, -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite, lock_pid_sql);
+
	stmt = prepare_sql(db->sqlite, lock_pid_sql);
+
	if (stmt == NULL)
		return (EPKG_FATAL);
-
	}
	sqlite3_bind_int64(stmt, 1, (int64_t)getpid());

	if (sqlite3_step(stmt) != SQLITE_DONE) {
@@ -2800,13 +2762,10 @@ pkgdb_remove_lock_pid(struct pkgdb *db, int64_t pid)
	const char lock_pid_sql[] = ""
			"DELETE FROM pkg_lock_pid WHERE pid = ?1;";
	sqlite3_stmt	*stmt = NULL;
-
	int ret;

-
	ret = sqlite3_prepare_v2(db->sqlite, lock_pid_sql, -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite, lock_pid_sql);
+
	stmt = prepare_sql(db->sqlite, lock_pid_sql);
+
	if (stmt == NULL)
		return (EPKG_FATAL);
-
	}
	sqlite3_bind_int64(stmt, 1, pid);

	if (sqlite3_step(stmt) != SQLITE_DONE) {
@@ -2823,16 +2782,13 @@ static int
pkgdb_check_lock_pid(struct pkgdb *db)
{
	sqlite3_stmt	*stmt = NULL;
-
	int ret, found = 0;
+
	int found = 0;
	int64_t pid, lpid;
	const char query[] = "SELECT pid FROM pkg_lock_pid;";

-
	ret = sqlite3_prepare_v2(db->sqlite, query, -1,
-
			&stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite, query);
+
	stmt = prepare_sql(db->sqlite, query);
+
	if (stmt == NULL)
		return (EPKG_FATAL);
-
	}

	lpid = getpid();

@@ -3088,20 +3044,17 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)
{
	sqlite3_stmt	*stmt = NULL;
	int64_t		 stats = 0;
-
	UT_string	*sql = NULL;
-
	int		 ret;
+
	const char *sql = NULL;
	struct _pkg_repo_list_item *rit;

	assert(db != NULL);

-
	utstring_new(sql);
-

	switch(type) {
	case PKG_STATS_LOCAL_COUNT:
-
		utstring_printf(sql, "SELECT COUNT(id) FROM main.packages;");
+
		sql = "SELECT COUNT(id) FROM main.packages;";
		break;
	case PKG_STATS_LOCAL_SIZE:
-
		utstring_printf(sql, "SELECT SUM(flatsize) FROM main.packages;");
+
		sql = "SELECT SUM(flatsize) FROM main.packages;";
		break;
	case PKG_STATS_REMOTE_UNIQUE:
	case PKG_STATS_REMOTE_COUNT:
@@ -3112,23 +3065,20 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)
			if (repo->ops->stat != NULL)
				stats += repo->ops->stat(repo, type);
		}
-
		goto remote;
+
		return (stats);
		break;
	case PKG_STATS_REMOTE_REPOS:
		LL_FOREACH(db->repos, rit) {
			stats ++;
		}
-
		goto remote;
+
		return (stats);
		break;
	}

-
	pkg_debug(4, "Pkgdb: running '%s'", utstring_body(sql));
-
	ret = sqlite3_prepare_v2(db->sqlite, utstring_body(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite, utstring_body(sql));
-
		utstring_free(sql);
+
	pkg_debug(4, "Pkgdb: running '%s'", sql);
+
	stmt = prepare_sql(db->sqlite, sql);
+
	if (stmt == NULL)
		return (-1);
-
	}

	while (sqlite3_step(stmt) != SQLITE_DONE) {
		stats = sqlite3_column_int64(stmt, 0);
@@ -3136,9 +3086,6 @@ pkgdb_stats(struct pkgdb *db, pkg_stats_t type)

	sqlite3_finalize(stmt);

-
remote:
-
	utstring_free(sql);
-

	return (stats);
}

@@ -3235,10 +3182,9 @@ pkgdb_is_dir_used(struct pkgdb *db, struct pkg *p, const char *dir, int64_t *res
		"WHERE directory_id = directories.id AND directories.path = ?1 "
		"AND package_id != ?2;";

-
	if (sqlite3_prepare_v2(db->sqlite, sql, -1, &stmt, NULL) != SQLITE_OK) {
-
		ERROR_SQLITE(db->sqlite, sql);
+
	stmt = prepare_sql(db->sqlite, sql);
+
	if (stmt == NULL)
		return (EPKG_FATAL);
-
	}

	sqlite3_bind_text(stmt, 1, dir, -1, SQLITE_TRANSIENT);
	sqlite3_bind_int64(stmt, 2, p->id);
modified libpkg/plugins.c
@@ -52,7 +52,7 @@ struct plugin_hook {
};

struct pkg_plugin {
-
	UT_string *fields[PLUGIN_NUMFIELDS];
+
	xstring *fields[PLUGIN_NUMFIELDS];
	void *lh;						/* library handle */
	bool parsed;
	struct plugin_hook *hooks;
@@ -88,7 +88,7 @@ plug_free(struct pkg_plugin *p)
	unsigned int i;

	for (i = 0; i < PLUGIN_NUMFIELDS; i++)
-
		utstring_free(p->fields[i]);
+
		xstring_free(p->fields[i]);

	ucl_object_unref(p->conf);
	pkg_plugin_hook_free(p);
@@ -150,8 +150,9 @@ pkg_plugin_set(struct pkg_plugin *p, pkg_plugin_key key, const char *str)
{
	assert(p != NULL);

-
	utstring_renew(p->fields[key]);
-
	utstring_printf(p->fields[key], "%s", str);
+
	xstring_renew(p->fields[key]);
+
	fputs(str, p->fields[key]->fp);
+
	fflush(p->fields[key]->fp);
	return (EPKG_OK);
}

@@ -163,7 +164,7 @@ pkg_plugin_get(struct pkg_plugin *p, pkg_plugin_key key)
	if (p->fields[key] == NULL)
		return (NULL);

-
	return (utstring_body(p->fields[key]));
+
	return (p->fields[key]->buf);
}

int
modified libpkg/private/event.h
@@ -96,5 +96,8 @@ void pkg_emit_file_missing(struct pkg *p, struct pkg_file *f);
void pkg_register_cleanup_callback(void (*cleanup_cb)(void *data), void *data);
void pkg_unregister_cleanup_callback(void (*cleanup_cb)(void *data), void *data);
void pkg_emit_conflicts(struct pkg *p1, struct pkg *p2, const char *path);
+
void pkg_emit_triggers_begin(void);
+
void pkg_emit_trigger(const char *name, bool cleanup);
+
void pkg_emit_triggers_finished(void);

#endif
added libpkg/private/lua.h
@@ -0,0 +1,37 @@
+
/*-
+
 * Copyright (c) 2019 Baptiste Daroussin <bapt@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.
+
 */
+

+
#include <lauxlib.h>
+
#include <lualib.h>
+

+
lua_CFunction stack_dump(lua_State *L);
+
int lua_print_msg(lua_State *L);
+
int lua_pkg_copy(lua_State *L);
+
int lua_pkg_filecmp(lua_State *L);
+
int lua_prefix_path(lua_State *L);
+
void lua_override_ios(lua_State *L);
+
int lua_stat(lua_State *L);
+
void lua_args_table(lua_State *L, char **argv, int argc);
modified libpkg/private/pkg.h
@@ -40,7 +40,6 @@
#include <stdbool.h>
#include <uthash.h>
#include <utlist.h>
-
#include <utstring.h>
#include <ucl.h>

#include "xmalloc.h"
@@ -240,6 +239,14 @@
	}						\
} while (0)

+
KHASH_MAP_INIT_STR(pkg_deps, struct pkg_dep *);
+
KHASH_MAP_INIT_STR(pkg_files, struct pkg_file *);
+
KHASH_MAP_INIT_STR(pkg_dirs, struct pkg_dir *);
+
KHASH_MAP_INIT_STR(pkg_config_files, struct pkg_config_file *);
+
KHASH_MAP_INIT_STR(strings, char *);
+
KHASH_MAP_INIT_STR(pkg_options, struct pkg_option *);
+
KHASH_MAP_INIT_STR(pkg_conflicts, struct pkg_conflict *);
+

struct pkg_ctx {
	int eventpipe;
	int64_t debug_level;
@@ -254,6 +261,9 @@ struct pkg_ctx {
	int osversion;
	bool backup_libraries;
	const char *backup_library_path;
+
	bool triggers;
+
	const char *triggers_path;
+
	kh_strings_t *touched_dir_hash;
};

extern struct pkg_ctx ctx;
@@ -263,21 +273,13 @@ struct pkg_repo;
struct pkg_message;
struct pkg_lua_script;

-
KHASH_MAP_INIT_STR(pkg_deps, struct pkg_dep *);
-
KHASH_MAP_INIT_STR(pkg_files, struct pkg_file *);
-
KHASH_MAP_INIT_STR(pkg_dirs, struct pkg_dir *);
-
KHASH_MAP_INIT_STR(pkg_config_files, struct pkg_config_file *);
-
KHASH_MAP_INIT_STR(strings, char *);
-
KHASH_MAP_INIT_STR(pkg_options, struct pkg_option *);
-
KHASH_MAP_INIT_STR(pkg_conflicts, struct pkg_conflict *);
-

struct pkg {
	bool		 direct;
	bool		 locked;
	bool		 automatic;
	bool		 vital;
	int64_t		 id;
-
	UT_string	*scripts[PKG_NUM_SCRIPTS];
+
	xstring		*scripts[PKG_NUM_SCRIPTS];
	struct pkg_lua_script	*lua_scripts[PKG_NUM_LUA_SCRIPTS];
	char			*name;
	char			*origin;
@@ -337,8 +339,32 @@ struct pkg {
	struct pkg_repo		*repo;
};

+
typedef enum {
+
	SCRIPT_UNKNOWN = 0,
+
	SCRIPT_SHELL,
+
	SCRIPT_LUA,
+
} script_type_t;
+

+
struct trigger {
+
	char *name;
+
	ucl_object_t *path;
+
	ucl_object_t *path_glob;
+
	ucl_object_t *path_regex;
+
	struct {
+
		char *script;
+
		int type;
+
	} script;
+
	struct {
+
		char *script;
+
		int type;
+
	} cleanup;
+
	kh_strings_t *matched;
+
	struct trigger *prev, *next;
+
};
+

struct pkg_create {
	bool overwrite;
+
	bool expand_manifest;
	int compression_level;
	pkg_formats format;
	time_t timestamp;
@@ -613,11 +639,13 @@ struct plist {
	char last_file[MAXPATHLEN];
	const char *stage;
	int stagefd;
+
	bool in_include;
+
	int plistdirfd;
	char prefix[MAXPATHLEN];
-
	UT_string *pre_install_buf;
-
	UT_string *post_install_buf;
-
	UT_string *pre_deinstall_buf;
-
	UT_string *post_deinstall_buf;
+
	xstring *pre_install_buf;
+
	xstring *post_install_buf;
+
	xstring *pre_deinstall_buf;
+
	xstring *post_deinstall_buf;
	struct pkg *pkg;
	char *uname;
	char *gname;
@@ -728,6 +756,7 @@ int pkg_start_stop_rc_scripts(struct pkg *, pkg_rc_attr attr);
int pkg_script_run(struct pkg *, pkg_script type, bool upgrade);
int pkg_lua_script_run(struct pkg *, pkg_lua_script type, bool upgrade);
ucl_object_t *pkg_lua_script_to_ucl(struct pkg_lua_script *);
+
int pkg_script_run_child(int pid, int *pstat, int inputfd, const char* script_name);

int pkg_open2(struct pkg **p, struct archive **a, struct archive_entry **ae,
	      const char *path, struct pkg_manifest_key *keys, int flags, int fd);
@@ -768,7 +797,6 @@ int pkg_delete_dirs(struct pkgdb *db, struct pkg *pkg, struct pkg *p);
/* pkgdb commands */
int sql_exec(sqlite3 *, const char *, ...);
int get_pragma(sqlite3 *, const char *sql, int64_t *res, bool silence);
-
int get_sql_string(sqlite3 *, const char *sql, char **res);

int pkgdb_register_pkg(struct pkgdb *db, struct pkg *pkg, int forced, const char *);
int pkgdb_update_shlibs_required(struct pkg *pkg, int64_t package_id, sqlite3 *s);
@@ -782,11 +810,11 @@ int pkgdb_is_dir_used(struct pkgdb *db, struct pkg *p, const char *dir, int64_t
int pkgdb_file_set_cksum(struct pkgdb *db, struct pkg_file *file, const char *sha256);


-
int pkg_emit_manifest_buf(struct pkg*, UT_string *, short, char **);
+
int pkg_emit_manifest_buf(struct pkg*, xstring *, short, char **);
int pkg_emit_filelist(struct pkg *, FILE *);

bool ucl_object_emit_buf(const ucl_object_t *obj, enum ucl_emitter emit_type,
-
    UT_string **buf);
+
    xstring **buf);
bool ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type,
    FILE *);

@@ -833,8 +861,11 @@ int pkg_open_root_fd(struct pkg *pkg);
void pkg_add_dir_to_del(struct pkg *pkg, const char *file, const char *dir);
struct plist *plist_new(struct pkg *p, const char *stage);
int plist_parse_line(struct plist *p, char *line);
+
char *extract_keywords(char *line, char **keyword, struct file_attr **attr);
+
struct file_attr *parse_keyword_args(char *args, char *keyword);
void plist_free(struct plist *);
int pkg_appendscript(struct pkg *pkg, const char *cmd, pkg_script type);
+
void free_file_attr(struct file_attr *a);

int pkg_add_lua_script(struct pkg *pkg, const char *data, pkg_lua_script type);
int pkg_addscript(struct pkg *pkg, const char *data, pkg_script type);
@@ -893,7 +924,13 @@ void backup_library(struct pkgdb *, struct pkg *, const char *);
int suggest_arch(struct pkg *, bool);
int set_attrsat(int fd, const char *path, mode_t perm, uid_t uid, gid_t gid, const struct timespec *ats, const struct timespec *mts);

+
<<<<<<< HEAD
/* Filesystem extended attribute support */
int pkg_archive_extattrs(int, struct archive_entry *);
+
=======
+
struct trigger *triggers_load(bool cleanup_only);
+
int triggers_execute(struct trigger *cleanup_triggers);
+
void append_touched_file(const char *path);
+
>>>>>>> 74fd3389a4ee2611603ee8658d2f3b4182ace73e

#endif
modified libpkg/private/pkg_printf.h
@@ -161,79 +161,79 @@ struct percent_esc {
	unsigned	 flags;
	int		 width;
	unsigned	 trailer_status;
-
	UT_string	*item_fmt;
-
	UT_string	*sep_fmt;
+
	xstring	*item_fmt;
+
	xstring	*sep_fmt;
	fmt_code_t	 fmt_code;
};

/* Format handler function prototypes */

-
_static UT_string *format_annotation_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_annotation_value(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_annotations(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_shlibs_required(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_shlib_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_categories(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_category_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_directories(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_directory_group(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_directory_path(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_directory_perms(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_directory_user(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_files(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_file_group(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_file_path(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_file_perms(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_file_sha256(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_file_user(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_groups(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_group_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_row_counter(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_licenses(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_license_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_message(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_repo_ident(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_options(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_option_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_option_value(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_option_default(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_option_description(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_repo_path(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_char_string(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_users(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_user_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_old_version(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_autoremove(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_shlibs_provided(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_comment(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_dependencies(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_dependency_lock(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_dependency_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_dependency_origin(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_dependency_version(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_description(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_lock_status(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_license_logic(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_maintainer(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_origin(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_prefix(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_architecture(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_altabi(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_requirements(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_flatsize(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_install_tstamp(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_checksum(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_version(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_home_url(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_pkgsize(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_short_checksum(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_literal_percent(UT_string *, __unused const void *, __unused struct percent_esc *);
-
_static UT_string *format_unknown(UT_string *, __unused const void *, __unused struct percent_esc *);
-
_static UT_string *format_provided(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_required(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_provide_name(UT_string *, const void *, struct percent_esc *);
-
_static UT_string *format_int_checksum(UT_string *, const void *, struct percent_esc *);
+
_static xstring *format_annotation_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_annotation_value(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_annotations(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_shlibs_required(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_shlib_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_categories(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_category_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_directories(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_directory_group(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_directory_path(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_directory_perms(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_directory_user(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_files(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_file_group(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_file_path(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_file_perms(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_file_sha256(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_file_user(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_groups(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_group_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_row_counter(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_licenses(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_license_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_message(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_repo_ident(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_options(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_option_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_option_value(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_option_default(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_option_description(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_repo_path(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_char_string(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_users(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_user_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_old_version(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_autoremove(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_shlibs_provided(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_comment(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_dependencies(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_dependency_lock(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_dependency_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_dependency_origin(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_dependency_version(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_description(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_lock_status(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_license_logic(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_maintainer(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_origin(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_prefix(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_architecture(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_altabi(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_requirements(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_flatsize(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_install_tstamp(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_checksum(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_version(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_home_url(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_pkgsize(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_short_checksum(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_literal_percent(xstring *, __unused const void *, __unused struct percent_esc *);
+
_static xstring *format_unknown(xstring *, __unused const void *, __unused struct percent_esc *);
+
_static xstring *format_provided(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_required(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_provide_name(xstring *, const void *, struct percent_esc *);
+
_static xstring *format_int_checksum(xstring *, const void *, struct percent_esc *);

/* Other static function prototypes */

@@ -243,19 +243,19 @@ _static void free_percent_esc(struct percent_esc *);

_static char *gen_format(char *, size_t, unsigned, const char *);

-
_static UT_string *human_number(UT_string *, int64_t, struct percent_esc *);
-
_static UT_string *string_val(UT_string *, const char *,
+
_static xstring *human_number(xstring *, int64_t, struct percent_esc *);
+
_static xstring *string_val(xstring *, const char *,
			       struct percent_esc *);
-
_static UT_string *int_val(UT_string *, int64_t, struct percent_esc *);
-
_static UT_string *bool_val(UT_string *, bool, struct percent_esc *);
-
_static UT_string *mode_val(UT_string *, mode_t, struct percent_esc *);
-
_static UT_string *liclog_val(UT_string *, lic_t, struct percent_esc *);
-
_static UT_string *list_count(UT_string *, int64_t, struct percent_esc *);
+
_static xstring *int_val(xstring *, int64_t, struct percent_esc *);
+
_static xstring *bool_val(xstring *, bool, struct percent_esc *);
+
_static xstring *mode_val(xstring *, mode_t, struct percent_esc *);
+
_static xstring *liclog_val(xstring *, lic_t, struct percent_esc *);
+
_static xstring *list_count(xstring *, int64_t, struct percent_esc *);

_static struct percent_esc *set_list_defaults(struct percent_esc *,
					      const char *, const char *);

-
_static UT_string *iterate_item(UT_string *, const struct pkg *,
+
_static xstring *iterate_item(xstring *, const struct pkg *,
				  const char *, const void *, int, unsigned);

_static const char *field_modifier(const char *, struct percent_esc *);
@@ -264,14 +264,14 @@ _static const char *format_code(const char *, unsigned , struct percent_esc *);
_static const char *format_trailer(const char *, struct percent_esc *);
_static const char *parse_format(const char *, unsigned, struct percent_esc *);

-
_static const char *maybe_read_hex_byte(UT_string *, const char *);
-
_static const char *read_oct_byte(UT_string *, const char *);
-
_static const char *process_escape(UT_string *, const char *);
+
_static const char *maybe_read_hex_byte(xstring *, const char *);
+
_static const char *read_oct_byte(xstring *, const char *);
+
_static const char *process_escape(xstring *, const char *);

-
_static const char *process_format_trailer(UT_string *, struct percent_esc *,
+
_static const char *process_format_trailer(xstring *, struct percent_esc *,
					   const char *, const struct pkg *,
					   const void *, int, unsigned);
-
_static const char *process_format_main(UT_string *, struct percent_esc *,
+
_static const char *process_format_main(xstring *, struct percent_esc *,
					const char *, const char *, void *);

#endif
modified libpkg/private/pkgdb.h
@@ -182,5 +182,6 @@ int pkgdb_update_config_file_content(struct pkg *pkg, sqlite3 *s);
void pkgdb_syscall_overload(void);
void pkgdb_nfs_corruption(sqlite3 *s);
bool pkgdb_file_exists(struct pkgdb *db, const char *path);
+
struct sqlite3_stmt *prepare_sql(sqlite3 *s, const char *sql);

#endif
modified libpkg/private/utils.h
@@ -34,7 +34,7 @@
#include <ucl.h>
#include <khash.h>
#include <pkg.h>
-
#include <utstring.h>
+
#include <xstring.h>

#define STARTS_WITH(string, needle) (strncasecmp(string, needle, strlen(needle)) == 0)
#define RELATIVE_PATH(p) (p + (*p == '/' ? 1 : 0))
@@ -67,7 +67,7 @@ int mkdirs(const char *path);
int file_to_buffer(const char *, char **, off_t *);
int file_to_bufferat(int, const char *, char **, off_t *);
int format_exec_cmd(char **, const char *, const char *, const char *, char *,
-
    int argc, char **argv);
+
    int argc, char **argv, bool lua);
int is_dir(const char *);
int is_link(const char *);

@@ -97,10 +97,13 @@ pid_t process_spawn_pipe(FILE *inout[2], const char *command);

void *parse_mode(const char *str);
int *text_diff(char *a, char *b);
-
int merge_3way(char *pivot, char *v1, char *v2, UT_string *out);
+
int merge_3way(char *pivot, char *v1, char *v2, xstring *out);
bool string_end_with(const char *path, const char *str);
bool mkdirat_p(int fd, const char *path);
int get_socketpair(int *);
int checkflags(const char *mode, int *optr);
+
bool match_ucl_lists(const char *buffer, const ucl_object_t *globs, const ucl_object_t *regexes);
+
char *get_dirname(char *dir);
+
char *rtrimspace(char *buf);

#endif
modified libpkg/repo/binary/fetch.c
@@ -131,7 +131,6 @@ pkg_repo_binary_try_fetch(struct pkg_repo *repo, struct pkg *pkg,
	char *dir = NULL;
	bool fetched = false;
	struct stat st;
-
	char *path = NULL;
	const char *packagesite = NULL;
	ssize_t offset = -1;

@@ -165,14 +164,8 @@ pkg_repo_binary_try_fetch(struct pkg_repo *repo, struct pkg *pkg,
	}

	/* Create the dirs in cachedir */
-
	dir = xstrdup(dest);
-
	if ((path = dirname(dir)) == NULL) {
-
		pkg_emit_errno("dirname", dest);
-
		retcode = EPKG_FATAL;
-
		goto cleanup;
-
	}
-

-
	if ((retcode = mkdirs(path)) != EPKG_OK)
+
	dir = get_dirname(xstrdup(dest));
+
	if ((retcode = mkdirs(dir)) != EPKG_OK)
		goto cleanup;

	/*
@@ -244,8 +237,8 @@ cleanup:

	if (retcode != EPKG_OK)
		unlink(dest);
-
	else if (!mirror && path != NULL) {
-
		(void)pkg_repo_binary_create_symlink(pkg, dest, path);
+
	else if (!mirror && dir != NULL) {
+
		(void)pkg_repo_binary_create_symlink(pkg, dest, dir);
	}

	/* allowed even if dir is NULL */
modified libpkg/repo/binary/init.c
@@ -55,7 +55,7 @@ sqlite_file_exists(sqlite3_context *ctx, int argc, sqlite3_value **argv)
{
	char	 fpath[MAXPATHLEN];
	sqlite3	*db = sqlite3_context_db_handle(ctx);
-
	char	*path = bsd_dirname(sqlite3_db_filename(db, "main"));
+
	char	*path = get_dirname(xstrdup(sqlite3_db_filename(db, "main")));
	char	*cksum;

	if (argc != 2) {
@@ -75,6 +75,7 @@ sqlite_file_exists(sqlite3_context *ctx, int argc, sqlite3_value **argv)
	} else {
		sqlite3_result_int(ctx, 0);
	}
+
	free(path);
}

static int
modified libpkg/repo/binary/query.c
@@ -100,39 +100,27 @@ pkg_repo_binary_query(struct pkg_repo *repo, const char *pattern, match_t match)
{
	sqlite3 *sqlite = PRIV_GET(repo);
	sqlite3_stmt	*stmt = NULL;
-
	UT_string	*sql = NULL;
+
	char *sql = NULL;
	const char	*comp = NULL;
-
	int		 ret;
-
	char		 basesql[BUFSIZ] = ""
+
	char basesql[] = ""
		"SELECT id, origin, name, name as uniqueid, version, comment, "
		"prefix, desc, arch, maintainer, www, "
		"licenselogic, flatsize, pkgsize, "
		"cksum, manifestdigest, path AS repopath, '%s' AS dbname "
-
		"FROM packages AS p";
+
		"FROM packages AS p %s ORDER BY NAME;";

	if (match != MATCH_ALL && (pattern == NULL || pattern[0] == '\0'))
		return (NULL);

-
	utstring_new(sql);
	comp = pkgdb_get_pattern_query(pattern, match);
-
	if (comp && comp[0])
-
		strlcat(basesql, comp, sizeof(basesql));
+
	xasprintf(&sql, basesql, repo->name, comp ? comp : "");

-
	utstring_printf(sql, basesql, repo->name);
-

-
	utstring_printf(sql, "%s", " ORDER BY name;");
-

-
	pkg_debug(4, "Pkgdb: running '%s' query for %s", utstring_body(sql),
+
	pkg_debug(4, "Pkgdb: running '%s' query for %s", sql,
	     pattern == NULL ? "all": pattern);
-
	ret = sqlite3_prepare_v2(sqlite, utstring_body(sql), utstring_len(sql), &stmt,
-
	    NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, utstring_body(sql));
-
		utstring_free(sql);
+
	stmt = prepare_sql(sqlite, sql);
+
	free(sql);
+
	if (stmt == NULL)
		return (NULL);
-
	}
-

-
	utstring_free(sql);

	if (match != MATCH_ALL && match != MATCH_CONDITION)
		sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);
@@ -145,8 +133,7 @@ pkg_repo_binary_shlib_provide(struct pkg_repo *repo, const char *require)
{
	sqlite3_stmt	*stmt;
	sqlite3 *sqlite = PRIV_GET(repo);
-
	UT_string	*sql = NULL;
-
	int		 ret;
+
	char *sql = NULL;
	const char	 basesql[] = ""
			"SELECT p.id, p.origin, p.name, p.version, p.comment, "
			"p.name as uniqueid, "
@@ -158,18 +145,13 @@ pkg_repo_binary_shlib_provide(struct pkg_repo *repo, const char *require)
			"WHERE ps.shlib_id IN (SELECT id FROM shlibs WHERE "
			"name BETWEEN ?1 AND ?1 || '.9');";

-
	utstring_new(sql);
-
	utstring_printf(sql, basesql, repo->name);
+
	xasprintf(&sql, basesql, repo->name);

-
	pkg_debug(4, "Pkgdb: running '%s'", utstring_body(sql));
-
	ret = sqlite3_prepare_v2(sqlite, utstring_body(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, utstring_body(sql));
-
		utstring_free(sql);
+
	pkg_debug(4, "Pkgdb: running '%s'", sql);
+
	stmt = prepare_sql(sqlite, sql);
+
	free(sql);
+
	if (stmt == NULL)
		return (NULL);
-
	}
-

-
	utstring_free(sql);

	sqlite3_bind_text(stmt, 1, require, -1, SQLITE_TRANSIENT);

@@ -181,8 +163,7 @@ pkg_repo_binary_provide(struct pkg_repo *repo, const char *require)
{
	sqlite3_stmt	*stmt;
	sqlite3 *sqlite = PRIV_GET(repo);
-
	UT_string	*sql = NULL;
-
	int		 ret;
+
	char *sql = NULL;
	const char	 basesql[] = ""
			"SELECT p.id, p.origin, p.name, p.version, p.comment, "
			"p.name as uniqueid, "
@@ -194,18 +175,13 @@ pkg_repo_binary_provide(struct pkg_repo *repo, const char *require)
			"WHERE ps.provide_id IN (SELECT id from provides WHERE "
			"provide = ?1 );";

-
	utstring_new(sql);
-
	utstring_printf(sql, basesql, repo->name);
+
	xasprintf(&sql, basesql, repo->name);

-
	pkg_debug(4, "Pkgdb: running '%s'", utstring_body(sql));
-
	ret = sqlite3_prepare_v2(sqlite, utstring_body(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, utstring_body(sql));
-
		utstring_free(sql);
+
	pkg_debug(4, "Pkgdb: running '%s'", sql);
+
	stmt = prepare_sql(sqlite, sql);
+
	free(sql);
+
	if (stmt == NULL)
		return (NULL);
-
	}
-

-
	utstring_free(sql);

	sqlite3_bind_text(stmt, 1, require, -1, SQLITE_TRANSIENT);

@@ -217,8 +193,7 @@ pkg_repo_binary_shlib_require(struct pkg_repo *repo, const char *provide)
{
	sqlite3_stmt	*stmt;
	sqlite3 *sqlite = PRIV_GET(repo);
-
	UT_string	*sql = NULL;
-
	int		 ret;
+
	char *sql = NULL;
	const char	 basesql[] = ""
			"SELECT p.id, p.origin, p.name, p.version, p.comment, "
			"p.name as uniqueid, "
@@ -229,18 +204,13 @@ pkg_repo_binary_shlib_require(struct pkg_repo *repo, const char *provide)
			"p.id = ps.package_id "
			"WHERE ps.shlib_id = (SELECT id FROM shlibs WHERE name=?1);";

-
	utstring_new(sql);
-
	utstring_printf(sql, basesql, repo->name);
+
	xasprintf(&sql, basesql, repo->name);

-
	pkg_debug(4, "Pkgdb: running '%s'", utstring_body(sql));
-
	ret = sqlite3_prepare_v2(sqlite, utstring_body(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, utstring_body(sql));
-
		utstring_free(sql);
+
	pkg_debug(4, "Pkgdb: running '%s'", sql);
+
	stmt = prepare_sql(sqlite, sql);
+
	free(sql);
+
	if (stmt == NULL)
		return (NULL);
-
	}
-

-
	utstring_free(sql);

	pkg_debug(1, "> loading provides");
	sqlite3_bind_text(stmt, 1, provide, -1, SQLITE_TRANSIENT);
@@ -253,8 +223,7 @@ pkg_repo_binary_require(struct pkg_repo *repo, const char *provide)
{
	sqlite3_stmt	*stmt;
	sqlite3 *sqlite = PRIV_GET(repo);
-
	UT_string	*sql = NULL;
-
	int		 ret;
+
	char *sql = NULL;
	const char	 basesql[] = ""
			"SELECT p.id, p.origin, p.name, p.version, p.comment, "
			"p.name as uniqueid, "
@@ -265,18 +234,13 @@ pkg_repo_binary_require(struct pkg_repo *repo, const char *provide)
			"p.id = ps.package_id "
			"WHERE ps.require_id = (SELECT id FROM requires WHERE require=?1);";

-
	utstring_new(sql);
-
	utstring_printf(sql, basesql, repo->name);
+
	xasprintf(&sql, basesql, repo->name);

-
	pkg_debug(4, "Pkgdb: running '%s'", utstring_body(sql));
-
	ret = sqlite3_prepare_v2(sqlite, utstring_body(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, utstring_body(sql));
-
		utstring_free(sql);
+
	pkg_debug(4, "Pkgdb: running '%s'", sql);
+
	stmt = prepare_sql(sqlite, sql);
+
	free(sql);
+
	if (stmt == NULL)
		return (NULL);
-
	}
-

-
	utstring_free(sql);

	sqlite3_bind_text(stmt, 1, provide, -1, SQLITE_TRANSIENT);

@@ -313,7 +277,7 @@ pkg_repo_binary_search_how(match_t match)
}

static int
-
pkg_repo_binary_build_search_query(UT_string *sql, match_t match,
+
pkg_repo_binary_build_search_query(xstring *sql, match_t match,
    pkgdb_field field, pkgdb_field sort)
{
	const char	*how = NULL;
@@ -344,7 +308,7 @@ pkg_repo_binary_build_search_query(UT_string *sql, match_t match,
	}

	if (what != NULL && how != NULL)
-
		utstring_printf(sql, how, what);
+
		fprintf(sql->fp, how, what);

	switch (sort) {
	case FIELD_NONE:
@@ -368,7 +332,7 @@ pkg_repo_binary_build_search_query(UT_string *sql, match_t match,
	}

	if (orderby != NULL)
-
		utstring_printf(sql, "%s", orderby);
+
		fprintf(sql->fp, "%s", orderby);

	return (EPKG_OK);
}
@@ -379,8 +343,8 @@ pkg_repo_binary_search(struct pkg_repo *repo, const char *pattern, match_t match
{
	sqlite3 *sqlite = PRIV_GET(repo);
	sqlite3_stmt	*stmt = NULL;
-
	UT_string	*sql = NULL;
-
	int		 ret;
+
	xstring	*sql = NULL;
+
	char *sqlcmd = NULL;
	const char	*multireposql = ""
		"SELECT id, origin, name, version, comment, "
		"prefix, desc, arch, maintainer, www, "
@@ -391,24 +355,21 @@ pkg_repo_binary_search(struct pkg_repo *repo, const char *pattern, match_t match
	if (pattern == NULL || pattern[0] == '\0')
		return (NULL);

-
	utstring_new(sql);
-
	utstring_printf(sql, multireposql, repo->name, repo->url);
+
	sql = xstring_new();
+
	fprintf(sql->fp, multireposql, repo->name, repo->url);

	/* close the UNIONs and build the search query */
-
	utstring_printf(sql, "%s", "WHERE ");
+
	fprintf(sql->fp, "%s", "WHERE ");

	pkg_repo_binary_build_search_query(sql, match, field, sort);
-
	utstring_printf(sql, "%s", ";");
+
	fprintf(sql->fp, "%s", ";");
+
	sqlcmd = xstring_get(sql);

-
	pkg_debug(4, "Pkgdb: running '%s'", utstring_body(sql));
-
	ret = sqlite3_prepare_v2(sqlite, utstring_body(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, utstring_body(sql));
-
		utstring_free(sql);
+
	pkg_debug(4, "Pkgdb: running '%s'", sqlcmd);
+
	stmt = prepare_sql(sqlite, sqlcmd);
+
	free(sqlcmd);
+
	if (stmt == NULL)
		return (NULL);
-
	}
-

-
	utstring_free(sql);

	sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT);

@@ -467,47 +428,35 @@ pkg_repo_binary_stat(struct pkg_repo *repo, pkg_stats_t type)
	sqlite3 *sqlite = PRIV_GET(repo);
	sqlite3_stmt	*stmt = NULL;
	int64_t		 stats = 0;
-
	UT_string	*sql = NULL;
-
	int		 ret;
-

-
	utstring_new(sql);
+
	const char *sql = NULL;

	switch(type) {
	case PKG_STATS_LOCAL_COUNT:
-
		goto out;
-
		break;
+
	case PKG_STATS_REMOTE_REPOS:
	case PKG_STATS_LOCAL_SIZE:
-
		goto out;
-
		break;
+
		return (stats);
	case PKG_STATS_REMOTE_UNIQUE:
-
		utstring_printf(sql, "SELECT COUNT(id) FROM main.packages;");
+
		sql = "SELECT COUNT(id) FROM main.packages;";
		break;
	case PKG_STATS_REMOTE_COUNT:
-
		utstring_printf(sql, "SELECT COUNT(id) FROM main.packages;");
+
		sql = "SELECT COUNT(id) FROM main.packages;";
		break;
	case PKG_STATS_REMOTE_SIZE:
-
		utstring_printf(sql, "SELECT SUM(pkgsize) FROM main.packages;");
-
		break;
-
	case PKG_STATS_REMOTE_REPOS:
-
		goto out;
+
		sql = "SELECT SUM(pkgsize) FROM main.packages;";
		break;
	}

-
	pkg_debug(4, "binary_repo: running '%s'", utstring_body(sql));
-
	ret = sqlite3_prepare_v2(sqlite, utstring_body(sql), -1, &stmt, NULL);
-
	if (ret != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, utstring_body(sql));
-
		goto out;
-
	}
+
	pkg_debug(4, "binary_repo: running '%s'", sql);
+
	stmt = prepare_sql(sqlite, sql);
+

+
	if (stmt == NULL)
+
		return (stats);

	while (sqlite3_step(stmt) != SQLITE_DONE) {
		stats = sqlite3_column_int64(stmt, 0);
	}

-
out:
-
	utstring_free(sql);
-
	if (stmt != NULL)
-
		sqlite3_finalize(stmt);
+
	sqlite3_finalize(stmt);

	return (stats);
}
modified libpkg/repo/binary/update.c
@@ -295,10 +295,9 @@ pkg_repo_binary_register_conflicts(const char *origin, char **conflicts,
	int64_t origin_id, conflict_id;

	pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", select_id_sql);
-
	if (sqlite3_prepare_v2(sqlite, select_id_sql, -1, &stmt, NULL) != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, select_id_sql);
+
	stmt = prepare_sql(sqlite, select_id_sql);
+
	if (stmt == NULL)
		return (EPKG_FATAL);
-
	}

	sqlite3_bind_text(stmt, 1, origin, -1, SQLITE_TRANSIENT);
	ret = sqlite3_step(stmt);
@@ -313,10 +312,9 @@ pkg_repo_binary_register_conflicts(const char *origin, char **conflicts,
	sqlite3_finalize(stmt);

	pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", clean_conflicts_sql);
-
	if (sqlite3_prepare_v2(sqlite, clean_conflicts_sql, -1, &stmt, NULL) != SQLITE_OK) {
-
		ERROR_SQLITE(sqlite, clean_conflicts_sql);
+
	stmt = prepare_sql(sqlite, clean_conflicts_sql);
+
	if (stmt == NULL)
		return (EPKG_FATAL);
-
	}

	sqlite3_bind_int64(stmt, 1, origin_id);
	/* Ignore cleanup result */
@@ -327,10 +325,9 @@ pkg_repo_binary_register_conflicts(const char *origin, char **conflicts,
	for (i = 0; i < conflicts_num; i ++) {
		/* Select a conflict */
		pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", select_id_sql);
-
		if (sqlite3_prepare_v2(sqlite, select_id_sql, -1, &stmt, NULL) != SQLITE_OK) {
-
			ERROR_SQLITE(sqlite, select_id_sql);
+
		stmt = prepare_sql(sqlite, select_id_sql);
+
		if (stmt == NULL)
			return (EPKG_FATAL);
-
		}

		sqlite3_bind_text(stmt, 1, conflicts[i], -1, SQLITE_TRANSIENT);
		ret = sqlite3_step(stmt);
@@ -347,10 +344,9 @@ pkg_repo_binary_register_conflicts(const char *origin, char **conflicts,

		/* Insert a pair */
		pkg_debug(4, "pkgdb_repo_register_conflicts: running '%s'", insert_conflict_sql);
-
		if (sqlite3_prepare_v2(sqlite, insert_conflict_sql, -1, &stmt, NULL) != SQLITE_OK) {
-
			ERROR_SQLITE(sqlite, insert_conflict_sql);
+
		stmt = prepare_sql(sqlite, insert_conflict_sql);
+
		if (stmt == NULL)
			return (EPKG_FATAL);
-
		}

		sqlite3_bind_int64(stmt, 1, origin_id);
		sqlite3_bind_int64(stmt, 2, conflict_id);
@@ -628,8 +624,8 @@ pkg_repo_binary_update(struct pkg_repo *repo, bool force)

		snprintf(filepath, sizeof(filepath), "%s/%s", ctx.dbdir,
			pkg_repo_binary_get_filename(pkg_repo_name(repo)));
-
		if (stat(filepath, &st) != -1) {
-
			if (!got_meta && !force)
+
		if (got_meta && stat(filepath, &st) != -1) {
+
			if (!force)
				t = st.st_mtime;
		}
	}
modified libpkg/rsa.c
@@ -50,7 +50,7 @@ _load_rsa_private_key(struct rsa_key *rsa)
{
	FILE *fp;

-
	if ((fp = fopen(rsa->path, "r")) == NULL)
+
	if ((fp = fopen(rsa->path, "re")) == NULL)
		return (EPKG_FATAL);

	if ((rsa->key = RSA_new()) == NULL) {
modified libpkg/scripts.c
@@ -42,7 +42,7 @@
#include <stdlib.h>
#include <limits.h>
#include <string.h>
-
#include <utstring.h>
+
#include <xstring.h>

#include "pkg.h"
#include "private/pkg.h"
@@ -53,8 +53,8 @@ extern char **environ;
int
pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
{
-
	UT_string *script_cmd;
-
	size_t i, j;
+
	xstring *script_cmd = NULL;
+
	size_t i, j, script_len;
	int error, pstat;
	pid_t pid;
	const char *script_cmd_p;
@@ -67,21 +67,14 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
	bool use_pipe = 0;
	bool debug = false;
	ssize_t bytes_written;
-
	size_t script_cmd_len;
	long argmax;
-
	int cur_pipe[2];
+
	int cur_pipe[2] = {-1, -1};
#ifdef PROC_REAP_KILL
	bool do_reap;
	pid_t mypid;
	struct procctl_reaper_status info;
	struct procctl_reaper_kill killemall;
#endif
-
	struct pollfd pfd;
-
	bool should_waitpid;
-
	ssize_t linecap = 0;
-
	char *line = NULL;
-
	FILE *f;
-

	struct {
		const char * const arg;
		const pkg_script b;
@@ -94,10 +87,7 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
		{"POST-DEINSTALL", PKG_SCRIPT_DEINSTALL, PKG_SCRIPT_POST_DEINSTALL},
	};

-
	utstring_new(script_cmd);
-

	if (!pkg_object_bool(pkg_config_get("RUN_SCRIPTS"))) {
-
		utstring_free(script_cmd);
		return (EPKG_OK);
	}

@@ -116,7 +106,7 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
		if (pkg_script_get(pkg, j) == NULL)
			continue;
		if (j == map[i].a || j == map[i].b) {
-
			utstring_clear(script_cmd);
+
			xstring_renew(script_cmd);
			if (upgrade) {
				setenv("PKG_UPGRADE", "true", 1);
			}
@@ -126,16 +116,15 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
			setenv("PKG_ROOTDIR", ctx.pkg_rootdir, 1);
			debug = pkg_object_bool(pkg_config_get("DEBUG_SCRIPTS"));
			if (debug)
-
				utstring_printf(script_cmd, "set -x\n");
-
			pkg_utstring_printf(script_cmd, "set -- %n-%v", pkg, pkg);
+
				fprintf(script_cmd->fp, "set -x\n");
+
			pkg_fprintf(script_cmd->fp, "set -- %n-%v", pkg, pkg);

			if (j == map[i].b) {
				/* add arg **/
-
				utstring_printf(script_cmd, " %s", map[i].arg);
+
				fprintf(script_cmd->fp, " %s", map[i].arg);
			}

-
			utstring_printf(script_cmd, "\n%s",
-
			    utstring_body(pkg->scripts[j]));
+
			fprintf(script_cmd->fp, "\n%s", pkg->scripts[j]->buf);

			/* Determine the maximum argument length for the given
			   script to determine if /bin/sh -c can be used, or
@@ -148,13 +137,20 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
				argmax -= strlen(*ep) + 1 + sizeof(*ep);
			argmax -= 1 + sizeof(*ep);

-
			pkg_debug(3, "Scripts: executing\n--- BEGIN ---\n%s\nScripts: --- END ---", utstring_body(script_cmd));
+
			fflush(script_cmd->fp);
+
			script_len = strlen(script_cmd->buf);
+
			pkg_debug(3, "Scripts: executing\n--- BEGIN ---\n%s\nScripts: --- END ---", script_cmd->buf);
			posix_spawn_file_actions_init(&action);
			if (get_socketpair(cur_pipe) == -1) {
				pkg_emit_errno("pkg_script_run", "socketpair");
				goto cleanup;
			}

+
			if (fcntl(cur_pipe[0], F_SETFL, O_NONBLOCK) == -1) {
+
				pkg_emit_errno("pkg_script_run", "fcntl");
+
				goto cleanup;
+
			}
+

			setenv("PKG_MSGFD", "4", 1);

			posix_spawn_file_actions_adddup2(&action, cur_pipe[1], 4);
@@ -167,12 +163,10 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
				if (i != cur_pipe[0])
					posix_spawn_file_actions_addclose(&action, i);
			}
-
			if (utstring_len(script_cmd) > argmax) {
+
			if (script_len > argmax) {
				if (pipe(stdin_pipe) < 0) {
					ret = EPKG_FATAL;
					posix_spawn_file_actions_destroy(&action);
-
					close(cur_pipe[0]);
-
					close(cur_pipe[1]);
					goto cleanup;
				}

@@ -191,8 +185,6 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
					pkg_errno("Cannot open %s", "/dev/null");
					ret = EPKG_FATAL;
					posix_spawn_file_actions_destroy(&action);
-
					close(cur_pipe[0]);
-
					close(cur_pipe[1]);
					goto cleanup;
				}
				posix_spawn_file_actions_adddup2(&action,
@@ -200,7 +192,7 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)

				argv[0] = _PATH_BSHELL;
				argv[1] = "-c";
-
				argv[2] = utstring_body(script_cmd);
+
				argv[2] = script_cmd->buf;
				argv[3] = NULL;

				use_pipe = 0;
@@ -212,8 +204,6 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
				errno = error;
				pkg_errno("Cannot runscript %s", map[i].arg);
				posix_spawn_file_actions_destroy(&action);
-
				close(cur_pipe[0]);
-
				close(cur_pipe[1]);
				goto cleanup;
			}
			posix_spawn_file_actions_destroy(&action);
@@ -221,18 +211,17 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
			if (fd != -1)
				close(fd);
			if (use_pipe) {
-
				script_cmd_p = utstring_body(script_cmd);
-
				script_cmd_len = utstring_len(script_cmd);
-
				while (script_cmd_len > 0) {
+
				script_cmd_p = script_cmd->buf;
+
				while (script_len > 0) {
					if ((bytes_written = write(stdin_pipe[1], script_cmd_p,
-
					    script_cmd_len)) == -1) {
+
					    script_len)) == -1) {
						if (errno == EINTR)
							continue;
						ret = EPKG_FATAL;
						goto cleanup;
					}
					script_cmd_p += bytes_written;
-
					script_cmd_len -= bytes_written;
+
					script_len -= bytes_written;
				}
				close(stdin_pipe[1]);
			}
@@ -240,83 +229,26 @@ pkg_script_run(struct pkg * const pkg, pkg_script type, bool upgrade)
			unsetenv("PKG_PREFIX");

			close(cur_pipe[1]);
-
			memset(&pfd, 0, sizeof(pfd));
-
			pfd.fd = cur_pipe[0];
-
			pfd.events = POLLIN | POLLERR | POLLHUP;
-

-
			f = fdopen(pfd.fd, "r");
-
			should_waitpid = true;
-
			for (;;) {
-
				errno = 0;
-
				int pres = poll(&pfd, 1, 1000);
-
				if (pres == -1) {
-
					if (errno == EINTR) {
-
						continue;
-
					} else {
-
						pkg_emit_error("poll() "
-
						    "failed: %s", strerror(errno));
-
						ret = EPKG_FATAL;
-
						goto cleanup;
-
					}
-
				}
-
				if (pres == 0) {
-
					pid_t p;
-
					assert(should_waitpid);
-
					while ((p = waitpid(pid, &pstat, WNOHANG)) == -1) {
-
						if (errno != EINTR) {
-
							pkg_emit_error("waitpid() "
-
							    "failed: %s", strerror(errno));
-
							ret = EPKG_FATAL;
-
							goto cleanup;
-
						}
-
					}
-
					if (p > 0) {
-
						should_waitpid = false;
-
						break;
-
					}
-
					continue;
-
				}
-
				if (pfd.revents & (POLLERR|POLLHUP))
-
					break;
-
				if (getline(&line, &linecap, f) > 0)
-
					pkg_emit_message(line);
-
				if (feof(f))
-
					break;
-
			}
-
			/* Gather any remaining output */
-
			while (!feof(f) && !ferror(f) && getline(&line, &linecap, f) > 0) {
-
				pkg_emit_message(line);
-
			}
-
			fclose(f);
-

-
			while (should_waitpid && waitpid(pid, &pstat, 0) == -1) {
-
				if (errno != EINTR) {
-
					pkg_emit_error("waitpid() failed: %s",
-
					    strerror(errno));
-
					ret = EPKG_FATAL;
-
					goto cleanup;
-
				}
-
			}
+
			cur_pipe[1] = -1;

-
			if (WEXITSTATUS(pstat) != 0) {
-
				if (WEXITSTATUS(pstat) == 3)
-
					exit(0);
+
			ret = pkg_script_run_child(pid, &pstat, cur_pipe[0], map[i].arg);

-
				pkg_emit_error("%s script failed", map[i].arg);
-
				ret = EPKG_FATAL;
-
				goto cleanup;
-
			}
+
			close(cur_pipe[0]);
+
			cur_pipe[0] = -1;
		}
	}

cleanup:

-
	free(line);
-
	utstring_free(script_cmd);
+
	xstring_free(script_cmd);
	if (stdin_pipe[0] != -1)
		close(stdin_pipe[0]);
	if (stdin_pipe[1] != -1)
		close(stdin_pipe[1]);
+
	if (cur_pipe[0] != -1)
+
		close(cur_pipe[0]);
+
	if (cur_pipe[1] != -1)
+
		close(cur_pipe[1]);

#ifdef PROC_REAP_KILL
	/*
@@ -340,3 +272,73 @@ cleanup:
	return (ret);
}

+

+
int
+
pkg_script_run_child(int pid, int *pstat, int inputfd, const char* script_name) {
+
	struct pollfd pfd;
+
	bool wait_for_child;
+
	char msgbuf[16384+1];
+

+

+
	memset(&pfd, 0, sizeof(pfd));
+
	pfd.events = POLLIN | POLLERR | POLLHUP;
+
	pfd.fd = inputfd;
+

+
	// Wait for child to exit, and read input, including all queued input on child exit.
+
	wait_for_child = true;
+
	do {
+
		pfd.revents = 0;
+
		errno = 0;
+
		// Check if child is running, get exitstatus if newly terminated.
+
		pid_t p = 0;
+
		while (wait_for_child && (p = waitpid(pid, pstat, WNOHANG)) == -1) {
+
			if (errno != EINTR) {
+
				pkg_emit_error("waitpid() failed: %s",
+
				    strerror(errno));
+
				return (EPKG_FATAL);
+
			}
+
		}
+
		if (p > 0) {
+
			wait_for_child = false;
+
		}
+
		// Check for input from child, but only wait for more if child is still running.
+
		// Read/print all available input.
+
		ssize_t readsize;
+
		do {
+
			readsize = 0;
+
			int pres;
+
			while ((pres = poll(&pfd, 1, wait_for_child ? 1000 : 0)) == -1) {
+
				if (errno != EINTR) {
+
					pkg_emit_error("poll() failed: %s",
+
					    strerror(errno));
+
					return (EPKG_FATAL);
+
				}
+
			}
+
			if (pres > 0 && pfd.revents & POLLIN) {
+
				while ((readsize = read(inputfd, msgbuf, sizeof msgbuf - 1)) < 0) {
+
					// MacOS gives us ECONNRESET on child exit
+
					if (errno == EAGAIN || errno == ECONNRESET) {
+
						break;
+
					}
+
					if (errno != EINTR) {
+
						pkg_emit_errno(__func__, "read");
+
						return (EPKG_FATAL);
+
					}
+
				}
+
				if (readsize > 0) {
+
					msgbuf[readsize] = '\0';
+
					pkg_emit_message(msgbuf);
+
				}
+
			}
+
		} while (readsize > 0);
+
	} while (wait_for_child);
+

+
	if (WEXITSTATUS(*pstat) != 0) {
+
		if (WEXITSTATUS(*pstat) == 3)
+
			exit(0);
+

+
		pkg_emit_error("%s script failed", script_name);
+
		return (EPKG_FATAL);
+
	}
+
	return (EPKG_OK);
+
}
added libpkg/triggers.c
@@ -0,0 +1,547 @@
+
/*-
+
 * Copyright (c) 2020 Baptiste Daroussin <bapt@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.
+
 */
+

+
#include "pkg_config.h"
+

+
#ifdef HAVE_CAPSICUM
+
#include <sys/capsicum.h>
+
#endif
+

+
#include <sys/stat.h>
+
#include <sys/wait.h>
+

+
#include <dirent.h>
+
#include <err.h>
+
#include <errno.h>
+
#include <fcntl.h>
+
#include <paths.h>
+
#include <spawn.h>
+

+
#include <private/pkg.h>
+
#include <private/event.h>
+
#include <private/lua.h>
+

+
extern char **environ;
+

+
static script_type_t
+
get_script_type(const char *str)
+
{
+
	if (strcasecmp(str, "lua") == 0)
+
		return (SCRIPT_LUA);
+
	if (strcasecmp(str, "shell") == 0)
+
		return (SCRIPT_SHELL);
+
	return (SCRIPT_UNKNOWN);
+
}
+

+
static ucl_object_t *
+
trigger_open_schema(void)
+
{
+
	struct ucl_parser *parser;
+
	ucl_object_t *trigger_schema;
+
	static const char trigger_schema_str[] = ""
+
		"{"
+
		"  type = object;"
+
		"  properties {"
+
		"    description: { type = string };"
+
		"    path: { "
+
		"      type = array; "
+
		"      item = { type = string };"
+
		"    };"
+
		"    path_glob: { "
+
		"      type = array; "
+
		"      item = { type = string };"
+
		"    };"
+
		"    path_regexp: { "
+
		"      type = array; "
+
		"      item = { type = string };"
+
		"    };"
+
		"    cleanup = { "
+
		"      type = object; "
+
		"      properties = {"
+
		"        type = { "
+
		"          type = string,"
+
		"          enum: [lua, shell];"
+
		"        };"
+
		"        script = { type = string };"
+
		"      }; "
+
		"      required = [ type, script ];"
+
		"    };"
+
		"    trigger = { "
+
		"      type = object; "
+
		"      properties = {"
+
		"        type = { "
+
		"          type = string,"
+
		"          enum: [lua, shell];"
+
		"        };"
+
		"        script = { type = string };"
+
		"      }; "
+
		"      required = [ type, script ];"
+
		"    };"
+
		"  }\n"
+
		"  required = [ description, trigger ];"
+
		"}";
+

+
	parser = ucl_parser_new(UCL_PARSER_NO_FILEVARS);
+
	if (!ucl_parser_add_chunk(parser, trigger_schema_str,
+
	    sizeof(trigger_schema_str) -1)) {
+
		pkg_emit_error("Cannot parse schema for trigger: %s",
+
		    ucl_parser_get_error(parser));
+
		ucl_parser_free(parser);
+
		return (NULL);
+
	}
+

+
	trigger_schema = ucl_parser_get_object(parser);
+
	ucl_parser_free(parser);
+
	return (trigger_schema);
+
}
+

+
static struct trigger *
+
trigger_load(int dfd, const char *name, bool cleanup_only, ucl_object_t *schema)
+
{
+
	struct ucl_parser *p;
+
	ucl_object_t *obj = NULL;
+
	const ucl_object_t *o = NULL, *trigger = NULL, *cleanup = NULL;
+
	int fd;
+
	struct ucl_schema_error err;
+
	struct trigger *t;
+

+
	fd = openat(dfd, name, O_RDONLY);
+
	if (fd == -1) {
+
		pkg_emit_error("Unable to open the tigger: %s", name);
+
		return (NULL);
+
	}
+

+
	p = ucl_parser_new(0);
+
	if (!ucl_parser_add_fd(p, fd)) {
+
		pkg_emit_error("Error parsing trigger '%s': %s", name,
+
		    ucl_parser_get_error(p));
+
		ucl_parser_free(p);
+
		close(fd);
+
		return (NULL);
+
	}
+
	close(fd);
+

+
	obj = ucl_parser_get_object(p);
+
	ucl_parser_free(p);
+
	if (obj == NULL)
+
		return (NULL);
+

+
	if (!ucl_object_validate(obj, schema, &err)) {
+
		pkg_emit_error("trigger definition %s cannot be validated: %s", name, err.msg);
+
		ucl_object_unref(obj);
+
		return (NULL);
+
	}
+

+
	t = xcalloc(1, sizeof(*t));
+
	t->name = xstrdup(name);
+

+
	if (cleanup_only) {
+
		cleanup = ucl_object_find_key(obj, "cleanup");
+
		if (cleanup == NULL)
+
			goto err;
+
		o = ucl_object_find_key(cleanup, "type");
+
		if (o == NULL) {
+
			pkg_emit_error("cleanup %s doesn't have a script type", name);
+
			goto err;
+
		}
+
		t->cleanup.type = get_script_type(ucl_object_tostring(o));
+
		if (t->cleanup.type == SCRIPT_UNKNOWN) {
+
			pkg_emit_error("Unknown script type for cleanup in %s", name);
+
			goto err;
+
		}
+
		o = ucl_object_find_key(cleanup, "script");
+
		if (o == NULL) {
+
			pkg_emit_error("No script in cleanup %s", name);
+
			goto err;
+
		}
+

+
		t->cleanup.script = xstrdup(ucl_object_tostring(o));
+
		ucl_object_unref(obj);
+
		return (t);
+
	}
+

+
	trigger = ucl_object_find_key(obj, "trigger");
+
	if (trigger == NULL) {
+
		pkg_emit_error("trigger %s doesn't have any trigger block, ignoring", name);
+
		goto err;
+
	}
+

+
	o = ucl_object_find_key(trigger, "type");
+
	if (o == NULL) {
+
		pkg_emit_error("trigger %s doesn't have a script type", name);
+
		goto err;
+
	}
+
	t->script.type = get_script_type(ucl_object_tostring(o));
+
	if (t->script.type == SCRIPT_UNKNOWN) {
+
		pkg_emit_error("Unknown script type for trigger in %s", name);
+
		goto err;
+
	}
+
	o = ucl_object_find_key(trigger, "script");
+
	if (o == NULL) {
+
		pkg_emit_error("No script in trigger %s", name);
+
		goto err;
+
	}
+

+
	t->script.script = xstrdup(ucl_object_tostring(o));
+

+
	o = ucl_object_find_key(obj, "path");
+
	if (o != NULL)
+
		t->path = ucl_object_ref(o);
+
	o = ucl_object_find_key(obj, "path_glob");
+
	if (o != NULL)
+
		t->path_glob = ucl_object_ref(o);
+
	o = ucl_object_find_key(obj, "path_regex");
+
	if (o != NULL)
+
		t->path_regex = ucl_object_ref(o);
+
	if (t->path == NULL &&
+
	    t->path_glob == NULL &&
+
	    t->path_regex == NULL) {
+
		pkg_emit_error("No path* in trigger %s, skipping", name);
+
		goto err;
+
	}
+

+
	ucl_object_unref(obj);
+
	return (t);
+

+
err:
+
	if (t) {
+
		if (t->path != NULL)
+
			ucl_object_unref(t->path);
+
		if (t->path_glob != NULL)
+
			ucl_object_unref(t->path_glob);
+
		if (t->path_regex != NULL)
+
			ucl_object_unref(t->path_regex);
+
		if (t->script.script != NULL)
+
			free(t->script.script);
+
		if (t->cleanup.script != NULL)
+
			free(t->cleanup.script);
+
		free(t);
+
	}
+
	ucl_object_unref(obj);
+
	return (NULL);
+
}
+

+
struct trigger *
+
triggers_load(bool cleanup_only)
+
{
+
	int dfd;
+
	DIR *d;
+
	struct dirent *e;
+
	struct trigger *triggers, *t;
+
	ucl_object_t *schema;
+
	struct stat st;
+

+
	triggers = NULL;
+

+
	dfd = openat(ctx.rootfd, RELATIVE_PATH(ctx.triggers_path), O_DIRECTORY);
+
	if (dfd == -1) {
+
		if (errno != ENOENT)
+
			pkg_emit_error("Unable to open the trigger directory");
+
		return (NULL);
+
	}
+
	d = fdopendir(dfd);
+
	if (d == NULL) {
+
		pkg_emit_error("Unable to open the trigger directory");
+
		close(dfd);
+
		return (NULL);
+
	}
+

+
	schema = trigger_open_schema();
+

+
	while ((e = readdir(d)) != NULL) {
+
		/* ignore all hidden files */
+
		if (e->d_name[0] ==  '.')
+
			continue;
+
		/* only regular files are considered */
+
		if (fstatat(dfd, e->d_name, &st, AT_SYMLINK_NOFOLLOW) != 0) {
+
			pkg_emit_errno("fstatat", e->d_name);
+
			return (NULL);
+
		}
+
		t = trigger_load(dfd, e->d_name, cleanup_only, schema);
+
		if (t != NULL)
+
			DL_APPEND(triggers, t);
+
	}
+

+
	closedir(d);
+
	ucl_object_unref(schema);
+
	return (triggers);
+
}
+

+
void
+
trigger_free(struct trigger *t)
+
{
+
	if (t == NULL)
+
		return;
+
	free(t->name);
+
	if (t->path != NULL)
+
		ucl_object_unref(t->path);
+
	if (t->path != NULL)
+
		ucl_object_unref(t->path_glob);
+
	if (t->path != NULL)
+
		ucl_object_unref(t->path_regex);
+
	free(t->cleanup.script);
+
	free(t->script.script);
+
}
+

+
static int
+
trigger_execute_shell(const char *script, kh_strings_t *args __unused)
+
{
+
	posix_spawn_file_actions_t action;
+
	int stdin_pipe[2] = {-1, -1};
+
	const char *argv[3];
+
	const char *script_p;
+
	size_t len;
+
	ssize_t bw;
+
	int error, pstat;
+
	int ret = EPKG_OK;
+
	pid_t pid;
+

+
	if (pipe(stdin_pipe) < 0)
+
		return (EPKG_FATAL);
+

+
	posix_spawn_file_actions_init(&action);
+
	posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO);
+
	posix_spawn_file_actions_addclose(&action, stdin_pipe[1]);
+

+
	argv[0] = _PATH_BSHELL;
+
	argv[1] = "-s";
+
	argv[2] = NULL;
+

+
	if ((error = posix_spawn(&pid, _PATH_BSHELL, &action, NULL,
+
	    __DECONST(char **, argv), environ)) != 0) {
+
		errno = error;
+
		pkg_errno("Cannot run trigger script %s", script);
+
		posix_spawn_file_actions_destroy(&action);
+
		ret = EPKG_FATAL;
+
		goto cleanup;
+
	}
+
	posix_spawn_file_actions_destroy(&action);
+
	len = strlen(script);
+

+
	while (len > 0) {
+
		script_p = script;
+
		if ((bw = write(stdin_pipe[1], script_p, len)) == -1) {
+
			if (errno == EINTR)
+
				continue;
+
			ret = EPKG_FATAL;
+
			goto cleanup;
+
		}
+
		script_p += bw;
+
		len -= bw;
+
	}
+
	close(stdin_pipe[1]);
+

+
	while (waitpid(pid, &pstat, 0) == -1) {
+
		if (errno != EINTR) {
+
			pkg_emit_error("waitpid() failed: %s", strerror(errno));
+
			ret = EPKG_FATAL;
+
			goto cleanup;
+
		}
+
	}
+

+
	if (WEXITSTATUS(pstat) != 0)
+
		ret = EPKG_FATAL;
+

+
cleanup:
+
	if (stdin_pipe[0] != -1)
+
		close(stdin_pipe[0]);
+
	if (stdin_pipe[1] != -1)
+
		close(stdin_pipe[1]);
+

+
	return (ret);
+
}
+

+
static int
+
trigger_execute_lua(const char *script, kh_strings_t *args)
+
{
+
	lua_State *L;
+
	int pstat;
+

+
	pid_t pid = fork();
+
	if (pid == 0) {
+
		L = luaL_newstate();
+
		luaL_openlibs(L);
+
		lua_override_ios(L);
+
		char *dir;
+
		char **arguments = NULL;
+
		int i = 0;
+
		if (args != NULL) {
+
			arguments = xcalloc(kh_count(args), sizeof(char*));
+
			kh_foreach_value(args, dir, {
+
				arguments[i++] = dir;
+
			});
+
		}
+
		lua_args_table(L, arguments, i);
+
#ifdef HAVE_CAPSICUM
+
		if (cap_enter() < 0 && errno != ENOSYS) {
+
			err(1, "cap_enter failed");
+
		}
+
#endif
+
		if (luaL_dostring(L, script)) {
+
			pkg_emit_error("Failed to execute lua trigger: "
+
					"%s", lua_tostring(L, -1));
+
			_exit(1);
+
		}
+
		if (lua_tonumber(L, -1) != 0) {
+
			lua_close(L);
+
			_exit(1);
+
		}
+
		lua_close(L);
+
		_exit(0);
+
	} else if (pid < 0) {
+
		pkg_emit_errno("Cannot fork", "lua_script");
+
		return (EPKG_FATAL);
+
	}
+
	while (waitpid(pid, &pstat, 0) == -1) {
+
		if (errno != EINTR) {
+
			pkg_emit_error("waitpid() failed: %s", strerror(errno));
+
			return (EPKG_FATAL );
+
		}
+
	}
+
	if (WEXITSTATUS(pstat) != 0) {
+
		pkg_emit_error("lua script failed");
+
		return (EPKG_FATAL);
+
	}
+

+
	return (EPKG_OK);
+
}
+

+
static void
+
trigger_check_match(struct trigger *t, char *dir)
+
{
+
	const ucl_object_t *cur;
+
	ucl_object_iter_t it;
+

+
	if (t->path != NULL) {
+
		it = NULL;
+
		while ((cur = ucl_iterate_object(t->path, &it, true))) {
+
			if (strcmp(dir, ucl_object_tostring(cur)) == 0) {
+
				kh_safe_add(strings, t->matched, dir, dir);
+
				return;
+
			}
+
		}
+
	}
+

+
	if (match_ucl_lists(dir, t->path_glob, t->path_regex))
+
		kh_safe_add(strings, t->matched, dir, dir);
+
}
+

+
/*
+
 * first execute all triggers then the cleanup scripts
+
 * from the triggers that are not there anymore
+
 * Then execute all triggers
+
 */
+
int
+
triggers_execute(struct trigger *cleanup_triggers)
+
{
+
	struct trigger *triggers, *t, *trigger;
+
	kh_strings_t *th = NULL;
+
	char *dir;
+
	int ret = EPKG_OK;
+

+
	triggers = triggers_load(false);
+

+
	/*
+
	 * Generate a hash table to ease the lookup later
+
	 */
+
	if (cleanup_triggers != NULL) {
+
		LL_FOREACH(triggers, t) {
+
			kh_add(strings, th, t->name, t->name, free);
+
		}
+
	}
+

+
	/*
+
	 * only keep from the cleanup the one that are not anymore in triggers
+
	 */
+
	LL_FOREACH_SAFE(cleanup_triggers, trigger, t) {
+
		if (kh_contains(strings, th, trigger->name)) {
+
			DL_DELETE(cleanup_triggers, trigger);
+
			trigger_free(trigger);
+
		}
+
	}
+
	kh_destroy_strings(th);
+

+
	pkg_emit_triggers_begin();
+
	LL_FOREACH(cleanup_triggers, t) {
+
		pkg_emit_trigger(t->name, true);
+
		if (t->cleanup.type == SCRIPT_LUA) {
+
			ret = trigger_execute_lua(t->cleanup.script, NULL);
+
		} else if (t->cleanup.type == SCRIPT_SHELL) {
+
			ret = trigger_execute_shell(t->cleanup.script, NULL);
+
		}
+
		if (ret != EPKG_OK)
+
			goto cleanup;
+
	}
+

+
	if (ctx.touched_dir_hash) {
+
		kh_foreach_value(ctx.touched_dir_hash, dir, {
+
				LL_FOREACH(triggers, t) {
+
				trigger_check_match(t, dir);
+
				}
+
				/* We need to check if that matches a trigger */
+
				});
+
	}
+

+
	LL_FOREACH(triggers, t) {
+
		if (t->matched == NULL)
+
			continue;
+
		pkg_emit_trigger(t->name, false);
+
		if (t->script.type == SCRIPT_LUA) {
+
			ret = trigger_execute_lua(t->script.script, t->matched);
+
		} else if (t->script.type == SCRIPT_SHELL) {
+
			ret = trigger_execute_shell(t->script.script, t->matched);
+
		}
+
		if (ret != EPKG_OK)
+
			goto cleanup;
+
	}
+
	pkg_emit_triggers_finished();
+

+
cleanup:
+
	DL_FOREACH_SAFE(cleanup_triggers, trigger, t) {
+
		DL_DELETE(cleanup_triggers, trigger);
+
		trigger_free(trigger);
+
	}
+

+
	DL_FOREACH_SAFE(triggers, trigger, t) {
+
		DL_DELETE(triggers, trigger);
+
		trigger_free(trigger);
+
	}
+
	return (EPKG_OK);
+
}
+

+
void
+
append_touched_file(const char *path)
+
{
+
	char *newpath, *walk;
+

+
	newpath = xstrdup(path);
+
	walk = strrchr(newpath, '/');
+
	if (walk == NULL)
+
		return;
+
	*walk = '\0';
+

+
	kh_add(strings, ctx.touched_dir_hash, newpath, newpath, free);
+
}
modified libpkg/utils.c
@@ -1,5 +1,5 @@
/*-
-
 * Copyright (c) 2011-2016 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2011-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
 * Copyright (c) 2013 Vsevolod Stakhov <vsevolod@FreeBSD.org>
 * All rights reserved.
@@ -46,6 +46,7 @@
#include <paths.h>
#include <float.h>
#include <math.h>
+
#include <regex.h>

#include <bsd_compat.h>

@@ -57,6 +58,40 @@

extern struct pkg_ctx ctx;

+
bool
+
match_ucl_lists(const char *buf, const ucl_object_t *globs, const ucl_object_t *regexes)
+
{
+
	const ucl_object_t *cur;
+
	ucl_object_iter_t it;
+

+
	if (globs == NULL && regexes == NULL)
+
		return (false);
+

+
	if (globs != NULL) {
+
		it = NULL;
+
		while ((cur = ucl_iterate_object(globs, &it, true))) {
+
			if (fnmatch(ucl_object_tostring(cur), buf, 0) == 0)
+
				return (true);
+
		}
+
	}
+

+
	if (regexes != NULL) {
+
		it = NULL;
+
		while ((cur = ucl_iterate_object(regexes, &it, true))) {
+
			regex_t re;
+
			regcomp(&re, ucl_object_tostring(cur),
+
			   REG_EXTENDED|REG_NOSUB);
+
			if (regexec(&re, buf, 0, NULL, 0) == 0) {
+
				regfree(&re);
+
				return (true);
+
			}
+
			regfree(&re);
+
		}
+
	}
+

+
	return (false);
+
}
+

int
mkdirs(const char *_path)
{
@@ -180,40 +215,48 @@ file_to_buffer(const char *path, char **buffer, off_t *sz)

int
format_exec_cmd(char **dest, const char *in, const char *prefix,
-
    const char *plist_file, char *line, int argc, char **argv)
+
    const char *plist_file, char *line, int argc, char **argv, bool lua)
{
-
	UT_string *buf;
+
	xstring *buf;
	char path[MAXPATHLEN];
	char *cp;
	size_t sz;

-
	utstring_new(buf);
+
	buf = xstring_new();
+

+
	if (line != NULL && argv != NULL) {
+
		if (lua) {
+
			fprintf(buf->fp, "-- args: %s\n", line);
+
		} else {
+
			fprintf(buf->fp, "# args: %s\n", line);
+
		}
+
	}

	while (in[0] != '\0') {
		if (in[0] != '%') {
-
			utstring_printf(buf, "%c", in[0]);
+
			fputc(in[0], buf->fp);
			in++;
			continue;
		}
		in++;
		switch(in[0]) {
		case 'D':
-
			utstring_printf(buf, "%s", prefix);
+
			fprintf(buf->fp, "%s", prefix);
			break;
		case 'F':
			if (plist_file == NULL || plist_file[0] == '\0') {
				pkg_emit_error("No files defined %%F couldn't "
				    "be expanded, ignoring %s", in);
-
				utstring_free(buf);
+
				xstring_free(buf);
				return (EPKG_FATAL);
			}
-
			utstring_printf(buf, "%s", plist_file);
+
			fprintf(buf->fp, "%s", plist_file);
			break;
		case 'f':
			if (plist_file == NULL || plist_file[0] == '\0') {
				pkg_emit_error("No files defined %%f couldn't "
				    "be expanded, ignoring %s", in);
-
				utstring_free(buf);
+
				xstring_free(buf);
				return (EPKG_FATAL);
			}
			if (prefix[strlen(prefix) - 1] == '/')
@@ -224,13 +267,13 @@ format_exec_cmd(char **dest, const char *in, const char *prefix,
				    prefix, plist_file);
			cp = strrchr(path, '/');
			cp ++;
-
			utstring_printf(buf, "%s", cp);
+
			fprintf(buf->fp, "%s", cp);
			break;
		case 'B':
			if (plist_file == NULL || plist_file[0] == '\0') {
				pkg_emit_error("No files defined %%B couldn't "
				    "be expanded, ignoring %s", in);
-
				utstring_free(buf);
+
				xstring_free(buf);
				return (EPKG_FATAL);
			}
			if (prefix[strlen(prefix) - 1] == '/')
@@ -241,14 +284,14 @@ format_exec_cmd(char **dest, const char *in, const char *prefix,
				    plist_file);
			cp = strrchr(path, '/');
			cp[0] = '\0';
-
			utstring_printf(buf, "%s", path);
+
			fprintf(buf->fp, "%s", path);
			break;
		case '%':
-
			utstring_printf(buf, "%c", '%');
+
			fputc('%', buf->fp);
			break;
		case '@':
			if (line != NULL) {
-
				utstring_printf(buf, "%s", line);
+
				fprintf(buf->fp, "%s", line);
				break;
			}

@@ -259,7 +302,7 @@ format_exec_cmd(char **dest, const char *in, const char *prefix,
			 */
			/* FALLTHRU */
		case '#':
-
			utstring_printf(buf, "%d", argc);
+
			fprintf(buf->fp, "%d", argc);
			break;
		default:
			if ((sz = strspn(in, "0123456789")) > 0) {
@@ -268,23 +311,22 @@ format_exec_cmd(char **dest, const char *in, const char *prefix,
					pkg_emit_error("Requesting argument "
					    "%%%d while only %d arguments are"
					    " available", pos, argc);
-
					utstring_free(buf);
+
					xstring_free(buf);
					return (EPKG_FATAL);
				}
-
				utstring_printf(buf, "%s", argv[pos -1]);
+
				fprintf(buf->fp, "%s", argv[pos -1]);
				in += sz -1;
				break;
			}
-
			utstring_printf(buf, "%c%c", '%', in[0]);
+
			fprintf(buf->fp, "%c%c", '%', in[0]);
			break;
		}

		in++;
	}

-
	*dest = xstrdup(utstring_body(buf));
-
	utstring_free(buf);
-
	
+
	*dest = xstring_get(buf);
+

	return (EPKG_OK);
}

@@ -553,11 +595,11 @@ ucl_file_append_double(double val, void *data)
static int
ucl_buf_append_character(unsigned char c, size_t len, void *data)
{
-
	UT_string *buf = data;
+
	xstring *buf = data;
	size_t i;

	for (i = 0; i < len; i++)
-
		utstring_printf(buf, "%c", c);
+
		fprintf(buf->fp, "%c", c);

	return (0);
}
@@ -565,9 +607,9 @@ ucl_buf_append_character(unsigned char c, size_t len, void *data)
static int
ucl_buf_append_len(const unsigned char *str, size_t len, void *data)
{
-
	UT_string *buf = data;
+
	xstring *buf = data;

-
	utstring_bincpy(buf, str, len);
+
	fprintf(buf->fp, "%.*s", (int)len, str);

	return (0);
}
@@ -575,9 +617,9 @@ ucl_buf_append_len(const unsigned char *str, size_t len, void *data)
static int
ucl_buf_append_int(int64_t val, void *data)
{
-
	UT_string *buf = data;
+
	xstring *buf = data;

-
	utstring_printf(buf, "%"PRId64, val);
+
	fprintf(buf->fp, "%"PRId64, val);

	return (0);
}
@@ -585,15 +627,15 @@ ucl_buf_append_int(int64_t val, void *data)
static int
ucl_buf_append_double(double val, void *data)
{
-
	UT_string *buf = data;
+
	xstring *buf = data;
	const double delta = 0.0000001;

	if (val == (double)(int)val) {
-
		utstring_printf(buf, "%.1lf", val);
+
		fprintf(buf->fp, "%.1lf", val);
	} else if (fabs(val - (double)(int)val) < delta) {
-
		utstring_printf(buf, "%.*lg", DBL_DIG, val);
+
		fprintf(buf->fp, "%.*lg", DBL_DIG, val);
	} else {
-
		utstring_printf(buf, "%lf", val);
+
		fprintf(buf->fp, "%lf", val);
	}

	return (0);
@@ -620,7 +662,7 @@ ucl_object_emit_file(const ucl_object_t *obj, enum ucl_emitter emit_type,

bool
ucl_object_emit_buf(const ucl_object_t *obj, enum ucl_emitter emit_type,
-
                     UT_string **buf)
+
                     xstring **buf)
{
	bool ret = false;
	struct ucl_emitter_functions func = {
@@ -630,10 +672,7 @@ ucl_object_emit_buf(const ucl_object_t *obj, enum ucl_emitter emit_type,
		.ucl_emitter_append_double = ucl_buf_append_double
	};

-
	if (*buf == NULL)
-
		utstring_new(*buf);
-
	else
-
		utstring_clear(*buf);
+
	xstring_renew(*buf);

	func.ud = *buf;

@@ -844,3 +883,35 @@ get_socketpair(int *pipe)

	return (r);
}
+

+
char *
+
get_dirname(char *d)
+
{
+
	char *walk;
+

+
	if (d == NULL)
+
		return (__DECONST(char *, "."));
+

+
	walk = strrchr(d, '/');
+
	if (walk == NULL) {
+
		d[0] = '.';
+
		d[1] = '\0';
+
	} else {
+
		*walk = '\0';
+
	}
+

+
	return (d);
+
}
+

+
char *
+
rtrimspace(char *buf)
+
{
+
	char *cp = buf + strlen(buf) -1;
+

+
	while (cp > buf && isspace(*cp)) {
+
		*cp = 0;
+
		cp --;
+
	}
+

+
	return (buf);
+
}
modified libpkg/xmalloc.h
@@ -1,7 +1,9 @@
#ifndef XMALLOC_H
#define XMALLOC_H

+
#include <stdarg.h>
#include <stdio.h>
+
#include <string.h>

static inline void *xmalloc(size_t size)
{
added libpkg/xstring.h
@@ -0,0 +1,67 @@
+
#ifndef __XSTRING_H_
+
#define __XSTRING_H_
+

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

+
struct xstring {
+
	char* buf;
+
	size_t size;
+
	FILE* fp;
+
};
+

+
typedef struct xstring xstring;
+

+
static inline xstring *
+
xstring_new(void)
+
{
+
	xstring *str;
+

+
	str = calloc(1, sizeof(*str));
+
	if (str == NULL)
+
		abort();
+
	str->fp = open_memstream(&str->buf, &str->size);
+
	if (str->fp == NULL)
+
		abort();
+

+
	return (str);
+
}
+

+
static inline void
+
xstring_reset(xstring *str)
+
{
+
	memset(str->buf, 0, str->size);
+
	rewind(str->fp);
+

+
}
+

+
static inline void
+
xstring_free(xstring *str)
+
{
+
	if (str == NULL)
+
		return;
+
	fclose(str->fp);
+
	free(str->buf);
+
	free(str);
+
}
+

+
#define xstring_renew(s)      \
+
do {                          \
+
   if (s) {                   \
+
     xstring_reset(s);        \
+
   } else {                   \
+
     s = xstring_new();       \
+
   }                          \
+
} while(0)
+

+
static inline char *
+
xstring_get(xstring *str)
+
{
+
	fclose(str->fp);
+
	char *ret = str->buf;
+
	free(str);
+
	return (ret);
+
}
+

+
#endif
modified mk/defs.mk.in
@@ -5,6 +5,13 @@ CC= @CC@
CFLAGS+=	@COVERAGE_CFLAGS@
LDFLAGS+=	@COVERAGE_LDFLAGS@
@endif
+
@if asan
+
CFLAGS+=	-O0 -ggdb -fsanitize=address
+
LDFLAGS+=	-fsanitize=address
+
@endif
+
@if ubsan
+
CFLAGS+=	-fsanitize=undefined
+
@endif
SHOBJ_CFLAGS=	@SHOBJ_CFLAGS@
LIBSOEXT=	@LIBSOEXT@
SH_SOEXT=	@SH_SOEXT@
modified scripts/completion/_pkg.in
@@ -143,8 +143,6 @@ _pkg_config_opts() {
		'PKG_ENABLE_PLUGINS[activate plugin support]:boolean:(yes no)' \
		'PKG_ENV[key/value pair of environment variables]:key/value list' \
		'PKG_PLUGINS_DIR[specify directory for plugins]:directory:_files -/' \
-
		'PKG_REPO_HASH[make pkg_repo(8) rename files using a short hash]:boolean:(yes no)' \
-
		'PKG_REPO_SYMLINK[make pkg_repo(8) symlink the hashed filename to the regular filename]:boolean:(yes no)' \
		'PKG_SSH_ARGS[extra arguments for ssh(1)]:ssh(1) arguments' \
		'PLIST_KEYWORDS_DIR[directory containing definitions of plist keywords]:directory:_files -/' \
		'PLIST_ACCEPT_DIRECTORIES[accept directories listed like plain files in plist]:boolean:(yes no)' \
@@ -314,7 +312,7 @@ _pkg_args() {
				'(-n --dry-run -y --yes)'{-y,--yes}'[assume yes when asked for confirmation]' \
				'(-y --yes -n --dry-run)'{-n,--dry-run}'[assume no (dry run) when asked for confirmation]' \
				'(-f --force)'{-f,--force}'[force the package(s) to be removed]' \
-
				'(-D --no-deinstall-script)'{-D,--no-deinstall-script}"[don't execute deinstallation scripts]" \
+
				'(-D --no-scripts)'{-D,--no-scripts}"[don't execute deinstallation scripts]" \
				'(-R --recursive)'{-R,--recursive}'[delete all packages that require the list packages as well]' \
				- '(all)' \
				{-a,--all}'[process all packages]' \
@@ -387,7 +385,7 @@ _pkg_args() {
				'(-A --automatic)'{-A,--automatic}'[mark the installed packages as automatic]' \
				'(-r --repository)'{-r+,--repository=}'[specify the repository to install packages from]:repository:->repositories' \
				'(-U --no-repo-update)'{-U,--no-repo-update}'[suppress the automatic update of the repo catalogue]' \
-
				'(-I --no-install-scripts)'{-I,--no-install-scripts}"[don't execute any pre/post-install scripts]" \
+
				'(-I --no-scripts)'{-I,--no-scripts}"[don't execute any pre/post-install scripts]" \
				'(-M --ignore-missing)'{-M,--ignore-missing}'[ignore missing dependencies]' \
				'(-n --dry-run -y --yes)'{-y,--yes}'[assume yes when asked for confirmation]' \
				'(-y --yes -n --dry-run)'{-n,--dry-run}'[assume no (dry run) when asked for confirmation]' \
@@ -589,7 +587,7 @@ _pkg_args() {
				'(-i --case-insensitive -C --case-sensitive)'{-C,--case-sensitive}'[case sensitive pattern matching]' \
				'(-i --case-insensitive -C --case-sensitive)'{-i,--case-insensitive}'[case insensitive pattern matching]' \
				'(-U --no-repo-update)'{-U,--no-repo-update}'[suppress the automatic update of the repo catalogue]' \
-
				'(-I --no-install-scripts)'{-I,--no-install-scripts}"[don't execute any pre/post-install scripts]" \
+
				'(-I --no-scripts)'{-I,--no-scripts}"[don't execute any pre/post-install scripts]" \
				'(-q --quiet)'{-q,--quiet}'[be quiet]' \
				'(-r --repository)'{-r+,--repository=}'[specify the repository to upgrade from]:repository:->repositories' \
				'(-g --glob -x --regex)'{-g,--glob}'[process packages that match a glob pattern]' \
modified src/Makefile.autosetup
@@ -39,6 +39,7 @@ SRCS= add.c \
LOCAL_CFLAGS=	-I$(top_srcdir)/external/uthash \
		-I$(top_srcdir)/compat \
		-I$(top_srcdir)/external/libucl/klib \
+
		-I$(top_srcdir)/external/libucl/include \
		-I$(top_builddir)/ \
		-I$(top_builddir)/libpkg \
		-DGITHASH=\"@GITHASH@\" \
modified src/add.c
@@ -33,10 +33,9 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <getopt.h>
-
#include <utstring.h>
+
#include <xstring.h>

#include <pkg.h>

@@ -66,7 +65,7 @@ int
exec_add(int argc, char **argv)
{
	struct pkgdb *db = NULL;
-
	UT_string *failedpkgs = NULL;
+
	xstring *failedpkgs = NULL;
	char path[MAXPATHLEN];
	char *file;
	int retcode;
@@ -111,7 +110,7 @@ exec_add(int argc, char **argv)
			break;
		default:
			usage_add();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -119,7 +118,7 @@ exec_add(int argc, char **argv)

	if (argc < 1) {
		usage_add();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	retcode = pkgdb_access(PKGDB_MODE_READ  |
@@ -128,25 +127,30 @@ exec_add(int argc, char **argv)
			       PKGDB_DB_LOCAL);
	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to add packages");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

-
	utstring_new(failedpkgs);
+
	failedpkgs = xstring_new();
	pkg_manifest_keys_new(&keys);
	for (i = 0; i < argc; i++) {
		if (is_url(argv[i]) == EPKG_OK) {
+
			const char *name = strrchr(argv[i], '/');
+
			if (name == NULL)
+
				name = argv[i];
+
			else
+
				name++;
			snprintf(path, sizeof(path), "%s/%s.XXXXX",
-
			    getenv("TMPDIR") != NULL ? getenv("TMPDIR") : "/tmp", basename(argv[i]));
+
			    getenv("TMPDIR") != NULL ? getenv("TMPDIR") : "/tmp", name);
			if ((retcode = pkg_fetch_file(NULL, argv[i], path, 0, 0, 0)) != EPKG_OK)
				break;

@@ -164,9 +168,9 @@ exec_add(int argc, char **argv)
				warn("%s", file);
				if (errno == ENOENT)
					warnx("Was 'pkg install %s' meant?", file);
-
				utstring_printf(failedpkgs, "%s", argv[i]);
+
				fprintf(failedpkgs->fp, "%s", argv[i]);
				if (i != argc - 1)
-
					utstring_printf(failedpkgs, ", ");
+
					fprintf(failedpkgs->fp, ", ");
				failedpkgcount++;
				continue;
			}
@@ -174,9 +178,9 @@ exec_add(int argc, char **argv)
		}

		if ((retcode = pkg_add(db, file, f, keys, location)) != EPKG_OK) {
-
			utstring_printf(failedpkgs, "%s", argv[i]);
+
			fprintf(failedpkgs->fp, "%s", argv[i]);
			if (i != argc - 1)
-
				utstring_printf(failedpkgs, ", ");
+
				fprintf(failedpkgs->fp, ", ");
			failedpkgcount++;
		}

@@ -189,15 +193,17 @@ exec_add(int argc, char **argv)
	pkgdb_close(db);
	
	if(failedpkgcount > 0) {
-
		printf("\nFailed to install the following %d package(s): %s\n", failedpkgcount, utstring_body(failedpkgs));
+
		fflush(failedpkgs->fp);
+
		printf("\nFailed to install the following %d package(s): %s\n", failedpkgcount, failedpkgs->buf);
		retcode = EPKG_FATAL;
	}
-
	utstring_free(failedpkgs);
+
	xstring_free(failedpkgs);

	if (messages != NULL) {
-
		printf("%s", utstring_body(messages));
+
		fflush(messages->fp);
+
		printf("%s", messages->buf);
	}

-
	return (retcode == EPKG_OK ? EX_OK : EX_SOFTWARE);
+
	return (retcode == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
}

modified src/alias.c
@@ -31,10 +31,8 @@
#include <libgen.h>
#include <stdio.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <getopt.h>
-
#include <utstring.h>

#include <pkg.h>

@@ -54,7 +52,7 @@ exec_alias(int argc, char **argv)
	const pkg_object *alias;
	pkg_iter it = NULL;
	int ch;
-
	int ret = EX_OK;
+
	int ret = EXIT_SUCCESS;
	bool list = false;

	struct option longopts[] = {
@@ -73,7 +71,7 @@ exec_alias(int argc, char **argv)
			break;
		default:
			usage_alias();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -109,7 +107,7 @@ exec_alias(int argc, char **argv)
				printf("%-20s '%s'\n", argv[i], pkg_object_string(alias));
		} else {
			warnx("No such alias: '%s'", argv[i]);
-
			ret = EX_UNAVAILABLE;
+
			ret = EXIT_FAILURE;
		}
	}

modified src/annotate.c
@@ -35,9 +35,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>
-
#include <utstring.h>

#include <pkg.h>

@@ -163,13 +161,13 @@ do_show(struct pkg *pkg, const char *tag)
}


-
static UT_string *
+
static char *
read_input(void)
{
-
	UT_string	*input;
+
	xstring	*input;
	int		 ch;

-
	utstring_new(input);
+
	input = xstring_new();

	for (;;) {
		ch = getc(stdin);
@@ -177,12 +175,12 @@ read_input(void)
			if (feof(stdin))
				break;
			if (ferror(stdin))
-
				err(EX_NOINPUT, "Failed to read stdin");
+
				err(EXIT_FAILURE, "Failed to read stdin");
		}
-
		utstring_printf(input, "%c", ch);
+
		fputc(ch, input->fp);
	}

-
	return (input);
+
	return (xstring_get(input));
}

int
@@ -195,11 +193,11 @@ exec_annotate(int argc, char **argv)
	const char	*tag;
	const char	*value;
	const char	*pkgname;
-
	UT_string	*input    = NULL;
+
	char		*input    = NULL;
	int		 ch;
	int		 match    = MATCH_EXACT;
	int		 retcode;
-
	int		 exitcode = EX_OK;
+
	int		 exitcode = EXIT_SUCCESS;
	int		 flags = 0;
	int		 lock_type = PKGDB_LOCK_EXCLUSIVE;
	int		 mode = PKGDB_MODE_READ;
@@ -264,7 +262,7 @@ exec_annotate(int argc, char **argv)
			break;
		default:
			usage_annotate();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
        }
	argc -= optind;
@@ -274,7 +272,7 @@ exec_annotate(int argc, char **argv)
	    (match == MATCH_ALL && argc < 1) ||
	    (match != MATCH_ALL && argc < 2)) {
		usage_annotate();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (match == MATCH_ALL) {
@@ -289,8 +287,7 @@ exec_annotate(int argc, char **argv)

	if ((action == ADD || action == MODIFY) && value == NULL) {
		/* try and read data for the value from stdin. */
-
		input = read_input();
-
		value = utstring_body(input);
+
		value = input = read_input();
	}

	if (lock_type == PKGDB_LOCK_EXCLUSIVE)
@@ -298,38 +295,37 @@ exec_annotate(int argc, char **argv)
	retcode = pkgdb_access(mode, PKGDB_DB_LOCAL);
	if (retcode == EPKG_ENODB) {
		if (match == MATCH_ALL) {
-
			exitcode = EX_OK;
+
			exitcode = EXIT_SUCCESS;
			goto cleanup;
		}
		if (!quiet)
			warnx("No packages installed.  Nothing to do!");
-
		exitcode = EX_OK;
+
		exitcode = EXIT_SUCCESS;
		goto cleanup;
	} else if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to modify the package database");
-
		exitcode = EX_NOPERM;
+
		exitcode = EXIT_FAILURE;
		goto cleanup;
	} else if (retcode != EPKG_OK) {
		warnx("Error accessing the package database");
-
		exitcode = EX_SOFTWARE;
+
		exitcode = EXIT_FAILURE;
		goto cleanup;
	}

	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
	if (retcode != EPKG_OK) {
-
		if (input != NULL)
-
			utstring_free(input);
-
		return (EX_IOERR);
+
		free(input);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
-
		exitcode = EX_IOERR;
+
		exitcode = EXIT_FAILURE;
		goto cleanup;
	}

@@ -338,7 +334,7 @@ exec_annotate(int argc, char **argv)
		switch(action) {
		case NONE:	/* Should never happen */
			usage_annotate();
-
			exitcode = EX_USAGE;
+
			exitcode = EXIT_FAILURE;
			break;
		case ADD:
			retcode = do_add(db, pkg, tag, value);
@@ -355,10 +351,10 @@ exec_annotate(int argc, char **argv)
		}

		if (retcode == EPKG_WARN)
-
			exitcode = EX_DATAERR;
+
			exitcode = EXIT_FAILURE;

		if (retcode != EPKG_OK && retcode != EPKG_WARN) {
-
			exitcode = EX_IOERR;
+
			exitcode = EXIT_FAILURE;
			goto cleanup;
		}
	}
@@ -369,8 +365,7 @@ cleanup:

	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);
	pkgdb_close(db);
-
	if (input != NULL)
-
		utstring_free(input);
+
	free(input);

	return (exitcode);
}
modified src/audit.c
@@ -43,9 +43,9 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
-
#include <sysexits.h>
#include <khash.h>
-
#include <utstring.h>
+
#include <utlist.h>
+
#include <ucl.h>

#ifdef HAVE_SYS_CAPSICUM_H
#include <sys/capsicum.h>
@@ -56,12 +56,23 @@
#endif

#include <pkg.h>
+
#include <pkg/audit.h>
#include "pkgcli.h"
+
#include "xmalloc.h"
+

+
static const char* vop_names[] = {
+
	[0] = "",
+
	[EQ] = "=",
+
	[LT] = "<",
+
	[LTE] = "<=",
+
	[GT] = ">",
+
	[GTE] = ">="
+
};

void
usage_audit(void)
{
-
	fprintf(stderr, "Usage: pkg audit [-Fqr] [-f file] <pattern>\n\n");
+
	fprintf(stderr, "Usage: pkg audit [-RFqr] [--raw[=format]|-R[format]| [-f file] <pattern>\n\n");
	fprintf(stderr, "For more information see 'pkg help audit'.\n");
}

@@ -82,8 +93,7 @@ add_to_check(kh_pkgs_t *check, struct pkg *pkg)
}

static void
-
print_recursive_rdeps(kh_pkgs_t *head, struct pkg *p, UT_string *sb,
-
    kh_pkgs_t *seen, bool top)
+
print_recursive_rdeps(kh_pkgs_t *head, struct pkg *p, kh_pkgs_t *seen, bool top, ucl_object_t *array)
{
	struct pkg_dep *dep = NULL;
	int ret;
@@ -100,43 +110,136 @@ print_recursive_rdeps(kh_pkgs_t *head, struct pkg *p, UT_string *sb,
			continue;

		kh_put_pkgs(seen, name, &ret);
-
		if (!top)
-
			utstring_printf(sb, ", ");
+
		if (array == NULL) {
+
			if (!top)
+
				printf(", ");

-
		utstring_printf(sb, "%s", name);
+
			printf("%s", name);
+
		} else {
+
			ucl_array_append(array, ucl_object_fromstring(name));
+
		}

-
		print_recursive_rdeps(head, kh_val(head, h), sb, seen, false);
+
		print_recursive_rdeps(head, kh_val(head, h), seen, false, array);

		top = false;
	}
}

+
static void
+
print_issue(struct pkg *p, struct pkg_audit_issue *issue)
+
{
+
	const char *version;
+
	struct pkg_audit_versions_range *vers;
+
	const struct pkg_audit_entry *e;
+
	struct pkg_audit_cve *cve;
+

+
	pkg_get(p, PKG_VERSION, &version);
+

+
	e = issue->audit;
+
	if (version == NULL) {
+
		printf("  Affected versions:\n");
+
		LL_FOREACH(e->versions, vers) {
+
			if (vers->v1.type > 0 && vers->v2.type > 0)
+
				printf("  %s %s : %s %s\n",
+
				    vop_names[vers->v1.type], vers->v1.version,
+
				    vop_names[vers->v2.type], vers->v2.version);
+
			else if (vers->v1.type > 0)
+
				printf("  %s %s\n",
+
				    vop_names[vers->v1.type], vers->v1.version);
+
			else
+
				printf("  %s %s\n",
+
				    vop_names[vers->v2.type], vers->v2.version);
+
		}
+
	}
+
	printf("  %s\n", e->desc);
+
	LL_FOREACH(e->cve, cve) {
+
		printf("  CVE: %s\n", cve->cvename);
+
	}
+
	if (e->url)
+
		printf("  WWW: %s\n\n", e->url);
+
	else if (e->id)
+
		printf("  WWW: https://vuxml.FreeBSD.org/freebsd/%s.html\n\n", e->id);
+
}
+

+
static void
+
format_issue(struct pkg *p, struct pkg_audit_issue *issue, ucl_object_t *array)
+
{
+
	const char *version;
+
	struct pkg_audit_versions_range *vers;
+
	const struct pkg_audit_entry *e;
+
	struct pkg_audit_cve *cve;
+
	ucl_object_t *o = ucl_object_typed_new(UCL_OBJECT);
+
	ucl_object_t *affected_versions = ucl_object_typed_new(UCL_ARRAY);
+

+
	pkg_get(p, PKG_VERSION, &version);
+
	ucl_array_append(array, o);
+

+
	e = issue->audit;
+
	ucl_object_insert_key(o, affected_versions, "Affected versions", 17, false);
+
	LL_FOREACH(e->versions, vers) {
+
		char *ver;
+
		if (vers->v1.type > 0 && vers->v2.type > 0)
+
			xasprintf(&ver, "%s %s : %s %s",
+
			    vop_names[vers->v1.type], vers->v1.version,
+
			    vop_names[vers->v2.type], vers->v2.version);
+
		else if (vers->v1.type > 0)
+
			xasprintf(&ver, "%s %s",
+
			    vop_names[vers->v1.type], vers->v1.version);
+
		else
+
			xasprintf(&ver, "%s %s",
+
			    vop_names[vers->v2.type], vers->v2.version);
+
		ucl_array_append(affected_versions, ucl_object_fromstring(ver));
+
		free(ver);
+
	}
+
	ucl_object_insert_key(o, ucl_object_fromstring(e->desc), "description", 11, false);
+
	if (e->cve) {
+
		ucl_object_t *acve = ucl_object_typed_new(UCL_ARRAY);
+
		LL_FOREACH(e->cve, cve) {
+
			ucl_array_append(acve, ucl_object_fromstring(cve->cvename));
+
		}
+
		ucl_object_insert_key(o, acve, "cve", 3, false);
+
	}
+
	if (e->url)
+
		ucl_object_insert_key(o, ucl_object_fromstring(e->url), "url", 3, false);
+
	else if (e->id) {
+
		char *url;
+
		xasprintf(&url, "https://vuxml.FreeBSD.org/freebsd/%s.html\n\n", e->id);
+
		ucl_object_insert_key(o, ucl_object_fromstring(url), "url", 3, false);
+
		free(url);
+
	}
+
}
+

int
exec_audit(int argc, char **argv)
{
	struct pkg_audit	*audit;
+
	struct pkg_audit_issues	*issues;
+
	struct pkg_audit_issue	*issue;
	struct pkgdb		*db = NULL;
	struct pkgdb_it		*it = NULL;
	struct pkg		*pkg = NULL;
	char			*name;
	char			*version;
	char			*audit_file = NULL;
-
	unsigned int		 affected = 0, vuln = 0;
+
	int			 affected = 0, vuln = 0;
	bool			 fetch = false, recursive = false;
	int			 ch, i;
-
	int			 ret = EX_OK;
-
	UT_string		*sb;
+
	int			 raw;
+
	int			 ret = EXIT_SUCCESS;
	kh_pkgs_t		*check = NULL;
+
	ucl_object_t		*top = NULL, *vuln_objs = NULL;
+
	ucl_object_t		*obj = NULL;

	struct option longopts[] = {
		{ "fetch",	no_argument,		NULL,	'F' },
		{ "file",	required_argument,	NULL,	'f' },
		{ "recursive",	no_argument,	NULL,	'r' },
+
		{ "raw",	optional_argument,	NULL,	'R' },
		{ "quiet",	no_argument,		NULL,	'q' },
		{ NULL,		0,			NULL,	0   },
	};

-
	while ((ch = getopt_long(argc, argv, "+Ff:qr", longopts, NULL)) != -1) {
+
	while ((ch = getopt_long(argc, argv, "+Ff:qrR::", longopts, NULL)) != -1) {
		switch (ch) {
		case 'F':
			fetch = true;
@@ -150,9 +253,25 @@ exec_audit(int argc, char **argv)
		case 'r':
			recursive = true;
			break;
+
		case 'R':
+
			if (optarg == NULL) {
+
				raw = UCL_EMIT_CONFIG;
+
			} else if (strcasecmp(optarg, "ucl") == 0) {
+
				raw = UCL_EMIT_CONFIG;
+
			} else if (strcasecmp(optarg, "json") == 0) {
+
				raw = UCL_EMIT_JSON;
+
			} else if (strcasecmp(optarg, "json-compact") == 0) {
+
				raw = UCL_EMIT_JSON_COMPACT;
+
			} else if (strcasecmp(optarg, "yaml") == 0) {
+
				raw = UCL_EMIT_YAML;
+
			} else {
+
				errx(EXIT_FAILURE, "invalid argument %s for --raw option", optarg);
+
			}
+
			top = ucl_object_typed_new(UCL_OBJECT);
+
			break;
		default:
			usage_audit();
-
			return(EX_USAGE);
+
			return(EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -163,7 +282,7 @@ exec_audit(int argc, char **argv)
	if (fetch == true) {
		if (pkg_audit_fetch(NULL, audit_file) != EPKG_OK) {
			pkg_audit_free(audit);
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
		}
	}

@@ -177,7 +296,7 @@ exec_audit(int argc, char **argv)
					audit_file);

		pkg_audit_free(audit);
-
		return (EX_DATAERR);
+
		return (EXIT_FAILURE);
	}

	check = kh_init_pkgs();
@@ -190,7 +309,7 @@ exec_audit(int argc, char **argv)
				version++;
			}
			if (pkg_new(&pkg, PKG_FILE) != EPKG_OK)
-
				err(EX_OSERR, "malloc");
+
				err(EXIT_FAILURE, "malloc");
			if (version != NULL)
				pkg_set(pkg, PKG_NAME, name, PKG_VERSION, version);
			else
@@ -212,23 +331,23 @@ exec_audit(int argc, char **argv)
		if (ret == EPKG_ENODB) {
			pkg_audit_free(audit);
			kh_destroy_pkgs(check);
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		} else if (ret == EPKG_ENOACCESS) {
			warnx("Insufficient privileges to read the package database");
			pkg_audit_free(audit);
			kh_destroy_pkgs(check);
-
			return (EX_NOPERM);
+
			return (EXIT_FAILURE);
		} else if (ret != EPKG_OK) {
			warnx("Error accessing the package database");
			pkg_audit_free(audit);
			kh_destroy_pkgs(check);
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
		}

		if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
			pkg_audit_free(audit);
			kh_destroy_pkgs(check);
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
		}

		if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
@@ -236,12 +355,12 @@ exec_audit(int argc, char **argv)
			pkg_audit_free(audit);
			kh_destroy_pkgs(check);
			warnx("Cannot get a read lock on a database, it is locked by another process");
-
			return (EX_TEMPFAIL);
+
			return (EXIT_FAILURE);
		}

		if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
			warnx("Error accessing the package database");
-
			ret = EX_IOERR;
+
			ret = EXIT_FAILURE;
		}
		else {
			while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC|PKG_LOAD_RDEPS))
@@ -249,14 +368,14 @@ exec_audit(int argc, char **argv)
				add_to_check(check, pkg);
				pkg = NULL;
			}
-
			ret = EX_OK;
+
			ret = EXIT_SUCCESS;
		}
		if (db != NULL) {
			pkgdb_it_free(it);
			pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
			pkgdb_close(db);
		}
-
		if (ret != EX_OK) {
+
		if (ret != EXIT_SUCCESS) {
			pkg_audit_free(audit);
			kh_destroy_pkgs(check);
			return (ret);
@@ -277,38 +396,94 @@ exec_audit(int argc, char **argv)

	if (pkg_audit_process(audit) == EPKG_OK) {
		kh_foreach_value(check, pkg, {
-
			if (pkg_audit_is_vulnerable(audit, pkg, quiet, &sb, &affected)) {
+
			issues = NULL;
+
			if (pkg_audit_is_vulnerable(audit, pkg, &issues, quiet)) {
+
				const char *version;
+
				const char *name = NULL;
+
				ucl_object_t *array = NULL;
				vuln ++;
-
				printf("%s", utstring_body(sb));

-
				if (recursive) {
-
					const char *name;
+
				if (top == NULL) {
+
					affected += issues->count;
+
					pkg_get(pkg, PKG_VERSION, &version);
+
					if (quiet) {
+
						if (version != NULL)
+
							pkg_printf("%n-%v\n", pkg, pkg);
+
							else
+
						pkg_printf("%s\n", pkg);
+
						continue;
+
					}
+

+
					pkg_printf("%n", pkg);
+
					if (version != NULL)
+
						pkg_printf("-%v", pkg);
+
					if (!quiet)
+
						printf(" is vulnerable");
+
					printf(":\n");
+
				} else {
+
					if (vuln_objs == NULL)
+
						vuln_objs = ucl_object_typed_new(UCL_OBJECT);
+
					obj = ucl_object_typed_new(UCL_OBJECT);
+
					pkg_get(pkg, PKG_NAME, &name, PKG_VERSION, &version);
+
					if (version != NULL)
+
						ucl_object_insert_key(obj, ucl_object_fromstring(version), "version", 7 , false);
+
					ucl_object_insert_key(obj, ucl_object_fromint(issues->count), "issue_count", 11, false);
+
				}
+

+
				if (top != NULL)
+
					array = ucl_object_typed_new(UCL_ARRAY);
+
				LL_FOREACH(issues->issues, issue) {
+
					if (top == NULL)
+
						print_issue(pkg, issue);
+
					else
+
						format_issue(pkg, issue, array);
+
				}
+
				if (top != NULL)
+
					ucl_object_insert_key(obj, array, "issues", 6, false);
+
				array = NULL;
+

+
				if (top != NULL || recursive) {
					kh_pkgs_t *seen = kh_init_pkgs();

-
					pkg_get(pkg, PKG_NAME, &name);
-
					utstring_clear(sb);
-
					utstring_printf(sb, "Packages that depend on %s: ", name);
-
					print_recursive_rdeps(check, pkg , sb, seen, true);
-
					printf("%s\n\n", utstring_body(sb));
+
					if (name == NULL)
+
						pkg_get(pkg, PKG_NAME, &name);
+
					if (top == NULL) {
+
						printf("  Packages that depend on %s: ", name);
+
					} else {
+
						array = ucl_object_typed_new(UCL_ARRAY);
+
					}
+
					print_recursive_rdeps(check, pkg , seen, true, array);
+
					if (top == NULL)
+
						printf("\n\n");

					kh_destroy_pkgs(seen);
				}
-
				utstring_free(sb);
+
				if (top != NULL) {
+
					ucl_object_insert_key(obj, array, "reverse dependencies", 20, false);
+
					ucl_object_insert_key(vuln_objs, obj, xstrdup(name), strlen(name), false);
+
				}
			}
+
			pkg_audit_issues_free(issues);
			pkg_free(pkg);
		});
		kh_destroy_pkgs(check);

		if (ret == EPKG_END && vuln == 0)
-
			ret = EX_OK;
+
			ret = EXIT_SUCCESS;

-
		if (!quiet)
+
		if (top == NULL && !quiet) {
			printf("%u problem(s) in %u installed package(s) found.\n",
			   affected, vuln);
-
	}
-
	else {
+
	
+
		} else {
+
			ucl_object_insert_key(top, ucl_object_fromint(vuln), "pkg_count", 9, false );
+
			ucl_object_insert_key(top, vuln_objs, "packages", 8, false);
+
			fprintf(stdout, "%s\n", ucl_object_emit(top, raw));
+
			ucl_object_unref(top);
+
		}
+
	} else {
		warnx("cannot process vulnxml");
-
		ret = EX_SOFTWARE;
+
		ret = EXIT_FAILURE;
		kh_destroy_pkgs(check);
	}

modified src/autoremove.c
@@ -30,7 +30,6 @@
#include <err.h>
#include <getopt.h>
#include <stdio.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -49,7 +48,7 @@ exec_autoremove(int argc, char **argv)
{
	struct pkgdb *db = NULL;
	struct pkg_jobs *jobs = NULL;
-
	int retcode = EX_OK;
+
	int retcode = EXIT_SUCCESS;
	int ch;
	nbactions = nbdone = 0;
	pkg_flags f = PKG_FLAG_FORCE;
@@ -84,7 +83,7 @@ exec_autoremove(int argc, char **argv)

	if (argc != 0) {
		usage_autoremove();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (dry_run)
@@ -95,34 +94,34 @@ exec_autoremove(int argc, char **argv)

	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to autoremove packages");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode == EPKG_ENODB) {
		warnx("No packages installed.  Nothing to do!");
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	} else if (retcode != EPKG_OK) {
		warnx("Error accessing the package database");
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}
	/* Always force packages to be removed */
	if (pkg_jobs_new(&jobs, PKG_JOBS_AUTOREMOVE, db) != EPKG_OK) {
		pkgdb_close(db);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	pkg_jobs_set_flags(jobs, f);

	if ((retcode = pkg_jobs_solve(jobs)) != EPKG_OK) {
-
		retcode = EX_SOFTWARE;
+
		retcode = EXIT_FAILURE;
		goto cleanup;
	}

modified src/backup.c
@@ -28,7 +28,6 @@

#include <pkg.h>
#include <getopt.h>
-
#include <sysexits.h>
#include <unistd.h>

#include "pkgcli.h"
@@ -72,33 +71,33 @@ exec_backup(int argc, char **argv)
			break;
		default:
			usage_backup();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

	if ( dump == restore ) {
		usage_backup();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (dump) {
		if (!quiet)
			printf("Dumping database:\n");
		if (pkgdb_dump(db, backup_file) == EPKG_FATAL)
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
	}

	if (restore) {
		if (!quiet)
			printf("Restoring database:\n");
		if (pkgdb_load(db, backup_file) == EPKG_FATAL)
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
	}

	pkgdb_close(db);

-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}
modified src/check.c
@@ -33,14 +33,12 @@
#include <err.h>
#include <assert.h>
#include <getopt.h>
-
#include <sysexits.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <utlist.h>
-
#include <utstring.h>

#include <pkg.h>

@@ -53,14 +51,14 @@ struct deps_entry {
};

static int check_deps(struct pkgdb *db, struct pkg *pkg, struct deps_entry **dh,
-
    bool noinstall, UT_string *out);
+
    bool noinstall, xstring *out);
static void add_missing_dep(struct pkg_dep *d, struct deps_entry **dh, int *nbpkgs);
static void deps_free(struct deps_entry *dh);
static int fix_deps(struct pkgdb *db, struct deps_entry *dh, int nbpkgs);
static void check_summary(struct pkgdb *db, struct deps_entry *dh);

static int
-
check_deps(struct pkgdb *db, struct pkg *p, struct deps_entry **dh, bool noinstall, UT_string *out)
+
check_deps(struct pkgdb *db, struct pkg *p, struct deps_entry **dh, bool noinstall, xstring *out)
{
	struct pkg_dep *dep = NULL;
	struct pkgdb_it *it;
@@ -74,9 +72,9 @@ check_deps(struct pkgdb *db, struct pkg *p, struct deps_entry **dh, bool noinsta
		/* do we have a missing dependency? */
		if (pkg_is_installed(db, pkg_dep_name(dep)) != EPKG_OK) {
			if (quiet)
-
				pkg_utstring_printf(out, "%n\t%sn\n", p, dep);
+
				pkg_fprintf(out->fp, "%n\t%sn\n", p, dep);
			else
-
				pkg_utstring_printf(out, "%n has a missing dependency: %dn\n",
+
				pkg_fprintf(out->fp, "%n has a missing dependency: %dn\n",
				    p, dep);
			if (!noinstall)
				add_missing_dep(dep, dh, &nbpkgs);
@@ -93,9 +91,9 @@ check_deps(struct pkgdb *db, struct pkg *p, struct deps_entry **dh, bool noinsta
		}
		pkgdb_it_free(it);
		if (quiet)
-
			pkg_utstring_printf(out, "%n\t%S\n", p, buf);
+
			pkg_fprintf(out->fp, "%n\t%S\n", p, buf);
		else
-
			pkg_utstring_printf(out, "%n is missing a required shared library: %S\n",
+
			pkg_fprintf(out->fp, "%n is missing a required shared library: %S\n",
			    p, buf);
	}

@@ -109,9 +107,9 @@ check_deps(struct pkgdb *db, struct pkg *p, struct deps_entry **dh, bool noinsta
		}
		pkgdb_it_free(it);
		if (quiet)
-
			pkg_utstring_printf(out, "%n\tS\n", p, buf);
+
			pkg_fprintf(out->fp, "%n\tS\n", p, buf);
		else
-
			pkg_utstring_printf(out, "%n has a missing requirement: %S\n",
+
			pkg_fprintf(out->fp, "%n has a missing requirement: %S\n",
			    p, buf);
	}

@@ -277,10 +275,10 @@ exec_check(int argc, char **argv)
	struct pkg *pkg = NULL;
	struct pkgdb_it *it = NULL;
	struct pkgdb *db = NULL;
-
	UT_string *msg = NULL;
+
	xstring *msg = NULL;
	match_t match = MATCH_EXACT;
	int flags = PKG_LOAD_BASIC;
-
	int ret, rc = EX_OK;
+
	int ret, rc = EXIT_SUCCESS;
	int ch;
	bool dcheck = false;
	bool checksums = false;
@@ -359,7 +357,7 @@ exec_check(int argc, char **argv)
			break;
		default:
			usage_check();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -370,7 +368,7 @@ exec_check(int argc, char **argv)
		match = MATCH_ALL;
	} else if ((argc == 0 && match != MATCH_ALL) || !(dcheck || checksums || recompute || reanalyse_shlibs)) {
		usage_check();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (recompute || reanalyse_shlibs)
@@ -382,28 +380,28 @@ exec_check(int argc, char **argv)
	if (ret == EPKG_ENODB) {
		if (!quiet)
			warnx("No packages installed.  Nothing to do!");
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	} else if (ret == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to access the package database");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (ret != EPKG_OK) {
		warnx("Error accessing the package database");
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_access(PKGDB_MODE_WRITE, PKGDB_DB_LOCAL) == EPKG_ENOACCESS) {
		warnx("Insufficient privileges");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	}

	ret = pkgdb_open(&db, PKGDB_DEFAULT);
	if (ret != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_ADVISORY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	i = 0;
@@ -412,7 +410,7 @@ exec_check(int argc, char **argv)
		/* XXX: This is really quirky, it would be cleaner to pass
		 * in multiple matches and only run this top-loop once. */
		if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
-
			rc = EX_IOERR;
+
			rc = EXIT_FAILURE;
			goto cleanup;
		}
		nbactions = pkgdb_it_count(it);
@@ -423,22 +421,22 @@ exec_check(int argc, char **argv)
		}

		if (msg == NULL)
-
			utstring_new(msg);
+
			msg = xstring_new();
		if (!verbose) {
			if (!quiet) {
				if (match == MATCH_ALL)
					progressbar_start("Checking all packages");
				else {
-
					utstring_printf(msg, "Checking %s", argv[i]);
-
					progressbar_start(utstring_body(msg));
+
					fprintf(msg->fp, "Checking %s", argv[i]);
+
					fflush(msg->fp);
+
					progressbar_start(msg->buf);
				}
			}
			processed = 0;
			total = pkgdb_it_count(it);
		}

-
		UT_string *out;
-
		utstring_new(out);
+
		xstring *out = xstring_new();
		while (pkgdb_it_next(it, &pkg, flags) == EPKG_OK) {
			if (!quiet) {
				if (!verbose)
@@ -446,9 +444,11 @@ exec_check(int argc, char **argv)
				else {
					++nbdone;
					job_status_begin(msg);
-
					pkg_utstring_printf(msg, "Checking %n-%v:",
+
					pkg_fprintf(msg->fp, "Checking %n-%v:",
					    pkg, pkg);
-
					utstring_flush(msg);
+
					fflush(msg->fp);
+
					printf("%s", msg->buf);
+
					xstring_reset(msg);
				}
			}

@@ -458,14 +458,14 @@ exec_check(int argc, char **argv)
					printf(" dependencies...");
				nbpkgs += check_deps(db, pkg, &dh, noinstall, out);
				if (noinstall && nbpkgs > 0) {
-
					rc = EX_UNAVAILABLE;
+
					rc = EXIT_FAILURE;
				}
			}
			if (checksums) {
				if (!quiet && verbose)
					printf(" checksums...");
				if (pkg_test_filesum(pkg) != EPKG_OK) {
-
					rc = EX_DATAERR;
+
					rc = EXIT_FAILURE;
				}
			}
			if (recompute) {
@@ -474,14 +474,14 @@ exec_check(int argc, char **argv)
					if (!quiet && verbose)
						printf(" recomputing...");
					if (pkg_recompute(db, pkg) != EPKG_OK) {
-
						rc = EX_DATAERR;
+
						rc = EXIT_FAILURE;
					}
					pkgdb_downgrade_lock(db,
					    PKGDB_LOCK_EXCLUSIVE,
					    PKGDB_LOCK_ADVISORY);
				}
				else {
-
					rc = EX_TEMPFAIL;
+
					rc = EXIT_FAILURE;
				}
			}
			if (reanalyse_shlibs) {
@@ -493,14 +493,14 @@ exec_check(int argc, char **argv)
						pkg_fprintf(stderr, "Failed to "
						    "reanalyse for shlibs: "
						    "%n-%v\n", pkg, pkg);
-
						rc = EX_UNAVAILABLE;
+
						rc = EXIT_FAILURE;
					}
					pkgdb_downgrade_lock(db,
					    PKGDB_LOCK_EXCLUSIVE,
					    PKGDB_LOCK_ADVISORY);
				}
				else {
-
					rc = EX_TEMPFAIL;
+
					rc = EXIT_FAILURE;
				}
			}

@@ -513,14 +513,13 @@ exec_check(int argc, char **argv)
		}
		if (!quiet && !verbose)
			progressbar_tick(processed, total);
-
		if (utstring_len(out) > 0) {
-
			printf("%s", utstring_body(out));
-
		}
-
		utstring_free(out);
-
		if (msg != NULL) {
-
			utstring_free(msg);
-
			msg = NULL;
+
		fflush(out->fp);
+
		if (out->buf[0] != '\0') {
+
			printf("%s", out->buf);
		}
+
		xstring_free(out);
+
		xstring_free(msg);
+
		msg = NULL;

		if (dcheck && nbpkgs > 0 && !noinstall) {
			printf("\n>>> Missing package dependencies were detected.\n");
@@ -532,15 +531,15 @@ exec_check(int argc, char **argv)
					check_summary(db, dh);
				else if (ret == EPKG_ENODB) {
					db = NULL;
-
					rc = EX_IOERR;
+
					rc = EXIT_FAILURE;
				}
-
				if (rc == EX_IOERR)
+
				if (rc == EXIT_FAILURE)
					goto cleanup;
				pkgdb_downgrade_lock(db, PKGDB_LOCK_EXCLUSIVE,
				    PKGDB_LOCK_ADVISORY);
			}
			else {
-
				rc = EX_TEMPFAIL;
+
				rc = EXIT_FAILURE;
				goto cleanup;
			}
		}
@@ -551,8 +550,7 @@ exec_check(int argc, char **argv)
cleanup:
	if (!verbose)
		progressbar_stop();
-
	if (msg != NULL)
-
		utstring_free(msg);
+
	xstring_free(msg);
	deps_free(dh);
	pkg_free(pkg);
	pkgdb_release_lock(db, PKGDB_LOCK_ADVISORY);
modified src/clean.c
@@ -48,7 +48,6 @@
#include <pkg.h>
#include <stdbool.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <khash.h>
#include <kvec.h>
@@ -113,7 +112,7 @@ static int
delete_dellist(int fd, const char *cachedir,  dl_list *dl, int total)
{
	struct stat st;
-
	int retcode = EX_OK;
+
	int retcode = EXIT_SUCCESS;
	int flag = 0;
	size_t i;
	unsigned int count = 0, processed = 0;
@@ -135,7 +134,7 @@ delete_dellist(int fd, const char *cachedir, dl_list *dl, int total)
			flag = AT_REMOVEDIR;
		if (unlinkat(fd, relpath, flag) == -1) {
			warn("unlink(%s)", file);
-
			retcode = EX_SOFTWARE;
+
			retcode = EXIT_FAILURE;
		}
		free(file);
		kv_A(*dl, i) = NULL;
@@ -145,7 +144,7 @@ delete_dellist(int fd, const char *cachedir, dl_list *dl, int total)
	progressbar_tick(processed, total);

	if (!quiet) {
-
		if (retcode == EX_OK)
+
		if (retcode == EXIT_SUCCESS)
			printf("All done\n");
		else
			printf("%d package%s could not be deleted\n",
@@ -333,7 +332,7 @@ exec_clean(int argc, char **argv)
			break;
		default:
			usage_clean();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -341,7 +340,7 @@ exec_clean(int argc, char **argv)
	cachefd = pkg_get_cachedirfd();
	if (cachefd == -1) {
		warn("Impossible to open %s", cachedir);
-
		return (errno == ENOENT ? EX_OK : EX_IOERR);
+
		return (errno == ENOENT ? EXIT_SUCCESS : EXIT_FAILURE);
	}

	retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_REPO);
@@ -349,22 +348,22 @@ exec_clean(int argc, char **argv)
	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to clean old packages");
		close(cachefd);
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode == EPKG_ENODB) {
		warnx("No package database installed.  Nothing to do!");
		close(cachefd);
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	} else if (retcode != EPKG_OK) {
		warnx("Error accessing the package database");
		close(cachefd);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

-
	retcode = EX_SOFTWARE;
+
	retcode = EXIT_FAILURE;

	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) {
		close(cachefd);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
@@ -372,7 +371,7 @@ exec_clean(int argc, char **argv)
		close(cachefd);
		warnx("Cannot get a read lock on a database, it is locked by "
		    "another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

#ifdef HAVE_CAPSICUM
@@ -381,13 +380,13 @@ exec_clean(int argc, char **argv)
		if (cap_rights_limit(cachefd, &rights) < 0 && errno != ENOSYS ) {
			warn("cap_rights_limit() failed");
			close(cachefd);
-
			return (EX_SOFTWARE);
+
			return (EXIT_FAILURE);
		}

		if (cap_enter() < 0 && errno != ENOSYS) {
			warn("cap_enter() failed");
			close(cachefd);
-
			return (EX_SOFTWARE);
+
			return (EXIT_FAILURE);
		}
#endif

@@ -406,7 +405,7 @@ exec_clean(int argc, char **argv)
	if (kv_size(dl) == 0) {
		if (!quiet)
			printf("Nothing to do.\n");
-
		retcode = EX_OK;
+
		retcode = EXIT_SUCCESS;
		goto cleanup;
	}

@@ -421,7 +420,7 @@ exec_clean(int argc, char **argv)
				retcode = delete_dellist(cachefd, cachedir, &dl, kv_size(dl));
			}
	} else {
-
		retcode = EX_OK;
+
		retcode = EXIT_SUCCESS;
	}

cleanup:
modified src/config.c
@@ -28,7 +28,6 @@
#include <err.h>
#include <inttypes.h>
#include <stdio.h>
-
#include <sysexits.h>

#include <pkg.h>

@@ -55,7 +54,7 @@ exec_config(int argc, char **argv)

	if (argc != 2) {
		usage_config();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	key = argv[1];
@@ -65,7 +64,7 @@ exec_config(int argc, char **argv)
	conf = pkg_config_get(key);
	if (conf == NULL) {
		warnx("No such configuration options: %s", key);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	switch (pkg_object_type(conf)) {
@@ -95,5 +94,5 @@ exec_config(int argc, char **argv)
		break;
	}

-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}
modified src/create.c
@@ -49,7 +49,6 @@
#include <strings.h>
#include <unistd.h>
#include <utlist.h>
-
#include <sysexits.h>

#include "pkgcli.h"

@@ -63,13 +62,13 @@ struct pkg_entry *pkg_head = NULL;
void
usage_create(void)
{
-
	fprintf(stderr, "Usage: pkg create [-Ohnqv] [-f format] [-l level] "
+
	fprintf(stderr, "Usage: pkg create [-eOhnqv] [-f format] [-l level] "
		"[-o outdir] [-p plist] [-r rootdir] -m metadatadir\n");
-
	fprintf(stderr, "Usage: pkg create [-Ohnqv] [-f format] [-l level] "
+
	fprintf(stderr, "Usage: pkg create [-eOhnqv] [-f format] [-l level] "
		"[-o outdir] [-r rootdir] -M manifest\n");
-
	fprintf(stderr, "       pkg create [-Ohgnqvx] [-f format] [-l level] "
+
	fprintf(stderr, "       pkg create [-eOhgnqvx] [-f format] [-l level] "
		"[-o outdir] [-r rootdir] pkg-name ...\n");
-
	fprintf(stderr, "       pkg create [-Ohnqv] [-f format] [-l level] "
+
	fprintf(stderr, "       pkg create [-eOhnqv] [-f format] [-l level] "
		"[-o outdir] [-r rootdir] -a\n\n");
	fprintf(stderr, "For more information see 'pkg help create'.\n");
}
@@ -92,13 +91,13 @@ pkg_create_matches(int argc, char **argv, match_t match, struct pkg_create *pc)

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
		pkgdb_close(db);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}
	/* XXX: get rid of hardcoded timeouts */
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	for (i = 0; i < argc || match == MATCH_ALL; i++) {
@@ -182,6 +181,7 @@ exec_create(int argc, char **argv)
	int		 ret;
	bool		 hash = false;
	bool		 overwrite = true;
+
	bool		 expand_manifest = false;
	time_t		 ts = (time_t)-1;

	/* Sentinel values: INT_MIN (fast), 0 (default), INT_MAX (best). */
@@ -195,6 +195,7 @@ exec_create(int argc, char **argv)

	struct option longopts[] = {
		{ "all",	no_argument,		NULL,	'a' },
+
		{ "expand-manifest",	no_argument,	NULL,	'e' },
		{ "format",	required_argument,	NULL,	'f' },
		{ "glob",	no_argument,		NULL,	'g' },
		{ "hash",	no_argument,		NULL,	'h' },
@@ -212,11 +213,14 @@ exec_create(int argc, char **argv)
		{ NULL,		0,			NULL,	0   },
	};

-
	while ((ch = getopt_long(argc, argv, "+aghxf:l:r:m:M:no:p:qvt:", longopts, NULL)) != -1) {
+
	while ((ch = getopt_long(argc, argv, "+aeghxf:l:r:m:M:no:p:qvt:", longopts, NULL)) != -1) {
		switch (ch) {
		case 'a':
			match = MATCH_ALL;
			break;
+
		case 'e':
+
			expand_manifest = true;
+
			break;
		case 'f':
			format = optarg;
			break;
@@ -241,7 +245,7 @@ exec_create(int argc, char **argv)
				break;
			}
			warnx("Invalid compression level %s", optarg);
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
			}
		case 'm':
			metadatadir = optarg;
@@ -269,7 +273,7 @@ exec_create(int argc, char **argv)
			ts = (time_t)strtoimax(optarg, &endptr, 10);
			if (*endptr != '\0') {
				warnx("Invalid timestamp %s", optarg);
-
				return (EX_USAGE);
+
				return (EXIT_FAILURE);
			}
			break;
		case 'v':
@@ -280,7 +284,7 @@ exec_create(int argc, char **argv)
			break;
		default:
			usage_create();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -289,14 +293,14 @@ exec_create(int argc, char **argv)
	if (match != MATCH_ALL && metadatadir == NULL && manifest == NULL &&
	    argc == 0) {
		usage_create();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (metadatadir == NULL && manifest == NULL && rootdir != NULL) {
		warnx("Do not specify a rootdir without also specifying "
		    "either a metadatadir or manifest");
		usage_create();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (outdir == NULL)
@@ -313,11 +317,12 @@ exec_create(int argc, char **argv)
	pkg_create_set_overwrite(pc, overwrite);
	pkg_create_set_rootdir(pc, rootdir);
	pkg_create_set_output_dir(pc, outdir);
+
	pkg_create_set_expand_manifest(pc, expand_manifest);
	if (ts != (time_t)-1)
		pkg_create_set_timestamp(pc, ts);

	if (metadatadir == NULL && manifest == NULL)
-
		return (pkg_create_matches(argc, argv, match, pc) == EPKG_OK ? EX_OK : EX_SOFTWARE);
+
		return (pkg_create_matches(argc, argv, match, pc) == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
	ret = pkg_create(pc, metadatadir != NULL ? metadatadir : manifest, plist,
	    hash);
	if (ret == EPKG_EXIST || ret == EPKG_OK)
modified src/delete.c
@@ -32,7 +32,6 @@
#include <getopt.h>
#include <stdio.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -55,7 +54,7 @@ exec_delete(int argc, char **argv)
	match_t		 match = MATCH_EXACT;
	pkg_flags	 f = PKG_FLAG_NONE;
	bool		 recursive_flag = false, rc = false;
-
	int		 retcode = EX_SOFTWARE;
+
	int		 retcode = EXIT_FAILURE;
	int		 ch;
	int		 i;
	int		 lock_type = PKGDB_LOCK_ADVISORY;
@@ -64,7 +63,7 @@ exec_delete(int argc, char **argv)
	struct option longopts[] = {
		{ "all",			no_argument,	NULL,	'a' },
		{ "case-sensitive",		no_argument,	NULL,	'C' },
-
		{ "no-deinstall-script",	no_argument,	NULL,	'D' },
+
		{ "no-scripts",			no_argument,	NULL,	'D' },
		{ "force",			no_argument,	NULL,	'f' },
		{ "glob",			no_argument,	NULL,	'g' },
		{ "case-insensitive",		no_argument,	NULL,	'i' },
@@ -118,7 +117,7 @@ exec_delete(int argc, char **argv)
			break;
		default:
			usage_delete();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -127,7 +126,7 @@ exec_delete(int argc, char **argv)

	if (argc < 1 && match != MATCH_ALL) {
		usage_delete();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (dry_run)
@@ -138,28 +137,28 @@ exec_delete(int argc, char **argv)

	if (retcode == EPKG_ENODB) {
		warnx("No packages installed.  Nothing to do!");
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	} else if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to delete packages");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK) {
		warnx("Error accessing the package database");
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}


	if (pkg_jobs_new(&jobs, PKG_JOBS_DEINSTALL, db) != EPKG_OK) {
		pkgdb_close(db);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	/*
@@ -187,7 +186,7 @@ exec_delete(int argc, char **argv)

	if (pkg_jobs_solve(jobs) != EPKG_OK) {
		fprintf(stderr, "Cannot perform request\n");
-
		retcode = EX_NOPERM;
+
		retcode = EXIT_FAILURE;
		goto cleanup;
	}

@@ -204,7 +203,7 @@ exec_delete(int argc, char **argv)
			if (!quiet)
				printf("Nothing to do.\n");

-
			retcode = EX_OK;
+
			retcode = EXIT_SUCCESS;
			goto cleanup;
		}
		if (!quiet) {
@@ -215,7 +214,7 @@ exec_delete(int argc, char **argv)
		if (locked_pkgs > 0) {
			retcode = EPKG_LOCKED;
		} else {
-
			retcode = EX_DATAERR;
+
			retcode = EXIT_FAILURE;
		}
		goto cleanup;
	}
@@ -228,7 +227,7 @@ exec_delete(int argc, char **argv)
				pkg_jobs_total(jobs));
		}
		if (dry_run) {
-
			retcode = EX_OK;
+
			retcode = EXIT_SUCCESS;
			goto cleanup;
		}
		rc = query_yesno(false,
@@ -241,12 +240,13 @@ exec_delete(int argc, char **argv)
		goto cleanup;

	if (messages != NULL) {
-
		printf("%s", utstring_body(messages));
+
		fflush(messages->fp);
+
		printf("%s", messages->buf);
	}
	pkgdb_compact(db);

	if (rc)
-
		retcode = EX_OK;
+
		retcode = EXIT_SUCCESS;
	else
		retcode = EXIT_FAILURE;

modified src/event.c
@@ -57,15 +57,14 @@
#include <kvec.h>

#include <bsd_compat.h>
-
#include <utstring.h>

#include "pkg.h"
#include "pkgcli.h"

#define STALL_TIME 5

-
UT_string *messages = NULL;
-
UT_string *conflicts = NULL;
+
xstring *messages = NULL;
+
xstring *conflicts = NULL;

struct cleanup {
	void *data;
@@ -73,7 +72,7 @@ struct cleanup {
};

static char *progress_message = NULL;
-
static UT_string *msg_buf = NULL;
+
static xstring *msg_buf = NULL;
static int last_progress_percent = -1;
static bool progress_started = false;
static bool progress_interrupted = false;
@@ -128,19 +127,19 @@ format_rate_SI(char *buf, int size, off_t bytes)
}

void
-
job_status_end(UT_string *msg)
+
job_status_end(xstring *msg)
{
-
	printf("%s\n", utstring_body(msg));
-
	/*printf("\033]0; %s\007", utstring_body(msg));*/
-
	utstring_clear(msg);
+
	fflush(msg->fp);
+
	printf("%s\n", msg->buf);
+
	xstring_reset(msg);
}

void
-
job_status_begin(UT_string *msg)
+
job_status_begin(xstring *msg)
{
	int n;

-
	utstring_clear(msg);
+
	xstring_reset(msg);
#ifdef HAVE_LIBJAIL
	static char hostname[MAXHOSTNAMELEN] = "";
	static int jailed = -1;
@@ -157,7 +156,7 @@ job_status_begin(UT_string *msg)
		if (hostname[0] == '\0')
			gethostname(hostname, sizeof(hostname));

-
		utstring_printf(msg, "[%s] ", hostname);
+
		fprintf(msg->fp, "[%s] ", hostname);
	}
#endif

@@ -166,16 +165,16 @@ job_status_begin(UT_string *msg)
		if (add_deps_depth > 1) {
			for (n = 0; n < (2 * add_deps_depth); ++n) {
				if (n % 4 == 0 && n < (2 * add_deps_depth))
-
					utstring_printf(msg, "|");
+
					fprintf(msg->fp, "|");
				else
-
					utstring_printf(msg, " ");
+
					fprintf(msg->fp, " ");
			}
		}
-
		utstring_printf(msg, "`-- ");
+
		fprintf(msg->fp, "`-- ");
	}

	if ((nbtodl > 0 || nbactions > 0) && nbdone > 0)
-
		utstring_printf(msg, "[%d/%d] ", nbdone, (nbtodl) ? nbtodl : nbactions);
+
		fprintf(msg->fp, "[%d/%d] ", nbdone, (nbtodl) ? nbtodl : nbactions);
	if (nbtodl > 0 && nbtodl == nbdone) {
		nbtodl = 0;
		nbdone = 0;
@@ -229,7 +228,7 @@ event_sandboxed_call(pkg_sandbox_cb func, int fd, void *ud)
#ifdef HAVE_CAPSICUM
	if (cap_enter() < 0 && errno != ENOSYS) {
		warn("cap_enter() failed");
-
		return (EPKG_FATAL);
+
		_exit(EXIT_FAILURE);
	}
#endif

@@ -358,7 +357,8 @@ progressbar_start(const char *pmsg)
	if (pmsg != NULL)
		progress_message = strdup(pmsg);
	else {
-
		progress_message = strdup(utstring_body(msg_buf));
+
		fflush(msg_buf->fp);
+
		progress_message = strdup(msg_buf->buf);
	}
	last_progress_percent = -1;
	last_tick = 0;
@@ -541,7 +541,7 @@ event_callback(void *data, struct pkg_event *ev)
	const char *filename, *reponame;

	if (msg_buf == NULL) {
-
		utstring_new(msg_buf);
+
		msg_buf = xstring_new();
	}

	/*
@@ -600,7 +600,7 @@ event_callback(void *data, struct pkg_event *ev)
		}
		job_status_begin(msg_buf);
		progress_debit = true;
-
		utstring_printf(msg_buf, "Fetching %s", filename);
+
		fprintf(msg_buf->fp, "Fetching %s", filename);
		break;
	case PKG_EVENT_FETCH_FINISHED:
		progress_debit = false;
@@ -611,9 +611,10 @@ event_callback(void *data, struct pkg_event *ev)
		job_status_begin(msg_buf);

		pkg = ev->e_install_begin.pkg;
-
		pkg_utstring_printf(msg_buf, "Installing %n-%v...\n", pkg,
+
		pkg_fprintf(msg_buf->fp, "Installing %n-%v...\n", pkg,
		    pkg);
-
		printf("%s", utstring_body(msg_buf));
+
		fflush(msg_buf->fp);
+
		printf("%s", msg_buf->buf);
		break;
	case PKG_EVENT_INSTALL_FINISHED:
		if (quiet)
@@ -625,7 +626,8 @@ event_callback(void *data, struct pkg_event *ev)
		else {
			job_status_begin(msg_buf);
			pkg = ev->e_install_begin.pkg;
-
			pkg_utstring_printf(msg_buf, "Extracting %n-%v", pkg, pkg);
+
			pkg_fprintf(msg_buf->fp, "Extracting %n-%v", pkg, pkg);
+
			fflush(msg_buf->fp);
		}
		break;
	case PKG_EVENT_EXTRACT_FINISHED:
@@ -646,7 +648,9 @@ event_callback(void *data, struct pkg_event *ev)
			break;
		printf(" done (%d conflicting)\n", ev->e_integrity_finished.conflicting);
		if (conflicts != NULL) {
-
			printf("%s", utstring_body(conflicts));
+
			fflush(conflicts->fp);
+
			printf("%s", conflicts->buf);
+
			xstring_free(conflicts);
			conflicts = NULL;
		}
		break;
@@ -674,8 +678,9 @@ event_callback(void *data, struct pkg_event *ev)
		job_status_begin(msg_buf);

		pkg = ev->e_install_begin.pkg;
-
		pkg_utstring_printf(msg_buf, "Deinstalling %n-%v...\n", pkg, pkg);
-
		printf("%s", utstring_body(msg_buf));
+
		pkg_fprintf(msg_buf->fp, "Deinstalling %n-%v...\n", pkg, pkg);
+
		fflush(msg_buf->fp);
+
		printf("%s", msg_buf->buf);
		break;
	case PKG_EVENT_DEINSTALL_FINISHED:
		if (quiet)
@@ -687,7 +692,7 @@ event_callback(void *data, struct pkg_event *ev)
		else {
			job_status_begin(msg_buf);
			pkg = ev->e_install_begin.pkg;
-
			pkg_utstring_printf(msg_buf, "Deleting files for %n-%v",
+
			pkg_fprintf(msg_buf->fp, "Deleting files for %n-%v",
			    pkg, pkg);
		}
		break;
@@ -703,19 +708,20 @@ event_callback(void *data, struct pkg_event *ev)

		switch (pkg_version_change_between(pkg_new, pkg_old)) {
		case PKG_DOWNGRADE:
-
			pkg_utstring_printf(msg_buf, "Downgrading %n from %v to %v...\n",
+
			pkg_fprintf(msg_buf->fp, "Downgrading %n from %v to %v...\n",
			    pkg_new, pkg_old, pkg_new);
			break;
		case PKG_REINSTALL:
-
			pkg_utstring_printf(msg_buf, "Reinstalling %n-%v...\n",
+
			pkg_fprintf(msg_buf->fp, "Reinstalling %n-%v...\n",
		    pkg_old, pkg_old);
			break;
		case PKG_UPGRADE:
-
			pkg_utstring_printf(msg_buf, "Upgrading %n from %v to %v...\n",
+
			pkg_fprintf(msg_buf->fp, "Upgrading %n from %v to %v...\n",
			    pkg_new, pkg_old, pkg_new);
			break;
		}
-
		printf("%s", utstring_body(msg_buf));
+
		fflush(msg_buf->fp);
+
		printf("%s", msg_buf->buf);
		break;
	case PKG_EVENT_UPGRADE_FINISHED:
		if (quiet)
@@ -827,18 +833,18 @@ event_callback(void *data, struct pkg_event *ev)
		    ev->e_progress_tick.total);
		break;
	case PKG_EVENT_BACKUP:
-
		utstring_printf(msg_buf, "Backing up");
+
		fprintf(msg_buf->fp, "Backing up");
		break;
	case PKG_EVENT_RESTORE:
-
		utstring_printf(msg_buf, "Restoring");
+
		fprintf(msg_buf->fp, "Restoring");
		break;
	case PKG_EVENT_NEW_ACTION:
		nbdone++;
		break;
	case PKG_EVENT_MESSAGE:
		if (messages == NULL)
-
			utstring_new(messages);
-
		utstring_printf(messages, "%s", ev->e_pkg_message.msg);
+
			messages = xstring_new();
+
		fprintf(messages->fp, "%s", ev->e_pkg_message.msg);
		break;
	case PKG_EVENT_CLEANUP_CALLBACK_REGISTER:
		if (!signal_handler_installed) {
@@ -865,25 +871,32 @@ event_callback(void *data, struct pkg_event *ev)
		break;
	case PKG_EVENT_CONFLICTS:
		if (conflicts == NULL) {
-
			utstring_new(conflicts);
+
			conflicts = xstring_new();
		}
-
		pkg_utstring_printf(conflicts, "  - %n-%v",
+
		pkg_fprintf(conflicts->fp, "  - %n-%v",
		    ev->e_conflicts.p1, ev->e_conflicts.p1);
		if (pkg_repos_total_count() > 1) {
			pkg_get(ev->e_conflicts.p1, PKG_REPONAME, &reponame);
-
			utstring_printf(conflicts, " [%s]",
+
			fprintf(conflicts->fp, " [%s]",
			    reponame == NULL ? "installed" : reponame);
		}
-
		pkg_utstring_printf(conflicts, " conflicts with %n-%v",
+
		pkg_fprintf(conflicts->fp, " conflicts with %n-%v",
		    ev->e_conflicts.p2, ev->e_conflicts.p2);
		if (pkg_repos_total_count() > 1) {
			pkg_get(ev->e_conflicts.p2, PKG_REPONAME, &reponame);
-
			utstring_printf(conflicts, " [%s]",
+
			fprintf(conflicts->fp, " [%s]",
			    reponame == NULL ? "installed" : reponame);
		}
-
		utstring_printf(conflicts, " on %s\n",
+
		fprintf(conflicts->fp, " on %s\n",
		    ev->e_conflicts.path);
		break;
+
	case PKG_EVENT_TRIGGER:
+
		if (!quiet) {
+
			if (ev->e_trigger.cleanup)
+
				printf("==> Cleaning up trigger: %s\n", ev->e_trigger.name);
+
			else
+
				printf("==> Running trigger: %s\n", ev->e_trigger.name);
+
		}
	default:
		break;
	}
modified src/fetch.c
@@ -35,7 +35,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -60,7 +59,7 @@ exec_fetch(int argc, char **argv)
	const char	*reponame = NULL;
	const char *destdir = NULL;
	int		 ch;
-
	int		 retcode = EX_SOFTWARE;
+
	int		 retcode = EXIT_FAILURE;
	bool		 upgrades_for_installed = false, rc, csum_only = false;
	unsigned	 mode;
	match_t		 match = MATCH_EXACT;
@@ -124,7 +123,7 @@ exec_fetch(int argc, char **argv)
			break;
		default:
			usage_fetch();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -132,12 +131,12 @@ exec_fetch(int argc, char **argv)

	if (argc < 1 && match != MATCH_ALL && !upgrades_for_installed) {
		usage_fetch();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

        if (match == MATCH_ALL && upgrades_for_installed) {
		usage_fetch();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (auto_update)
@@ -149,18 +148,18 @@ exec_fetch(int argc, char **argv)

	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to access repo catalogue");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (upgrades_for_installed) {
		retcode = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);

		if (retcode == EPKG_ENOACCESS) {
			warnx("Insufficient privileges to access the package database");
-
			return (EX_NOPERM);
+
			return (EXIT_FAILURE);
		} else if (retcode != EPKG_OK)
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
	}

	/* first update the remote repositories if needed */
@@ -169,12 +168,12 @@ exec_fetch(int argc, char **argv)
		return (retcode);

	if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}


@@ -224,7 +223,7 @@ exec_fetch(int argc, char **argv)
	if (csum_only && !quiet)
		printf("Integrity check was successful.\n");

-
	retcode = EX_OK;
+
	retcode = EXIT_SUCCESS;

cleanup:
	pkg_jobs_free(jobs);
modified src/info.c
@@ -44,7 +44,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

#include "pkgcli.h"
@@ -215,13 +214,13 @@ exec_info(int argc, char **argv)
			else if (strcasecmp(optarg, "ucl") == 0)
				opt |= INFO_RAW_UCL;
			else
-
				errx(EX_USAGE, "Invalid format '%s' for the "
+
				errx(EXIT_FAILURE, "Invalid format '%s' for the "
				    "raw output, expecting json, json-compact "
				    "or yaml", optarg);
			break;
		default:
			usage_info();
-
			return(EX_USAGE);
+
			return(EXIT_FAILURE);
		}
	}

@@ -234,9 +233,9 @@ exec_info(int argc, char **argv)
	if (argc == 0 && file == NULL && match != MATCH_ALL) {
		/* which -O bsd.*.mk always execpt clean output */
		if (origin_search)
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		usage_info();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	/* When no other data is requested, default is to print
@@ -266,7 +265,7 @@ exec_info(int argc, char **argv)
	if (file != NULL) {
		if ((fd = open(file, O_RDONLY)) == -1) {
			warn("Unable to open %s", file);
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
		}

		drop_privileges();
@@ -275,13 +274,13 @@ exec_info(int argc, char **argv)
		if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS ) {
			warn("cap_rights_limit() failed");
			close(fd);
-
			return (EX_SOFTWARE);
+
			return (EXIT_FAILURE);
		}

		if (cap_enter() < 0 && errno != ENOSYS) {
			warn("cap_enter() failed");
			close(fd);
-
			return (EX_SOFTWARE);
+
			return (EXIT_FAILURE);
		}
#endif
		if (opt == INFO_TAG_NAMEVER)
@@ -300,32 +299,32 @@ exec_info(int argc, char **argv)
		print_info(pkg, opt);
		close(fd);
		pkg_free(pkg);
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	}

	ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
	if (ret == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to query the package database");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (ret == EPKG_ENODB) {
		if (match == MATCH_ALL)
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		if (origin_search)
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		if (!quiet)
			warnx("No packages installed");
-
		return (EX_UNAVAILABLE);
+
		return (EXIT_FAILURE);
	} else if (ret != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	ret = pkgdb_open(&db, PKGDB_DEFAULT);
	if (ret != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	drop_privileges();
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	i = 0;
@@ -500,18 +499,18 @@ exec_info(int argc, char **argv)
				}
			}
			if (pkg_exists)
-
				retcode = EX_OK;
+
				retcode = EXIT_SUCCESS;
			else
				print_info(pkg, opt);
		}
		if (ret != EPKG_END) {
-
			retcode = EX_IOERR;
+
			retcode = EXIT_FAILURE;
		}

-
		if (retcode == EX_OK && !gotone && match != MATCH_ALL) {
+
		if (retcode == EXIT_SUCCESS && !gotone && match != MATCH_ALL) {
			if (!quiet)
				warnx("No package(s) matching %s", argv[i]);
-
			retcode = EX_SOFTWARE;
+
			retcode = EXIT_FAILURE;
		}

		pkgdb_it_free(it);
modified src/install.c
@@ -37,7 +37,6 @@
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -76,7 +75,7 @@ exec_install(int argc, char **argv)
		{ "fetch-only",		no_argument,		NULL,	'F' },
		{ "glob",		no_argument,		NULL,	'g' },
		{ "case-insensitive",	no_argument,		NULL,	'i' },
-
		{ "no-install-scripts",	no_argument,		NULL,	'I' },
+
		{ "no-scripts",		no_argument,		NULL,	'I' },
		{ "local-only",		no_argument,		NULL,	'l' },
		{ "ignore-missing",	no_argument,		NULL,	'M' },
		{ "dry-run",		no_argument,		NULL,	'n' },
@@ -154,7 +153,7 @@ exec_install(int argc, char **argv)
			break;
		default:
			usage_install();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -162,7 +161,7 @@ exec_install(int argc, char **argv)

	if (argc < 1) {
		usage_install();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (dry_run && !auto_update)
@@ -186,11 +185,11 @@ exec_install(int argc, char **argv)

	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to install packages");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	else
-
		retcode = EX_SOFTWARE;
+
		retcode = EXIT_FAILURE;

	/* first update the remote repositories if needed */
	if (auto_update && pkg_repos_total_count() > 0 &&
@@ -200,12 +199,12 @@ exec_install(int argc, char **argv)
	if (pkgdb_open_all(&db,
	    local_only ? PKGDB_DEFAULT : PKGDB_MAYBE_REMOTE,
	    reponame) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (pkg_jobs_new(&jobs, PKG_JOBS_INSTALL, db) != EPKG_OK)
@@ -254,7 +253,8 @@ exec_install(int argc, char **argv)
		}

		if (messages != NULL) {
-
			printf("%s", utstring_body(messages));
+
			fflush(messages->fp);
+
			printf("%s", messages->buf);
		}
		break;
	}
@@ -263,7 +263,7 @@ exec_install(int argc, char **argv)
		printf("The most recent versions of packages are already installed\n");

	if (rc)
-
		retcode = EX_OK;
+
		retcode = EXIT_SUCCESS;
	else
		retcode = EXIT_FAILURE;

modified src/lock.c
@@ -29,7 +29,6 @@
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -101,17 +100,17 @@ do_lock_unlock(struct pkgdb *db, int match, const char *pkgname,
	struct pkgdb_it	*it = NULL;
	struct pkg	*pkg = NULL;
	int		 retcode;
-
	int		 exitcode = EX_OK;
+
	int		 exitcode = EXIT_SUCCESS;

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an exclusive lock on database. "
		      "It is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
-
		exitcode = EX_IOERR;
+
		exitcode = EXIT_FAILURE;
		goto cleanup;
	}

@@ -122,7 +121,7 @@ do_lock_unlock(struct pkgdb *db, int match, const char *pkgname,
			retcode = do_unlock(db, pkg);

		if (retcode != EPKG_OK) {
-
			exitcode = EX_IOERR;
+
			exitcode = EXIT_FAILURE;
			goto cleanup;
		}
	}
@@ -157,7 +156,7 @@ list_locked(struct pkgdb *db, bool has_locked)

	if ((it = pkgdb_query(db, " where locked=1", MATCH_CONDITION)) == NULL) {
		pkgdb_close(db);
-
		return (EX_UNAVAILABLE);
+
		return (EXIT_FAILURE);
	}

	if (!quiet && !has_locked)
@@ -176,7 +175,7 @@ list_locked(struct pkgdb *db, bool has_locked)
	if (has_locked)
		return (gotone ? EXIT_SUCCESS : EXIT_FAILURE);

-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}

static int
@@ -185,7 +184,7 @@ exec_lock_unlock(int argc, char **argv, enum action action)
	struct pkgdb	*db = NULL;
	int		 match = MATCH_EXACT;
	int		 retcode, i;
-
	int		 exitcode = EX_OK;
+
	int		 exitcode = EXIT_SUCCESS;
	int		 ch;
	bool		 show_locked = false;
	bool		 read_only = false;
@@ -235,7 +234,7 @@ exec_lock_unlock(int argc, char **argv, enum action action)
			break;
		default:
			usage_lock();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
        }
	argc -= optind;
@@ -251,7 +250,7 @@ exec_lock_unlock(int argc, char **argv, enum action action)

	if (!show_locked && match != MATCH_ALL && argc == 0) {
		usage_lock();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (read_only)
@@ -261,21 +260,21 @@ exec_lock_unlock(int argc, char **argv, enum action action)
			       PKGDB_DB_LOCAL);
	if (retcode == EPKG_ENODB) {
		if (match == MATCH_ALL)
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		if (!quiet)
			warnx("No packages installed.  Nothing to do!");
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	} else if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to modify the package database");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK) {
		warnx("Error accessing the package database");
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
	if (retcode != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (!read_only) {
		if (match == MATCH_ALL) {
@@ -283,7 +282,7 @@ exec_lock_unlock(int argc, char **argv, enum action action)
		} else {
			for (i = 0; i < argc; i++) {
				retcode = do_lock_unlock(db, match, argv[i], action);
-
				if (retcode != EX_OK)
+
				if (retcode != EXIT_SUCCESS)
					exitcode = retcode;
			}
		}
@@ -294,5 +293,5 @@ exec_lock_unlock(int argc, char **argv, enum action action)

	pkgdb_close(db);

-
	return (exitcode);
+
	return (exitcode == EPKG_OK ? EXIT_SUCCESS : EXIT_FAILURE);
}
modified src/main.c
@@ -54,7 +54,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <utlist.h>
#include <unistd.h>
#ifdef HAVE_LIBJAIL
@@ -197,13 +196,13 @@ usage(const char *conffile, const char *reposdir, FILE *out, enum pkg_usage_reas
			fprintf(out, "\t%-15s%s\n", cmd[i].name, cmd[i].desc);

		if (!pkg_initialized() && pkg_ini(conffile, reposdir, 0) != EPKG_OK)
-
			errx(EX_SOFTWARE, "Cannot parse configuration file!");
+
			errx(EXIT_FAILURE, "Cannot parse configuration file!");

		plugins_enabled = pkg_object_bool(pkg_config_get("PKG_ENABLE_PLUGINS"));

		if (plugins_enabled) {
			if (pkg_plugins_init() != EPKG_OK)
-
				errx(EX_SOFTWARE, "Plugins cannot be loaded");
+
				errx(EXIT_FAILURE, "Plugins cannot be loaded");

			fprintf(out, "\nCommands provided by plugins:\n");

@@ -218,7 +217,7 @@ usage(const char *conffile, const char *reposdir, FILE *out, enum pkg_usage_reas

out:
	fprintf(out, "\nFor more information on available commands and options see 'pkg help'.\n");
-
	exit(EX_USAGE);
+
	exit(EXIT_FAILURE);
}

static void
@@ -240,13 +239,13 @@ exec_help(int argc, char **argv)

	if ((argc != 2) || (strcmp("help", argv[1]) == 0)) {
		usage_help();
-
		return(EX_USAGE);
+
		return(EXIT_FAILURE);
	}

	for (i = 0; i < cmd_len; i++) {
		if (strcmp(cmd[i].name, argv[1]) == 0) {
			if (asprintf(&manpage, "/usr/bin/man pkg-%s", cmd[i].name) == -1)
-
				errx(EX_SOFTWARE, "cannot allocate memory");
+
				errx(EXIT_FAILURE, "cannot allocate memory");

			system(manpage);
			free(manpage);
@@ -261,7 +260,7 @@ exec_help(int argc, char **argv)
		DL_FOREACH(plugins, c) {
			if (strcmp(c->name, argv[1]) == 0) {
				if (asprintf(&manpage, "/usr/bin/man pkg-%s", c->name) == -1)
-
					errx(EX_SOFTWARE, "cannot allocate memory");
+
					errx(EXIT_FAILURE, "cannot allocate memory");

				system(manpage);
				free(manpage);
@@ -293,7 +292,7 @@ exec_help(int argc, char **argv)

	fprintf(stderr, "See 'pkg help' for more information on the commands.\n");

-
	return (EX_USAGE);
+
	return (EXIT_FAILURE);
}

static void
@@ -383,13 +382,13 @@ show_version_info(int version)
	printf(PKG_PORTVERSION""GITHASH"\n");

	if (version == 1)
-
		exit(EX_OK);
+
		exit(EXIT_SUCCESS);

	printf("%s\n", pkg_config_dump());
	show_plugin_info();
	show_repository_info();

-
	exit(EX_OK);
+
	exit(EXIT_SUCCESS);
	/* NOTREACHED */
}

@@ -406,19 +405,19 @@ do_activation_test(int argc)

	switch (pkg_status(&count)) {
	case PKG_STATUS_UNINSTALLED: /* This case shouldn't ever happen... */
-
		errx(EX_UNAVAILABLE, "can't execute " PKG_EXEC_NAME
+
		errx(EXIT_FAILURE, "can't execute " PKG_EXEC_NAME
		    " or " PKG_STATIC_NAME "\n");
		/* NOTREACHED */
	case PKG_STATUS_NODB:
-
		errx(EX_UNAVAILABLE, "package database non-existent");
+
		errx(EXIT_FAILURE, "package database non-existent");
		/* NOTREACHED */
	case PKG_STATUS_NOPACKAGES:
-
		errx(EX_UNAVAILABLE, "no packages registered");
+
		errx(EXIT_FAILURE, "no packages registered");
		/* NOTREACHED */
	case PKG_STATUS_ACTIVE:
		if (argc == 0) {
			warnx("%d packages installed", count);
-
			exit(EX_OK);
+
			exit(EXIT_SUCCESS);
		}
		break;
	}
@@ -450,7 +449,7 @@ export_arg_option (char *arg)
static void
start_process_worker(char *const *save_argv)
{
-
	int	ret = EX_OK;
+
	int	ret = EXIT_SUCCESS;
	int	status;
	pid_t	child_pid;

@@ -471,11 +470,11 @@ start_process_worker(char *const *save_argv)
			return;
		} else {
			if (child_pid == -1)
-
				err(EX_OSERR, "Failed to fork worker process");
+
				err(EXIT_FAILURE, "Failed to fork worker process");

			while (waitpid(child_pid, &status, 0) == -1) {
				if (errno != EINTR)
-
					err(EX_OSERR, "Child process pid=%d", (int)child_pid);
+
					err(EXIT_FAILURE, "Child process pid=%d", (int)child_pid);
			}

			ret = WEXITSTATUS(status);
@@ -537,7 +536,7 @@ expand_aliases(int argc, char ***argv)
	veclen = sizeof(char *) * (spaces + argc + 1);
	buf = malloc(veclen + arglen);
	if (buf == NULL)
-
		err(EX_OSERR, "expanding aliases");
+
		err(EXIT_FAILURE, "expanding aliases");

	newargv = (char **) buf;
	args = (char *) (buf + veclen);
@@ -596,7 +595,7 @@ main(int argc, char **argv)
	signed char	  ch;
	int64_t		  debug = 0;
	int		  version = 0;
-
	int		  ret = EX_OK;
+
	int		  ret = EXIT_SUCCESS;
	bool		  plugins_enabled = false;
	bool		  plugin_found = false;
	bool		  show_commands = false;
@@ -642,7 +641,7 @@ main(int argc, char **argv)
	 * line concept. */

	if (setenv("POSIXLY_CORRECT", "1",  1) == -1)
-
		err(EX_SOFTWARE, "setenv() failed");
+
		err(EXIT_FAILURE, "setenv() failed");

	save_argv = argv;

@@ -705,7 +704,7 @@ main(int argc, char **argv)

	if (show_commands && version == 0) {
		show_command_names();
-
		exit(EX_OK);
+
		exit(EXIT_SUCCESS);
	}

	if (argc == 0 && version == 0 && !activation_test)
@@ -735,7 +734,7 @@ main(int argc, char **argv)

	if (chroot_path != NULL) {
		if (chroot(chroot_path) == -1) {
-
			err(EX_SOFTWARE, "chroot failed");
+
			err(EXIT_FAILURE, "chroot failed");
		}
	}

@@ -751,26 +750,26 @@ main(int argc, char **argv)

	if (jail_str != NULL || chroot_path != NULL)
		if (chdir("/") == -1)
-
			errx(EX_SOFTWARE, "chdir() failed");
+
			errx(EXIT_FAILURE, "chdir() failed");
#endif

	if (rootdir != NULL) {
		if (realpath(rootdir, realrootdir) == NULL)
-
			err(EX_SOFTWARE, "Invalid rootdir");
+
			err(EXIT_FAILURE, "Invalid rootdir");
		if (chdir(rootdir) == -1)
-
			errx(EX_SOFTWARE, "chdir() failed");
+
			errx(EXIT_FAILURE, "chdir() failed");
		if (pkg_set_rootdir(realrootdir) != EPKG_OK)
-
			exit(EX_SOFTWARE);
+
			exit(EXIT_FAILURE);
	}

	if (pkg_ini(conffile, reposdir, init_flags) != EPKG_OK)
-
		errx(EX_SOFTWARE, "Cannot parse configuration file!");
+
		errx(EXIT_FAILURE, "Cannot parse configuration file!");

	if (debug > 0)
		pkg_set_debug_level(debug);

	if (atexit(&pkg_shutdown) != 0)
-
		errx(EX_SOFTWARE, "register pkg_shutdown() to run at exit");
+
		errx(EXIT_FAILURE, "register pkg_shutdown() to run at exit");

	if (jail_str == NULL && !pkg_compiled_for_same_os_major())
		warnx("Warning: Major OS version upgrade detected.  Running "
@@ -783,10 +782,10 @@ main(int argc, char **argv)
		struct pkg_plugin	*p = NULL;

		if (pkg_plugins_init() != EPKG_OK)
-
			errx(EX_SOFTWARE, "Plugins cannot be loaded");
+
			errx(EXIT_FAILURE, "Plugins cannot be loaded");

		if (atexit(&pkg_plugins_shutdown) != 0)
-
			errx(EX_SOFTWARE,
+
			errx(EXIT_FAILURE,
                            "register pkg_plugins_shutdown() to run at exit");

		/* load commands plugins */
@@ -891,7 +890,7 @@ main(int argc, char **argv)
	if (save_argv != argv)
		free(argv);

-
	if (ret == EX_OK && newpkgversion)
+
	if (ret == EXIT_SUCCESS && newpkgversion)
		return (EX_NEEDRESTART);

	return (ret);
modified src/pkgcli.h
@@ -30,7 +30,8 @@

#include <search.h>
#include <stdint.h>
-
#include <utstring.h>
+
#include <string.h>
+
#include <xstring.h>
#include <bsd_compat.h>

#define pkg_warnx(fmt, ...) pkg_fprintf(stderr, "%S: " fmt, getprogname(), __VA_ARGS__, -1)
@@ -265,8 +266,8 @@ int info_flags(uint64_t opt, bool remote);
void print_info(struct pkg * const pkg, uint64_t opt);
int print_jobs_summary(struct pkg_jobs *j, const char *msg, ...);

-
void job_status_begin(UT_string *);
-
void job_status_end(UT_string *);
+
void job_status_begin(xstring *);
+
void job_status_end(xstring *);

int event_callback(void *data, struct pkg_event *ev);
int print_pkg(struct pkg *p, void *ctx);
@@ -274,10 +275,9 @@ void progressbar_start(const char *pmsg);
void progressbar_tick(int64_t current, int64_t total);
void progressbar_stop(void);

-
void utstring_flush(UT_string *buf);
void drop_privileges(void);

-
extern UT_string *messages;
+
extern xstring *messages;


/* pkg-query / pkg-rquery */
@@ -289,7 +289,7 @@ struct query_flags {
};

void print_query(struct pkg *pkg, char *qstr, char multiline);
-
int format_sql_condition(const char *str, UT_string *sqlcond,
+
int format_sql_condition(const char *str, xstring *sqlcond,
			 bool for_remote);
int analyse_query_string(char *qstr, struct query_flags *q_flags,
			 const unsigned int q_flags_len, int *flags,
modified src/plugins.c
@@ -28,7 +28,6 @@
#include <getopt.h>
#include <stdio.h>
#include <stdbool.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -61,7 +60,7 @@ exec_plugins(int argc, char **argv)
			break;
		default:
			usage_plugins();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -77,5 +76,5 @@ exec_plugins(int argc, char **argv)
		       pkg_plugin_get(p, PKG_PLUGIN_DESC),
		       pkg_plugin_get(p, PKG_PLUGIN_VERSION));

-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}
modified src/query.c
@@ -36,10 +36,8 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

-
#include <utstring.h>
#include <pkg.h>
#include "pkgcli.h"

@@ -80,101 +78,101 @@ static struct query_flags accepted_query_flags[] = {
};

static void
-
format_str(struct pkg *pkg, UT_string *dest, const char *qstr, const void *data)
+
format_str(struct pkg *pkg, xstring *dest, const char *qstr, const void *data)
{
	bool automatic;
	bool locked;
	bool vital;

-
	utstring_clear(dest);
+
	xstring_reset(dest);

	while (qstr[0] != '\0') {
		if (qstr[0] == '%') {
			qstr++;
			switch (qstr[0]) {
			case 'n':
-
				pkg_utstring_printf(dest, "%n", pkg);
+
				pkg_fprintf(dest->fp, "%n", pkg);
				break;
			case 'v':
-
				pkg_utstring_printf(dest, "%v", pkg);
+
				pkg_fprintf(dest->fp, "%v", pkg);
				break;
			case 'o':
-
				pkg_utstring_printf(dest, "%o", pkg);
+
				pkg_fprintf(dest->fp, "%o", pkg);
				break;
			case 'R':
-
				pkg_utstring_printf(dest, "%N", pkg);
+
				pkg_fprintf(dest->fp, "%N", pkg);
				break;
			case 'p':
-
				pkg_utstring_printf(dest, "%p", pkg);
+
				pkg_fprintf(dest->fp, "%p", pkg);
				break;
			case 'm':
-
				pkg_utstring_printf(dest, "%m", pkg);
+
				pkg_fprintf(dest->fp, "%m", pkg);
				break;
			case 'c':
-
				pkg_utstring_printf(dest, "%c", pkg);
+
				pkg_fprintf(dest->fp, "%c", pkg);
				break;
			case 'w':
-
				pkg_utstring_printf(dest, "%w", pkg);
+
				pkg_fprintf(dest->fp, "%w", pkg);
				break;
			case 'a':
				pkg_get(pkg, PKG_AUTOMATIC, &automatic);
-
				utstring_printf(dest, "%d", automatic);
+
				fprintf(dest->fp, "%d", automatic);
				break;
			case 'k':
				pkg_get(pkg, PKG_LOCKED, &locked);
-
				utstring_printf(dest, "%d", locked);
+
				fprintf(dest->fp, "%d", locked);
				break;
			case 't':
-
				pkg_utstring_printf(dest, "%t", pkg);
+
				pkg_fprintf(dest->fp, "%t", pkg);
				break;
			case 's':
				qstr++;
				if (qstr[0] == 'h') 
-
					pkg_utstring_printf(dest, "%#sB", pkg);
+
					pkg_fprintf(dest->fp, "%#sB", pkg);
			        else if (qstr[0] == 'b')
-
					pkg_utstring_printf(dest, "%s", pkg);
+
					pkg_fprintf(dest->fp, "%s", pkg);
				break;
			case 'e':
-
				pkg_utstring_printf(dest, "%e", pkg);
+
				pkg_fprintf(dest->fp, "%e", pkg);
				break;
			case '?':
				qstr++;
				switch (qstr[0]) {
				case 'd':
-
					pkg_utstring_printf(dest, "%?d", pkg);
+
					pkg_fprintf(dest->fp, "%?d", pkg);
					break;
				case 'r':
-
					pkg_utstring_printf(dest, "%?r", pkg);
+
					pkg_fprintf(dest->fp, "%?r", pkg);
					break;
				case 'C':
-
					pkg_utstring_printf(dest, "%?C", pkg);
+
					pkg_fprintf(dest->fp, "%?C", pkg);
					break;
				case 'F':
-
					pkg_utstring_printf(dest, "%?F", pkg);
+
					pkg_fprintf(dest->fp, "%?F", pkg);
					break;
				case 'O':
-
					pkg_utstring_printf(dest, "%?O", pkg);
+
					pkg_fprintf(dest->fp, "%?O", pkg);
					break;
				case 'D':
-
					pkg_utstring_printf(dest, "%?D", pkg);
+
					pkg_fprintf(dest->fp, "%?D", pkg);
					break;
				case 'L':
-
					pkg_utstring_printf(dest, "%?L", pkg);
+
					pkg_fprintf(dest->fp, "%?L", pkg);
					break;
				case 'U':
-
					pkg_utstring_printf(dest, "%?U", pkg);
+
					pkg_fprintf(dest->fp, "%?U", pkg);
					break;
				case 'G':
-
					pkg_utstring_printf(dest, "%?G", pkg);
+
					pkg_fprintf(dest->fp, "%?G", pkg);
					break;
				case 'B':
-
					pkg_utstring_printf(dest, "%?B", pkg);
+
					pkg_fprintf(dest->fp, "%?B", pkg);
					break;
				case 'b':
-
					pkg_utstring_printf(dest, "%?b", pkg);
+
					pkg_fprintf(dest->fp, "%?b", pkg);
					break;
				case 'A':
-
					pkg_utstring_printf(dest, "%?A", pkg);
+
					pkg_fprintf(dest->fp, "%?A", pkg);
					break;
				}
				break;
@@ -182,167 +180,168 @@ format_str(struct pkg *pkg, UT_string *dest, const char *qstr, const void *data)
				qstr++;
				switch (qstr[0]) {
				case 'd':
-
					pkg_utstring_printf(dest, "%#d", pkg);
+
					pkg_fprintf(dest->fp, "%#d", pkg);
					break;
				case 'r':
-
					pkg_utstring_printf(dest, "%#r", pkg);
+
					pkg_fprintf(dest->fp, "%#r", pkg);
					break;
				case 'C':
-
					pkg_utstring_printf(dest, "%#C", pkg);
+
					pkg_fprintf(dest->fp, "%#C", pkg);
					break;
				case 'F':
-
					pkg_utstring_printf(dest, "%#F", pkg);
+
					pkg_fprintf(dest->fp, "%#F", pkg);
					break;
				case 'O':
-
					pkg_utstring_printf(dest, "%#O", pkg);
+
					pkg_fprintf(dest->fp, "%#O", pkg);
					break;
				case 'D':
-
					pkg_utstring_printf(dest, "%#D", pkg);
+
					pkg_fprintf(dest->fp, "%#D", pkg);
					break;
				case 'L':
-
					pkg_utstring_printf(dest, "%#L", pkg);
+
					pkg_fprintf(dest->fp, "%#L", pkg);
					break;
				case 'U':
-
					pkg_utstring_printf(dest, "%#U", pkg);
+
					pkg_fprintf(dest->fp, "%#U", pkg);
					break;
				case 'G':
-
					pkg_utstring_printf(dest, "%#G", pkg);
+
					pkg_fprintf(dest->fp, "%#G", pkg);
					break;
				case 'B':
-
					pkg_utstring_printf(dest, "%#B", pkg);
+
					pkg_fprintf(dest->fp, "%#B", pkg);
					break;
				case 'b':
-
					pkg_utstring_printf(dest, "%#b", pkg);
+
					pkg_fprintf(dest->fp, "%#b", pkg);
					break;
				case 'A':
-
					pkg_utstring_printf(dest, "%#A", pkg);
+
					pkg_fprintf(dest->fp, "%#A", pkg);
					break;
				}
				break;
			case 'Q':
-
				pkg_utstring_printf(dest, "%Q", pkg);
+
				pkg_fprintf(dest->fp, "%Q", pkg);
				break;
			case 'q':
-
				pkg_utstring_printf(dest, "%q", pkg);
+
				pkg_fprintf(dest->fp, "%q", pkg);
				break;
			case 'l':
-
				pkg_utstring_printf(dest, "%l", pkg);
+
				pkg_fprintf(dest->fp, "%l", pkg);
				break;
			case 'd':
				qstr++;
				if (qstr[0] == 'n')
-
					pkg_utstring_printf(dest, "%dn", data);
+
					pkg_fprintf(dest->fp, "%dn", data);
				else if (qstr[0] == 'o')
-
					pkg_utstring_printf(dest, "%do", data);
+
					pkg_fprintf(dest->fp, "%do", data);
				else if (qstr[0] == 'v')
-
					pkg_utstring_printf(dest, "%dv", data);
+
					pkg_fprintf(dest->fp, "%dv", data);
				break;
			case 'r':
				qstr++;
				if (qstr[0] == 'n')
-
					pkg_utstring_printf(dest, "%rn", data);
+
					pkg_fprintf(dest->fp, "%rn", data);
				else if (qstr[0] == 'o')
-
					pkg_utstring_printf(dest, "%ro", data);
+
					pkg_fprintf(dest->fp, "%ro", data);
				else if (qstr[0] == 'v')
-
					pkg_utstring_printf(dest, "%rv", data);
+
					pkg_fprintf(dest->fp, "%rv", data);
				break;
			case 'C':
-
				pkg_utstring_printf(dest, "%Cn", data);
+
				pkg_fprintf(dest->fp, "%Cn", data);
				break;
			case 'F':
				qstr++;
				if (qstr[0] == 'p')
-
					pkg_utstring_printf(dest, "%Fn", data);
+
					pkg_fprintf(dest->fp, "%Fn", data);
				else if (qstr[0] == 's')
-
					pkg_utstring_printf(dest, "%Fs", data);
+
					pkg_fprintf(dest->fp, "%Fs", data);
				break;
			case 'O':
				qstr++;
				if (qstr[0] == 'k')
-
					pkg_utstring_printf(dest, "%On", data);
+
					pkg_fprintf(dest->fp, "%On", data);
				else if (qstr[0] == 'v')
-
					pkg_utstring_printf(dest, "%Ov", data);
+
					pkg_fprintf(dest->fp, "%Ov", data);
				else if (qstr[0] == 'd') /* default value */
-
					pkg_utstring_printf(dest, "%Od", data);
+
					pkg_fprintf(dest->fp, "%Od", data);
				else if (qstr[0] == 'D') /* description */
-
					pkg_utstring_printf(dest, "%OD", data);
+
					pkg_fprintf(dest->fp, "%OD", data);
				break;
			case 'D':
-
				pkg_utstring_printf(dest, "%Dn", data);
+
				pkg_fprintf(dest->fp, "%Dn", data);
				break;
			case 'L':
-
				pkg_utstring_printf(dest, "%Ln", data);
+
				pkg_fprintf(dest->fp, "%Ln", data);
				break;
			case 'U':
-
				pkg_utstring_printf(dest, "%Un", data);
+
				pkg_fprintf(dest->fp, "%Un", data);
				break;
			case 'G':
-
				pkg_utstring_printf(dest, "%Gn", data);
+
				pkg_fprintf(dest->fp, "%Gn", data);
				break;
			case 'B':
-
				pkg_utstring_printf(dest, "%Bn", data);
+
				pkg_fprintf(dest->fp, "%Bn", data);
				break;
			case 'b':
-
				pkg_utstring_printf(dest, "%bn", data);
+
				pkg_fprintf(dest->fp, "%bn", data);
				break;
			case 'A':
				qstr++;
				if (qstr[0] == 't')
-
					pkg_utstring_printf(dest, "%An", data);
+
					pkg_fprintf(dest->fp, "%An", data);
				else if (qstr[0] == 'v')
-
					pkg_utstring_printf(dest, "%Av", data);
+
					pkg_fprintf(dest->fp, "%Av", data);
				break;
			case 'M':
				if (pkg_has_message(pkg))
-
					pkg_utstring_printf(dest, "%M", pkg);
+
					pkg_fprintf(dest->fp, "%M", pkg);
				break;
			case 'V':
				pkg_get(pkg, PKG_VITAL, &vital);
-
				utstring_printf(dest, "%d", vital);
+
				fprintf(dest->fp, "%d", vital);
				break;
			case 'X':
-
				pkg_utstring_printf(dest, "%X", pkg);
+
				pkg_fprintf(dest->fp, "%X", pkg);
				break;
			case '%':
-
				utstring_printf(dest, "%c", '%');
+
				fprintf(dest->fp, "%c", '%');
				break;
			}
		} else  if (qstr[0] == '\\') {
			qstr++;
			switch (qstr[0]) {
			case 'n':
-
				utstring_printf(dest, "%c", '\n');
+
				fprintf(dest->fp, "%c", '\n');
				break;
			case 'a':
-
				utstring_printf(dest, "%c", '\a');
+
				fprintf(dest->fp, "%c", '\a');
				break;
			case 'b':
-
				utstring_printf(dest, "%c", '\b');
+
				fprintf(dest->fp, "%c", '\b');
				break;
			case 'f':
-
				utstring_printf(dest, "%c", '\f');
+
				fprintf(dest->fp, "%c", '\f');
				break;
			case 'r':
-
				utstring_printf(dest, "%c", '\r');
+
				fprintf(dest->fp, "%c", '\r');
				break;
			case '\\':
-
				utstring_printf(dest, "%c", '\\');
+
				fprintf(dest->fp, "%c", '\\');
				break;
			case 't':
-
				utstring_printf(dest, "%c", '\t');
+
				fprintf(dest->fp, "%c", '\t');
				break;
			}
		} else {
-
			utstring_printf(dest, "%c", qstr[0]);
+
			fprintf(dest->fp, "%c", qstr[0]);
		}
		qstr++;
	}
+
	fflush(dest->fp);
}

void
print_query(struct pkg *pkg, char *qstr, char multiline)
{
-
	UT_string		*output;
+
	xstring			*output;
	struct pkg_dep		*dep    = NULL;
	struct pkg_option	*option = NULL;
	struct pkg_file		*file   = NULL;
@@ -350,95 +349,95 @@ print_query(struct pkg *pkg, char *qstr, char multiline)
	char			*buf;
	struct pkg_kv		*kv;

-
	utstring_new(output);
+
	output = xstring_new();

	switch (multiline) {
	case 'd':
		while (pkg_deps(pkg, &dep) == EPKG_OK) {
			format_str(pkg, output, qstr, dep);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'r':
		while (pkg_rdeps(pkg, &dep) == EPKG_OK) {
			format_str(pkg, output, qstr, dep);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'C':
		buf = NULL;
		while (pkg_categories(pkg, &buf) == EPKG_OK) {
			format_str(pkg, output, qstr, buf);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'O':
		while (pkg_options(pkg, &option) == EPKG_OK) {
			format_str(pkg, output, qstr, option);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'F':
		while (pkg_files(pkg, &file) == EPKG_OK) {
			format_str(pkg, output, qstr, file);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'D':
		while (pkg_dirs(pkg, &dir) == EPKG_OK) {
			format_str(pkg, output, qstr, dir);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'L':
		buf = NULL;
		while (pkg_licenses(pkg, &buf) == EPKG_OK) {
			format_str(pkg, output, qstr, buf);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'U':
		buf = NULL;
		while (pkg_users(pkg, &buf) == EPKG_OK) {
			format_str(pkg, output, qstr, buf);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'G':
		buf = NULL;
		while (pkg_groups(pkg, &buf) == EPKG_OK) {
			format_str(pkg, output, qstr, buf);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'B':
		buf = NULL;
		while (pkg_shlibs_required(pkg, &buf) == EPKG_OK) {
			format_str(pkg, output, qstr, buf);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'b':
		buf = NULL;
		while (pkg_shlibs_provided(pkg, &buf) == EPKG_OK) {
			format_str(pkg, output, qstr, buf);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
		}
		break;
	case 'A':
		pkg_get(pkg, PKG_ANNOTATIONS, &kv);
		while (kv != NULL) {
			format_str(pkg, output, qstr, kv);
-
			printf("%s\n", utstring_body(output));
+
			printf("%s\n", output->buf);
			kv = kv->next;
		}
		break;
	default:
		format_str(pkg, output, qstr, dep);
-
		printf("%s\n", utstring_body(output));
+
		printf("%s\n", output->buf);
		break;
	}
-
	utstring_free(output);
+
	xstring_free(output);
}

typedef enum {
@@ -455,82 +454,82 @@ typedef enum {
} state_t;

int
-
format_sql_condition(const char *str, UT_string *sqlcond, bool for_remote)
+
format_sql_condition(const char *str, xstring *sqlcond, bool for_remote)
{
	state_t state = NONE;
	unsigned int bracket_level = 0;
	const char *sqlop;

-
	utstring_printf(sqlcond, " WHERE ");
+
	fprintf(sqlcond->fp, " WHERE ");
	while (str[0] != '\0') {
		if (state == NONE) {
			if (str[0] == '%') {
				str++;
				switch (str[0]) {
				case 'n':
-
					utstring_printf(sqlcond, "name");
+
					fprintf(sqlcond->fp, "name");
					state = OPERATOR_STRING;
					break;
				case 'o':
-
					utstring_printf(sqlcond, "origin");
+
					fprintf(sqlcond->fp, "origin");
					state = OPERATOR_STRING;
					break;
				case 'p':
-
					utstring_printf(sqlcond, "prefix");
+
					fprintf(sqlcond->fp, "prefix");
					state = OPERATOR_STRING;
					break;
				case 'm':
-
					utstring_printf(sqlcond, "maintainer");
+
					fprintf(sqlcond->fp, "maintainer");
					state = OPERATOR_STRING;
					break;
				case 'c':
-
					utstring_printf(sqlcond, "comment");
+
					fprintf(sqlcond->fp, "comment");
					state = OPERATOR_STRING;
					break;
				case 'w':
-
					utstring_printf(sqlcond, "www");
+
					fprintf(sqlcond->fp, "www");
					state = OPERATOR_STRING;
					break;
				case 's':
-
					utstring_printf(sqlcond, "flatsize");
+
					fprintf(sqlcond->fp, "flatsize");
					state = OPERATOR_INT;
					break;
				case 'a':
					if (for_remote)
						goto bad_option;
-
					utstring_printf(sqlcond, "automatic");
+
					fprintf(sqlcond->fp, "automatic");
					state = OPERATOR_INT;
					break;
				case 'q':
-
					utstring_printf(sqlcond, "arch");
+
					fprintf(sqlcond->fp, "arch");
					state = OPERATOR_STRING;
					break;
				case 'k':
					if (for_remote)
						goto bad_option;
-
					utstring_printf(sqlcond, "locked");
+
					fprintf(sqlcond->fp, "locked");
					state = OPERATOR_INT;
					break;
				case 'M':
					if (for_remote)
						goto bad_option;
-
					utstring_printf(sqlcond, "message");
+
					fprintf(sqlcond->fp, "message");
					state = OPERATOR_STRING;
					break;
				case 't':
					if (for_remote)
						goto bad_option;
-
					utstring_printf(sqlcond, "time");
+
					fprintf(sqlcond->fp, "time");
					state = OPERATOR_INT;
					break;
				case 'e':
-
					utstring_printf(sqlcond, "desc");
+
					fprintf(sqlcond->fp, "desc");
					state = OPERATOR_STRING;
					break;
				case 'V':
					if (for_remote)
						goto bad_option;
-
					utstring_printf(sqlcond, "vital");
+
					fprintf(sqlcond->fp, "vital");
					state = OPERATOR_INT;
					break;
				case '#': /* FALLTHROUGH */
@@ -539,48 +538,48 @@ format_sql_condition(const char *str, UT_string *sqlcond, bool for_remote)
					str++;
					switch (str[0]) {
						case 'd':
-
							utstring_printf(sqlcond, "(SELECT %s FROM deps AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM deps AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'r':
-
							utstring_printf(sqlcond, "(SELECT %s FROM deps AS d WHERE d.origin=p.origin)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM deps AS d WHERE d.origin=p.origin)", sqlop);
							break;
						case 'C':
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_categories AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_categories AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'F':
							if (for_remote)
								goto bad_option;
-
							utstring_printf(sqlcond, "(SELECT %s FROM files AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM files AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'O':
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_option AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_option AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'D':
							if (for_remote)
								goto bad_option;
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_directories AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_directories AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'L':
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_licenses AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_licenses AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'U':
							if (for_remote)
								goto bad_option;
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_users AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_users AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'G':
							if (for_remote)
								goto bad_option;
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_groups AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_groups AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'B':
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_shlibs_required AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_shlibs_required AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'b':
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_shlibs_provided AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_shlibs_provided AS d WHERE d.package_id=p.id)", sqlop);
							break;
						case 'A':
-
							utstring_printf(sqlcond, "(SELECT %s FROM pkg_annotation AS d WHERE d.package_id=p.id)", sqlop);
+
							fprintf(sqlcond->fp, "(SELECT %s FROM pkg_annotation AS d WHERE d.package_id=p.id)", sqlop);
							break;
						default:
							goto bad_option;
@@ -596,7 +595,7 @@ bad_option:
				switch (str[0]) {
				case '(':
					bracket_level++;
-
					utstring_printf(sqlcond, "%c", str[0]);
+
					fprintf(sqlcond->fp, "%c", str[0]);
					break;
				case ' ':
				case '\t':
@@ -614,7 +613,7 @@ bad_option:
					return (EPKG_FATAL);
				}
				bracket_level--;
-
				utstring_printf(sqlcond, "%c", str[0]);
+
				fprintf(sqlcond->fp, "%c", str[0]);
				break;
			case ' ':
			case '\t':
@@ -623,7 +622,7 @@ bad_option:
				if (str[1] == '|') {
					str++;
					state = NONE;
-
					utstring_printf(sqlcond, " OR ");
+
					fprintf(sqlcond->fp, " OR ");
					break;
				} else {
					fprintf(stderr, "unexpected character %c\n", str[1]);
@@ -633,7 +632,7 @@ bad_option:
				if (str[1] == '&') {
					str++;
					state = NONE;
-
					utstring_printf(sqlcond, " AND ");
+
					fprintf(sqlcond->fp, " AND ");
					break;
				} else {
					fprintf(stderr, "unexpected character %c\n", str[1]);
@@ -653,17 +652,17 @@ bad_option:
					return (EPKG_FATAL);
				}
				state = NEXT_IS_STRING;
-
				utstring_printf(sqlcond, " GLOB ");
+
				fprintf(sqlcond->fp, " GLOB ");
			} else if (str[0] == '>' || str[0] == '<') {
				if (state != OPERATOR_INT) {
					fprintf(stderr, "> expected only for integers\n");
					return (EPKG_FATAL);
				}
				state = NEXT_IS_INT;
-
				utstring_printf(sqlcond, "%c", str[0]);
+
				fprintf(sqlcond->fp, "%c", str[0]);
				if (str[1] == '=') {
					str++;
-
					utstring_printf(sqlcond, "%c", str[0]);
+
					fprintf(sqlcond->fp, "%c", str[0]);
				}
			} else if (str[0] == '=') {
				if (state == OPERATOR_STRING) {
@@ -671,17 +670,17 @@ bad_option:
				} else {
					state = NEXT_IS_INT;
				}
-
				utstring_printf(sqlcond, "%c", str[0]);
+
				fprintf(sqlcond->fp, "%c", str[0]);
				if (str[1] == '=') {
					str++;
-
					utstring_printf(sqlcond, "%c", str[0]);
+
					fprintf(sqlcond->fp, "%c", str[0]);
				}
			} else if (str[0] == '!') {
				if (str[1] == '=') {
-
					utstring_printf(sqlcond, "%c", str[0]);
-
					utstring_printf(sqlcond, "%c", str[1]);
+
					fprintf(sqlcond->fp, "%c", str[0]);
+
					fprintf(sqlcond->fp, "%c", str[1]);
				} else if (str[1] == '~') {
-
					utstring_printf(sqlcond, " NOT GLOB ");
+
					fprintf(sqlcond->fp, " NOT GLOB ");
				} else {
					fprintf(stderr, "expecting = or ~ after !\n");
					return (EPKG_FATAL);
@@ -709,14 +708,14 @@ bad_option:
						state = STRING;
						str--;
					}
-
					utstring_printf(sqlcond, "%c", '\'');
+
					fprintf(sqlcond->fp, "%c", '\'');
				} else {
					if (!isdigit(str[0])) {
						fprintf(stderr, "a number is expected, got: %c\n", str[0]);
						return (EPKG_FATAL);
					}
					state = INT;
-
					utstring_printf(sqlcond, "%c", str[0]);
+
					fprintf(sqlcond->fp, "%c", str[0]);
				}
			}
		} else if (state == INT) {
@@ -724,26 +723,26 @@ bad_option:
				state = POST_EXPR;
				str--;
			} else {
-
				utstring_printf(sqlcond, "%c", str[0]);
+
				fprintf(sqlcond->fp, "%c", str[0]);
			}
		} else if (state == STRING || state == QUOTEDSTRING || state == SQUOTEDSTRING) {
			if ((state == STRING && isspace(str[0])) ||
			    (state == QUOTEDSTRING && str[0] == '"') ||
			    (state == SQUOTEDSTRING && str[0] == '\'')) {
-
				utstring_printf(sqlcond, "%c", '\'');
+
				fprintf(sqlcond->fp, "%c", '\'');
				state = POST_EXPR;
			} else {
-
				utstring_printf(sqlcond, "%c", str[0]);
+
				fprintf(sqlcond->fp, "%c", str[0]);
				if (str[0] == '\'')
-
					utstring_printf(sqlcond, "%c", str[0]);
+
					fprintf(sqlcond->fp, "%c", str[0]);
				else if (str[0] == '%' && for_remote)
-
					utstring_printf(sqlcond, "%c", str[0]);
+
					fprintf(sqlcond->fp, "%c", str[0]);
			}
		}
		str++;
	}
	if (state == STRING) {
-
		utstring_printf(sqlcond, "%c", '\'');
+
		fprintf(sqlcond->fp, "%c", '\'');
		state = POST_EXPR;
	}

@@ -867,11 +866,11 @@ exec_query(int argc, char **argv)
	match_t			 match = MATCH_EXACT;
	int			 ch;
	int			 ret;
-
	int			 retcode = EX_OK;
+
	int			 retcode = EXIT_SUCCESS;
	int			 i;
	char			 multiline = 0;
	char			*condition = NULL;
-
	UT_string		*sqlcond = NULL;
+
	xstring			*sqlcond = NULL;
	const unsigned int	 q_flags_len = NELEM(accepted_query_flags);

	struct option longopts[] = {
@@ -911,7 +910,7 @@ exec_query(int argc, char **argv)
			break;
		default:
			usage_query();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -920,7 +919,7 @@ exec_query(int argc, char **argv)

	if (argc == 0) {
		usage_query();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	/* Default to all packages if no pkg provided */
@@ -929,67 +928,87 @@ exec_query(int argc, char **argv)
	} else if (((argc == 1) ^ (match == MATCH_ALL)) && pkgname == NULL
			&& condition == NULL) {
		usage_query();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (analyse_query_string(argv[0], accepted_query_flags, q_flags_len,
			&query_flags, &multiline) != EPKG_OK)
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);

	if (pkgname != NULL) {
+
		/* Use a manifest or compact manifest if possible. */
+
		int open_flags = 0;
+
		if ((query_flags & ~(PKG_LOAD_DEPS|
+
				     PKG_LOAD_OPTIONS|
+
				     PKG_LOAD_CATEGORIES|
+
				     PKG_LOAD_LICENSES|
+
				     PKG_LOAD_USERS|
+
				     PKG_LOAD_GROUPS|
+
				     PKG_LOAD_SHLIBS_REQUIRED|
+
				     PKG_LOAD_SHLIBS_PROVIDED|
+
				     PKG_LOAD_ANNOTATIONS|
+
				     PKG_LOAD_CONFLICTS|
+
				     PKG_LOAD_PROVIDES|
+
				     PKG_LOAD_REQUIRES)) == 0) {
+
			open_flags = PKG_OPEN_MANIFEST_COMPACT;
+
		} else if ((query_flags & PKG_LOAD_FILES) == 0) {
+
			open_flags = PKG_OPEN_MANIFEST_ONLY;
+
		}
		pkg_manifest_keys_new(&keys);
-
		if (pkg_open(&pkg, pkgname, keys, 0) != EPKG_OK) {
-
			return (EX_IOERR);
+
		if (pkg_open(&pkg, pkgname, keys, open_flags) != EPKG_OK) {
+
			return (EXIT_FAILURE);
		}

		pkg_manifest_keys_free(keys);
		print_query(pkg, argv[0], multiline);
		pkg_free(pkg);
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	}

	if (condition != NULL) {
-
		utstring_new(sqlcond);
+
		sqlcond = xstring_new();
		if (format_sql_condition(condition, sqlcond, false) != EPKG_OK) {
-
			utstring_free(sqlcond);
-
			return (EX_USAGE);
+
			xstring_free(sqlcond);
+
			return (EXIT_FAILURE);
		}
	}

	ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_LOCAL);
	if (ret == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to query the package database");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (ret == EPKG_ENODB) {
		if (!quiet)
			warnx("No packages installed");
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	} else if (ret != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	ret = pkgdb_open(&db, PKGDB_DEFAULT);
	if (ret != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	drop_privileges();
	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (match == MATCH_ALL || match == MATCH_CONDITION) {
		const char *condition_sql = NULL;
-
		if (match == MATCH_CONDITION && sqlcond)
-
			condition_sql = utstring_body(sqlcond);
+
		if (match == MATCH_CONDITION && sqlcond) {
+
			fflush(sqlcond->fp);
+
			condition_sql = sqlcond->buf;
+
		}
		if ((it = pkgdb_query(db, condition_sql, match)) == NULL)
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);

		while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK)
			print_query(pkg, argv[0],  multiline);

		if (ret != EPKG_END)
-
			retcode = EX_SOFTWARE;
+
			retcode = EXIT_FAILURE;

		pkgdb_it_free(it);
	} else {
@@ -998,7 +1017,7 @@ exec_query(int argc, char **argv)
			pkgname = argv[i];

			if ((it = pkgdb_query(db, pkgname, match)) == NULL) {
-
				retcode = EX_IOERR;
+
				retcode = EXIT_FAILURE;
				goto cleanup;
			}

@@ -1008,16 +1027,16 @@ exec_query(int argc, char **argv)
			}

			if (ret != EPKG_END) {
-
				retcode = EX_SOFTWARE;
+
				retcode = EXIT_FAILURE;
				break;
			}

			pkgdb_it_free(it);
		}
-
		if (nprinted == 0 && retcode == EX_OK) {
+
		if (nprinted == 0 && retcode == EXIT_SUCCESS) {
			/* ensure to return a non-zero status when no package
			 were found. */
-
			retcode = EX_UNAVAILABLE;
+
			retcode = EXIT_FAILURE;
		}
	}

modified src/register.c
@@ -32,7 +32,6 @@
#include <err.h>
#include <stdio.h>
#include <pkg.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdbool.h>
@@ -67,7 +66,7 @@ exec_register(int argc, char **argv)

	int		 ch;
	int		 ret     = EPKG_OK;
-
	int		 retcode = EX_OK;
+
	int		 retcode = EXIT_SUCCESS;

	/* options descriptor */
	struct option longopts[] = {
@@ -84,7 +83,7 @@ exec_register(int argc, char **argv)
	};

	if (pkg_new(&pkg, PKG_INSTALLED) != EPKG_OK)
-
		err(EX_OSERR, "malloc");
+
		err(EXIT_FAILURE, "malloc");

	while ((ch = getopt_long(argc, argv, "+Adf:i:lM:m:t", longopts, NULL)) != -1) {
		switch (ch) {
@@ -117,7 +116,7 @@ exec_register(int argc, char **argv)
			warnx("Unrecognised option -%c\n", ch);
			usage_register();
			pkg_free(pkg);
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -128,10 +127,10 @@ exec_register(int argc, char **argv)
	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to register packages");
		pkg_free(pkg);
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK) {
		pkg_free(pkg);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	/*
@@ -155,7 +154,7 @@ exec_register(int argc, char **argv)
		warnx("Cannot use both -m and -M together");
		usage_register();
		pkg_free(pkg);
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}


@@ -163,43 +162,44 @@ exec_register(int argc, char **argv)
		warnx("One of either -m or -M flags is required");
		usage_register();
		pkg_free(pkg);
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (testing_mode && input_path != NULL) {
		warnx("-i incompatible with -t option");
		usage_register();
		pkg_free(pkg);
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	ret = pkg_load_metadata(pkg, mfile, mdir, plist, input_path, testing_mode);
	if (ret != EPKG_OK) {
		pkg_free(pkg);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}


	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
		pkg_free(pkg);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		pkg_free(pkg);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	retcode = pkg_add_port(db, pkg, input_path, location, testing_mode);

	if (!legacy && retcode == EPKG_OK && messages != NULL) {
-
		printf("%s\n", utstring_body(messages));
+
		fflush(messages->fp);
+
		printf("%s\n", messages->buf);
	}

	pkg_free(pkg);
	pkgdb_release_lock(db, PKGDB_LOCK_EXCLUSIVE);

-
	return (retcode != EPKG_OK ? EX_SOFTWARE : EX_OK);
+
	return (retcode != EPKG_OK ? EXIT_FAILURE : EXIT_SUCCESS);
}
modified src/repo.c
@@ -31,7 +31,6 @@
#include <bsd_compat.h>
#include <getopt.h>
#include <signal.h>
-
#include <sysexits.h>
#include <stdio.h>
#include <string.h>

@@ -99,8 +98,8 @@ exec_repo(int argc, char **argv)
	bool	 hash = false;
	bool	 hash_symlink = false;

-
	hash = pkg_object_bool(pkg_config_get("PKG_REPO_HASH"));
-
	hash_symlink = pkg_object_bool(pkg_config_get("PKG_REPO_SYMLINK"));
+
	hash = (getenv("PKG_REPO_HASH") != NULL);
+
	hash_symlink = (getenv("PKG_REPO_SYMLINK") != NULL);

	struct option longopts[] = {
		{ "hash",	no_argument,		NULL,	'h' },
@@ -134,7 +133,7 @@ exec_repo(int argc, char **argv)
			break;
		default:
			usage_repo();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -142,12 +141,12 @@ exec_repo(int argc, char **argv)

	if (argc < 1) {
		usage_repo();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (argc > 2 && strcmp(argv[1], "signing_command:") != 0) {
		usage_repo();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (output_dir == NULL)
@@ -158,12 +157,12 @@ exec_repo(int argc, char **argv)

	if (ret != EPKG_OK) {
		printf("Cannot create repository catalogue\n");
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (pkg_finish_repo(output_dir, password_cb, argv + 1, argc - 1,
	    filelist) != EPKG_OK)
-
		return (EX_DATAERR);
+
		return (EXIT_FAILURE);

-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}
modified src/rquery.c
@@ -33,12 +33,10 @@
#include <err.h>
#include <getopt.h>
#include <inttypes.h>
-
#include <utstring.h>
#include <pkg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

#include "pkgcli.h"
@@ -112,12 +110,12 @@ exec_rquery(int argc, char **argv)
	match_t			 match = MATCH_EXACT;
	int			 ch;
	int			 ret = EPKG_OK;
-
	int			 retcode = EX_OK;
+
	int			 retcode = EXIT_SUCCESS;
	int			 i;
	char			 multiline = 0;
	char			*condition = NULL;
	const char		*portsdir;
-
	UT_string		*sqlcond = NULL;
+
	xstring			*sqlcond = NULL;
	const unsigned int	 q_flags_len = NELEM(accepted_rquery_flags);
	const char		*reponame = NULL;
	bool			 onematched = false;
@@ -171,7 +169,7 @@ exec_rquery(int argc, char **argv)
			break;
		default:
			usage_rquery();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -180,7 +178,7 @@ exec_rquery(int argc, char **argv)

	if (argc == 0 && !index_output) {
		usage_rquery();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	/* Default to all packages if no pkg provided */
@@ -189,7 +187,7 @@ exec_rquery(int argc, char **argv)
			match = MATCH_ALL;
		} else if (((argc == 1) ^ (match == MATCH_ALL )) && condition == NULL) {
			usage_rquery();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	} else {
		if (argc == 0)
@@ -197,43 +195,39 @@ exec_rquery(int argc, char **argv)
	}

	if (!index_output && analyse_query_string(argv[0], accepted_rquery_flags, q_flags_len, &query_flags, &multiline) != EPKG_OK)
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);

	if (condition != NULL) {
-
		utstring_new(sqlcond);
+
		sqlcond = xstring_new();
		if (format_sql_condition(condition, sqlcond, true) != EPKG_OK) {
-
			utstring_free(sqlcond);
-
			return (EX_USAGE);
+
			xstring_free(sqlcond);
+
			return (EXIT_FAILURE);
		}
	}

	ret = pkgdb_access(PKGDB_MODE_READ, PKGDB_DB_REPO);
	if (ret == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to query the package database");
-
		if (sqlcond != NULL)
-
			utstring_free(sqlcond);
-
		return (EX_NOPERM);
+
		xstring_free(sqlcond);
+
		return (EXIT_FAILURE);
	} else if (ret != EPKG_OK) {
-
		if (sqlcond != NULL)
-
			utstring_free(sqlcond);
-
		return (EX_IOERR);
+
		xstring_free(sqlcond);
+
		return (EXIT_FAILURE);
	}

	/* first update the remote repositories if needed */
	old_quiet = quiet;
	quiet = true;
	if (auto_update && (ret = pkgcli_update(false, false, reponame)) != EPKG_OK) {
-
		if (sqlcond != NULL)
-
			utstring_free(sqlcond);
+
		xstring_free(sqlcond);
		return (ret);
	}
	quiet = old_quiet;

	ret = pkgdb_open_all(&db, PKGDB_REMOTE, reponame);
	if (ret != EPKG_OK) {
-
		if (sqlcond != NULL)
-
			utstring_free(sqlcond);
-
		return (EX_IOERR);
+
		xstring_free(sqlcond);
+
		return (EXIT_FAILURE);
	}
	drop_privileges();

@@ -242,12 +236,13 @@ exec_rquery(int argc, char **argv)

	if (match == MATCH_ALL || match == MATCH_CONDITION) {
		const char *condition_sql = NULL;
-
		if (match == MATCH_CONDITION && sqlcond)
-
			condition_sql = utstring_body(sqlcond);
+
		if (match == MATCH_CONDITION && sqlcond) {
+
			fflush(sqlcond->fp);
+
			condition_sql = sqlcond->buf;
+
		}
		if ((it = pkgdb_repo_query(db, condition_sql, match, reponame)) == NULL) {
-
			if (sqlcond != NULL)
-
				utstring_free(sqlcond);
-
			return (EX_IOERR);
+
			xstring_free(sqlcond);
+
			return (EXIT_FAILURE);
		}

		while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
@@ -258,7 +253,7 @@ exec_rquery(int argc, char **argv)
		}

		if (ret != EPKG_END)
-
			retcode = EX_SOFTWARE;
+
			retcode = EXIT_FAILURE;

		pkgdb_it_free(it);
	} else {
@@ -266,9 +261,8 @@ exec_rquery(int argc, char **argv)
			pkgname = argv[i];

			if ((it = pkgdb_repo_query(db, pkgname, match, reponame)) == NULL) {
-
				if (sqlcond != NULL)
-
					utstring_free(sqlcond);
-
				return (EX_IOERR);
+
				xstring_free(sqlcond);
+
				return (EXIT_FAILURE);
			}

			while ((ret = pkgdb_it_next(it, &pkg, query_flags)) == EPKG_OK) {
@@ -280,18 +274,17 @@ exec_rquery(int argc, char **argv)
			}

			if (ret != EPKG_END) {
-
				retcode = EX_SOFTWARE;
+
				retcode = EXIT_FAILURE;
				break;
			}

			pkgdb_it_free(it);
		}
-
		if (!onematched && retcode == EX_OK)
-
			retcode = EX_UNAVAILABLE;
+
		if (!onematched && retcode == EXIT_SUCCESS)
+
			retcode = EXIT_FAILURE;
	}

-
	if (sqlcond != NULL)
-
		utstring_free(sqlcond);
+
	xstring_free(sqlcond);
	pkg_free(pkg);
	pkgdb_close(db);

modified src/search.c
@@ -34,8 +34,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-
#include <sysexits.h>
-


#include <pkg.h>

@@ -131,7 +129,7 @@ search_label_opt(const char *optionarg)
		break;
	default:
		usage_search();
-
		errx(EX_USAGE, "Unknown search/label option: %s", optionarg);
+
		errx(EXIT_FAILURE, "Unknown search/label option: %s", optionarg);
		/* NOTREACHED */
	}
	return field;
@@ -209,7 +207,7 @@ modifier_opt(const char *optionarg)
		break;
	default:
		usage_search();
-
		errx(EX_USAGE, "Unkown modifier option %s", optionarg);
+
		errx(EXIT_FAILURE, "Unkown modifier option %s", optionarg);
		/* NOTREACHED */
	}
	return opt;
@@ -351,13 +349,13 @@ exec_search(int argc, char **argv)
			else if (strcasecmp(optarg, "ucl") == 0)
				opt |= INFO_RAW_UCL;
			else
-
				errx(EX_USAGE, "Invalid format '%s' for the "
+
				errx(EXIT_FAILURE, "Invalid format '%s' for the "
				    "raw output, expecting json, json-compact "
				    "or yaml", optarg);
			break;
		default:
			usage_search();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -366,13 +364,13 @@ exec_search(int argc, char **argv)

	if (argc != 1) {
		usage_search();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	pattern = argv[0];
	if (pattern[0] == '\0') {
		fprintf(stderr, "Pattern must not be empty.\n");
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}
	if (search == FIELD_NONE) {
		if (strchr(pattern, '/') != NULL)
@@ -412,17 +410,17 @@ exec_search(int argc, char **argv)
	switch(ret) {
	case EPKG_ENOACCESS:
		warnx("Insufficient privileges to query the package database");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	case EPKG_ENODB:
		if (!auto_update) {
			warnx("Unable to open remote repository catalogues. Try running '%s update' first.", getprogname());
-
			return (EX_IOERR);
+
			return (EXIT_FAILURE);
		}
		break;
	case EPKG_OK:
		break;
	default:
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	/* first update the remote repositories if needed */
@@ -433,12 +431,12 @@ exec_search(int argc, char **argv)
	quiet = old_quiet;

	if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if ((it = pkgdb_repo_search(db, pattern, match, search, search,
	    reponame)) == NULL) {
		pkgdb_close(db);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (opt & INFO_RAW) {
@@ -459,5 +457,5 @@ exec_search(int argc, char **argv)
	if (!atleastone)
		ret = EPKG_FATAL;

-
	return ((ret == EPKG_OK || ret == EPKG_END) ? EX_OK : EX_SOFTWARE);
+
	return ((ret == EPKG_OK || ret == EPKG_END) ? EXIT_SUCCESS : EXIT_FAILURE);
}
modified src/set.c
@@ -32,7 +32,6 @@
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -130,7 +129,7 @@ exec_set(int argc, char **argv)
			sets |= AUTOMATIC;
			newautomatic = optarg[0] - '0';
			if (newautomatic != 0 && newautomatic != 1)
-
				errx(EX_USAGE, "Wrong value for -A. "
+
				errx(EXIT_FAILURE, "Wrong value for -A. "
				    "Expecting 0 or 1, got: %s",
				    optarg);
			break;
@@ -152,7 +151,7 @@ exec_set(int argc, char **argv)
			match = MATCH_ALL;
			changed = "origin";
			if (!check_change_values(optarg, &oldvalue, &newvalue, '/')) {
-
				 errx(EX_USAGE, "Wrong format for -o. "
+
				 errx(EXIT_FAILURE, "Wrong format for -o. "
					 "Expecting oldorigin:neworigin, got: %s",
					 optarg);
			}
@@ -163,7 +162,7 @@ exec_set(int argc, char **argv)
			match = MATCH_ALL;
			changed = "name";
			if (!check_change_values(optarg, &oldvalue, &newvalue, '\0')) {
-
				 errx(EX_USAGE, "Wrong format for -n. "
+
				 errx(EXIT_FAILURE, "Wrong format for -n. "
					 "Expecting oldname:newname, got: %s",
					 optarg);
			}
@@ -175,7 +174,7 @@ exec_set(int argc, char **argv)
			sets |= VITAL;
			newvital = optarg[0] - '0';
			if (newvital != 0 && newvital != 1)
-
				errx(EX_USAGE, "Wrong value for -v. "
+
				errx(EXIT_FAILURE, "Wrong value for -v. "
				    "Expecting 0 or 1, got: %s",
				    optarg);
			break;
@@ -186,7 +185,7 @@ exec_set(int argc, char **argv)
			free(oldvalue);
			free(newvalue);
			usage_set();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -198,7 +197,7 @@ exec_set(int argc, char **argv)
		(sets & (NAME|ORIGIN)) == (NAME|ORIGIN)) {
		free(newvalue);
		usage_set();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (sets & NAME) {
@@ -215,44 +214,44 @@ exec_set(int argc, char **argv)
	if (retcode == EPKG_ENODB) {
		free(newvalue);
		if (match == MATCH_ALL)
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		if (!quiet)
			warnx("No packages installed.  Nothing to do!");
-
		return (EX_OK);
+
		return (EXIT_SUCCESS);
	} else if (retcode == EPKG_ENOACCESS) {
		free(newvalue);
		warnx("Insufficient privileges to modify the package database");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK) {
		warnx("Error accessing package database");
		free(newvalue);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
		free(newvalue);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_EXCLUSIVE) != EPKG_OK) {
		pkgdb_close(db);
		free(newvalue);
		warnx("Cannot get an exclusive lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_transaction_begin(db, NULL) != EPKG_OK) {
		pkgdb_close(db);
		free(newvalue);
		warnx("Cannot start transaction for update");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}


	if (oldvalue != NULL) {
		match = MATCH_ALL;
		if ((it = pkgdb_query(db, oldvalue, MATCH_EXACT)) == NULL) {
-
			retcode = EX_IOERR;
+
			retcode = EXIT_FAILURE;
			goto cleanup;
		}

@@ -262,7 +261,7 @@ exec_set(int argc, char **argv)
			free(oldorigin);
			pkgdb_it_free(it);
			pkgdb_close(db);
-
			return (EX_SOFTWARE);*/
+
			return (EXIT_FAILURE);*/
		}

		rc = yes;
@@ -276,7 +275,7 @@ exec_set(int argc, char **argv)
		}
		if (pkg != NULL && rc) {
			if (pkgdb_set(db, pkg, field, newvalue) != EPKG_OK) {
-
				retcode = EX_IOERR;
+
				retcode = EXIT_FAILURE;
				goto cleanup;
			}
		}
@@ -285,13 +284,15 @@ exec_set(int argc, char **argv)
	i = 0;
	do {
		bool saved_rc = rc;
+
		bool gotone = false;

		if ((it = pkgdb_query(db, argv[i], match)) == NULL) {
-
			retcode = EX_IOERR;
+
			retcode = EXIT_FAILURE;
			goto cleanup;
		}

		while (pkgdb_it_next(it, &pkg, loads) == EPKG_OK) {
+
			gotone = true;
			if ((sets & AUTOMATIC) == AUTOMATIC) {
				pkg_get(pkg, PKG_AUTOMATIC, &automatic);
				if (automatic == newautomatic)
@@ -336,12 +337,16 @@ exec_set(int argc, char **argv)
					 * been queried.
					 */
					if (pkgdb_set(db, pkg, depfield, oldvalue, newvalue) != EPKG_OK) {
-
						retcode = EX_IOERR;
+
						retcode = EXIT_FAILURE;
						goto cleanup;
					}
				}
			}
		}
+
		if (!gotone) {
+
			warnx("No package(s) matching %s", argv[i]);
+
			retcode = EXIT_FAILURE;
+
		}
		pkgdb_it_free(it);
		i++;
	} while (i < argc);
modified src/shell.c
@@ -25,7 +25,6 @@
 */

#include <stdio.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -44,5 +43,5 @@ exec_shell(int argc, char **argv)
{
	/* XXX: need exclusive lock here */
	pkgdb_cmd(argc, argv);
-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}
modified src/shlib.c
@@ -34,7 +34,6 @@
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-
#include <sysexits.h>
#include <ctype.h>

#include "pkgcli.h"
@@ -164,7 +163,7 @@ exec_shlib(int argc, char **argv)
			break;
		default:
			usage_shlib();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -172,27 +171,27 @@ exec_shlib(int argc, char **argv)

	if (argc < 1 || (provides_only && requires_only)) {
		usage_shlib();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (argc >= 2) {
		warnx("multiple libraries per run not allowed");
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (sanitize(libname, argv[0], sizeof(libname)) == NULL) {
		usage_shlib();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	retcode = pkgdb_open(&db, PKGDB_DEFAULT);
	if (retcode != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (retcode == EPKG_OK && !requires_only)
@@ -202,7 +201,7 @@ exec_shlib(int argc, char **argv)
		retcode = pkgs_requiring_lib(db, libname);

	if (retcode != EPKG_OK)
-
		retcode = (EX_IOERR);
+
		retcode = (EXIT_FAILURE);
		
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);
modified src/ssh.c
@@ -32,7 +32,6 @@
#include <sys/capsicum.h>
#endif

-
#include <sysexits.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
@@ -62,7 +61,7 @@ exec_ssh(int argc, char **argv __unused)

	if (argc > 1) {
		usage_ssh();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	restricted = pkg_object_string(pkg_config_get("SSH_RESTRICT_DIR"));
@@ -71,7 +70,7 @@ exec_ssh(int argc, char **argv __unused)

	if ((fd = open(restricted, O_DIRECTORY|O_RDONLY|O_CLOEXEC)) < 0) {
		warn("Impossible to open the restricted directory");
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

#ifdef HAVE_CAPSICUM
@@ -79,21 +78,21 @@ exec_ssh(int argc, char **argv __unused)
	if (cap_rights_limit(fd, &rights) < 0 && errno != ENOSYS ) {
		warn("cap_rights_limit() failed");
		close(fd);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	if (cap_enter() < 0 && errno != ENOSYS) {
		warn("cap_enter() failed");
		close(fd);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

#endif
	if (pkg_sshserve(fd) != EPKG_OK) {
		close(fd);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	close(fd);
-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}
modified src/stats.c
@@ -36,7 +36,6 @@
#include <libutil.h>
#endif
#include <stdio.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -86,7 +85,7 @@ exec_stats(int argc, char **argv)
			break;
		default:
			usage_stats();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argv += optind;
@@ -96,13 +95,13 @@ exec_stats(int argc, char **argv)
		opt |= (STATS_LOCAL | STATS_REMOTE);

	if (pkgdb_open(&db, PKGDB_REMOTE) != EPKG_OK) {
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (opt & STATS_LOCAL) {
@@ -140,5 +139,5 @@ exec_stats(int argc, char **argv)
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
	pkgdb_close(db);

-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}
modified src/update.c
@@ -35,7 +35,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>

#include <pkg.h>
@@ -156,14 +155,14 @@ exec_update(int argc, char **argv)
			break;
		default:
			usage_update();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;

	if (argc != 0) {
		usage_update();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	ret = pkgdb_access(PKGDB_MODE_WRITE|PKGDB_MODE_CREATE,
@@ -171,12 +170,12 @@ exec_update(int argc, char **argv)
	if (ret == EPKG_ENOACCESS) {
		warnx("Insufficient privileges to update the repository "
		      "catalogue.");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (ret != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	/* For pkg-update update op is strict */
	ret = pkgcli_update(force, true, reponame);

-
	return ((ret == EPKG_OK) ? EX_OK : EX_SOFTWARE);
+
	return ((ret == EPKG_OK) ? EXIT_SUCCESS : EXIT_FAILURE);
}
modified src/updating.c
@@ -42,7 +42,6 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <ctype.h>
#include <regex.h>
@@ -275,7 +274,7 @@ exec_updating(int argc, char **argv)
			break;
		default:
			usage_updating();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -284,21 +283,21 @@ exec_updating(int argc, char **argv)
	/* checking date format */
	if (date != NULL)
		if (strlen(date) != 8 || strspn(date, "0123456789") != 8)
-
			err(EX_DATAERR, "Invalid date format");
+
			err(EXIT_FAILURE, "Invalid date format");

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (updatingfile == NULL) {
		const char *portsdir = pkg_object_string(pkg_config_get("PORTSDIR"));
		if (portsdir == NULL) {
-
			retcode = EX_CONFIG;
+
			retcode = EXIT_FAILURE;
			goto cleanup;
		}
		asprintf(&updatingfile, "%s/UPDATING", portsdir);
@@ -315,20 +314,20 @@ exec_updating(int argc, char **argv)
	if (cap_rights_limit(fileno(fd), &rights) < 0 && errno != ENOSYS ) {
		warn("cap_rights_limit() failed");
		fclose(fd);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}

	if (cap_enter() < 0 && errno != ENOSYS) {
		warn("cap_enter() failed");
		fclose(fd);
-
		return (EX_SOFTWARE);
+
		return (EXIT_FAILURE);
	}
#endif

	SLIST_INIT(&origins);
	if (argc == 0) {
		if ((it = pkgdb_query(db, NULL, MATCH_ALL)) == NULL) {
-
			retcode = EX_UNAVAILABLE;
+
			retcode = EXIT_FAILURE;
			fclose(fd);
			goto cleanup;
		}
modified src/upgrade.c
@@ -35,12 +35,11 @@
#include <err.h>
#include <getopt.h>
#include <stdio.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <khash.h>
-
#include <utstring.h>
+
#include <xstring.h>
#include <pkg.h>

#ifdef HAVE_SYS_CAPSICUM_H
@@ -51,6 +50,7 @@
#include <sys/capsicum.h>
#endif
#include "pkgcli.h"
+
#include <pkg/audit.h>

static const char vuln_end_lit[] = "**END**";

@@ -80,11 +80,11 @@ add_to_check(kh_pkgs_t *check, struct pkg *pkg)
static void
check_vulnerable(struct pkg_audit *audit, struct pkgdb *db, int sock)
{
+
	struct pkg_audit_issues	*issues;
	struct pkgdb_it	*it = NULL;
	struct pkg		*pkg = NULL;
	kh_pkgs_t		*check = NULL;
	const char		*uid;
-
	UT_string		*sb;
	int				ret;
	FILE			*out;

@@ -111,7 +111,7 @@ check_vulnerable(struct pkg_audit *audit, struct pkgdb *db, int sock)
			}
		}

-
		ret = EX_OK;
+
		ret = EXIT_SUCCESS;
	}

	if (db != NULL) {
@@ -119,7 +119,7 @@ check_vulnerable(struct pkg_audit *audit, struct pkgdb *db, int sock)
		pkgdb_close(db);
	}

-
	if (ret != EX_OK) {
+
	if (ret != EXIT_SUCCESS) {
		pkg_audit_free(audit);
		kh_destroy_pkgs(check);
		fclose(out);
@@ -149,12 +149,13 @@ check_vulnerable(struct pkg_audit *audit, struct pkgdb *db, int sock)

	if (pkg_audit_process(audit) == EPKG_OK) {
		kh_foreach_value(check, pkg, {
-
				if (pkg_audit_is_vulnerable(audit, pkg, true, &sb, NULL)) {
+
				issues = NULL;
+
				if (pkg_audit_is_vulnerable(audit, pkg, &issues, true)) {
					pkg_get(pkg, PKG_UNIQUEID, &uid);
					fprintf(out, "%s\n", uid);
					fflush(out);
-
					utstring_free(sb);
				}
+
				pkg_audit_issues_free(issues);
				pkg_free(pkg);
		});

@@ -188,7 +189,7 @@ add_vulnerable_upgrades(struct pkg_jobs *jobs, struct pkgdb *db)

	if (pkg_audit_fetch(NULL, NULL) != EPKG_OK) {
		pkg_audit_free(audit);
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	/* Create socketpair to execute audit check in a detached mode */
@@ -205,7 +206,7 @@ add_vulnerable_upgrades(struct pkg_jobs *jobs, struct pkgdb *db)
		close(sp[1]);
		check_vulnerable(audit, db, sp[0]);
		close(sp[0]);
-
		exit(EXIT_SUCCESS);
+
		_exit(EXIT_SUCCESS);
		break;
	case -1:
		warnx("Cannot fork");
@@ -282,7 +283,7 @@ exec_upgrade(int argc, char **argv)
		{ "fetch-only",		no_argument,		NULL,	'F' },
		{ "glob",		no_argument,		NULL,	'g' },
		{ "case-insensitive",	no_argument,		NULL,	'i' },
-
		{ "no-install-scripts",	no_argument,		NULL,	'I' },
+
		{ "no-scripts",		no_argument,		NULL,	'I' },
		{ "dry-run",		no_argument,		NULL,	'n' },
		{ "quiet",		no_argument,		NULL,	'q' },
		{ "repository",		required_argument,	NULL,	'r' },
@@ -341,7 +342,7 @@ exec_upgrade(int argc, char **argv)
			break;
		default:
			usage_upgrade();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
			/* NOTREACHED */
		}
	}
@@ -364,11 +365,11 @@ exec_upgrade(int argc, char **argv)

	if (retcode == EPKG_ENOACCESS) {
		warnx("Insufficient privilege to upgrade packages");
-
		return (EX_NOPERM);
+
		return (EXIT_FAILURE);
	} else if (retcode != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	else
-
		retcode = EX_SOFTWARE;
+
		retcode = EXIT_FAILURE;

	/* first update the remote repositories if needed */
	if (auto_update &&
@@ -376,12 +377,12 @@ exec_upgrade(int argc, char **argv)
		return (updcode);

	if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, lock_type) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get an advisory lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (pkg_jobs_new(&jobs, PKG_JOBS_UPGRADE, db) != EPKG_OK)
@@ -436,7 +437,8 @@ exec_upgrade(int argc, char **argv)
		}

		if (messages != NULL) {
-
			printf("%s", utstring_body(messages));
+
			fflush(messages->fp);
+
			printf("%s", messages->buf);
		}
		break;
	}
@@ -445,7 +447,7 @@ exec_upgrade(int argc, char **argv)
		printf("Your packages are up to date.\n");

	if (rc)
-
		retcode = EX_OK;
+
		retcode = EXIT_SUCCESS;
	else
		retcode = EXIT_FAILURE;

modified src/utils.c
@@ -334,7 +334,7 @@ print_info(struct pkg * const pkg, uint64_t options)
	int64_t flatsize, oldflatsize, pkgsize;
	int cout = 0;		/* Number of characters output */
	int info_num;		/* Number of different data items to print */
-
	int outflags = 0;
+
	int outflags = PKG_MANIFEST_EMIT_LOCAL_METADATA;

	pkg_get(pkg,
		PKG_REPOURL,       &repourl,
@@ -1023,13 +1023,6 @@ print_jobs_summary(struct pkg_jobs *jobs, const char *msg, ...)
}

void
-
utstring_flush(UT_string *buf)
-
{
-
	printf("%s", utstring_body(buf));
-
	utstring_clear(buf);
-
}
-

-
void
drop_privileges(void)
{
	struct passwd *nobody;
modified src/version.c
@@ -42,14 +42,12 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <fnmatch.h>
#include <spawn.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <khash.h>
-
#include <utstring.h>

#include "pkgcli.h"

@@ -157,7 +155,7 @@ do_testversion(unsigned int opt, int argc, char ** restrict argv)
	/* -t must be unique and takes two arguments */
	if ( opt != VERSION_TESTVERSION || argc < 2 ) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	switch (pkg_version_cmp(argv[0], argv[1])) {
@@ -172,7 +170,7 @@ do_testversion(unsigned int opt, int argc, char ** restrict argv)
		break;
	}

-
	return (EX_OK);
+
	return (EXIT_SUCCESS);
}

static int
@@ -188,7 +186,7 @@ do_testpattern(unsigned int opt, int argc, char ** restrict argv)
	/* -T must be unique and takes two arguments */
	if ( opt != VERSION_TESTPATTERN || argc < 2 ) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (strncmp(argv[0], "-", 1) == 0)
@@ -199,7 +197,7 @@ do_testpattern(unsigned int opt, int argc, char ** restrict argv)

	if (pattern_from_stdin && pkgname_from_stdin) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (!pattern_from_stdin && !pkgname_from_stdin)
@@ -262,13 +260,13 @@ indexfilename(char *filebuf, size_t filebuflen)
		indexdir = pkg_object_string(pkg_config_get("PORTSDIR"));

		if (indexdir == NULL)
-
			err(EX_SOFTWARE, "Cannot get either INDEXDIR or "
+
			err(EXIT_FAILURE, "Cannot get either INDEXDIR or "
			    "PORTSDIR config entry!");
	}

	indexfile = pkg_object_string(pkg_config_get("INDEXFILE"));
	if (indexfile == NULL)
-
		err(EX_SOFTWARE, "Cannot get INDEXFILE config entry!");
+
		err(EXIT_FAILURE, "Cannot get INDEXFILE config entry!");

	strlcpy(filebuf, indexdir, filebuflen);

@@ -296,9 +294,9 @@ hash_indexfile(const char *indexfilename)
	/* Create a hash table of all the package names and port
	 * directories from the index file. */

-
	indexfile = fopen(indexfilename, "r");
+
	indexfile = fopen(indexfilename, "re");
	if (!indexfile)
-
		err(EX_NOINPUT, "Unable to open %s", indexfilename);
+
		err(EXIT_FAILURE, "Unable to open %s", indexfilename);

	while (getline(&line, &linecap, indexfile) > 0) {
		/* line is pkgname|portdir|... */
@@ -309,7 +307,7 @@ hash_indexfile(const char *indexfilename)
		name = version;
		version = strrchr(version, '-');
		if (version == NULL)
-
			errx(EX_IOERR, "Invalid INDEX file format: %s",
+
			errx(EXIT_FAILURE, "Invalid INDEX file format: %s",
			    indexfilename);
		version[0] = '\0';
		version++;
@@ -322,7 +320,7 @@ hash_indexfile(const char *indexfilename)

		if (entry == NULL || entry->version == NULL ||
		    entry->name == NULL)
-
			err(EX_SOFTWARE, "Out of memory while reading %s",
+
			err(EXIT_FAILURE, "Out of memory while reading %s",
			    indexfilename);

		if (index == NULL)
@@ -341,7 +339,7 @@ hash_indexfile(const char *indexfilename)
	fclose(indexfile);

	if (index == NULL)
-
		errx(EX_DATAERR, "No valid entries found in '%s'",
+
		errx(EXIT_FAILURE, "No valid entries found in '%s'",
		    indexfilename);

	return (index);
@@ -420,11 +418,11 @@ do_source_index(unsigned int opt, char limchar, char *pattern, match_t match,

	if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_INDEX) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	index = hash_indexfile(indexfile);

@@ -433,7 +431,7 @@ do_source_index(unsigned int opt, char limchar, char *pattern, match_t match,
		free_index(index);
		warnx("Cannot get a read lock on the database. "
		      "It is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	it = pkgdb_query(db, pattern, match);
@@ -487,7 +485,7 @@ do_source_remote(unsigned int opt, char limchar, char *pattern, match_t match,

	if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_REMOTE ) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	/* Only force remote mode if looking up remote, otherwise
@@ -500,18 +498,18 @@ do_source_remote(unsigned int opt, char limchar, char *pattern, match_t match,
	}

	if (pkgdb_open_all(&db, PKGDB_REMOTE, reponame) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database. "
		      "It is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	it = pkgdb_query(db, pattern, match);
	if (it == NULL) {
-
		retcode = EX_IOERR;
+
		retcode = EXIT_FAILURE;
		goto cleanup;
	}

@@ -534,7 +532,7 @@ do_source_remote(unsigned int opt, char limchar, char *pattern, match_t match,

		it_remote = pkgdb_repo_query(db, is_origin ? origin : name, MATCH_EXACT, reponame);
		if (it_remote == NULL) {
-
			retcode = EX_IOERR;
+
			retcode = EXIT_FAILURE;
			goto cleanup;
		}

@@ -561,7 +559,7 @@ cleanup:
}

static int
-
exec_buf(UT_string *res, char **argv) {
+
exec_buf(xstring *res, char **argv) {
	char buf[BUFSIZ];
	int spawn_err;
	pid_t pid;
@@ -595,9 +593,9 @@ exec_buf(UT_string *res, char **argv) {

	close(pfd[1]);

-
	utstring_clear(res);
+
	xstring_reset(res);
	while ((r = read(pfd[0], buf, BUFSIZ)) > 0)
-
		utstring_bincpy(res, buf, r);
+
		fwrite(buf, sizeof(char), r, res->fp);

	close(pfd[0]);
	while (waitpid(pid, &pstat, 0) == -1) {
@@ -607,20 +605,21 @@ exec_buf(UT_string *res, char **argv) {
	if (WEXITSTATUS(pstat) != 0)
		return (-1);

-
	return (utstring_len(res));
+
	fflush(res->fp);
+
	return (strlen(res->buf));
}

static struct category *
category_new(char *categorypath, const char *category)
{
	struct category	*cat = NULL;
-
	UT_string	*makecmd;
+
	xstring		*makecmd;
	char		*results, *d, *key;
	char		*argv[5];
	int		 ret;
	khint_t		 k;

-
	utstring_new(makecmd);
+
	makecmd = xstring_new();

	argv[0] = "make";
	argv[1] = "-C";
@@ -631,7 +630,8 @@ category_new(char *categorypath, const char *category)
	if (exec_buf(makecmd, argv) <= 0)
		goto cleanup;

-
	results = utstring_body(makecmd);
+
	fflush(makecmd->fp);
+
	results = makecmd->buf;

	if (categories == NULL)
		categories = kh_init_categories();
@@ -655,7 +655,7 @@ category_new(char *categorypath, const char *category)
	}

cleanup:
-
	utstring_free(makecmd);
+
	xstring_free(makecmd);

	return (cat);
}
@@ -704,7 +704,7 @@ validate_origin(const char *portsdir, const char *origin)
}

static const char *
-
port_version(UT_string *cmd, const char *portsdir, const char *origin,
+
port_version(xstring *cmd, const char *portsdir, const char *origin,
    const char *pkgname)
{
	char	*output, *walk, *name;
@@ -716,16 +716,18 @@ port_version(UT_string *cmd, const char *portsdir, const char *origin,
	   version from the port itself. */

	if (validate_origin(portsdir, origin)) {
-
		utstring_printf(cmd, "%s/%s", portsdir, origin);
+
		fprintf(cmd->fp, "%s/%s", portsdir, origin);

+
		fflush(cmd->fp);
		argv[0] = "make";
		argv[1] = "-C";
-
		argv[2] = utstring_body(cmd);
+
		argv[2] = cmd->buf;
		argv[3] = "flavors-package-names";
		argv[4] = NULL;

		if (exec_buf(cmd, argv) > 0) {
-
			output = utstring_body(cmd);
+
			fflush(cmd->fp);
+
			output = cmd->buf;
			while ((walk = strsep(&output, "\n")) != NULL) {
				name = walk;
				walk = strrchr(walk, '-');
@@ -751,33 +753,33 @@ do_source_ports(unsigned int opt, char limchar, char *pattern, match_t match,
	struct pkgdb	*db = NULL;
	struct pkgdb_it	*it = NULL;
	struct pkg	*pkg = NULL;
-
	UT_string	*cmd;
+
	xstring		*cmd;
	const char	*name;
	const char	*origin;
	const char	*version;

	if ( (opt & VERSION_SOURCES) != VERSION_SOURCE_PORTS ) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (chdir(portsdir) != 0)
-
		err(EX_SOFTWARE, "Cannot chdir to %s\n", portsdir); 
+
		err(EXIT_FAILURE, "Cannot chdir to %s\n", portsdir); 

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK)
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database. "
		      "It is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if ((it = pkgdb_query(db, pattern, match)) == NULL)
			goto cleanup;

-
	utstring_new(cmd);
+
	cmd = xstring_new();

	while (pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC) == EPKG_OK) {
		pkg_get(pkg, PKG_NAME, &name, PKG_ORIGIN, &origin);
@@ -794,10 +796,10 @@ do_source_ports(unsigned int opt, char limchar, char *pattern, match_t match,

		version = port_version(cmd, portsdir, origin, name);
		print_version(pkg, "port", version, limchar, opt);
-
		utstring_clear(cmd);
+
		xstring_reset(cmd);
	}

-
	utstring_free(cmd);
+
	xstring_free(cmd);

cleanup:
	pkgdb_release_lock(db, PKGDB_LOCK_READONLY);
@@ -866,7 +868,7 @@ exec_version(int argc, char **argv)
			break;
		case 'h':
			usage_version();
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		case 'I':
			opt |= VERSION_SOURCE_INDEX;
			break;
@@ -923,7 +925,7 @@ exec_version(int argc, char **argv)
			break;
		default:
			usage_version();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}
	argc -= optind;
@@ -938,7 +940,7 @@ exec_version(int argc, char **argv)

	if (matchorigin != NULL && matchname != NULL) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if ( (opt & VERSION_TESTVERSION) == VERSION_TESTVERSION )
@@ -954,7 +956,7 @@ exec_version(int argc, char **argv)
		    limchar != '?' &&
		    limchar != '!') {
			usage_version();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -963,7 +965,7 @@ exec_version(int argc, char **argv)

	if (argc > 1) {
		usage_version();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if ( !(opt & VERSION_SOURCES ) ) {
@@ -990,7 +992,7 @@ exec_version(int argc, char **argv)
	if ( (opt & VERSION_SOURCE_INDEX) == VERSION_SOURCE_INDEX ) {
		if (!have_indexfile(&indexfile, filebuf, sizeof(filebuf),
		     argc, argv, true))
-
			return (EX_SOFTWARE);
+
			return (EXIT_FAILURE);
		else
			return (do_source_index(opt, limchar, pattern, match,
				    matchorigin, matchname, indexfile));
@@ -1002,7 +1004,7 @@ exec_version(int argc, char **argv)

	if ( (opt & VERSION_SOURCE_PORTS) == VERSION_SOURCE_PORTS ) {
		if (!have_ports(&portsdir, true))
-
			return (EX_SOFTWARE);
+
			return (EXIT_FAILURE);
		else
			return (do_source_ports(opt, limchar, pattern,
				    match, matchorigin, matchname, portsdir));
@@ -1028,7 +1030,7 @@ exec_version(int argc, char **argv)
	}

	/* NOTREACHED */
-
	return (EX_SOFTWARE);
+
	return (EXIT_FAILURE);
}
/*
 * That's All Folks!
modified src/which.c
@@ -34,7 +34,6 @@
#include <getopt.h>
#include <stdio.h>
#include <string.h>
-
#include <sysexits.h>
#include <unistd.h>
#include <kvec.h>
#include <fnmatch.h>
@@ -75,7 +74,7 @@ exec_which(int argc, char **argv)
	struct pkg_file *file = NULL;
	char		 pathabs[MAXPATHLEN];
	char		*p, *path, *match, *savedpath;
-
	int		 ret = EPKG_OK, retcode = EX_SOFTWARE;
+
	int		 ret = EPKG_OK, retcode = EXIT_FAILURE;
	int		 ch, res, pathlen = 0;
	size_t		 i;
	bool		 orig = false;
@@ -115,7 +114,7 @@ exec_which(int argc, char **argv)
			break;
		default:
			usage_which();
-
			return (EX_USAGE);
+
			return (EXIT_FAILURE);
		}
	}

@@ -124,17 +123,17 @@ exec_which(int argc, char **argv)

	if (argc < 1) {
		usage_which();
-
		return (EX_USAGE);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_open(&db, PKGDB_DEFAULT) != EPKG_OK) {
-
		return (EX_IOERR);
+
		return (EXIT_FAILURE);
	}

	if (pkgdb_obtain_lock(db, PKGDB_LOCK_READONLY) != EPKG_OK) {
		pkgdb_close(db);
		warnx("Cannot get a read lock on a database, it is locked by another process");
-
		return (EX_TEMPFAIL);
+
		return (EXIT_FAILURE);
	}

	if (search_s) {
@@ -148,7 +147,7 @@ exec_which(int argc, char **argv)

	while (argc >= 1) {
		kv_init(patterns);
-
		retcode = EX_SOFTWARE;
+
		retcode = EXIT_FAILURE;
		if (search_s) {
			if ((argv[0][0] == '.') || (argv[0][0] == '/')) {
				search = false;
@@ -156,13 +155,13 @@ exec_which(int argc, char **argv)
				search = true;

				if (strlen(argv[0]) >= FILENAME_MAX) {
-
					retcode = EX_USAGE;
+
					retcode = EXIT_FAILURE;
					goto cleanup;
				}

				p = malloc(pathlen);
				if (p == NULL) {
-
					retcode = EX_OSERR;
+
					retcode = EXIT_FAILURE;
					goto cleanup;
				}
				strlcpy(p, path, pathlen);
@@ -174,11 +173,11 @@ exec_which(int argc, char **argv)
					if (p == NULL)
						break;

-
					if (res == (EX_USAGE)) {
+
					if (res == (EXIT_FAILURE)) {
						printf("%s was not found in PATH, falling back to non-search behaviour\n", argv[0]);
						search = false;
-
					} else if (res == (EX_OSERR)) {
-
						retcode = EX_OSERR;
+
					} else if (res == (EXIT_FAILURE)) {
+
						retcode = EXIT_FAILURE;
						free(savedpath);
						goto cleanup;
					} else {
@@ -199,7 +198,7 @@ exec_which(int argc, char **argv)
			kv_push(char *, patterns, strdup(pathabs));
		} else if (!search) {
			if (strlcpy(pathabs, argv[0], sizeof(pathabs)) >= sizeof(pathabs)) {
-
				retcode = EX_USAGE;
+
				retcode = EXIT_FAILURE;
				goto cleanup;
                        }
			kv_push(char *, patterns, strdup(pathabs));
@@ -208,13 +207,13 @@ exec_which(int argc, char **argv)

		for (i = 0; i < kv_size(patterns); i++) {
			if ((it = pkgdb_query_which(db, kv_A(patterns, i), glob)) == NULL) {
-
				retcode = EX_IOERR;
+
				retcode = EXIT_FAILURE;
				goto cleanup;
			}

			pkg = NULL;
			while ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_FILES)) == EPKG_OK) {
-
				retcode = EX_OK;
+
				retcode = EXIT_SUCCESS;
				if (quiet && orig && !show_match)
					pkg_printf("%o\n", pkg);
				else if (quiet && !orig && !show_match)
@@ -229,14 +228,14 @@ exec_which(int argc, char **argv)
					while(pkg_files(pkg, &file) == EPKG_OK) {
						pkg_asprintf(&match, "%Fn", file);
						if (match == NULL)
-
							err(EX_DATAERR, "pkg_asprintf");
+
							err(EXIT_FAILURE, "pkg_asprintf");
						if(!fnmatch(kv_A(patterns, i), match, 0))
							printf("%s\n", match);
						free(match);
					}
				}
			}
-
			if (retcode != EX_OK && !quiet)
+
			if (retcode != EXIT_SUCCESS && !quiet)
				printf("%s was not found in the database\n", kv_A(patterns, i));

			pkg_free(pkg);
@@ -279,10 +278,10 @@ get_match(char **pathabs, char **path, char *filename)
			len = strlen(candidate) + 1;
			*pathabs = malloc(len);
			if (*pathabs == NULL)
-
				return (EX_OSERR);
+
				return (EXIT_FAILURE);
			strlcpy(*pathabs, candidate, len);
-
			return (EX_OK);
+
			return (EXIT_SUCCESS);
		}
	}
-
	return (EX_USAGE);
+
	return (EXIT_FAILURE);
}
modified tests/Makefile.autosetup
@@ -54,7 +54,8 @@ TESTS_SH= \
	frontend/clean.sh \
	frontend/backup_lib.sh \
	frontend/abi.sh \
-
	frontend/http.sh
+
	frontend/http.sh \
+
	frontend/triggers.sh

merge_OBJS=	lib/merge.o
plist_OBJS=	lib/plist.o
@@ -126,7 +127,7 @@ all: $(TESTS) Kyuafile ${TESTS_SHELL} frontend/Kyuafile
$(TESTS): $(OBJS)
	$(CC) -o $@ $($@_OBJS) $(LDFLAGS) $(LOCAL_LDFLAGS)

-
lib/pkg_printf.c:
+
lib/pkg_printf.c: $(top_srcdir)/libpkg/pkg_printf.c
	cp $(top_srcdir)/libpkg/pkg_printf.c $@

.sh:
deleted tests/frontend/Kyuafile.in
@@ -1,43 +0,0 @@
-
syntax(2)
-

-
test_suite('prop: test-suite = frontend')
-

-
atf_test_program{name='add'}
-
atf_test_program{name='alias'}
-
atf_test_program{name='annotate'}
-
atf_test_program{name='autoremove'}
-
atf_test_program{name='autoupgrade'}
-
atf_test_program{name='config'}
-
atf_test_program{name='configmerge'}
-
atf_test_program{name='conflicts'}
-
atf_test_program{name='conflicts-multirepo'}
-
atf_test_program{name='create'}
-
atf_test_program{name='delete'}
-
atf_test_program{name='extract'}
-
atf_test_program{name='install'}
-
atf_test_program{name='jpeg'}
-
atf_test_program{name='lock'}
-
atf_test_program{name='messages'}
-
atf_test_program{name='multipleprovider'}
-
atf_test_program{name='packagesplit'}
-
atf_test_program{name='packagemerge'}
-
atf_test_program{name='php-pr'}
-
atf_test_program{name='pkg'}
-
atf_test_program{name='pubkey'}
-
atf_test_program{name='query'}
-
atf_test_program{name='register'}
-
atf_test_program{name='repo'}
-
atf_test_program{name='requires'}
-
atf_test_program{name='rootdir'}
-
atf_test_program{name='rubypuppet'}
-
atf_test_program{name='search'}
-
atf_test_program{name='set'}
-
atf_test_program{name='update'}
-
atf_test_program{name='updating'}
-
atf_test_program{name='version'}
-
atf_test_program{name='vital'}
-
atf_test_program{name='issue1374'}
-
atf_test_program{name='issue1425'}
-
atf_test_program{name='issue1440'}
-
atf_test_program{name='issue1445'}
-
atf_test_program{name='fingerprint'}
modified tests/frontend/add.sh
@@ -126,7 +126,7 @@ EOF
	atf_check \
		-o inline:"${JAILED}Installing test-1...\n\nFailed to install the following 1 package(s): test-1.txz\n" \
		-e inline:"${PROGNAME}: Missing dependency 'b'\n" \
-
		-s exit:70 \
+
		-s exit:1 \
		pkg add test-1.txz

OUTPUT="${JAILED}Installing test-1...
@@ -196,7 +196,7 @@ EOF
	cat test-1.txz | atf_check \
		-o inline:"${JAILED}Installing test-1...\n\nFailed to install the following 1 package(s): -\n" \
		-e inline:"${PROGNAME}: Missing dependency 'b'\n" \
-
		-s exit:70 \
+
		-s exit:1 \
		pkg add -

OUTPUT="${JAILED}Installing test-1...
modified tests/frontend/alias.sh
@@ -25,7 +25,7 @@ alias_body() {
	atf_check \
		-o empty \
		-e inline:"${PROGNAME}: No such alias: 'nonexistent'\n" \
-
		-s exit:69 \
+
		-s exit:1 \
		pkg -C "" alias nonexistent
}

@@ -57,7 +57,7 @@ list 'info -q'
	atf_check \
		-o empty \
		-e inline:"${PROGNAME}: No such alias: 'nonexistent'\n" \
-
		-s exit:69 \
+
		-s exit:1 \
		pkg -C "" alias nonexistent
}

@@ -96,6 +96,6 @@ list 'info -q'
	atf_check \
		-o empty \
		-e inline:"${PROGNAME}: No such alias: 'nonexistent'\n" \
-
		-s exit:69 \
+
		-s exit:1 \
		pkg -C config alias nonexistent
}
modified tests/frontend/autoupgrade.sh
@@ -40,8 +40,9 @@ EOF

	atf_check \
		-o match:".*New version of pkg detected.*" \
-
		-s exit:1 \
-
		pkg -o REPOS_DIR="$TMPDIR/repoconf" -o PKG_CACHEDIR="$TMPDIR" upgrade -n
+
		-e ignore \
+
		-s exit:0 \
+
		pkg -o REPOS_DIR="$TMPDIR/repoconf" -o PKG_CACHEDIR="$TMPDIR" upgrade -y
}

autoupgrade_multirepo_head() {
modified tests/frontend/config.sh
@@ -46,7 +46,7 @@ duplicate_pkgs_notallowed_body() {

	atf_check \
		-e empty \
-
		-s exit:70 \
+
		-s exit:1 \
		pkg register -M test1.ucl

	atf_check \
modified tests/frontend/conflicts.sh
@@ -3,6 +3,7 @@
. $(atf_get_srcdir)/test_environment.sh

tests_init \
+
	more_complex_choice \
	complex_conflicts \
	fileexists_notinpkg \
	find_conflicts
@@ -171,6 +172,188 @@ Number of packages to be upgraded: 1
		pkg info
}

+
# install foo
+
# foo depends on bar-1.0
+
# foo is upgraded to new dep on bar1-1.0 & bar is updated to 2.0
+
# other still depends on bar1-1.0
+
# bar1 and bar conflict with each other
+
more_complex_choice_body()
+
{
+
	echo "bar-1.0" > file1
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg bar bar 1.0 "${TMPDIR}"
+
	cat << EOF >> bar.ucl
+
files: {
+
	${TMPDIR}/file1: "",
+
}
+
EOF
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M ./bar.ucl -o ./repo/
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg foo foo 1.0 "${TMPDIR}"
+
	cat << EOF >> foo.ucl
+
deps: {
+
	bar: {
+
		origin: "bar",
+
		version: "1.0"
+
	}
+
}
+
EOF
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M ./foo.ucl -o ./repo/
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg other other 1.0 "${TMPDIR}"
+
	cat << EOF >> other.ucl
+
deps: {
+
	bar: {
+
		origin: "bar",
+
		version: "1.0"
+
	}
+
}
+
EOF
+

+
	atf_check pkg create -M ./other.ucl -o ./repo/
+

+
	cat << EOF > pkg.conf
+
PKG_DBDIR=${TMPDIR}
+
REPOS_DIR=[]
+
repositories: {
+
	local: { url : file://${TMPDIR}/repo }
+
}
+
EOF
+

+
	atf_check \
+
		-o inline:"Creating repository in ./repo:  done\nPacking files for repository:  done\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg -C ./pkg.conf repo ./repo
+

+
	atf_check \
+
		-o ignore \
+
		-s exit:0 \
+
		pkg -C ./pkg.conf update -f
+

+
	atf_check \
+
		-o match:"Installing foo-1\.0" \
+
		-o match:"Installing other-1\.0" \
+
		-s exit:0 \
+
		pkg -C ./pkg.conf install -y foo other
+

+
	# Upgrade bar
+
	rm -fr repo
+
	echo "bar-2.0" > file1
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg bar bar 2.0 "${TMPDIR}"
+
	cat << EOF >> bar.ucl
+
files: {
+
	${TMPDIR}/file1: "",
+
}
+
EOF
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M ./bar.ucl -o ./repo/
+

+
	# Create bar1-1.1
+
	echo "bar-1.1" > file1
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg bar1 bar1 1.1 "${TMPDIR}"
+
	cat << EOF >> bar1.ucl
+
files: {
+
	${TMPDIR}/file1: "",
+
}
+
EOF
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M ./bar1.ucl -o ./repo/
+

+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg foo foo 1.0_1 "${TMPDIR}"
+
	cat << EOF >> foo.ucl
+
deps: {
+
	bar1: {
+
		origin: "bar1",
+
		version: "1.1"
+
	}
+
}
+
EOF
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M ./foo.ucl -o ./repo/
+

+
	atf_check \
+
		-o inline:"Creating repository in ./repo:  done\nPacking files for repository:  done\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg -C ./pkg.conf repo ./repo
+

+
	atf_check \
+
		-o ignore \
+
		-e empty \
+
		-s exit:0 \
+
		pkg -C ./pkg.conf update -f
+

+
OUTPUT="Updating local repository catalogue...
+
local repository is up to date.
+
All repositories are up to date.
+
Checking for upgrades (2 candidates):  done
+
Processing candidates (2 candidates):  done
+
Checking integrity... done (2 conflicting)
+
  - bar1-1.1 conflicts with bar-2.0 on ${TMPDIR}/file1
+
  - bar1-1.1 conflicts with bar-1.0 on ${TMPDIR}/file1
+
Cannot solve problem using SAT solver, trying another plan
+
Checking integrity... done (0 conflicting)
+
The following 4 package(s) will be affected (of 0 checked):
+

+
Installed packages to be REMOVED:
+
	bar: 1.0
+
	other: 1.0
+

+
New packages to be INSTALLED:
+
	bar1: 1.1
+

+
Installed packages to be UPGRADED:
+
	foo: 1.0 -> 1.0_1
+

+
Number of packages to be removed: 2
+
Number of packages to be installed: 1
+
Number of packages to be upgraded: 1
+
[1/4] Deinstalling other-1.0...
+
[2/4] Deinstalling bar-1.0...
+
[2/4] Deleting files for bar-1.0:  done
+
[3/4] Installing bar1-1.1...
+
[3/4] Extracting bar1-1.1:  done
+
[4/4] Upgrading foo from 1.0 to 1.0_1...
+
"
+

+
	atf_check \
+
		-o inline:"${OUTPUT}" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg -C ./pkg.conf upgrade -y
+

+
	atf_check \
+
		-o match:"foo-1.0_1" \
+
		-o match:"bar1-1.1" \
+
		-o not-match:"other-1.0" \
+
		-o not-match:"bar-2.0" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg info
+
}
+

fileexists_notinpkg_body()
{
	mkdir -p ${TMPDIR}/target/${TMPDIR}
modified tests/frontend/create.sh
@@ -5,7 +5,7 @@
tests_init \
	create_from_plist \
	create_from_plist_set_owner \
-
	create_from_plist_set_group \
+
	create_from_plist_set_group_space \
	create_from_plist_gather_mode \
	create_from_plist_set_mode \
	create_from_plist_mini \
@@ -14,12 +14,19 @@ tests_init \
	create_from_plist_fflags create_from_plist_bad_fflags \
	create_from_plist_with_keyword_arguments \
	create_from_manifest_and_plist \
+
	create_from_manifest \
+
	create_from_manifest_dir \
	create_from_plist_pkg_descr \
	create_from_plist_hash \
	create_from_plist_with_keyword_and_message \
+
	create_from_plist_include \
	create_with_hardlink \
	create_no_clobber \
-
	time
+
	time \
+
	create_from_plist_keyword_validation \
+
	create_from_plist_keyword_real_args \
+
	create_from_plist_keyword_lua_actions \
+
	create_from_plist_keyword_deprecated

genmanifest() {
	cat << EOF >> +MANIFEST
@@ -124,6 +131,25 @@ create_from_plist_set_group_body() {
		tar tvf test-1.txz
}

+

+
create_from_plist_set_group_space_body() {
+

+
	preparetestcredentials "(, bla,)"
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -o ${TMPDIR} -m . -p test.plist -r .
+

+
	basic_validation
+
	atf_check \
+
		-o match:"-rw-r--r-- .*root[ /]+bla.* /file1$" \
+
		-e ignore \
+
		-s exit:0 \
+
		tar tvf test-1.txz
+
}
+

create_from_plist_gather_mode_body() {

	preparetestcredentials "(plop,bla,)"
@@ -238,11 +264,131 @@ create_from_plist_bad_fflags_body() {

	atf_check \
		-o empty \
-
		-e inline:"${PROGNAME}: Malformed keyword '', wrong fflags\n" \
+
		-e inline:"${PROGNAME}: Malformed keyword '', wrong fflags\n${PROGNAME}: Malformed keyword @(,,,schg,bad) file1, expecting @keyword or @keyword(owner,group,mode)\n" \
		-s exit:1 \
		pkg create -o ${TMPDIR} -m . -p test.plist -r .
}

+
create_from_plist_keyword_real_args_body() {
+
	preparetestcredentials "test"
+

+
cat << EOF > test.ucl
+
actions: []
+
arguments: true
+
post-install-lua: <<EOS
+
if arg ~= nil then
+
	print("yes")
+
end
+
for i = 1, #arg do
+
	print(arg[i])
+
end
+
EOS
+
EOF
+

+
	genplist "@test A B C D"
+

+
mkdir target
+

+
	atf_check \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
	atf_check \
+
		-o inline:"yes\nfile1\nyes\nA\nB\nC\nD\n" \
+
		pkg -o REPOS_DIR=/dev/null -r ${TMPDIR}/target install -qfy ${TMPDIR}/test-1.txz
+
}
+

+
create_from_plist_keyword_validation_body() {
+
	preparetestcredentials "test"
+

+
cat << EOF > test.ucl
+
actions: []
+
prepackaging: <<EOS
+
io.stderr:write("meh\n")
+
return 1
+
EOS
+
EOF
+
	atf_check \
+
		-o empty \
+
		-e inline:"meh\n${PROGNAME}: Fail to apply keyword 'test'\n" \
+
		-s exit:1 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
cat << EOF > test.ucl
+
actions: []
+
prepackaging: <<EOS
+
print(line)
+
io.stderr:write("meh\n")
+
return 1
+
EOS
+
EOF
+
	atf_check \
+
		-o inline:"file1\n" \
+
		-e inline:"meh\n${PROGNAME}: Fail to apply keyword 'test'\n" \
+
		-s exit:1 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
cat << EOF > test.ucl
+
actions: []
+
prepackaging: <<EOS
+
print(#arg)
+
io.stderr:write("meh\n")
+
return 1
+
EOS
+
EOF
+
	atf_check \
+
		-o inline:"0\n" \
+
		-e inline:"meh\n${PROGNAME}: Fail to apply keyword 'test'\n" \
+
		-s exit:1 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
cat << EOF > test.ucl
+
actions: []
+
arguments: true
+
prepackaging: <<EOS
+
print(#arg)
+
io.stderr:write("meh\n")
+
return 1
+
EOS
+
EOF
+
	atf_check \
+
		-o inline:"1\n" \
+
		-e inline:"meh\n${PROGNAME}: Fail to apply keyword 'test'\n" \
+
		-s exit:1 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
	genplist "@test A B"
+
	genplist "@test A A"
+
	genplist "@test A B C"
+
cat << EOF > test.ucl
+
actions: []
+
arguments: true
+
prepackaging: <<EOS
+
if #arg == 1 then
+
  return 0
+
end
+
if #arg == 2 then
+
  if arg[1] == arg[2] then
+
    io.stderr:write("The first and the second argument are identical\n")
+
    return 1
+
  end
+
  return 1
+
end
+
io.stderr:write("Invalid number of arguments '".. #arg .. "' expecting 1 or 2\n")
+
return 1
+
EOS
+
EOF
+
output="${PROGNAME}: Fail to apply keyword 'test'
+
The first and the second argument are identical
+
${PROGNAME}: Fail to apply keyword 'test'
+
Invalid number of arguments '3' expecting 1 or 2
+
${PROGNAME}: Fail to apply keyword 'test'
+
"
+
	atf_check \
+
		-e inline:"${output}" \
+
		-s exit:1 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+
}
+

create_from_plist_with_keyword_arguments_body() {
	preparetestcredentials "testkeyword"

@@ -261,7 +407,7 @@ EOF

	atf_check \
		-o empty \
-
		-e inline:"${PROGNAME}: Requesting argument %2 while only 1 arguments are available\n" \
+
		-e inline:"${PROGNAME}: Requesting argument %2 while only 1 arguments are available\n${PROGNAME}: Fail to apply keyword 'testkeyword'\n" \
		-s exit:1 \
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .

@@ -276,7 +422,7 @@ EOF

	atf_check \
		-o empty \
-
		-e inline:"${PROGNAME}: Invalid argument: expecting a number got (%1)\n" \
+
		-e inline:"${PROGNAME}: Invalid argument: expecting a number got (%1)\n${PROGNAME}: Fail to apply keyword 'testkeyword'\n" \
		-s exit:1 \
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .

@@ -323,7 +469,7 @@ directories {
    /B = "y";
}
scripts {
-
    post-install = "echo A B";
+
    post-install = "# args: A B\necho A B";
}

EOF
@@ -373,6 +519,66 @@ EOF
		pkg info -R --raw-format=ucl -F test-1.txz
}

+
create_from_manifest_body() {
+
	genmanifest
+
	cat <<EOF >> +MANIFEST
+
files: {
+
     /testfile: {perm: 0644}
+
}
+
EOF
+
	touch testfile
+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M ./+MANIFEST -r ${TMPDIR}
+

+
	cat << EOF > output.ucl
+
name = "test";
+
origin = "test";
+
version = "1";
+
comment = "a test";
+
maintainer = "test";
+
www = "http://test";
+
abi = "*";
+
arch = "*";
+
prefix = "/";
+
flatsize = 0;
+
desc = "Yet another test";
+
categories [
+
    "test",
+
]
+
files {
+
    /testfile = "1\$e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
+
}
+

+
EOF
+

+
	atf_check \
+
		-o file:output.ucl \
+
		-e empty \
+
		-s exit:0 \
+
		pkg info -R --raw-format=ucl -F test-1.txz
+
}
+

+
create_from_manifest_dir_body() {
+
	genmanifest
+
	cat <<EOF >> +MANIFEST
+
files: {
+
     /testfile: {perm: 0644}
+
     /testdir: {perm: 0644}
+
}
+
EOF
+
	touch testfile
+
	mkdir testdir
+
	atf_check \
+
		-o empty \
+
		-e not-empty \
+
		-s not-exit:0 \
+
		pkg create -M ./+MANIFEST -r ${TMPDIR}
+

+
}
+

create_from_plist_pkg_descr_body() {
	genmanifest
cat << EOF > ./+DISPLAY
@@ -474,7 +680,7 @@ EOF
	atf_check \
		-o match:"0 Jan +2 +1970.*/a" \
		tar tvf test-1.txz
-
	atf_check -e match:"Invalid" -s exit:64 pkg create -t meh -M test.ucl
+
	atf_check -e match:"Invalid" -s exit:1 pkg create -t meh -M test.ucl
	atf_check pkg create -t 172800 -M test.ucl
	atf_check \
		-o match:"0 Jan +3 +1970.*/a" \
@@ -506,3 +712,107 @@ create_no_clobber_body()
	after=$(ls -l test-1.txz)
	[ "$before" = "$after" ] || atf_fail "Package was recreated"
}
+

+
create_from_plist_include_body()
+
{
+
	genmanifest
+
	cat << EOF >> test.plist
+
file1
+
@include other-plist
+
file2
+
EOF
+
	cat <<EOF >> other-plist
+
file3
+
EOF
+

+
	touch file1
+
	touch file2
+
	touch file3
+

+
	atf_check \
+
		-s exit:0 \
+
		pkg create -o ${TMPDIR} -m . -p test.plist -r .
+

+
	atf_check -o inline:"/file1\n/file3\n/file2\n" pkg info -ql -F test*.txz
+
	cat << EOF >> other-plist
+
@include test.plist
+
EOF
+
	atf_check \
+
		-e inline:"pkg: Inside in @include it is not allowed to reuse @include\n" \
+
		-s exit:1 \
+
		pkg create -o ${TMPDIR} -m . -p test.plist -r .
+
}
+

+
create_from_plist_keyword_lua_actions_body()
+
{
+
	genmanifest
+
	genplist "@test(plop,,) A B C D"
+

+
cat << EOF > test.ucl
+
arguments: true
+
prepackaging: <<EOS
+
ok = true
+
for i = 1, #arg do
+
	if not pkg.file(arg[i]) then
+
		ok = false
+
	end
+
end
+
if not ok then
+
	return 1
+
end
+
EOS
+
arguments: true
+
EOF
+

+
touch C
+
touch D
+

+
output="${PROGNAME}: Unable to access file ./A:No such file or directory
+
${PROGNAME}: Unable to access file ./B:No such file or directory
+
${PROGNAME}: Fail to apply keyword 'test'
+
"
+

+
	atf_check \
+
		-e inline:"${output}" \
+
		-s exit:1 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
touch A B
+
	atf_check \
+
		-s exit:0 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
	atf_check \
+
		-o match:"-rw-r--r-- .*plop[ /]+wheel.* /A$" \
+
		tar tvf test-1.txz
+
}
+

+
create_from_plist_keyword_deprecated_body()
+
{
+
	genmanifest
+
	genplist "@test A B C D"
+

+
cat << EOF > test.ucl
+
arguments: true
+
deprecated: true
+
EOF
+

+
	atf_check \
+
		-e inline:"${PROGNAME}: Use of '@test' is deprecated\n" \
+
		-s exit:0 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
cat << EOF > test.ucl
+
arguments: true
+
deprecated: true
+
deprecation_message: <<EOM
+
we don't like it anymore
+
EOM
+
EOF
+

+
	atf_check \
+
		-e inline:"${PROGNAME}: Use of '@test' is deprecated: we don't like it anymore\n" \
+
		-s exit:0 \
+
		pkg -o PLIST_KEYWORDS_DIR=. create -o ${TMPDIR} -m . -p test.plist -r .
+

+
}
modified tests/frontend/pkg.sh
@@ -14,7 +14,7 @@ pkg_no_database_body() {
	atf_check \
	    -o empty \
	    -e inline:"${PROGNAME}: package database non-existent\n" \
-
	    -s exit:69 \
+
	    -s exit:1 \
	    env -i PATH="${PATH}" DYLD_LIBRARY_PATH="${DYLD_LIBRARY_PATH}" LD_LIBRARY_PATH="${LD_LIBRARY_PATH}" pkg -o PKG_DBDIR=/dev/null -N
}

modified tests/frontend/query.sh
@@ -31,6 +31,7 @@ files: {
}
EOF
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg plop plop 1
+
	sed -ie 's/comment: a test/comment: Nothing to see here/' plop.ucl
	cat >> plop.ucl << EOF
deps: {
    test: {
@@ -133,4 +134,47 @@ EOF
		-e empty \
		-s exit:0 \
		test "${sum1}" = "${sum2}"
+

+
	# Test 'pkg query -F'
+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M plop.ucl
+

+
	atf_check \
+
		-o inline:"${TMPDIR}/plop\n${TMPDIR}/bla\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg query -F ./test-1.txz '%Fp'
+

+
	atf_check \
+
		-o inline:"1\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg query -F ./test-1.txz '%?F'
+

+
	atf_check \
+
		-o inline:"2\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg query -F ./test-1.txz '%#F'
+

+
	atf_check \
+
		-o inline:"test 1\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg query -F ./test-1.txz '%n %v'
+

+
	atf_check \
+
		-o inline:"a test\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg query -F ./test-1.txz '%c'
+

+
	atf_check \
+
		-o inline:"Nothing to see here\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg query -F ./plop-1.txz '%c'
}
modified tests/frontend/register.sh
@@ -36,7 +36,7 @@ EOF
	atf_check \
	    -o match:".*Installing.*" \
	    -e match:".*conflicts.*" \
-
	    -s exit:70 \
+
	    -s exit:1 \
	    pkg register -i teststage -M test.ucl
	nsum=$(openssl dgst -sha256 -binary plop | hexdump -v -e '/1 "%x"')
	atf_check_equal ${sum} ${nsum}
@@ -120,6 +120,6 @@ file_not_found_body()
	mkdir -p ${TMPDIR}/target
	atf_check \
		-e match:"Unable to access file ${TMPDIR}/prefix/foo" \
-
		-s exit:74 \
+
		-s exit:1 \
		pkg -r ${TMPDIR}/target register -M ${TMPDIR}/test.ucl -f ${TMPDIR}/plist -i ${TMPDIR}
}
modified tests/frontend/shellscript.sh
@@ -5,6 +5,7 @@
tests_init \
	basic \
	message \
+
	daemon \
	upgrade

basic_body() {
@@ -61,6 +62,35 @@ EOF

}

+
daemon_body() {
+
	# We should not see the daemon's message
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1"
+
	cat << EOF >> test.ucl
+
scripts: {
+
  post-install: <<EOS
+
	echo this is post install1
+
	echo this is a message >&\${PKG_MSGFD}
+
	(sleep 2; echo this is a daemon >&\${PKG_MSGFD}) < /dev/null > /dev/null 2>&1 &
+
	echo this is post install2
+
EOS
+
}
+
EOF
+

+
	atf_check \
+
		-o empty \
+
		-e empty \
+
		-s exit:0 \
+
		pkg create -M test.ucl
+

+
	mkdir ${TMPDIR}/target
+
	atf_check \
+
		-o inline:"this is post install1\nthis is post install2\nthis is a message\n" \
+
		-e empty \
+
		-s exit:0 \
+
		pkg -o REPOS_DIR=/dev/null -r ${TMPDIR}/target install -qfy ${TMPDIR}/test-1.txz
+

+
}
+

upgrade_body() {
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1"
	cat << EOF >> test.ucl
added tests/frontend/triggers.sh
@@ -0,0 +1,59 @@
+
#! /usr/bin/env atf-sh
+

+
. $(atf_get_srcdir)/test_environment.sh
+

+
tests_init \
+
	cleanup_shell \
+
	cleanup_lua
+

+
cleanup_shell_body() {
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1" "/"
+
	mkdir trigger_dir/
+
	cat << EOF >> trigger_dir/trigger.ucl
+
path: [ "/" ]
+
cleanup: {
+
	type: shell
+
	script: <<EOS
+
echo "Cleaning up"
+
EOS
+
}
+
trigger: {
+
	type: shell
+
	script: <<EOS
+
exit 0
+
EOS
+
}
+
EOF
+
	echo trigger_dir/trigger.ucl > plist
+
	atf_check pkg create -M test.ucl -p plist -r .
+
	mkdir target
+
	atf_check pkg -o REPOS_DIR=/dev/null -r ${TMPDIR}/target install -qfy ${TMPDIR}/test-1.txz
+
	atf_check pkg -o REPOS_DIR=/dev/null -o PKG_TRIGGERS_DIR="/trigger_dir" -r ${TMPDIR}/target install -qfy ${TMPDIR}/test-1.txz
+
	atf_check -o inline:"Cleaning up\n" pkg -o REPOS_DIR=/dev/null -o PKG_TRIGGERS_DIR="/trigger_dir" -r ${TMPDIR}/target delete -qy test
+
}
+

+
cleanup_lua_body() {
+
	atf_check -s exit:0 sh ${RESOURCEDIR}/test_subr.sh new_pkg "test" "test" "1" "/"
+
	mkdir trigger_dir/
+
	cat << EOF >> trigger_dir/trigger.ucl
+
path: [ "/" ]
+
cleanup: {
+
	type: lua
+
	script: <<EOS
+
print "Cleaning up"
+
EOS
+
}
+
trigger: {
+
	type: lua
+
	script: <<EOS
+
	return 0
+
EOS
+
}
+
EOF
+
	echo trigger_dir/trigger.ucl > plist
+
	atf_check pkg create -M test.ucl -p plist -r .
+
	mkdir target
+
	atf_check pkg -o REPOS_DIR=/dev/null -r ${TMPDIR}/target install -qfy ${TMPDIR}/test-1.txz
+
	atf_check pkg -o REPOS_DIR=/dev/null -o PKG_TRIGGERS_DIR="/trigger_dir" -r ${TMPDIR}/target install -qfy ${TMPDIR}/test-1.txz
+
	atf_check -o inline:"Cleaning up\n" pkg -o REPOS_DIR=/dev/null -o PKG_TRIGGERS_DIR="/trigger_dir" -r ${TMPDIR}/target delete -qy test
+
}
modified tests/frontend/update.sh
@@ -18,6 +18,6 @@ EOF
	atf_check \
		-o match:"Unable to update repository test" \
		-e match:"pkg: file://empty//packagesite.txz: No such file or directory" \
-
		-s exit:70 \
+
		-s exit:1 \
		pkg -R repos update
}
modified tests/frontend/version.sh
@@ -25,7 +25,7 @@ compare_body() {
	atf_check \
		-o ignore \
		-e ignore \
-
		-s exit:70 \
+
		-s exit:1 \
		pkg info "test<5"
	atf_check \
		-o ignore \
@@ -33,11 +33,11 @@ compare_body() {
	atf_check \
		-o ignore \
		-e ignore \
-
		-s exit:70 \
+
		-s exit:1 \
		pkg info "test>5<5.20"
	atf_check \
		-o ignore \
		-e ignore \
-
		-s exit:70 \
+
		-s exit:1 \
		pkg info "test>5.20_3<6"
}
modified tests/lib/merge.c
@@ -27,7 +27,6 @@
#include <atf-c.h>
#include <sys/types.h>
#include <private/utils.h>
-
#include <utstring.h>

ATF_TC(merge);

@@ -39,40 +38,44 @@ ATF_TC_HEAD(merge, tc)

ATF_TC_BODY(merge, tc)
{
-
	UT_string *b;
-
	utstring_new(b);
+
	xstring *b;
+
	b = xstring_new();
	char *pivot = "test1\ntest2\n";
	char *modified = "test1\n#test2\n";
	char *new = "test1\ntest2\ntest3\n";

	ATF_REQUIRE_EQ(merge_3way(pivot, modified, new, b), 0);
-
	ATF_REQUIRE_STREQ(utstring_body(b), "test1\n#test2\ntest3\n");
+
	fflush(b->fp);
+
	ATF_REQUIRE_STREQ(b->buf, "test1\n#test2\ntest3\n");

-
	utstring_clear(b);
+
	xstring_reset(b);
	pivot = "test1\ntest2";
	modified = "test1\n#test2";
	new = "test1\ntest2\ntest3";

	ATF_REQUIRE_EQ(merge_3way(pivot, modified, new, b), 0);
-
	ATF_REQUIRE_STREQ(utstring_body(b), "test1\n#test2test3");
+
	fflush(b->fp);
+
	ATF_REQUIRE_STREQ(b->buf, "test1\n#test2test3");

-
	utstring_clear(b);
+
	xstring_reset(b);
	pivot = "test1\ntest2";
	modified = "test1\n";
	new = "test1\ntest2\ntest3";

	ATF_REQUIRE_EQ(merge_3way(pivot, modified, new, b), 0);
-
	ATF_REQUIRE_STREQ(utstring_body(b), "test1\ntest3");
+
	fflush(b->fp);
+
	ATF_REQUIRE_STREQ(b->buf, "test1\ntest3");

-
	utstring_clear(b);
+
	xstring_reset(b);
	pivot = "test1\ntest2\ntest3";
	modified = "test1\na\ntest2\ntest3";
	new = "test1\ntest2\ntest3";

	ATF_REQUIRE_EQ(merge_3way(pivot, modified, new, b), 0);
-
	ATF_REQUIRE_STREQ(utstring_body(b), "test1\na\ntest2\ntest3");
+
	fflush(b->fp);
+
	ATF_REQUIRE_STREQ(b->buf, "test1\na\ntest2\ntest3");

-
	utstring_free(b);
+
	xstring_free(b);
}

ATF_TP_ADD_TCS(tp)
modified tests/lib/pkg_printf_test.c
@@ -27,7 +27,7 @@
#include <string.h>

#include <atf-c.h>
-
#include <utstring.h>
+
#include <xstring.h>
#include <pkg.h>
#include <private/pkg_printf.h>

@@ -134,7 +134,7 @@ ATF_TC_HEAD(human_number, tc)
}
ATF_TC_BODY(human_number, tc)
{
-
	UT_string		*buf;
+
	xstring		*buf = NULL;
	struct percent_esc	*p;
	int			 i;

@@ -308,22 +308,21 @@ ATF_TC_BODY(human_number, tc)
		{ -1,                  NULL,     0, 0, },
	};

-
	utstring_new(buf);
	p = new_percent_esc();

-
	ATF_REQUIRE_EQ(buf != NULL, true);
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; hn_test_vals[i].out != NULL; i++) {
+
		xstring_renew(buf);
		p->width = hn_test_vals[i].width;
		p->flags = hn_test_vals[i].flags;
		buf = human_number(buf, hn_test_vals[i].in, p);
-
		ATF_CHECK_STREQ(utstring_body(buf), hn_test_vals[i].out);
-
		utstring_clear(buf);
+
		fflush(buf->fp);
+
		ATF_CHECK_STREQ(buf->buf, hn_test_vals[i].out);
	}

	free_percent_esc(p);
-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(string_val);
@@ -334,7 +333,7 @@ ATF_TC_HEAD(string_val, tc)
}
ATF_TC_BODY(string_val, tc)
{
-
	UT_string		*buf;
+
	xstring		*buf = NULL;
	struct percent_esc	*p;
	int			 i;

@@ -392,22 +391,21 @@ ATF_TC_BODY(string_val, tc)
		{ NULL, NULL, 0, 0, },
	};

-
	utstring_new(buf);
	p = new_percent_esc();

-
	ATF_REQUIRE_EQ(buf != NULL, true);
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; sv_test_vals[i].out != NULL; i++) {
+
		xstring_renew(buf);
		p->width = sv_test_vals[i].width;
		p->flags = sv_test_vals[i].flags;
		buf = string_val(buf, sv_test_vals[i].in, p);
-
		ATF_CHECK_STREQ(utstring_body(buf), sv_test_vals[i].out);
-
		utstring_clear(buf);
+
		fflush(buf->fp);
+
		ATF_CHECK_STREQ(buf->buf, sv_test_vals[i].out);
	}

	free_percent_esc(p);
-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(int_val);
@@ -418,7 +416,7 @@ ATF_TC_HEAD(int_val, tc)
}
ATF_TC_BODY(int_val, tc)
{
-
	UT_string		*buf;
+
	xstring		*buf = NULL;
	struct percent_esc	*p;
	int			 i;

@@ -632,22 +630,21 @@ ATF_TC_BODY(int_val, tc)
		{ -1, NULL, 0, 0, },
	}; 

-
	utstring_new(buf);
	p = new_percent_esc();

-
	ATF_REQUIRE_EQ(buf != NULL, true);
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; iv_test_vals[i].out != NULL; i++) {
+
		xstring_renew(buf);
		p->width = iv_test_vals[i].width;
		p->flags = iv_test_vals[i].flags;
		buf = int_val(buf, iv_test_vals[i].in, p);
-
		ATF_CHECK_STREQ(utstring_body(buf), iv_test_vals[i].out);
-
		utstring_clear(buf);
+
		fflush(buf->fp);
+
		ATF_CHECK_STREQ(buf->buf, iv_test_vals[i].out);
	}

	free_percent_esc(p);
-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(bool_val);
@@ -658,7 +655,7 @@ ATF_TC_HEAD(bool_val, tc)
}
ATF_TC_BODY(bool_val, tc)
{
-
	UT_string		*buf;
+
	xstring		*buf = NULL;
	struct percent_esc	*p;
	int			 i;

@@ -685,22 +682,21 @@ ATF_TC_BODY(bool_val, tc)
		{ false, NULL, 0, 0, },
	};

-
	utstring_new(buf);
	p = new_percent_esc();

-
	ATF_REQUIRE_EQ(buf != NULL, true);
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; bv_test_vals[i].out != NULL; i++) {
+
		xstring_renew(buf);
		p->width = bv_test_vals[i].width;
		p->flags = bv_test_vals[i].flags;
		buf = bool_val(buf, bv_test_vals[i].in, p);
-
		ATF_CHECK_STREQ(utstring_body(buf), bv_test_vals[i].out);
-
		utstring_clear(buf);
+
		fflush(buf->fp);
+
		ATF_CHECK_STREQ(buf->buf, bv_test_vals[i].out);
	}

	free_percent_esc(p);
-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(mode_val);
@@ -711,7 +707,7 @@ ATF_TC_HEAD(mode_val, tc)
}
ATF_TC_BODY(mode_val, tc)
{
-
	UT_string		*buf;
+
	xstring		*buf = NULL;
	struct percent_esc	*p;
	int			 i;

@@ -804,24 +800,23 @@ ATF_TC_BODY(mode_val, tc)
		{ 0, NULL, 0, 0, },
	};

-
	utstring_new(buf);
	p = new_percent_esc();

-
	ATF_REQUIRE_EQ(buf != NULL, true);
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; mv_test_vals[i].out != NULL; i++) {
+
		xstring_renew(buf);
		p->width = mv_test_vals[i].width;
		p->flags = mv_test_vals[i].flags;
		buf = mode_val(buf, mv_test_vals[i].in, p);
-
		ATF_CHECK_STREQ(utstring_body(buf), mv_test_vals[i].out);
-
		utstring_clear(buf);
+
		fflush(buf->fp);
+
		ATF_CHECK_STREQ(buf->buf, mv_test_vals[i].out);
	}

	free_percent_esc(p);
-
	utstring_free(buf);
+
	xstring_free(buf);
}
-
	
+

ATF_TC(liclog_val);
ATF_TC_HEAD(liclog_val, tc)
{
@@ -830,7 +825,7 @@ ATF_TC_HEAD(liclog_val, tc)
}
ATF_TC_BODY(liclog_val, tc)
{
-
	UT_string		*buf;
+
	xstring		*buf = NULL;
	struct percent_esc	*p;
	int			 i;

@@ -860,22 +855,21 @@ ATF_TC_BODY(liclog_val, tc)
		{ 0, NULL, 0, 0, },
	};

-
	utstring_new(buf);
	p = new_percent_esc();

-
	ATF_REQUIRE_EQ(buf != NULL, true);
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; lv_test_vals[i].out != NULL; i++) {
+
		xstring_renew(buf);
		p->width = lv_test_vals[i].width;
		p->flags = lv_test_vals[i].flags;
		buf = liclog_val(buf, lv_test_vals[i].in, p);
-
		ATF_CHECK_STREQ(utstring_body(buf), lv_test_vals[i].out);
-
		utstring_clear(buf);
+
		fflush(buf->fp);
+
		ATF_CHECK_STREQ(buf->buf, lv_test_vals[i].out);
	}

	free_percent_esc(p);
-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(list_count);
@@ -886,7 +880,7 @@ ATF_TC_HEAD(list_count, tc)
}
ATF_TC_BODY(list_count, tc)
{
-
	UT_string		*buf;
+
	xstring		*buf = NULL;
	struct percent_esc	*p;
	int			 i;

@@ -908,22 +902,21 @@ ATF_TC_BODY(list_count, tc)
		{ 0, NULL, 0, 0, },
	};

-
	utstring_new(buf);
	p = new_percent_esc();

-
	ATF_REQUIRE_EQ(buf != NULL, true);
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; lc_test_vals[i].out != NULL; i++) {
+
		xstring_renew(buf);
		p->width = lc_test_vals[i].width;
		p->flags = lc_test_vals[i].flags;
		buf = list_count(buf, lc_test_vals[i].in, p);
-
		ATF_CHECK_STREQ(utstring_body(buf), lc_test_vals[i].out);
-
		utstring_clear(buf);
+
		fflush(buf->fp);
+
		ATF_CHECK_STREQ(buf->buf, lc_test_vals[i].out);
	}

	free_percent_esc(p);
-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(maybe_read_hex_byte);
@@ -934,7 +927,7 @@ ATF_TC_HEAD(maybe_read_hex_byte, tc)
}
ATF_TC_BODY(maybe_read_hex_byte, tc)
{
-
	UT_string	*buf;
+
	xstring	*buf = NULL;
	const char	*f;
	int		 i;

@@ -1003,25 +996,21 @@ ATF_TC_BODY(maybe_read_hex_byte, tc)
		{ NULL,   NULL,    0, '\0', },
	};

-
	utstring_new(buf);
-

-
	ATF_REQUIRE_EQ(buf != NULL, true);
-

	for (i = 0; mrhb_test_vals[i].in != NULL; i++) {
+
		xstring_renew(buf);
		f = maybe_read_hex_byte(buf, mrhb_test_vals[i].in);
+
		fflush(buf->fp);

-
		ATF_CHECK_STREQ_MSG(utstring_body(buf), mrhb_test_vals[i].out,
+
		ATF_CHECK_STREQ_MSG(buf->buf, mrhb_test_vals[i].out,
				    "(test %d)", i);
		ATF_CHECK_EQ_MSG(f - mrhb_test_vals[i].in,
				 mrhb_test_vals[i].fend_offset,
				 "(test %d)", i);
		ATF_CHECK_EQ_MSG(*f, mrhb_test_vals[i].fend_val,
				 "(test %d)", i);
-
		
-
		utstring_clear(buf);
	}

-
	utstring_free(buf);
+
	xstring_free(buf);
}


@@ -1033,7 +1022,7 @@ ATF_TC_HEAD(read_oct_byte, tc)
}
ATF_TC_BODY(read_oct_byte, tc)
{
-
	UT_string	*buf;
+
	xstring	*buf = NULL;
	const char	*f;
	int		 i;

@@ -1109,25 +1098,21 @@ ATF_TC_BODY(read_oct_byte, tc)
		{ NULL,   NULL,    0, '\0', },
	};

-
	utstring_new(buf);
-

-
	ATF_REQUIRE_EQ(buf != NULL, true);
-

	for (i = 0; rob_test_vals[i].in != NULL; i++) {
+
		xstring_renew(buf);
		f = read_oct_byte(buf, rob_test_vals[i].in);
+
		fflush(buf->fp);

-
		ATF_CHECK_STREQ_MSG(utstring_body(buf), rob_test_vals[i].out,
+
		ATF_CHECK_STREQ_MSG(buf->buf, rob_test_vals[i].out,
				    "(test %d)", i);
		ATF_CHECK_EQ_MSG(f - rob_test_vals[i].in,
				 rob_test_vals[i].fend_offset,
				 "(test %d)", i);
		ATF_CHECK_EQ_MSG(*f, rob_test_vals[i].fend_val,
				 "(test %d)", i);
-
		
-
		utstring_clear(buf);
	}

-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(process_escape);
@@ -1138,7 +1123,7 @@ ATF_TC_HEAD(process_escape, tc)
}
ATF_TC_BODY(process_escape, tc)
{
-
	UT_string	*buf;
+
	xstring	*buf = NULL;
	const char	*f;
	int		 i;

@@ -1175,25 +1160,21 @@ ATF_TC_BODY(process_escape, tc)
		{ NULL,   NULL,    0, '\0', },
	};

-
	utstring_new(buf);
-

-
	ATF_REQUIRE_EQ(buf != NULL, true);
-

	for (i = 0; pe_test_vals[i].in != NULL; i++) {
+
		xstring_renew(buf);
		f = process_escape(buf, pe_test_vals[i].in);
+
		fflush(buf->fp);

-
		ATF_CHECK_STREQ_MSG(utstring_body(buf), pe_test_vals[i].out,
+
		ATF_CHECK_STREQ_MSG(buf->buf, pe_test_vals[i].out,
				    "(test %d)", i);
		ATF_CHECK_EQ_MSG(f - pe_test_vals[i].in,
				 pe_test_vals[i].fend_offset,
				 "(test %d)", i);
		ATF_CHECK_EQ_MSG(*f, pe_test_vals[i].fend_val,
				 "(test %d)", i);
-
		
-
		utstring_clear(buf);
	}

-
	utstring_free(buf);
+
	xstring_free(buf);
}

ATF_TC(field_modifier);
@@ -2211,15 +2192,16 @@ ATF_TC_BODY(format_trailer, tc)
	ATF_REQUIRE_EQ(p != NULL, true);

	for (i = 0; ft_test_vals[i].in != NULL; i++) {
-
		utstring_clear(p->item_fmt);
-
		utstring_clear(p->sep_fmt);
+
		clear_percent_esc(p);

		f = format_trailer(ft_test_vals[i].in, p);
+
		fflush(p->item_fmt->fp);
+
		fflush(p->sep_fmt->fp);

-
		ATF_CHECK_STREQ_MSG(utstring_body(p->item_fmt),
+
		ATF_CHECK_STREQ_MSG(p->item_fmt->buf,
				    ft_test_vals[i].item,
				    "(test %d)", i);
-
		ATF_CHECK_STREQ_MSG(utstring_body(p->sep_fmt),
+
		ATF_CHECK_STREQ_MSG(p->sep_fmt->buf,
				    ft_test_vals[i].sep,
				    "(test %d)", i);
		ATF_CHECK_EQ_MSG(f - ft_test_vals[i].in,
@@ -2290,10 +2272,12 @@ ATF_TC_BODY(parse_format, tc)
		ATF_CHECK_EQ_MSG(p->fmt_code, pf_test_vals[i].fmt_code,
				    "(test %d)", i);

-
		ATF_CHECK_STREQ_MSG(utstring_body(p->item_fmt),
+
		fflush(p->item_fmt->fp);
+
		fflush(p->sep_fmt->fp);
+
		ATF_CHECK_STREQ_MSG(p->item_fmt->buf,
				    pf_test_vals[i].item,
				    "(test %d)", i);
-
		ATF_CHECK_STREQ_MSG(utstring_body(p->sep_fmt),
+
		ATF_CHECK_STREQ_MSG(p->sep_fmt->buf,
				    pf_test_vals[i].sep,
				    "(test %d)", i);
		ATF_CHECK_EQ_MSG(f - pf_test_vals[i].in,
modified tests/lib/pkg_validation.c
@@ -29,7 +29,7 @@
#include <string.h>

#include <atf-c.h>
-
#include <utstring.h>
+
#include <xstring.h>
#include <pkg.h>

#ifndef __unused
@@ -40,7 +40,7 @@
# endif
#endif

-
UT_string *msg;
+
xstring *msg;

ATF_TC(valid_installed);

@@ -55,8 +55,8 @@ event_callback(void *data __unused, struct pkg_event *ev)
{
	switch (ev->type) {
	case PKG_EVENT_ERROR:
-
		utstring_clear(msg);
-
		utstring_printf(msg, "%s", ev->e_pkg_error.msg);
+
		xstring_reset(msg);
+
		fprintf(msg->fp, "%s", ev->e_pkg_error.msg);
		break;
	default:
		/* IGNORE */
@@ -69,43 +69,51 @@ event_callback(void *data __unused, struct pkg_event *ev)
void
check_valid(struct pkg *p)
{
-
	utstring_new(msg);
+
	msg = xstring_new();

	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property origin");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property origin");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_ORIGIN, "test/bla"));
	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property name");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property name");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_NAME, "test"));
	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property comment");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property comment");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_COMMENT, "test comment"));
	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property version");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property version");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_VERSION, "1.1.0"));
	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property desc");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property desc");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_DESC, "test description"));
	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property maintainer");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property maintainer");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_MAINTAINER, "tester"));
	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property www");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property www");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_WWW, "test website"));
	ATF_REQUIRE_EQ(EPKG_FATAL, pkg_is_valid(p));
-
	ATF_REQUIRE_STREQ(utstring_body(msg), "Invalid package: object has missing property prefix");
+
	fflush(msg->fp);
+
	ATF_REQUIRE_STREQ(msg->buf, "Invalid package: object has missing property prefix");

	ATF_REQUIRE_EQ(EPKG_OK, pkg_set(p, PKG_PREFIX, "/usr/local"));
	ATF_REQUIRE_EQ(EPKG_OK, pkg_is_valid(p));

-
	utstring_free(msg);
+
	xstring_free(msg);
}

ATF_TC_BODY(valid_installed, tc)
modified tests/lib/plist.c
@@ -1,5 +1,5 @@
/*-
-
 * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org>
+
 * Copyright (c) 2013-2020 Baptiste Daroussin <bapt@FreeBSD.org>
 * All rights reserved.
 *~
 * Redistribution and use in source and binary forms, with or without
@@ -57,6 +57,118 @@ ATF_TC_BODY(parse_mode, tc)
	free(set);
}

+
ATF_TC(parse_keyword_attributes);
+

+
ATF_TC_HEAD(parse_keyword_attributes, tc)
+
{
+
	atf_tc_set_md_var(tc, "descr",
+
	    "parse_keyword_attributes()");
+
}
+

+
ATF_TC_BODY(parse_keyword_attributes, tc)
+
{
+
	char buf[BUFSIZ];
+
	struct file_attr *a;
+

+
	strlcpy(buf, "()", BUFSIZ);
+
	ATF_REQUIRE(parse_keyword_args(buf, "plop") == NULL);
+

+
	strlcpy(buf, "(root, wheel)", BUFSIZ);
+
	ATF_REQUIRE((a = parse_keyword_args(buf, "plop")) != NULL);
+
	ATF_REQUIRE_STREQ(a->owner, "root");
+
	ATF_REQUIRE_STREQ(a->group, "wheel");
+
	free_file_attr(a);
+

+
	strlcpy(buf, "(root, wheel, 0755)", BUFSIZ);
+
	ATF_REQUIRE((a = parse_keyword_args(buf, "plop")) != NULL);
+
	ATF_REQUIRE_STREQ(a->owner, "root");
+
	ATF_REQUIRE_STREQ(a->group, "wheel");
+
	free_file_attr(a);
+

+
	strlcpy(buf, "(root, wheel, 0755,)", BUFSIZ);
+
	ATF_REQUIRE((a = parse_keyword_args(buf, "plop")) != NULL);
+
	ATF_REQUIRE_STREQ(a->owner, "root");
+
	ATF_REQUIRE_STREQ(a->group, "wheel");
+
	free_file_attr(a);
+
}
+

+
ATF_TC(parse_keyword);
+

+
ATF_TC_HEAD(parse_keyword, tc)
+
{
+
	atf_tc_set_md_var(tc, "descr",
+
	    "parse_keyword()");
+
}
+

+
ATF_TC_BODY(parse_keyword, tc)
+
{
+
	char *keyword;
+
	struct file_attr *attr;
+
	char buf[BUFSIZ];
+

+
	strlcpy(buf, "something", BUFSIZ);
+
	keyword = NULL;
+
	attr = NULL;
+
	ATF_REQUIRE_STREQ(extract_keywords(buf, &keyword, &attr), "");
+
	ATF_REQUIRE_STREQ(keyword, "something");
+
	ATF_REQUIRE_EQ(attr, NULL);
+

+
	/* empty keyword */
+
	strlcpy(buf, "", BUFSIZ);
+
	keyword = NULL;
+
	attr = NULL;
+
	ATF_REQUIRE_STREQ(extract_keywords(buf, &keyword, &attr), "");
+
	ATF_REQUIRE_STREQ(keyword, "");
+
	ATF_REQUIRE_EQ(attr, NULL);
+

+
	/* bad keyword */
+
	strlcpy(buf, "(", BUFSIZ);
+
	keyword = NULL;
+
	attr = NULL;
+
	ATF_REQUIRE_EQ(extract_keywords(buf, &keyword, &attr), NULL);
+
	ATF_REQUIRE_EQ(keyword, NULL);
+
	ATF_REQUIRE_EQ(attr, NULL);
+

+
	/* bad: empty keyword */
+
	strlcpy(buf, "()", BUFSIZ);
+
	keyword = NULL;
+
	attr = NULL;
+
	ATF_REQUIRE_EQ(extract_keywords(buf, &keyword, &attr), NULL);
+
	ATF_REQUIRE_EQ(keyword, NULL);
+
	ATF_REQUIRE_EQ(attr, NULL);
+

+
	/* ok only user keyword */
+
	strlcpy(buf, "(root) that", BUFSIZ);
+
	keyword = NULL;
+
	attr = NULL;
+
	ATF_REQUIRE_STREQ(extract_keywords(buf, &keyword, &attr), "that");
+
	ATF_REQUIRE_STREQ(keyword, "");
+
	ATF_REQUIRE(attr != NULL);
+
	ATF_REQUIRE_STREQ(attr->owner, "root");
+

+
	/* ok only group keyword */
+
	strlcpy(buf, "(,wheel) that", BUFSIZ);
+
	keyword = NULL;
+
	attr = NULL;
+
	ATF_REQUIRE_STREQ(extract_keywords(buf, &keyword, &attr), "that");
+
	ATF_REQUIRE_STREQ(keyword, "");
+
	ATF_REQUIRE(attr != NULL);
+
	ATF_REQUIRE_STREQ(attr->group, "wheel");
+

+
	/* ok only group with space keyword */
+
	strlcpy(buf, "( , wheel ,) that", BUFSIZ);
+
	keyword = NULL;
+
	attr = NULL;
+
	ATF_REQUIRE_STREQ(extract_keywords(buf, &keyword, &attr), "that");
+
	ATF_REQUIRE_STREQ(keyword, "");
+
	ATF_REQUIRE(attr != NULL);
+
	ATF_REQUIRE_STREQ(attr->group, "wheel");
+
	ATF_REQUIRE_EQ(attr->owner, NULL);
+

+
	strlcpy(buf, "(, wheel ,perm,ffags,)", BUFSIZ);
+
	ATF_REQUIRE_EQ(parse_keyword_args(buf, "plop"), NULL);
+
}
+

ATF_TC(parse_plist);

ATF_TC_HEAD(parse_plist, tc)
@@ -145,6 +257,7 @@ ATF_TC_BODY(parse_plist, tc)
	ATF_REQUIRE_EQ(EPKG_FATAL, plist_parse_line(plist, buf));

	strlcpy(buf, "@dirrm nonexisting", BUFSIZ);
+
	ATF_REQUIRE_EQ(EPKG_FATAL, plist_parse_line(plist, buf));

	pkg_free(p);
	plist_free(plist);
@@ -154,6 +267,8 @@ ATF_TP_ADD_TCS(tp)
{
	ATF_TP_ADD_TC(tp, parse_mode);
	ATF_TP_ADD_TC(tp, parse_plist);
+
	ATF_TP_ADD_TC(tp, parse_keyword_attributes);
+
	ATF_TP_ADD_TC(tp, parse_keyword);

	return (atf_no_error());
}