Radish alpha
H
rad:z3QDZAW2FAfuLvihrhiyDC9fAD8G9
HardenedBSD Package Manager
Radicle
Git
Add many function prototypes. Reorder functions in the file to group similar code together. Fill in the final missing code blocks. Should be complete, bar the debugging,
Matthew Seaman committed 12 years ago
commit 43ab34615d370bd6195c559b5fa3a8ed6fff959a
parent 6d4676d
1 file changed +1252 -1286
modified libpkg/pkg_printf.c
@@ -177,13 +177,16 @@
#define _PP_last	PP_r
#define PP_ALL	((_PP_last << 1) - 1) /* All contexts */

+
/*  %{ %| %} trailer context */
+
#define PP_TRAILER	(PP_B|PP_C|PP_D|PP_F|PP_G|PP_L|PP_O|PP_U|PP_d|PP_r)
+

/* Licence logic types */
#define PP_LIC_SINGLE	0
#define PP_LIC_OR	1
#define PP_LIC_AND	2

-
/* These are in ASCII order of format code: ie alphabetical with A-Z
-
 * sorting before a-z */
+
/* These are in alphabetical order of format code with A-Z sorting
+
 * before a-z */
typedef enum _fmt_code_t {
	PP_PKG_SHLIBS = 0,
	PP_PKG_SHLIB_NAME,
@@ -239,6 +242,7 @@ typedef enum _fmt_code_t {
	PP_PKG_HOME_PAGE,
	PP_LAST_FORMAT = PP_PKG_HOME_PAGE,
	PP_LITERAL_PERCENT,
+
	PP_UNKNOWN,
	PP_END_MARKER,
} fmt_code_t;

@@ -250,6 +254,94 @@ struct percent_esc {
	fmt_code_t	 fmt_code;
};

+
/* Format handler function prototypes */
+

+
static struct sbuf *format_shlibs(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_shlib_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_categories(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_category_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_directories(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_directory_group(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_directory_keepflag(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_directory_path(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_directory_perms(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_directory_tryflag(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_directory_user(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_files(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_file_group(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_file_keepflag(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_file_path(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_file_perms(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_file_sha256(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_file_user(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_groups(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_group_gidstr(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_group_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_row_counter(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_licenses(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_license_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_message(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_options(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_option_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_option_value(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_users(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_user_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_user_uidstr(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_autoremove(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_comment(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_dependencies(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_dependency_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_dependency_origin(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_dependency_version(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_add_info(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_lock_status(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_license_logic(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_maintainer(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_name(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_origin(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_prefix(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_requirements(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_flatsize(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_install_tstamp(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_version(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_home_url(struct sbuf *, const void *, struct percent_esc *);
+
static struct sbuf *format_literal_percent(struct sbuf *, __unused const void *, __unused struct percent_esc *);
+
static struct sbuf *format_unknown(struct sbuf *, __unused const void *, __unused struct percent_esc *);
+

+
/* Other static function prototypes */
+

+
static void free_percent_esc(struct percent_esc *);
+
static struct percent_esc *new_percent_esc(struct percent_esc *);
+

+
static char *gen_format(char *, size_t, unsigned, const char *);
+

+
static struct sbuf *human_number(struct sbuf *, int64_t, struct percent_esc *);
+
static struct sbuf *string_val(struct sbuf *, const char *,
+
			       struct percent_esc *);
+
static struct sbuf *int_val(struct sbuf *, int64_t, struct percent_esc *);
+
static struct sbuf *bool_val(struct sbuf *, bool, struct percent_esc *);
+
static struct sbuf *mode_val(struct sbuf *, mode_t, struct percent_esc *);
+
static struct sbuf *list_count(struct sbuf *, int64_t, struct percent_esc *);
+

+
static struct percent_esc *set_list_defaults(struct percent_esc *, const char *,
+
					     const char *);
+

+
static struct sbuf *iterate_item(struct sbuf *, const struct pkg *, const char *,
+
				 const void *, int, unsigned);
+

+
static inline const char *field_modifier(const char *, struct percent_esc *);
+
static inline const char *field_width(const char *, struct percent_esc *);
+
static const char *parse_format(const char *, unsigned, struct percent_esc *);
+

+
static inline const char *maybe_read_hex_byte(struct sbuf *, const char *);
+
static inline const char *read_oct_byte(struct sbuf *, const char *);
+
static const char *process_escape(struct sbuf *, const char *);
+

+
static const char *process_format_trailer(struct sbuf *, const char *, const struct pkg *,
+
					  const void *, int, unsigned);
+
static const char *process_format_main(struct sbuf *, const char *, va_list);
+

+

struct pkg_printf_fmt {
	char	         fmt_main;
	char		 fmt_sub;
@@ -258,60 +350,7 @@ struct pkg_printf_fmt {
					struct percent_esc *);
};

-
/* Format handler function prototypes */
-

-
static struct sbuf *format_shlibs(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_shlib_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_categories(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_category_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_directories(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_directory_group(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_directory_keepflag(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_directory_path(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_directory_perms(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_directory_tryflag(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_directory_user(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_files(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_file_group(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_file_keepflag(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_file_path(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_file_perms(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_file_sha256(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_file_user(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_groups(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_group_gidstr(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_group_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_row_counter(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_licenses(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_license_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_message(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_options(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_option_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_option_value(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_users(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_user_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_user_uidstr(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_autoremove(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_comment(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_dependencies(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_dependency_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_dependency_origin(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_dependency_version(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_add_info(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_lock_status(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_license_logic(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_maintainer(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_name(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_origin(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_prefix(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_requirements(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_flatsize(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_install_tstamp(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_version(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_home_url(struct sbuf *sbuf, const void *data, struct percent_esc *p);
-
static struct sbuf *format_literal_percent(struct sbuf *sbuf, __unused const void *data, __unused struct percent_esc *p);
-

-
/* These are in ASCII order: alphabetical with A-Z sorting before a-z */
+
/* These are in alphabetical order with A-Z sorting before a-z */
static const struct pkg_printf_fmt	fmt[] = {
	[PP_PKG_SHLIBS] =
	{ 'B',	'\0',	PP_PKG,		&format_shlibs, },
@@ -356,7 +395,7 @@ static const struct pkg_printf_fmt fmt[] = {
	[PP_PKG_GROUP_NAME] =
	{ 'G',	'n',	PP_PKG|PP_G,	&format_group_name, },
	[PP_ROW_COUNTER] =
-
	{ 'I',	'\0',	PP_ALL,		&format_row_counter, },
+
	{ 'I',	'\0',	PP_TRAILER,     &format_row_counter, },
	[PP_PKG_LICENSES] =
	{ 'L',	'\0',	PP_PKG,		&format_licenses, },
	[PP_PKG_LICENSE_NAME] =
@@ -419,6 +458,8 @@ static const struct pkg_printf_fmt fmt[] = {
	{ 'w',	'\0',	PP_ALL,		&format_home_url, },
	[PP_LITERAL_PERCENT] =
	{ '%',	'\0',	PP_ALL,		&format_literal_percent, },
+
	[PP_UNKNOWN] =
+
	{ '\0', '\0',   PP_ALL,         &format_unknown, },
	[PP_END_MARKER] =
	{ '\0',	'\0',	0,		NULL, },
};
@@ -434,549 +475,334 @@ static const char *boolean_str[2][3] = {
	[true]  = { "1", "yes", "true" },
};

-
static void
-
free_percent_esc(struct percent_esc *p)
-
{
-
	if (p->item_fmt)
-
		sbuf_delete(p->item_fmt);
-
	if (p->sep_fmt)
-
		sbuf_delete(p->sep_fmt);
-
	free(p);
-
	return;
-
}
+
/*
+
 * Note: List values -- special behaviour with ? and # modifiers.
+
 * Affects %B %C %D %F %G %L %O %U %d %r
+
 *
+
 * With ? -- Flag values.  Boolean.  %?X returns 0 if the %X list is
+
 * empty, 1 otherwise.
+
 *
+
 * With # -- Count values.  Integer.  %#X returns the number of items in
+
 * the %X list.
+
 */

-
static struct percent_esc *
-
new_percent_esc(struct percent_esc *p)
+
/*
+
 * %B -- Shared Libraries.  List of shlibs required by binaries in the
+
 * pkg.  Optionally accepts per-field format in %{ %| %}, where %n is
+
 * replaced by the shlib name.  Default %{%Bn\n%|%}
+
 */
+
static struct sbuf *
+
format_shlibs(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	/* reset or alloc new */
-
	if (p == NULL) {
-
		p = calloc(1, sizeof(struct percent_esc));
-
		if (p != NULL) {
-
			p->item_fmt = sbuf_new_auto();
-
			p->sep_fmt = sbuf_new_auto();
-
		}
-
		if (p == NULL || p->item_fmt == NULL || p->sep_fmt == NULL) {
-
			/* out of memory */
-
			free_percent_esc(p);
-
			return NULL;
+
	const struct pkg	*pkg = data;
+

+
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_SHLIBS), p));
+
	else {
+
		struct pkg_shlib	*shlib;
+
		int			 count;
+

+
		set_list_defaults(p, "%Bn\n", "");
+

+
		count = 1;
+
		while (pkg_shlibs(pkg, &shlib) == EPKG_OK) {
+
			if (count > 1)
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     shlib, count, PP_B);
+

+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     shlib, count, PP_B);
+
			count++;
		}
-
	} else {
-
		p->flags = 0;
-
		p->width = 0;
-
		sbuf_clear(p->item_fmt);
-
		sbuf_clear(p->sep_fmt);
-
		p->fmt_code = '\0';
	}
-
	return (p);
+
	return (sbuf);
}

-
static inline const char*
-
maybe_read_hex_byte(struct sbuf *sbuf, const char *f)
+
/*
+
 * %Bn -- Shared Library name.
+
 */
+
static struct sbuf *
+
format_shlib_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	int	val;
+
	const struct pkg_shlib	*shlib = data;

-
	/* Hex escapes are of the form \xNN -- always two hex digits */
+
	return (string_val(sbuf, pkg_shlib_name(shlib), p));
+
}

-
	if (isxdigit(f[0]) && isxdigit(f[1])) {
-
		switch(*f) {
-
		case '0':
-
			val = 0x0;
-
			break;
-
		case '1':
-
			val = 0x10;
-
			break;
-
		case '2':
-
			val = 0x20;
-
			break;
-
		case '3':
-
			val = 0x30;
-
			break;
-
		case '4':
-
			val = 0x40;
-
			break;
-
		case '5':
-
			val = 0x50;
-
			break;
-
		case '6':
-
			val = 0x60;
-
			break;
-
		case '7':
-
			val = 0x70;
-
			break;
-
		case '8':
-
			val = 0x80;
-
			break;
-
		case '9':
-
			val = 0x90;
-
			break;
-
		case 'a':
-
		case 'A':
-
			val = 0xa0;
-
			break;
-
		case 'b':
-
		case 'B':
-
			val = 0xb0;
-
			break;
-
		case 'c':
-
		case 'C':
-
			val = 0xc0;
-
			break;
-
		case 'd':
-
		case 'D':
-
			val = 0xd0;
-
			break;
-
		case 'e':
-
		case 'E':
-
			val = 0xe0;
-
			break;
-
		case 'f':
-
		case 'F':
-
			val = 0xf0;
-
			break;
-
		}
+
/*
+
 * %C -- Categories.  List of Category names (strings). 1ary category
+
 * is not distinguished -- look at the package origin for that.
+
 * Optionally accepts per-field format in %{ %| %}, where %n is
+
 * replaced by the category name.  Default %{%Cn%|, %}
+
 */
+
static struct sbuf *
+
format_categories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg	*pkg = data;

-
		f++;
+
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_CATEGORIES),
+
				   p));
+
	else {
+
		struct pkg_category	*cat;
+
		int			 count;

-
		switch(*f) {
-
		case '0':
-
			val += 0x0;
-
			break;
-
		case '1':
-
			val += 0x1;
-
			break;
-
		case '2':
-
			val += 0x2;
-
			break;
-
		case '3':
-
			val += 0x3;
-
			break;
-
		case '4':
-
			val += 0x4;
-
			break;
-
		case '5':
-
			val += 0x5;
-
			break;
-
		case '6':
-
			val += 0x6;
-
			break;
-
		case '7':
-
			val += 0x7;
-
			break;
-
		case '8':
-
			val += 0x8;
-
			break;
-
		case '9':
-
			val += 0x9;
-
			break;
-
		case 'a':
-
		case 'A':
-
			val += 0xa;
-
			break;
-
		case 'b':
-
		case 'B':
-
			val += 0xb;
-
			break;
-
		case 'c':
-
		case 'C':
-
			val += 0xc;
-
			break;
-
		case 'd':
-
		case 'D':
-
			val += 0xd;
-
			break;
-
		case 'e':
-
		case 'E':
-
			val += 0xe;
-
			break;
-
		case 'f':
-
		case 'F':
-
			val += 0xf;
-
			break;
-
		}
+
		set_list_defaults(p, "%Cn", ", ");

-
		sbuf_putc(sbuf, val);
-
	} else {
-
		/* Pass through unchanged if it's not a recognizable
-
		   hex byte. */
-
		sbuf_putc(sbuf, '\\');
-
		sbuf_putc(sbuf, 'x');
+
		count = 1;
+
		while (pkg_categories(pkg, &cat) == EPKG_OK) {
+
			if (count > 1)
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     cat, count, PP_C);
+

+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt), 
+
				     cat, count, PP_C);
+
			count++;
+
		}
	}
-
	return (f);
+
	return (sbuf);
}

-
static inline const char*
-
read_oct_byte(struct sbuf *sbuf, const char *f)
+
/*
+
 * %Cn -- Category name.
+
 */
+
static struct sbuf *
+
format_category_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	int	val = 0;
+
	const struct pkg_category	*cat = data;

-
	/* Octal escapes are upto three octal digits: \N, \NN or \NNN
-
	   up to a max of \377.  Note: this treats \400 as \40
-
	   followed by digit 0 passed through unchanged. */
+
	return (string_val(sbuf, pkg_category_name(cat), p));
+
}

-
	while (val < 32) {
-
		switch (*f) {
-
		case '0':
-
			val = val * 8 + 0;
-
			break;
-
		case '1':
-
			val = val * 8 + 1;
-
			break;
-
		case '2':
-
			val = val * 8 + 2;
-
			break;
-
		case '3':
-
			val = val * 8 + 3;
-
			break;
-
		case '4':
-
			val = val * 8 + 4;
-
			break;
-
		case '5':
-
			val = val * 8 + 5;
-
			break;
-
		case '6':
-
			val = val * 8 + 6;
-
			break;
-
		case '7':
-
			val = val * 8 + 7;
-
			break;
-
		default:	/* Non-octal digit */
-
			goto done;
-
		}
+
/*
+
 * %D -- Directories.  List of directory names (strings) possibly with
+
 * other meta-data.  Optionally accepts following per-field format in
+
 * %{ %| %}, where %Dn is replaced by the directory name.  Default
+
 * %{%Dn\n%|%}
+
 */
+
static struct sbuf *
+
format_directories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg	*pkg = data;

-
		f++;
-
	} 
-
done:
-
	f--;	/* point at the last octal digit */
-
	sbuf_putc(sbuf, val);
+
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_DIRS), p));
+
	else {
+
		struct pkg_dir	*dir;
+
		int		 count;

-
	return (f);
-
}
+
		set_list_defaults(p, "%Dn\n", "");

-
static inline const char *
-
process_escape(struct sbuf *sbuf, const char *f)
-
{
-
	f++;			/* Eat the \ */
+
		count = 1;
+
		while (pkg_dirs(pkg, &dir) == EPKG_OK) {
+
			if (count > 1)
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt), 
+
					     dir, count, PP_D);

-
	switch (*f) {
-
	case 'a':
-
		sbuf_putc(sbuf, '\a');
-
		break;
-
	case 'b':
-
		sbuf_putc(sbuf, '\b');
-
		break;
-
	case 'f':
-
		sbuf_putc(sbuf, '\f');
-
		break;
-
	case 'n':
-
		sbuf_putc(sbuf, '\n');
-
		break;
-
	case 't':
-
		sbuf_putc(sbuf, '\t');
-
		break;
-
	case 'v':
-
		sbuf_putc(sbuf, '\v');
-
		break;
-
	case '\'':
-
		sbuf_putc(sbuf, '\'');
-
		break;
-
	case '"':
-
		sbuf_putc(sbuf, '"');
-
		break;
-
	case '\\':
-
		sbuf_putc(sbuf, '\\');
-
		break;
-
	case 'x':		/* Hex escape: \xNN */
-
		f++;
-
		f = maybe_read_hex_byte(sbuf, f);
-
		break;
-
	case '0':
-
	case '1':
-
	case '2':
-
	case '3':
-
	case '4':
-
	case '5':
-
	case '6':
-
	case '7':		/* all fall through */
-
		f = read_oct_byte(sbuf, f);
-
		break;
-
	default:		/* If it's not a recognised escape,
-
				   pass it through unchanged */
-
		sbuf_putc(sbuf, '\\');
-
		sbuf_putc(sbuf, *f);
-
		break;
+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     dir, count, PP_D);
+
			count++;
+
		}
	}
-

-
	return (f);
+
	return (sbuf);
}

-
static char *
-
gen_format(char *buf, size_t buflen, unsigned flags, const char *tail)
+
/*
+
 * %Dg -- Directory group. TODO: numeric gid
+
 */
+
static struct sbuf *
+
format_directory_group(struct sbuf *sbuf, const void *data,
+
		       struct percent_esc *p)
{
-
	int	bp = 0;
-
	size_t	tlen;
-

-
	/* We need the length of tail plus at least 3 characters '%'
-
	   '*' '\0' but maybe as many as 7 '%' '#' '-' '+' '\'' '*'
-
	   '\0' */
-

-
	tlen = strlen(tail);
-

-
	if (buflen - bp < tlen + 3)
-
		return (NULL);
-

-
	buf[bp++] = '%';
-

-
	/* PP_ALTERNATE_FORM1 is not used by regular printf(3) */
-

-
	if (flags & PP_ALTERNATE_FORM2)
-
		buf[bp++] = '#';
+
	const struct pkg_dir	*dir = data;

-
	if (flags & PP_LEFT_ALIGN)
-
		buf[bp++] = '-';
-
	else if (flags & PP_ZERO_PAD)
-
		buf[bp++] = '0';
+
	return (string_val(sbuf, pkg_dir_gname(dir), p));
+
}

-
	if (buflen - bp < tlen + 2)
-
		return (NULL);
-
	
-
	if (flags & PP_EXPLICIT_PLUS)
-
		buf[bp++] = '+';
-
	else if (flags & PP_SPACE_FOR_PLUS)
-
		buf[bp++] = ' ';
+
/*
+
 * %Dk -- Directory Keep flag.
+
 */
+
static struct sbuf *
+
format_directory_keepflag(struct sbuf *sbuf, const void *data,
+
		       struct percent_esc *p)
+
{
+
	const struct pkg_dir	*dir = data;
+
	return (bool_val(sbuf, pkg_dir_keep(dir), p));
+
}

-
	if (flags & PP_THOUSANDS_SEP)
-
		buf[bp++] = '\'';
+
/*
+
 * %Dn -- Directory path name.
+
 */
+
static struct sbuf *
+
format_directory_path(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg_dir	*dir = data;

-
	if (buflen - bp < tlen + 2)
-
		return (NULL);
+
	return (string_val(sbuf, pkg_dir_path(dir), p));
+
}

-
	/* The effect of 0 meaning 'zero fill' is indisinguishable
-
	   from 0 meaning 'a field width of zero' */
+
/*
+
 * %Dp -- Directory permissions.
+
 */
+
static struct sbuf *
+
format_directory_perms(struct sbuf *sbuf, const void *data,
+
		       struct percent_esc *p)
+
{
+
	const struct pkg_dir	*dir = data;

-
	buf[bp++] = '*';
-
	buf[bp] = '\0';
+
	return (mode_val(sbuf, pkg_dir_mode(dir), p));
+
}

-
	strlcat(buf, tail, sizeof(buf));
+
/*
+
 * %Dt -- Directory Try flag.
+
 */
+
static struct sbuf *
+
format_directory_tryflag(struct sbuf *sbuf, const void *data,
+
			 struct percent_esc *p)
+
{
+
	const struct pkg_dir	*dir = data;

-
	return (buf);
+
	return (bool_val(sbuf, pkg_dir_try(dir), p));
}

-

+
/*
+
 * %Du -- Directory user. TODO: numeric UID
+
 */
static struct sbuf *
-
human_number(struct sbuf *sbuf, int64_t number, struct percent_esc *p)
+
format_directory_user(struct sbuf *sbuf, const void *data,
+
		      struct percent_esc *p)
{
-
	double		 num;
-
	int		 divisor;
-
	int		 scale;
-
	bool		 bin_scale;
+
	const struct pkg_dir	*dir = data;

-
#define MAXSCALE	7
+
	return (string_val(sbuf, pkg_dir_uname(dir), p));
+
}

-
	const char	 bin_pfx[MAXSCALE][3] =
-
		{ "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; 
-
	const char	 si_pfx[MAXSCALE][2] =
-
		{ "", "k", "M", "G", "T", "P", "E" };
-
	char		 format[16];
+
/*
+
 * %F -- Files.  List of filenames (strings) possibly with other
+
 * meta-data.  Optionally accepts following per-field format in %{ %|
+
 * %}, where %n is replaced by the filename, %s by the checksum, etc.
+
 * Default %{%Fn\n%|%}
+
 */
+
static struct sbuf *
+
format_files(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg	*pkg = data;

-
	bin_scale = ((p->flags & PP_ALTERNATE_FORM2) != 0);
+
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_FILES), p));
+
	else {
+
		struct pkg_file	*file;
+
		int		 count;

-
	p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
+
		set_list_defaults(p, "%Fn\n", "");

-
	num = number;
-
	divisor = bin_scale ? 1024 : 1000;
+
		count = 1;
+
		while (pkg_files(pkg, &file) == EPKG_OK) {
+
			if (count > 1)
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     file, count, PP_F);

-
	for (scale = 0; scale < MAXSCALE; scale++) {
-
		if (num <= divisor)
-
			break;
-
		num /= divisor;
+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     file, count, PP_F);
+
			count++;
+
		}
	}
-

-
	if (gen_format(format, sizeof(format), p->flags, ".3f %s") == NULL)
-
		return (NULL);
-

-
	sbuf_printf(sbuf, format, p->width, num,
-
		    bin_scale ? bin_pfx[scale] : si_pfx[scale]);
-

	return (sbuf);
}

+
/*
+
 * %Fg -- File group.
+
 */
static struct sbuf *
-
string_val(struct sbuf *sbuf, const char *str, struct percent_esc *p)
+
format_file_group(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	char	format[16];
-

-
	/* The '#' '?' '+' ' ' and '\'' modifiers have no meaning for
-
	   strings */
+
	const struct pkg_file	*file = data;

-
	p->flags &= ~(PP_ALTERNATE_FORM1 |
-
		      PP_ALTERNATE_FORM2 |
-
		      PP_EXPLICIT_PLUS   |
-
		      PP_SPACE_FOR_PLUS  |
-
		      PP_THOUSANDS_SEP);
-

-
	if (gen_format(format, sizeof(format), p->flags, "s") == NULL)
-
		return (NULL);
-

-
	sbuf_printf(sbuf, format, p->width, str);
-
	return (sbuf);
-
}
+
	return (string_val(sbuf, pkg_file_gname(file), p));
+
}

+
/*
+
 * %Fk -- File Keep flag.
+
 */
static struct sbuf *
-
int_val(struct sbuf *sbuf, int64_t value, struct percent_esc *p)
+
format_file_keepflag(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (human_number(sbuf, value, p));
-
	else {
-
		char	 format[16];
-

-
		if (gen_format(format, sizeof(format), p->flags, PRId64)
-
		    == NULL)
-
			return (NULL);
+
	const struct pkg_file	*file = data;

-
		sbuf_printf(sbuf, format, p->width, value);
-
	}
-
	return (sbuf);
+
	return (bool_val(sbuf, pkg_file_keep(file), p));
}

+
/*
+
 * %Fn -- File path name.
+
 */
static struct sbuf *
-
bool_val(struct sbuf *sbuf, bool value, struct percent_esc *p)
+
format_file_path(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	int	alternate;
-

-
	if (p->flags & PP_ALTERNATE_FORM2)
-
		alternate = 2;
-
	else if (p->flags & PP_ALTERNATE_FORM1)
-
		alternate = 1;
-
	else
-
		alternate = 0;
-

-
	p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
+
	const struct pkg_file	*file = data;

-
	return (string_val(sbuf, boolean_str[value][alternate], p));
+
	return (string_val(sbuf, pkg_file_path(file), p));
}

+
/*
+
 * %Fp -- File permissions.
+
 */
static struct sbuf *
-
mode_val(struct sbuf *sbuf, mode_t mode, struct percent_esc *p)
+
format_file_perms(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	/* Print mode as an octal integer '%o' by default.
-
	 * PP_ALTERNATE_FORM2 generates '%#o' pased to regular
-
	 * printf(). PP_ALTERNATE_FORM1 will generate drwxr-x--- style
-
	 * from strmode(3).  */
-

-
	/* Does the mode include the bits that indicate the inode type? */
-

-
	if (p->flags & PP_ALTERNATE_FORM1) {
-
		char	modebuf[12];
-

-
		strmode(mode, modebuf);
-

-
		return (string_val(sbuf, modebuf, p));
-
	} else {
-
		char	format[16];
-

-
		p->flags &= ~(PP_ALTERNATE_FORM1);
-

-
		if (gen_format(format, sizeof(format), p->flags, PRIo16)
-
		    == NULL)
-
			return (NULL);
+
	const struct pkg_file	*file = data;

-
		sbuf_printf(sbuf, format, p->width, mode);
-
	}
-
	return (sbuf);
+
	return (mode_val(sbuf, pkg_file_mode(file), p));
}

+
/*
+
 * %Fs -- File SHA256 Checksum.
+
 */
static struct sbuf *
-
list_count(struct sbuf *sbuf, int64_t count, struct percent_esc *p)
+
format_file_sha256(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	/* Convert to 0 or 1 for %?X */
-
	if (p->flags & PP_ALTERNATE_FORM1)
-
		count = (count > 0);
-

-
	/* Turn off %#X and %?X flags, then print as a normal integer */
-
	p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
-

-
	return (int_val(sbuf, count, p));
-
}
+
	const struct pkg_file	*file = data;

-
static struct percent_esc *
-
set_list_defaults(struct percent_esc *p, const char *item_fmt,
-
		  const char *sep_fmt)
-
{
-
	if (sbuf_len(p->item_fmt) == 0) {
-
		sbuf_cat(p->item_fmt, item_fmt);
-
		sbuf_finish(p->item_fmt);
-
	}
-
	if (sbuf_len(p->sep_fmt) == 0) {
-
		sbuf_cat(p->sep_fmt, sep_fmt);
-
		sbuf_finish(p->sep_fmt);
-
	}
-
	return (p);
+
	return (string_val(sbuf, pkg_file_cksum(file), p));
}

+
/*
+
 * %Fu -- File user.
+
 */
static struct sbuf *
-
iterate_item(struct sbuf *sbuf, const struct pkg *pkg, const void *data,
-
	     int count, const char *format, unsigned context)
+
format_file_user(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const char	*f;
+
	const struct pkg_file	*file = data;

-
	/* Scan the format string and interpret any escapes */
-
	for (f = format; f != '\0'; f++) {
-
		switch(*f) {
-
		case '%':
-
			/* @@@@@@@@@@@@ */
-
			break;
-
		case '\\':
-
			f = process_escape(sbuf, f);
-
			break;
-
		default:
-
			sbuf_putc(sbuf, *f);
-
			break;
-
		}
-
		if (f == NULL) {
-
			sbuf_clear(sbuf);
-
			break;	/* Out of memory */
-
		}
-
	}
-
	return (sbuf);
+
	return (string_val(sbuf, pkg_file_uname(file), p));
}

/*
-
 * Note: List values -- special behaviour with ? and # modifiers.
-
 * Affects %B %C %D %F %G %L %O %U %d %r
-
 *
-
 * With ? -- Flag values.  Boolean.  %?X returns 0 if the %X list is
-
 * empty, 1 otherwise.
-
 *
-
 * With # -- Count values.  Integer.  %#X returns the number of items in
-
 * the %X list.
-
 */
-

-
/*
-
 * %B -- Shared Libraries.  List of shlibs required by binaries in the
-
 * pkg.  Optionall accepts per-field format in %{ %| %}, where %n is
-
 * replaced by the shlib name.  Default %{%Bn\n%|%}
+
 * %G -- Groups. list of string values.  Optionally accepts following
+
 * per-field format in %{ %| %} where %Gn will be replaced by each
+
 * groupname or %#Gn by the gid or %Gg by the "gidstr" -- a line from
+
 * /etc/group. Default %{%Gn\n%|%}
 */
static struct sbuf *
-
format_shlibs(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_groups(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_SHLIBS), p));
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_GROUPS), p));
	else {
-
		struct pkg_shlib	*shlib;
+
		struct pkg_group	*group;
		int			 count;

-
		set_list_defaults(p, "%Bn\n", "");
+
		set_list_defaults(p, "%Gn\n", "");

		count = 1;
-
		while (pkg_shlibs(pkg, &shlib) == EPKG_OK) {
+
		while(pkg_groups(pkg, &group) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(sbuf, pkg, shlib, count,
-
					     sbuf_data(p->sep_fmt), PP_B);
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     group, count, PP_G);

-
			iterate_item(sbuf, pkg, shlib, count,
-
				     sbuf_data(p->item_fmt), PP_B);
+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     group, count, PP_G);
			count++;
		}
	}
@@ -984,44 +810,68 @@ format_shlibs(struct sbuf *sbuf, const void *data, struct percent_esc *p)
}

/*
-
 * %Bn -- Shared Library name.
+
 * %Gg -- Group 'gidstr' (one line from /etc/group).
 */
static struct sbuf *
-
format_shlib_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_group_gidstr(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_shlib	*shlib = data;
+
	const struct pkg_group	*group = data;

-
	return (string_val(sbuf, pkg_shlib_name(shlib), p));
+
	return (string_val(sbuf, pkg_group_gidstr(group), p));
}

/*
-
 * %C -- Categories.  List of Category names (strings). 1ary category
-
 * is not distinguished -- look at the package origin for that.
-
 * Optionally accepts per-field format in %{ %| %}, where %n is
-
 * replaced by the category name.  Default %{%Cn%|, %}
+
 * %Gn -- Group name.
 */
static struct sbuf *
-
format_categories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_group_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg_group	*group = data;
+

+
	return (string_val(sbuf, pkg_group_name(group), p));
+
}
+

+
/*
+
 * %I -- Row counter (integer*). Usually used only in per-field format.
+
 */
+
static struct sbuf *
+
format_row_counter(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const int *counter = data;
+

+
	return (int_val(sbuf, *counter, p));
+
}
+

+
/*
+
 * %L -- Licences. List of string values.  Optionally accepts
+
 * following per-field format in %{ %| %} where %Ln is replaced by the
+
 * license name and %l by the license logic.  Default %{%n%| %l %}
+
 */
+
static struct sbuf *
+
format_licenses(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_CATEGORIES),
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_LICENSES),
				   p));
	else {
-
		struct pkg_category	*cat;
+
		struct pkg_license	*lic;
		int			 count;
+
		lic_t			 license_logic;

-
		set_list_defaults(p, "%Cn", ", ");
+

+
		set_list_defaults(p, "%Ln", " %l ");
+
		pkg_get(pkg, PKG_LICENSE_LOGIC, &license_logic);

		count = 1;
-
		while (pkg_categories(pkg, &cat) == EPKG_OK) {
+
		while (pkg_licenses(pkg, &lic) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(sbuf, pkg, cat, count,
-
					     sbuf_data(p->sep_fmt), PP_C);
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     lic, count, PP_L);

-
			iterate_item(sbuf, pkg, cat, count,
-
				     sbuf_data(p->item_fmt), PP_C);
+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     lic, count, PP_L);
			count++;
		}
	}
@@ -1029,43 +879,55 @@ format_categories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
}

/*
-
 * %Cn -- Category name.
+
 * %Ln -- License name.
 */
static struct sbuf *
-
format_category_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_license_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_category	*cat = data;
+
	const struct pkg_license	*license = data;

-
	return (string_val(sbuf, pkg_category_name(cat), p));
+
	return (string_val(sbuf, pkg_license_name(license), p));
}

/*
-
 * %D -- Directories.  List of directory names (strings) possibly with
-
 * other meta-data.  Optionally accepts following per-field format in
-
 * %{ %| %}, where %Dn is replaced by the directory name.  Default
-
 * %{%Dn\n%|%}
+
 * %M -- Pkg message. string.  Accepts field-width, left-align
 */
static struct sbuf *
-
format_directories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_message(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg	*pkg = data;
+
	const char		*message;
+

+
	pkg_get(pkg, PKG_MESSAGE, &message);
+
	return (string_val(sbuf, message, p));
+
}
+

+
/*
+
 * %O -- Options. list of {option,value} tuples. Optionally accepts
+
 * following per-field format in %{ %| %}, where %On is replaced by the
+
 * option name and %Ov by the value.  Default %{%On %Ov\n%|%}
+
 */ 
+
static struct sbuf *
+
format_options(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_DIRS), p));
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_OPTIONS), p));
	else {
-
		struct pkg_dir	*dir;
-
		int		 count;
+
		struct pkg_option	*opt;
+
		int			 count;

-
		set_list_defaults(p, "%Dn\n", "");
+
		set_list_defaults(p, "%On %Ov\n", "");

		count = 1;
-
		while (pkg_dirs(pkg, &dir) == EPKG_OK) {
+
		while (pkg_options(pkg, &opt) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(sbuf, pkg, dir, count,
-
					     sbuf_data(p->sep_fmt), PP_D);
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     opt, count, PP_O);

-
			iterate_item(sbuf, pkg, dir, count,
-
				     sbuf_data(p->item_fmt), PP_D);
+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     opt, count, PP_O);
			count++;
		}
	}
@@ -1073,102 +935,136 @@ format_directories(struct sbuf *sbuf, const void *data, struct percent_esc *p)
}

/*
-
 * %Dg -- Directory group. TODO: numeric gid
+
 * %On -- Option name.
 */
static struct sbuf *
-
format_directory_group(struct sbuf *sbuf, const void *data,
-
		       struct percent_esc *p)
+
format_option_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_dir	*dir = data;
+
	const struct pkg_option	*option = data;

-
	return (string_val(sbuf, pkg_dir_gname(dir), p));
+
	return (string_val(sbuf, pkg_option_opt(option), p));
}

/*
-
 * %Dk -- Directory Keep flag.
+
 * %Ov -- Option value.
 */
static struct sbuf *
-
format_directory_keepflag(struct sbuf *sbuf, const void *data,
-
		       struct percent_esc *p)
+
format_option_value(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_dir	*dir = data;
-
	return (bool_val(sbuf, pkg_dir_keep(dir), p));
+
	const struct pkg_option	*option = data;
+

+
	return (string_val(sbuf, pkg_option_value(option), p));
}

/*
-
 * %Dn -- Directory path name.
+
 * %U -- Users. list of string values.  Optionally accepts following
+
 * per-field format in %{ %| %} where %Un will be replaced by each
+
 * username or %#Un by the uid or %Uu by the uidstr -- a line from
+
 * /etc/passwd. Default %{%Un\n%|%}
 */
static struct sbuf *
-
format_directory_path(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_users(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_dir	*dir = data;
+
	const struct pkg	*pkg = data;

-
	return (string_val(sbuf, pkg_dir_path(dir), p));
+
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_USERS), p));
+
	else {
+
		struct pkg_user	*user;
+
		int		 count;
+

+
		set_list_defaults(p, "%Un\n", "");
+

+
		count = 1;
+
		while (pkg_users(pkg, &user) == EPKG_OK) {
+
			if (count > 1)
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     user, count, PP_U);
+

+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     user, count, PP_U);
+
			count++;
+
		}
+
	}
+
	return (sbuf);
}

/*
-
 * %Dp -- Directory permissions.
+
 * %Un -- User name.
 */
static struct sbuf *
-
format_directory_perms(struct sbuf *sbuf, const void *data,
-
		       struct percent_esc *p)
+
format_user_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_dir	*dir = data;
+
	const struct pkg_user	*user = data;

-
	return (mode_val(sbuf, pkg_dir_mode(dir), p));
+
	return (string_val(sbuf, pkg_user_name(user), p));
}

/*
-
 * %Dt -- Directory Try flag.
+
 * %Uu -- User uidstr (one line from /etc/passwd).
 */
static struct sbuf *
-
format_directory_tryflag(struct sbuf *sbuf, const void *data,
-
			 struct percent_esc *p)
+
format_user_uidstr(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_dir	*dir = data;
+
	const struct pkg_user	*user = data;

-
	return (bool_val(sbuf, pkg_dir_try(dir), p));
+
	return (string_val(sbuf, pkg_user_uidstr(user), p));
}

/*
-
 * %Du -- Directory user. TODO: numeric UID
+
 * %a -- Autoremove flag. boolean.  Accepts field-width, left-align.
+
 * Standard form: 0, 1.  Alternate form1: no, yes.  Alternate form2:
+
 * false, true
 */
static struct sbuf *
-
format_directory_user(struct sbuf *sbuf, const void *data,
-
		      struct percent_esc *p)
+
format_autoremove(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_dir	*dir = data;
+
	const struct pkg	*pkg = data;
+
	bool			 automatic;

-
	return (string_val(sbuf, pkg_dir_uname(dir), p));
+
	pkg_get(pkg, PKG_AUTOMATIC, &automatic);
+
	return (bool_val(sbuf, automatic, p));
}

/*
-
 * %F -- Files.  List of filenames (strings) possibly with other
-
 * meta-data.  Optionally accepts following per-field format in %{ %|
-
 * %}, where %n is replaced by the filename, %s by the checksum, etc.
-
 * Default %{%Fn\n%|%}
+
 * %c -- Comment. string.  Accepts field-width, left-align
 */
static struct sbuf *
-
format_files(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_comment(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
{
+
	const struct pkg	*pkg = data;
+
	const char		*comment;
+

+
	pkg_get(pkg, PKG_COMMENT, &comment);
+
	return (string_val(sbuf, comment, p));
+
}
+

+
/*
+
 * %d -- Dependencies. List of pkgs. Can be optionally followed by
+
 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
+
 * formats. Defaults to printing "%dn-%dv\n" for each dependency.
+
 */
+
static struct sbuf *
+
format_dependencies(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_FILES), p));
+
		return (list_count(sbuf, pkg_list_count(pkg, PKG_DEPS), p));
	else {
-
		struct pkg_file	*file;
+
		struct pkg_dep	*dep;
		int		 count;

-
		set_list_defaults(p, "%Fn\n", "");
+
		set_list_defaults(p, "%dn-%dv\n", "");

		count = 1;
-
		while (pkg_files(pkg, &file) == EPKG_OK) {
+
		while (pkg_deps(pkg, &dep) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(sbuf, pkg, file, count,
-
					     sbuf_data(p->sep_fmt), PP_F);
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     dep, count, PP_d);

-
			iterate_item(sbuf, pkg, file, count,
-
				     sbuf_data(p->item_fmt), PP_F);
+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     dep, count, PP_d);
			count++;
		}
	}
@@ -1176,167 +1072,186 @@ format_files(struct sbuf *sbuf, const void *data, struct percent_esc *p)
}

/*
-
 * %Fg -- File group.
+
 * %dn -- Dependency name or %rn -- Requirement name.
 */
static struct sbuf *
-
format_file_group(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_dependency_name(struct sbuf *sbuf, const void *data,
+
		       struct percent_esc *p)
{
-
	const struct pkg_file	*file = data;
+
	const struct pkg_dep	*dep = data;

-
	return (string_val(sbuf, pkg_file_gname(file), p));
+
	return (string_val(sbuf, pkg_dep_name(dep), p));
}

/*
-
 * %Fk -- File Keep flag.
+
 * %do -- Dependency origin or %ro -- Requirement origin.
 */
static struct sbuf *
-
format_file_keepflag(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_dependency_origin(struct sbuf *sbuf, const void *data,
+
			 struct percent_esc *p)
{
-
	const struct pkg_file	*file = data;
+
	const struct pkg_dep	*dep = data;

-
	return (bool_val(sbuf, pkg_file_keep(file), p));
+
	return (string_val(sbuf, pkg_dep_origin(dep), p));
}

/*
-
 * %Fn -- File path name.
+
 * %dv -- Dependency version or %rv -- Requirement version.
 */
static struct sbuf *
-
format_file_path(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_dependency_version(struct sbuf *sbuf, const void *data,
+
			  struct percent_esc *p)
{
-
	const struct pkg_file	*file = data;
+
	const struct pkg_dep	*dep = data;

-
	return (string_val(sbuf, pkg_file_path(file), p));
+
	return (string_val(sbuf, pkg_dep_version(dep), p));
}

/*
-
 * %Fp -- File permissions.
+
 * %i -- Additional info. string. Accepts field-width, left-align
 */
static struct sbuf *
-
format_file_perms(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_add_info(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_file	*file = data;
+
	const struct pkg	*pkg = data;
+
	const char		*info;

-
	return (mode_val(sbuf, pkg_file_mode(file), p));
+
	pkg_get(pkg, PKG_INFOS, &info);
+
	return (string_val(sbuf, info, p));
}

/*
-
 * %Fs -- File SHA256 Checksum.
+
 * %k -- Locked flag. boolean.  Accepts field-width, left-align.
+
 * Standard form: 0, 1.  Alternate form1: no, yes.  Alternate form2:
+
 * false, true
 */
static struct sbuf *
-
format_file_sha256(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_lock_status(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_file	*file = data;
+
	const struct pkg	*pkg = data;
+
	bool			 locked;

-
	return (string_val(sbuf, pkg_file_cksum(file), p));
+
	pkg_get(pkg, PKG_LOCKED, &locked);
+
	return (bool_val(sbuf, locked, p));
}

/*
-
 * %Fu -- File user.
+
 * %l -- Licence logic. string.  Accepts field-width, left-align.
+
 * Standard form: and, or, single. Alternate form 1: &, |, ''.
+
 * Alternate form 2: &&, ||, ==
 */
static struct sbuf *
-
format_file_user(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_license_logic(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_file	*file = data;
+
	const struct pkg	*pkg = data;
+
	lic_t			 licenselogic;
+
	int			 alternate;
+
	int			 llogic;

-
	return (string_val(sbuf, pkg_file_uname(file), p));
+
	pkg_get(pkg, PKG_LICENSE_LOGIC, &licenselogic);
+

+
	switch (licenselogic) {
+
	case LICENSE_SINGLE:
+
		llogic = PP_LIC_SINGLE;
+
		break;
+
	case LICENSE_OR:
+
		llogic = PP_LIC_OR;
+
		break;
+
	case LICENSE_AND:
+
		llogic = PP_LIC_AND;
+
		break;
+
	}
+

+
	if (p->flags & PP_ALTERNATE_FORM2)
+
		alternate = 2;
+
	else if (p->flags & PP_ALTERNATE_FORM1)
+
		alternate = 1;
+
	else
+
		alternate = 0;
+

+
	p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
+

+
	return (string_val(sbuf, liclog_str[llogic][alternate], p));
}

/*
-
 * %G -- Groups. list of string values.  Optionally accepts following
-
 * per-field format in %{ %| %} where %Gn will be replaced by each
-
 * groupname or %#Gn by the gid or %Gg by the "gidstr" -- a line from
-
 * /etc/group. Default %{%Gn\n%|%}
+
 * %m -- Maintainer e-mail address. string.  Accepts field-width, left-align
 */
static struct sbuf *
-
format_groups(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_maintainer(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;
+
	const char		*maintainer;

-
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_GROUPS), p));
-
	else {
-
		struct pkg_group	*group;
-
		int			 count;
-

-
		set_list_defaults(p, "%Gn\n", "");
-

-
		count = 1;
-
		while(pkg_groups(pkg, &group) == EPKG_OK) {
-
			if (count > 1)
-
				iterate_item(sbuf, pkg, group, count,
-
					     sbuf_data(p->sep_fmt), PP_G);
-

-
			iterate_item(sbuf, pkg, group, count,
-
				     sbuf_data(p->item_fmt), PP_G);
-
			count++;
-
		}
-
	}
-
	return (sbuf);
+
	pkg_get(pkg, PKG_MAINTAINER, &maintainer);
+
	return (string_val(sbuf, maintainer, p));
}

/*
-
 * %Gg -- Group 'gidstr' (one line from /etc/group).
+
 * %n -- Package name. string.  Accepts field-width, left-align
 */
static struct sbuf *
-
format_group_gidstr(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_group	*group = data;
+
	const struct pkg	*pkg = data;
+
	const char		*name;

-
	return (string_val(sbuf, pkg_group_gidstr(group), p));
+
	pkg_get(pkg, PKG_NAME, &name);
+
	return (string_val(sbuf, name, p));
}

/*
-
 * %Gn -- Group name.
+
 * %o -- Package origin. string.  Accepts field-width, left-align
 */
static struct sbuf *
-
format_group_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_origin(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_group	*group = data;
+
	const struct pkg	*pkg = data;
+
	const char		*origin;

-
	return (string_val(sbuf, pkg_group_name(group), p));
+
	pkg_get(pkg, PKG_ORIGIN, &origin);
+
	return (string_val(sbuf, origin, p));
}

/*
-
 * %I -- Row counter (integer*). Usually used only in per-field format.
+
 * %p -- Installation prefix. string. Accepts field-width, left-align
 */
static struct sbuf *
-
format_row_counter(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_prefix(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const int *counter = data;
+
	const struct pkg	*pkg = data;
+
	const char		*prefix;

-
	return (int_val(sbuf, *counter, p));
+
	pkg_get(pkg, PKG_PREFIX, &prefix);
+
	return (string_val(sbuf, prefix, p));
}

/*
-
 * %L -- Licences. List of string values.  Optionally accepts
-
 * following per-field format in %{ %| %} where %Ln is replaced by the
-
 * license name and %l by the license logic.  Default %{%n%| %l %}
+
 * %r -- Requirements. List of pkgs. Can be optionally followed by
+
 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
+
 * formats. Defaults to printing "%{%rn-%rv\n%|%}" for each dependency.
 */
static struct sbuf *
-
format_licenses(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_requirements(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;

	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_LICENSES),
-
				   p));
+
		return(list_count(sbuf, pkg_list_count(pkg, PKG_RDEPS), p));
	else {
-
		struct pkg_license	*lic;
-
		int			 count;
-
		lic_t			 license_logic;
-

+
		struct pkg_dep	*req;
+
		int		 count;

-
		set_list_defaults(p, "%Ln", " %l ");
-
		pkg_get(pkg, PKG_LICENSE_LOGIC, &license_logic);
+
		set_list_defaults(p, "%rn-%rv\n", "");

		count = 1;
-
		while (pkg_licenses(pkg, &lic) == EPKG_OK) {
+
		while (pkg_rdeps(pkg, &req) == EPKG_OK) {
			if (count > 1)
-
				iterate_item(sbuf, pkg, lic, count,
-
					     sbuf_data(p->sep_fmt), PP_L);
+
				iterate_item(sbuf, pkg, sbuf_data(p->sep_fmt),
+
					     req, count, PP_r);

-
			iterate_item(sbuf, pkg, lic, count,
-
				     sbuf_data(p->item_fmt), PP_L);
+
			iterate_item(sbuf, pkg, sbuf_data(p->item_fmt),
+
				     req, count, PP_r);
			count++;
		}
	}
@@ -1344,472 +1259,370 @@ format_licenses(struct sbuf *sbuf, const void *data, struct percent_esc *p)
}

/*
-
 * %Ln -- License name.
+
 * %s -- Size of installed package. integer.  Accepts field-width,
+
 * left-align, zero-fill, space-for-plus, explicit-plus and
+
 * alternate-form.  Alternate form is a humanized number using decimal
+
 * exponents (k, M, G).  Alternate form 2, ditto, but using binary
+
 * scale prefixes (ki, Mi, Gi etc.)
 */
static struct sbuf *
-
format_license_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_flatsize(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_license	*license = data;
+
	const struct pkg	*pkg = data;
+
	int64_t			 flatsize;

-
	return (string_val(sbuf, pkg_license_name(license), p));
+
	pkg_get(pkg, PKG_FLATSIZE, &flatsize);
+
	return (int_val(sbuf, flatsize, p));
}

/*
-
 * %M -- Pkg message. string.  Accepts field-width, left-align
+
 * %t -- Installation timestamp (Unix time). integer.  Accepts
+
 * field-width, left-align.  Can be followed by optional strftime
+
 * format string in %{ %}.  Default is to print seconds-since-epoch as
+
 * an integer applying our integer format modifiers.
 */
static struct sbuf *
-
format_message(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_install_tstamp(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
	const struct pkg	*pkg = data;
-
	const char		*message;
-

-
	pkg_get(pkg, PKG_MESSAGE, &message);
-
	return (string_val(sbuf, message, p));
-
}
+
	int64_t			 timestamp;

-
/*
-
 * %O -- Options. list of {option,value} tuples. Optionally accepts
-
 * following per-field format in %{ %| %}, where %On is replaced by the
-
 * option name and %Ov by the value.  Default %{%On %Ov\n%|%}
-
 */ 
-
static struct sbuf *
-
format_options(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg	*pkg = data;
+
	pkg_get(pkg, PKG_TIME, &timestamp);

-
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_OPTIONS), p));
+
	if (sbuf_len(p->item_fmt) == 0)
+
		return (int_val(sbuf, timestamp, p));
	else {
-
		struct pkg_option	*opt;
-
		int			 count;
-

-
		set_list_defaults(p, "%On %Ov\n", "");
-

-
		count = 1;
-
		while (pkg_options(pkg, &opt) == EPKG_OK) {
-
			if (count > 1)
-
				iterate_item(sbuf, pkg, opt, count,
-
					     sbuf_data(p->sep_fmt), PP_O);
+
		char	 buf[1024];

-
			iterate_item(sbuf, pkg, opt, count,
-
				     sbuf_data(p->item_fmt), PP_O);
-
			count++;
-
		}
+
		strftime(buf, sizeof(buf), sbuf_data(p->item_fmt),
+
			 localtime(&timestamp));
+
		sbuf_cat(sbuf, buf); 
	}
	return (sbuf);
}

/*
-
 * %On -- Option name.
+
 * %v -- Package version. string. Accepts field width, left align
 */
static struct sbuf *
-
format_option_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_version(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_option	*option = data;
+
	const struct pkg	*pkg = data;
+
	const char		*version;

-
	return (string_val(sbuf, pkg_option_opt(option), p));
+
	pkg_get(pkg, PKG_VERSION, &version);
+
	return (string_val(sbuf, version, p));
}

/*
-
 * %Ov -- Option value.
+
 * %w -- Home page URL.  string.  Accepts field width, left align
 */
static struct sbuf *
-
format_option_value(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_home_url(struct sbuf *sbuf, const void *data, struct percent_esc *p)
{
-
	const struct pkg_option	*option = data;
+
	const struct pkg	*pkg = data;
+
	const char		*url;

-
	return (string_val(sbuf, pkg_option_value(option), p));
+
	pkg_get(pkg, PKG_WWW, &url);
+
	return (string_val(sbuf, url, p));
}

/*
-
 * %U -- Users. list of string values.  Optionally accepts following
-
 * per-field format in %{ %| %} where %Un will be replaced by each
-
 * username or %#Un by the uid or %Uu by the uidstr -- a line from
-
 * /etc/passwd. Default %{%Un\n%|%}
+
 * %% -- Output a literal '%' character
 */
static struct sbuf *
-
format_users(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_literal_percent(struct sbuf *sbuf, __unused const void *data,
+
		       __unused struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-

-
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_USERS), p));
-
	else {
-
		struct pkg_user	*user;
-
		int		 count;
-

-
		set_list_defaults(p, "%Un\n", "");
-

-
		count = 1;
-
		while (pkg_users(pkg, &user) == EPKG_OK) {
-
			if (count > 1)
-
				iterate_item(sbuf, pkg, user, count, 
-
					     sbuf_data(p->sep_fmt), PP_U);
-

-
			iterate_item(sbuf, pkg, user, count,
-
				     sbuf_data(p->item_fmt), PP_U);
-
			count++;
-
		}
-
	}
+
	sbuf_putc(sbuf, '%');
	return (sbuf);
}

/*
-
 * %Un -- User name.
+
 * Unknown format code -- return NULL to signal upper layers to pass
+
 * the text through unchanged.
 */
static struct sbuf *
-
format_user_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
format_unknown(struct sbuf *sbuf, __unused const void *data,
+
		       __unused struct percent_esc *p)
{
-
	const struct pkg_user	*user = data;
-

-
	return (string_val(sbuf, pkg_user_name(user), p));
+
	sbuf_putc(sbuf, '%');
+
	return (NULL);
}

-
/*
-
 * %Uu -- User uidstr (one line from /etc/passwd).
-
 */
-
static struct sbuf *
-
format_user_uidstr(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg_user	*user = data;
+
/* -------------------------------------------------------------- */

-
	return (string_val(sbuf, pkg_user_uidstr(user), p));
+
static void
+
free_percent_esc(struct percent_esc *p)
+
{
+
	if (p->item_fmt)
+
		sbuf_delete(p->item_fmt);
+
	if (p->sep_fmt)
+
		sbuf_delete(p->sep_fmt);
+
	free(p);
+
	return;
}

-
/*
-
 * %a -- Autoremove flag. boolean.  Accepts field-width, left-align.
-
 * Standard form: 0, 1.  Alternate form1: no, yes.  Alternate form2:
-
 * false, true
-
 */
-
static struct sbuf *
-
format_autoremove(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
static struct percent_esc *
+
new_percent_esc(struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-
	bool			 automatic;
-

-
	pkg_get(pkg, PKG_AUTOMATIC, &automatic);
-
	return (bool_val(sbuf, automatic, p));
+
	/* reset or alloc new */
+
	if (p == NULL) {
+
		p = calloc(1, sizeof(struct percent_esc));
+
		if (p != NULL) {
+
			p->item_fmt = sbuf_new_auto();
+
			p->sep_fmt = sbuf_new_auto();
+
		}
+
		if (p == NULL || p->item_fmt == NULL || p->sep_fmt == NULL) {
+
			/* out of memory */
+
			free_percent_esc(p);
+
			return NULL;
+
		}
+
	} else {
+
		p->flags = 0;
+
		p->width = 0;
+
		sbuf_clear(p->item_fmt);
+
		sbuf_clear(p->sep_fmt);
+
		p->fmt_code = '\0';
+
	}
+
	return (p);
}

-
/*
-
 * %c -- Comment. string.  Accepts field-width, left-align
-
 */
-
static struct sbuf *
-
format_comment(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
static char *
+
gen_format(char *buf, size_t buflen, unsigned flags, const char *tail)
{
-
	const struct pkg	*pkg = data;
-
	const char		*comment;
+
	int	bp = 0;
+
	size_t	tlen;

-
	pkg_get(pkg, PKG_COMMENT, &comment);
-
	return (string_val(sbuf, comment, p));
-
}
+
	/* We need the length of tail plus at least 3 characters '%'
+
	   '*' '\0' but maybe as many as 7 '%' '#' '-' '+' '\'' '*'
+
	   '\0' */

-
/*
-
 * %d -- Dependencies. List of pkgs. Can be optionally followed by
-
 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
-
 * formats. Defaults to printing "%dn-%dv\n" for each dependency.
-
 */
-
static struct sbuf *
-
format_dependencies(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg	*pkg = data;
+
	tlen = strlen(tail);

-
	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return (list_count(sbuf, pkg_list_count(pkg, PKG_DEPS), p));
-
	else {
-
		struct pkg_dep	*dep;
-
		int		 count;
+
	if (buflen - bp < tlen + 3)
+
		return (NULL);

-
		set_list_defaults(p, "%dn-%dv\n", "");
+
	buf[bp++] = '%';

-
		count = 1;
-
		while (pkg_deps(pkg, &dep) == EPKG_OK) {
-
			if (count > 1)
-
				iterate_item(sbuf, pkg, dep, count,
-
					     sbuf_data(p->sep_fmt), PP_d);
+
	/* PP_ALTERNATE_FORM1 is not used by regular printf(3) */

-
			iterate_item(sbuf, pkg, dep, count,
-
				     sbuf_data(p->item_fmt), PP_d);
-
			count++;
-
		}
-
	}
-
	return (sbuf);
-
}
+
	if (flags & PP_ALTERNATE_FORM2)
+
		buf[bp++] = '#';

-
/*
-
 * %dn -- Dependency name or %rn -- Requirement name.
-
 */
-
static struct sbuf *
-
format_dependency_name(struct sbuf *sbuf, const void *data,
-
		       struct percent_esc *p)
-
{
-
	const struct pkg_dep	*dep = data;
+
	if (flags & PP_LEFT_ALIGN)
+
		buf[bp++] = '-';
+
	else if (flags & PP_ZERO_PAD)
+
		buf[bp++] = '0';

-
	return (string_val(sbuf, pkg_dep_name(dep), p));
-
}
+
	if (buflen - bp < tlen + 2)
+
		return (NULL);
+
	
+
	if (flags & PP_EXPLICIT_PLUS)
+
		buf[bp++] = '+';
+
	else if (flags & PP_SPACE_FOR_PLUS)
+
		buf[bp++] = ' ';

-
/*
-
 * %do -- Dependency origin or %ro -- Requirement origin.
-
 */
-
static struct sbuf *
-
format_dependency_origin(struct sbuf *sbuf, const void *data,
-
			 struct percent_esc *p)
-
{
-
	const struct pkg_dep	*dep = data;
+
	if (flags & PP_THOUSANDS_SEP)
+
		buf[bp++] = '\'';

-
	return (string_val(sbuf, pkg_dep_origin(dep), p));
-
}
+
	if (buflen - bp < tlen + 2)
+
		return (NULL);

-
/*
-
 * %dv -- Dependency version or %rv -- Requirement version.
-
 */
-
static struct sbuf *
-
format_dependency_version(struct sbuf *sbuf, const void *data,
-
			  struct percent_esc *p)
-
{
-
	const struct pkg_dep	*dep = data;
+
	/* The effect of 0 meaning 'zero fill' is indisinguishable
+
	   from 0 meaning 'a field width of zero' */

-
	return (string_val(sbuf, pkg_dep_version(dep), p));
-
}
+
	buf[bp++] = '*';
+
	buf[bp] = '\0';

-
/*
-
 * %i -- Additional info. string. Accepts field-width, left-align
-
 */
-
static struct sbuf *
-
format_add_info(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg	*pkg = data;
-
	const char		*info;
+
	strlcat(buf, tail, sizeof(buf));

-
	pkg_get(pkg, PKG_INFOS, &info);
-
	return (string_val(sbuf, info, p));
+
	return (buf);
}

-
/*
-
 * %k -- Locked flag. boolean.  Accepts field-width, left-align.
-
 * Standard form: 0, 1.  Alternate form1: no, yes.  Alternate form2:
-
 * false, true
-
 */
-
static struct sbuf *
-
format_lock_status(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg	*pkg = data;
-
	bool			 locked;
-

-
	pkg_get(pkg, PKG_LOCKED, &locked);
-
	return (bool_val(sbuf, locked, p));
-
}

-
/*
-
 * %l -- Licence logic. string.  Accepts field-width, left-align.
-
 * Standard form: and, or, single. Alternate form 1: &, |, ''.
-
 * Alternate form 2: &&, ||, ==
-
 */
static struct sbuf *
-
format_license_logic(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
human_number(struct sbuf *sbuf, int64_t number, struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-
	lic_t			 licenselogic;
-
	int			 alternate;
-
	int			 llogic;
+
	double		 num;
+
	int		 divisor;
+
	int		 scale;
+
	bool		 bin_scale;

-
	pkg_get(pkg, PKG_LICENSE_LOGIC, &licenselogic);
+
#define MAXSCALE	7

-
	switch (licenselogic) {
-
	case LICENSE_SINGLE:
-
		llogic = PP_LIC_SINGLE;
-
		break;
-
	case LICENSE_OR:
-
		llogic = PP_LIC_OR;
-
		break;
-
	case LICENSE_AND:
-
		llogic = PP_LIC_AND;
-
		break;
-
	}
+
	const char	 bin_pfx[MAXSCALE][3] =
+
		{ "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" }; 
+
	const char	 si_pfx[MAXSCALE][2] =
+
		{ "", "k", "M", "G", "T", "P", "E" };
+
	char		 format[16];

-
	if (p->flags & PP_ALTERNATE_FORM2)
-
		alternate = 2;
-
	else if (p->flags & PP_ALTERNATE_FORM1)
-
		alternate = 1;
-
	else
-
		alternate = 0;
+
	bin_scale = ((p->flags & PP_ALTERNATE_FORM2) != 0);

	p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);

-
	return (string_val(sbuf, liclog_str[llogic][alternate], p));
-
}
+
	num = number;
+
	divisor = bin_scale ? 1024 : 1000;

-
/*
-
 * %m -- Maintainer e-mail address. string.  Accepts field-width, left-align
-
 */
-
static struct sbuf *
-
format_maintainer(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg	*pkg = data;
-
	const char		*maintainer;
+
	for (scale = 0; scale < MAXSCALE; scale++) {
+
		if (num <= divisor)
+
			break;
+
		num /= divisor;
+
	}

-
	pkg_get(pkg, PKG_MAINTAINER, &maintainer);
-
	return (string_val(sbuf, maintainer, p));
-
}
+
	if (gen_format(format, sizeof(format), p->flags, ".3f %s") == NULL)
+
		return (NULL);

-
/*
-
 * %n -- Package name. string.  Accepts field-width, left-align
-
 */
-
static struct sbuf *
-
format_name(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg	*pkg = data;
-
	const char		*name;
+
	sbuf_printf(sbuf, format, p->width, num,
+
		    bin_scale ? bin_pfx[scale] : si_pfx[scale]);

-
	pkg_get(pkg, PKG_NAME, &name);
-
	return (string_val(sbuf, name, p));
+
	return (sbuf);
}

-
/*
-
 * %o -- Package origin. string.  Accepts field-width, left-align
-
 */
static struct sbuf *
-
format_origin(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
string_val(struct sbuf *sbuf, const char *str, struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-
	const char		*origin;
+
	char	format[16];

-
	pkg_get(pkg, PKG_ORIGIN, &origin);
-
	return (string_val(sbuf, origin, p));
-
}
+
	/* The '#' '?' '+' ' ' and '\'' modifiers have no meaning for
+
	   strings */

-
/*
-
 * %p -- Installation prefix. string. Accepts field-width, left-align
-
 */
-
static struct sbuf *
-
format_prefix(struct sbuf *sbuf, const void *data, struct percent_esc *p)
-
{
-
	const struct pkg	*pkg = data;
-
	const char		*prefix;
+
	p->flags &= ~(PP_ALTERNATE_FORM1 |
+
		      PP_ALTERNATE_FORM2 |
+
		      PP_EXPLICIT_PLUS   |
+
		      PP_SPACE_FOR_PLUS  |
+
		      PP_THOUSANDS_SEP);

-
	pkg_get(pkg, PKG_PREFIX, &prefix);
-
	return (string_val(sbuf, prefix, p));
+
	if (gen_format(format, sizeof(format), p->flags, "s") == NULL)
+
		return (NULL);
+

+
	sbuf_printf(sbuf, format, p->width, str);
+
	return (sbuf);
}

-
/*
-
 * %r -- Requirements. List of pkgs. Can be optionally followed by
-
 * per-field format string in %{ %| %} using any pkg_printf() *scalar*
-
 * formats. Defaults to printing "%{%rn-%rv\n%|%}" for each dependency.
-
 */
static struct sbuf *
-
format_requirements(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
int_val(struct sbuf *sbuf, int64_t value, struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-

	if (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2))
-
		return(list_count(sbuf, pkg_list_count(pkg, PKG_RDEPS), p));
+
		return (human_number(sbuf, value, p));
	else {
-
		struct pkg_dep	*req;
-
		int		 count;
-

-
		set_list_defaults(p, "%rn-%rv\n", "");
+
		char	 format[16];

-
		count = 1;
-
		while (pkg_rdeps(pkg, &req) == EPKG_OK) {
-
			if (count > 1)
-
				iterate_item(sbuf, pkg, req, count,
-
					     sbuf_data(p->sep_fmt), PP_r);
+
		if (gen_format(format, sizeof(format), p->flags, PRId64)
+
		    == NULL)
+
			return (NULL);

-
			iterate_item(sbuf, pkg, req, count,
-
				     sbuf_data(p->item_fmt), PP_r);
-
			count++;
-
		}
+
		sbuf_printf(sbuf, format, p->width, value);
	}
	return (sbuf);
}

-
/*
-
 * %s -- Size of installed package. integer.  Accepts field-width,
-
 * left-align, zero-fill, space-for-plus, explicit-plus and
-
 * alternate-form.  Alternate form is a humanized number using decimal
-
 * exponents (k, M, G).  Alternate form 2, ditto, but using binary
-
 * scale prefixes (ki, Mi, Gi etc.)
-
 */
static struct sbuf *
-
format_flatsize(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
bool_val(struct sbuf *sbuf, bool value, struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-
	int64_t			 flatsize;
+
	int	alternate;

-
	pkg_get(pkg, PKG_FLATSIZE, &flatsize);
-
	return (int_val(sbuf, flatsize, p));
+
	if (p->flags & PP_ALTERNATE_FORM2)
+
		alternate = 2;
+
	else if (p->flags & PP_ALTERNATE_FORM1)
+
		alternate = 1;
+
	else
+
		alternate = 0;
+

+
	p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
+

+
	return (string_val(sbuf, boolean_str[value][alternate], p));
}

-
/*
-
 * %t -- Installation timestamp (Unix time). integer.  Accepts
-
 * field-width, left-align.  Can be followed by optional strftime
-
 * format string in %{ %}.  Default is to print seconds-since-epoch as
-
 * an integer applying our integer format modifiers.
-
 */
static struct sbuf *
-
format_install_tstamp(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
mode_val(struct sbuf *sbuf, mode_t mode, struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-
	int64_t			 timestamp;
+
	/* Print mode as an octal integer '%o' by default.
+
	 * PP_ALTERNATE_FORM2 generates '%#o' pased to regular
+
	 * printf(). PP_ALTERNATE_FORM1 will generate drwxr-x--- style
+
	 * from strmode(3).  */

-
	pkg_get(pkg, PKG_TIME, &timestamp);
+
	/* Does the mode include the bits that indicate the inode type? */

-
	if (sbuf_len(p->item_fmt) == 0)
-
		return (int_val(sbuf, timestamp, p));
-
	else {
-
		char	 buf[1024];
+
	if (p->flags & PP_ALTERNATE_FORM1) {
+
		char	modebuf[12];

-
		strftime(buf, sizeof(buf), sbuf_data(p->item_fmt),
-
			 localtime(&timestamp));
-
		sbuf_cat(sbuf, buf); 
+
		strmode(mode, modebuf);
+

+
		return (string_val(sbuf, modebuf, p));
+
	} else {
+
		char	format[16];
+

+
		p->flags &= ~(PP_ALTERNATE_FORM1);
+

+
		if (gen_format(format, sizeof(format), p->flags, PRIo16)
+
		    == NULL)
+
			return (NULL);
+

+
		sbuf_printf(sbuf, format, p->width, mode);
	}
	return (sbuf);
}

-
/*
-
 * %v -- Package version. string. Accepts field width, left align
-
 */
static struct sbuf *
-
format_version(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
list_count(struct sbuf *sbuf, int64_t count, struct percent_esc *p)
{
-
	const struct pkg	*pkg = data;
-
	const char		*version;
+
	/* Convert to 0 or 1 for %?X */
+
	if (p->flags & PP_ALTERNATE_FORM1)
+
		count = (count > 0);

-
	pkg_get(pkg, PKG_VERSION, &version);
-
	return (string_val(sbuf, version, p));
+
	/* Turn off %#X and %?X flags, then print as a normal integer */
+
	p->flags &= ~(PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2);
+

+
	return (int_val(sbuf, count, p));
+
}
+

+
static struct percent_esc *
+
set_list_defaults(struct percent_esc *p, const char *item_fmt,
+
		  const char *sep_fmt)
+
{
+
	if (sbuf_len(p->item_fmt) == 0) {
+
		sbuf_cat(p->item_fmt, item_fmt);
+
		sbuf_finish(p->item_fmt);
+
	}
+
	if (sbuf_len(p->sep_fmt) == 0) {
+
		sbuf_cat(p->sep_fmt, sep_fmt);
+
		sbuf_finish(p->sep_fmt);
+
	}
+
	return (p);
}

-
/*
-
 * %w -- Home page URL.  string.  Accepts field width, left align
-
 */
static struct sbuf *
-
format_home_url(struct sbuf *sbuf, const void *data, struct percent_esc *p)
+
iterate_item(struct sbuf *sbuf, const struct pkg *pkg, const char *format,
+
	     const void *data, int count, unsigned context)
{
-
	const struct pkg	*pkg = data;
-
	const char		*url;
-

-
	pkg_get(pkg, PKG_WWW, &url);
-
	return (string_val(sbuf, url, p));
-
}
+
	const char	*f;

-
/*
-
 * %% -- Output a literal '%' character
-
 */
-
static struct sbuf *
-
format_literal_percent(struct sbuf *sbuf, __unused const void *data,
-
		       __unused struct percent_esc *p)
-
{
-
	sbuf_putc(sbuf, '%');
+
	/* Scan the format string and interpret any escapes */
+
	for (f = format; f != '\0'; f++) {
+
		switch(*f) {
+
		case '%':
+
			f = process_format_trailer(sbuf, f, pkg, data, count, context);
+
			break;
+
		case '\\':
+
			f = process_escape(sbuf, f);
+
			break;
+
		default:
+
			sbuf_putc(sbuf, *f);
+
			break;
+
		}
+
		if (f == NULL) {
+
			sbuf_clear(sbuf);
+
			break;	/* Out of memory */
+
		}
+
	}
	return (sbuf);
}

-
static const char *
-
parse_escape(const char *f, unsigned context, struct percent_esc *p)
+
static inline const char *
+
field_modifier(const char *f, struct percent_esc *p)
{
-
	bool		done = false;
-
	fmt_code_t	fmt_code;
-

-
	f++;			/* Eat the % */
+
	bool	done;

	/* Field modifiers, if any:
	   '#' alternate form
@@ -1820,6 +1633,7 @@ parse_escape(const char *f, unsigned context, struct percent_esc *p)
           '\'' use thousands separator (numerics only)
	   Note '*' (dynamic field width) is not supported */

+
	done = false;
	while (!done) {
		switch (*f) {
		case '#':
@@ -1834,338 +1648,481 @@ parse_escape(const char *f, unsigned context, struct percent_esc *p)
		case '+':
			p->flags |= PP_EXPLICIT_PLUS;
			break;
-
		case ' ':
-
			p->flags |= PP_SPACE_FOR_PLUS;
+
		case ' ':
+
			p->flags |= PP_SPACE_FOR_PLUS;
+
			break;
+
		case '0':
+
			p->flags |= PP_ZERO_PAD;
+
			break;
+
		case '\'':
+
			p->flags |= PP_THOUSANDS_SEP;
+
			break;
+
		default:
+
			done = true;
+
			break;
+
		}
+
		if (!done)
+
			f++;
+
	}
+
	return (f);
+
}
+

+
static inline const char *
+
field_width(const char *f, struct percent_esc *p)
+
{
+
	bool	done;
+

+
	/* Field width, if any -- some number of decimal digits.
+
	   Note: field width set to zero could be interpreted as using
+
	   0 to request zero padding: it doesn't matter which -- the
+
	   result on output is exactly the same. */
+

+
	done = false;
+
	while (!done) {
+
		switch(*f) {
+
		case '0':
+
			p->width = p->width * 10 + 0;
+
			break;
+
		case '1':
+
			p->width = p->width * 10 + 1;
+
			break;
+
		case '2':
+
			p->width = p->width * 10 + 2;
+
			break;
+
		case '3':
+
			p->width = p->width * 10 + 3;
+
			break;
+
		case '4':
+
			p->width = p->width * 10 + 4;
+
			break;
+
		case '5':
+
			p->width = p->width * 10 + 5;
+
			break;
+
		case '6':
+
			p->width = p->width * 10 + 6;
+
			break;
+
		case '7':
+
			p->width = p->width * 10 + 7;
+
			break;
+
		case '8':
+
			p->width = p->width * 10 + 8;
+
			break;
+
		case '9':
+
			p->width = p->width * 10 + 9;
+
			break;
+
		default:
+
			done = true;
+
			break;
+
		}
+
		if (!done)
+
			f++;
+
	}
+
	return (f);
+
}
+

+
static inline const char *
+
format_trailer(const char *f, struct percent_esc *p)
+
{
+

+
	/* is the trailer even present? */
+

+
	if (f[0] == '%' && f[1] == '{') {
+
		bool		 sep = false;
+
		bool		 done = false;
+
		const char	*f1;
+
		const char	*f2;
+

+
		f1 = f + 2;
+

+
		for (f2 = f1; *f2 != '\0'; f2++) {
+
			if (f2[0] == '%' && ( f2[1] == '}' || f2[1] == '|')) {
+
				if (f2[1] == '|')
+
					sep = true;
+
				else
+
					done = true;
+
				f1 = f2 + 2;
+
				break;
+
			}
+
			sbuf_putc(p->item_fmt, *f2);
+
		}
+

+

+
		if (sep) {
+
			sep = false;
+

+
			for (f2 = f1; *f2 != '\0'; f2++) {
+
				if (f2[0] == '%' && f2[1] == '}') {
+
					done = true;
+
					f1 = f2 + 2;
+
					break;
+
				}
+
				sbuf_putc(p->sep_fmt, *f2);
+
			}
+
			
+
		}
+
		
+
		if (done) {
+
			sbuf_finish(p->item_fmt);
+
			sbuf_finish(p->sep_fmt);
+
			f = f1;
+
		} else {
+
			sbuf_clear(p->item_fmt);
+
			sbuf_clear(p->sep_fmt);
+
		}
+
	}
+

+
	return (f);
+
}
+

+
static const char *
+
parse_format(const char *f, unsigned context, struct percent_esc *p)
+
{
+
	bool		done;
+
	fmt_code_t	fmt_code;
+

+
	f++;			/* Eat the % */
+

+
	f = field_modifier(f, p);
+

+
	f = field_width(f, p);
+

+
	/* The next character or two will be a format code -- look
+
	   these up in the fmt table to make sure they are allowed in
+
	   context.  This could be optimized since the format codes
+
	   are arranged alphabetically in the fmt[] array. */
+

+
	done = false;
+
	for (fmt_code = PP_PKG_SHLIBS; fmt_code < PP_END_MARKER; fmt_code++) {
+
		if ((fmt[fmt_code].context & context) == context &&
+
		    fmt[fmt_code].fmt_main == f[0] &&
+
		    (fmt[fmt_code].fmt_sub == '\0' ||
+
		     fmt[fmt_code].fmt_sub == f[1])) {
+
			p->fmt_code = fmt_code;
+
			f++;
+
			if (fmt[fmt_code].fmt_sub != '\0')
+
				f++;
+
			done = true;
+
			break;
+
		}
+
	}
+

+
	/* Not a recognised format code -- mark for pass through */
+

+
	if (!done) {
+
		p->fmt_code = PP_UNKNOWN;
+
		return (f);	/* Caller will rewind */
+
	}
+

+
	/* Does this format take a trailing list item/separator format
+
	   like %{...%|...%} ?  It's only the list-valued items that
+
	   do, and they are *only* valid in PP_PKG context.  Also,
+
	   they only take the trailing stuff in the absence of %?X or
+
	   %#X modifiers. */
+

+
	if ((fmt[p->fmt_code].context & PP_PKG) == PP_PKG ||
+
	    (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2)) != 0)
+
		return (f);
+

+
	f = format_trailer(f, p);
+

+
	return (f);
+
}
+

+
static inline const char*
+
maybe_read_hex_byte(struct sbuf *sbuf, const char *f)
+
{
+
	int	val;
+

+
	/* Hex escapes are of the form \xNN -- always two hex digits */
+

+
	if (isxdigit(f[0]) && isxdigit(f[1])) {
+
		switch(*f) {
+
		case '0':
+
			val = 0x0;
+
			break;
+
		case '1':
+
			val = 0x10;
+
			break;
+
		case '2':
+
			val = 0x20;
+
			break;
+
		case '3':
+
			val = 0x30;
+
			break;
+
		case '4':
+
			val = 0x40;
+
			break;
+
		case '5':
+
			val = 0x50;
+
			break;
+
		case '6':
+
			val = 0x60;
+
			break;
+
		case '7':
+
			val = 0x70;
+
			break;
+
		case '8':
+
			val = 0x80;
+
			break;
+
		case '9':
+
			val = 0x90;
+
			break;
+
		case 'a':
+
		case 'A':
+
			val = 0xa0;
+
			break;
+
		case 'b':
+
		case 'B':
+
			val = 0xb0;
+
			break;
+
		case 'c':
+
		case 'C':
+
			val = 0xc0;
+
			break;
+
		case 'd':
+
		case 'D':
+
			val = 0xd0;
+
			break;
+
		case 'e':
+
		case 'E':
+
			val = 0xe0;
+
			break;
+
		case 'f':
+
		case 'F':
+
			val = 0xf0;
+
			break;
+
		}
+

+
		f++;
+

+
		switch(*f) {
+
		case '0':
+
			val += 0x0;
+
			break;
+
		case '1':
+
			val += 0x1;
+
			break;
+
		case '2':
+
			val += 0x2;
+
			break;
+
		case '3':
+
			val += 0x3;
+
			break;
+
		case '4':
+
			val += 0x4;
+
			break;
+
		case '5':
+
			val += 0x5;
+
			break;
+
		case '6':
+
			val += 0x6;
+
			break;
+
		case '7':
+
			val += 0x7;
+
			break;
+
		case '8':
+
			val += 0x8;
+
			break;
+
		case '9':
+
			val += 0x9;
+
			break;
+
		case 'a':
+
		case 'A':
+
			val += 0xa;
+
			break;
+
		case 'b':
+
		case 'B':
+
			val += 0xb;
+
			break;
+
		case 'c':
+
		case 'C':
+
			val += 0xc;
			break;
-
		case '0':
-
			p->flags |= PP_ZERO_PAD;
+
		case 'd':
+
		case 'D':
+
			val += 0xd;
			break;
-
		case '\'':
-
			p->flags |= PP_THOUSANDS_SEP;
+
		case 'e':
+
		case 'E':
+
			val += 0xe;
			break;
-
		default:
-
			done = true;
+
		case 'f':
+
		case 'F':
+
			val += 0xf;
			break;
		}
-
		if (!done)
-
			f++;
+

+
		sbuf_putc(sbuf, val);
+
	} else {
+
		/* Pass through unchanged if it's not a recognizable
+
		   hex byte. */
+
		sbuf_putc(sbuf, '\\');
+
		sbuf_putc(sbuf, 'x');
	}
+
	return (f);
+
}

-
	/* Field width, if any -- some number of decimal digits.
-
	   Note: field width set to zero could be interpreted as using
-
	   0 to request zero padding: it doesn't matter which -- the
-
	   result on output is exactly the same. */
+
static inline const char*
+
read_oct_byte(struct sbuf *sbuf, const char *f)
+
{
+
	int	val = 0;

-
	done = false;
-
	while (!done) {
-
		switch(*f) {
+
	/* Octal escapes are upto three octal digits: \N, \NN or \NNN
+
	   up to a max of \377.  Note: this treats \400 as \40
+
	   followed by character 0 passed through unchanged. */
+

+
	while (val < 32) {
+
		switch (*f) {
		case '0':
-
			p->width = p->width * 10 + 0;
+
			val = val * 8 + 0;
			break;
		case '1':
-
			p->width = p->width * 10 + 1;
+
			val = val * 8 + 1;
			break;
		case '2':
-
			p->width = p->width * 10 + 2;
+
			val = val * 8 + 2;
			break;
		case '3':
-
			p->width = p->width * 10 + 3;
+
			val = val * 8 + 3;
			break;
		case '4':
-
			p->width = p->width * 10 + 4;
+
			val = val * 8 + 4;
			break;
		case '5':
-
			p->width = p->width * 10 + 5;
+
			val = val * 8 + 5;
			break;
		case '6':
-
			p->width = p->width * 10 + 6;
+
			val = val * 8 + 6;
			break;
		case '7':
-
			p->width = p->width * 10 + 7;
-
			break;
-
		case '8':
-
			p->width = p->width * 10 + 8;
-
			break;
-
		case '9':
-
			p->width = p->width * 10 + 9;
-
			break;
-
		default:
-
			done = true;
+
			val = val * 8 + 7;
			break;
+
		default:	/* Non-octal digit */
+
			goto done;
		}
-
		if (!done)
-
			f++;
-
	}

-
	/* The next character or two will be a format code -- look
-
	   these up in the fmt table to make sure they are allowed in
-
	   context.  This could be optimized (but you know what they
-
	   say about premature optimization) since the format codes
-
	   are arranged alphabetically in the fmt[] array. */
+
		f++;
+
	} 
+
done:
+
	f--;	/* point at the last octal digit */
+
	sbuf_putc(sbuf, val);

-
	done = false;
-
	for (fmt_code = PP_PKG_SHLIBS; fmt_code < PP_END_MARKER; fmt_code++) {
-
		if ((fmt[fmt_code].context & context) == context &&
-
		    fmt[fmt_code].fmt_main == f[0] &&
-
		    (fmt[fmt_code].fmt_sub == '\0' ||
-
		     fmt[fmt_code].fmt_sub == f[1]))	{
-
			p->fmt_code = fmt_code;
-
			done = true;
-
			break;
-
		}
-
	}
+
	return (f);
+
}

-
	/* Not a recognised format code -- mark for pass through */
+
static const char *
+
process_escape(struct sbuf *sbuf, const char *f)
+
{
+
	f++;			/* Eat the \ */

-
	if (!done) {
-
		p->fmt_code = PP_END_MARKER;
-
		return (f);	/* Caller will rewind */
+
	switch (*f) {
+
	case 'a':
+
		sbuf_putc(sbuf, '\a');
+
		break;
+
	case 'b':
+
		sbuf_putc(sbuf, '\b');
+
		break;
+
	case 'f':
+
		sbuf_putc(sbuf, '\f');
+
		break;
+
	case 'n':
+
		sbuf_putc(sbuf, '\n');
+
		break;
+
	case 't':
+
		sbuf_putc(sbuf, '\t');
+
		break;
+
	case 'v':
+
		sbuf_putc(sbuf, '\v');
+
		break;
+
	case '\'':
+
		sbuf_putc(sbuf, '\'');
+
		break;
+
	case '"':
+
		sbuf_putc(sbuf, '"');
+
		break;
+
	case '\\':
+
		sbuf_putc(sbuf, '\\');
+
		break;
+
	case 'x':		/* Hex escape: \xNN */
+
		f++;
+
		f = maybe_read_hex_byte(sbuf, f);
+
		break;
+
	case '0':
+
	case '1':
+
	case '2':
+
	case '3':
+
	case '4':
+
	case '5':
+
	case '6':
+
	case '7':		/* all fall through */
+
		f = read_oct_byte(sbuf, f);
+
		break;
+
	default:		/* If it's not a recognised escape,
+
				   pass it through unchanged */
+
		sbuf_putc(sbuf, '\\');
+
		sbuf_putc(sbuf, *f);
+
		break;
	}

-
	/* Does this format take a trailing list item/separator format
-
	   like %{...%|...%} ?  It's only the list-valued items that
-
	   do, and they are *only* valid in PP_PKG context.  Also,
-
	   they only take the trailing stuff in the absence of %?X or
-
	   %#X modifiers. */
+
	return (f);
+
}

-
	if (fmt[p->fmt_code].context != PP_PKG ||
-
	    (p->flags & (PP_ALTERNATE_FORM1|PP_ALTERNATE_FORM2)) != 0)
-
		return (f);
+
static const char *
+
process_format_trailer(struct sbuf *sbuf, const char *f, const struct pkg *pkg, 
+
		       const void *data, int count, unsigned context)
+
{
+
	const char		*fstart;
+
	struct sbuf		*s;
+
	struct percent_esc	*p;

-
	/* ... and is the trailer present if so? */
+
	p = new_percent_esc(NULL);

-
	if (f[0] == '%' && f[1] == '{') {
-
		const char	*f2;
-
		bool		 item = false;
-
		bool		 sep = false;
+
	if (p == NULL)
+
		return (NULL);	/* Out of memory */

-
		for (f2 = f + 2; *f2 != '\0'; f2++) {
-
			if (f2[0] == '%' && ( f2[1] == '}' || f2[1] == '|')) {
-
				if (f2[1] == '|')
-
					sep = true;
-
				break;
-
			}
-
			sbuf_putc(p->item_fmt, *f2);
-
		}
-
		if (item) {
-
			sbuf_finish(p->item_fmt);
-
			f = f2 + 1;
-
		
-
			if (sep) {
-
				sep = false;
-

-
				for (f2 = f; *f2 != '\0'; f2++) {
-
					if (f2[0] == '%' && f2[1] == '}') {
-
						sep = true;
-
						break;
-
					}
-
					sbuf_putc(p->sep_fmt, *f2);
-
				}
+
	fstart = f;
+
	f = parse_format(f, context, p);
+

+
	if (p->fmt_code == PP_ROW_COUNTER)
+
		s = fmt[p->fmt_code].fmt_handler(sbuf, &count, p);
+
	else if (p->fmt_code > PP_LAST_FORMAT)
+
		s = fmt[p->fmt_code].fmt_handler(sbuf, NULL, p);
+
	else if (fmt[p->fmt_code].context & context)
+
		s = fmt[p->fmt_code].fmt_handler(sbuf, data, p);
+
	else
+
		s = fmt[p->fmt_code].fmt_handler(sbuf, pkg, p);

-
				if (sep) {
-
					sbuf_finish(p->sep_fmt);
-
					f = f2 + 1;
-
				} else {
-
					sbuf_clear(p->item_fmt);
-
					sbuf_clear(p->sep_fmt);
-
				}
-
			}
-
		} else {
-
			sbuf_clear(p->item_fmt);
-
			sbuf_clear(p->sep_fmt);
-
		}
-
	}
+

+
	if (s == NULL)
+
		f = fstart;	/* Pass through unprocessed on error */
+

+
	free_percent_esc(p);

	return (f);
}

static const char *
-
process_format(struct sbuf *sbuf, const char *f, va_list ap)
+
process_format_main(struct sbuf *sbuf, const char *f, va_list ap)
{
	const char		*fstart;
	struct sbuf		*s;
-
	struct percent_esc	*p = new_percent_esc(NULL);
+
	struct percent_esc	*p;
	void			*data;

+
	p = new_percent_esc(NULL);
+

	if (p == NULL)
		return (NULL);	/* Out of memory */

	fstart = f;
-
	f = parse_escape(f, PP_PKG, p);
+
	f = parse_format(f, PP_PKG, p);

	if (p->fmt_code <= PP_LAST_FORMAT)
		data = va_arg(ap, void *);
	else
		data = NULL;

-
	/* FFR.  Replace this monster switch statement by function
-
	 * pointers in fmt array. */
-
	switch (p->fmt_code) {
-
	case PP_PKG_SHLIBS:	/* list */
-
		s = format_shlibs(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_SHLIB_NAME:
-
		s = format_shlib_name(sbuf, (struct pkg_shlib *) data, p);
-
		break;
-
	case PP_PKG_CATEGORIES:	/* list */
-
		s = format_categories(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_CATEGORY_NAME:
-
		s = format_category_name(sbuf, (struct pkg_category *) data, p);
-
		break;
-
	case PP_PKG_DIRECTORIES: /* list */
-
		s = format_directories(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_DIRECTORY_GROUP:
-
		s = format_directory_group(sbuf, (struct pkg_dir *) data, p);
-
		break;
-
	case PP_PKG_DIRECTORY_KEEPFLAG:
-
		s = format_directory_keepflag(sbuf, (struct pkg_dir *) data, p);
-
		break;
-
	case PP_PKG_DIRECTORY_PATH:
-
		s = format_directory_path(sbuf, (struct pkg_dir *) data, p);
-
		break;
-
	case PP_PKG_DIRECTORY_PERMS:
-
		s = format_directory_perms(sbuf, (struct pkg_dir *) data, p);
-
		break;
-
	case PP_PKG_DIRECTORY_TRYFLAG:
-
		s = format_directory_tryflag(sbuf, (struct pkg_dir *) data, p);
-
		break;
-
	case PP_PKG_DIRECTORY_USER:
-
		s = format_directory_user(sbuf, (struct pkg_dir *) data, p);
-
		break;
-
	case PP_PKG_FILES:	/* list */
-
		s = format_files(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_FILE_GROUP:
-
		s = format_file_group(sbuf, (struct pkg_file *) data, p);
-
		break;
-
	case PP_PKG_FILE_KEEPFLAG:
-
		s = format_file_keepflag(sbuf, (struct pkg_file *) data, p);
-
		break;
-
	case PP_PKG_FILE_PATH:
-
		s = format_file_path(sbuf, (struct pkg_file *) data, p);
-
		break;
-
	case PP_PKG_FILE_PERMS:
-
		s = format_file_perms(sbuf, (struct pkg_file *) data, p);
-
		break;
-
	case PP_PKG_FILE_SHA256:
-
		s = format_file_sha256(sbuf, (struct pkg_file *) data, p);
-
		break;
-
	case PP_PKG_FILE_USER:
-
		s = format_file_user(sbuf, (struct pkg_file *) data, p);
-
		break;
-
	case PP_PKG_GROUPS:	/* list */
-
		s = format_groups(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_GROUP_GIDSTR:
-
		s = format_group_gidstr(sbuf, (struct pkg_group *) data, p);
-
		break;
-
	case PP_PKG_GROUP_NAME:
-
		s = format_group_name(sbuf, (struct pkg_group *) data, p);
-
		break;
-
	case PP_ROW_COUNTER:
-
		s = format_row_counter(sbuf, (int *) data, p);
-
		break;
-
	case PP_PKG_LICENSES:	/* list */
-
		s = format_licenses(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_LICENSE_NAME:
-
		s = format_license_name(sbuf, (struct pkg_license *) data, p);
-
		break;
-
	case PP_PKG_MESSAGE:
-
		s = format_message(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_OPTIONS:	/* list */
-
		s = format_options(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_OPTION_NAME:
-
		s = format_option_name(sbuf, (struct pkg_option *) data, p);
-
		break;
-
	case PP_PKG_OPTION_VALUE:
-
		s = format_option_value(sbuf, (struct pkg_option *) data, p);
-
		break;
-
	case PP_PKG_USERS:	/* list */
-
		s = format_users(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_USER_NAME:
-
		s = format_user_name(sbuf, (struct pkg_user *) data, p);
-
		break;
-
	case PP_PKG_USER_UIDSTR:
-
		s = format_user_uidstr(sbuf, (struct pkg_user *) data, p);
-
		break;
-
	case PP_PKG_AUTOREMOVE:
-
		s = format_autoremove(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_COMMENT:
-
		s = format_comment(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_DEPENDENCIES: /* list */
-
		s = format_dependencies(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_DEPENDENCY_NAME:
-
		s = format_dependency_name(sbuf, (struct pkg_dep *) data, p);
-
		break;
-
	case PP_PKG_DEPENDENCY_ORIGIN:
-
		s = format_dependency_origin(sbuf, (struct pkg_dep *) data, p);
-
		break;
-
	case PP_PKG_DEPENDENCY_VERSION:
-
		s = format_dependency_version(sbuf, (struct pkg_dep *) data, p);
-
		break;
-
	case PP_PKG_ADDITIONAL_INFO:
-
		s = format_add_info(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_LOCK_STATUS:
-
		s = format_lock_status(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_LICENSE_LOGIC:
-
		s = format_license_logic(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_MAINTAINER:
-
		s = format_maintainer(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_NAME:
-
		s = format_name(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_ORIGIN:
-
		s = format_origin(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_PREFIX:
-
		s = format_prefix(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_REQUIREMENTS: /* list */
-
		s = format_requirements(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_REQUIREMENT_NAME: /* printing as dependency */
-
		s = format_dependency_name(sbuf, (struct pkg_dep *) data, p);
-
		break;
-
	case PP_PKG_REQUIREMENT_ORIGIN: /* printing as dependency */
-
		s = format_dependency_origin(sbuf, (struct pkg_dep *) data, p);
-
		break;
-
	case PP_PKG_REQUIREMENT_VERSION: /* printing as dependency */
-
		s = format_dependency_version(sbuf, (struct pkg_dep *) data, p);
-
		break;
-
	case PP_PKG_FLATSIZE:
-
		s = format_flatsize(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_INSTALL_TIMESTAMP:
-
		s = format_install_tstamp(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_VERSION:
-
		s = format_version(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_PKG_HOME_PAGE:
-
		s = format_home_url(sbuf, (struct pkg *) data, p);
-
		break;
-
	case PP_LITERAL_PERCENT:
-
		s = format_literal_percent(sbuf, NULL, NULL);
-
		break;
-
	default:
-
		/* If it's not a known escape, pass through unchanged */
-
		sbuf_putc(sbuf, '%');
-
		s = NULL;
-
		break;
-
	}
+
	s = fmt[p->fmt_code].fmt_handler(sbuf, data, p);

	if (s == NULL)
		f = fstart;	/* Pass through unprocessed on error */
@@ -2184,10 +2141,12 @@ process_format(struct sbuf *sbuf, const char *f, va_list ap)
int
pkg_printf(const char *format, ...)
{
-
	struct sbuf	*sbuf = sbuf_new_auto();
+
	struct sbuf	*sbuf;
	int		 count;
	va_list		 ap;

+
	sbuf  = sbuf_new_auto();
+

	va_start(ap, format);
	if (sbuf)
		sbuf = pkg_sbuf_vprintf(sbuf, format, ap);
@@ -2211,10 +2170,12 @@ pkg_printf(const char *format, ...)
int
pkg_fprintf(FILE *stream, const char *format, ...)
{
-
	struct sbuf	*sbuf = sbuf_new_auto();
+
	struct sbuf	*sbuf;
	int		 count;
	va_list		 ap;

+
	sbuf  = sbuf_new_auto();
+

	va_start(ap, format);
	if (sbuf)
		sbuf = pkg_sbuf_vprintf(sbuf, format, ap);
@@ -2240,10 +2201,12 @@ pkg_fprintf(FILE *stream, const char *format, ...)
int
pkg_dprintf(int fd, const char *format, ...)
{
-
	struct sbuf	*sbuf = sbuf_new_auto();
+
	struct sbuf	*sbuf;
	int		 count;
	va_list		 ap;

+
	sbuf  = sbuf_new_auto();
+

	va_start(ap, format);
	if (sbuf)
		sbuf = pkg_sbuf_vprintf(sbuf, format, ap);
@@ -2271,10 +2234,12 @@ pkg_dprintf(int fd, const char *format, ...)
int
pkg_snprintf(char *str, size_t size, const char *format, ...)
{
-
	struct sbuf	*sbuf = sbuf_new_auto();
+
	struct sbuf	*sbuf;
	int		 count;
	va_list		 ap;

+
	sbuf  = sbuf_new_auto();
+

	va_start(ap, format);
	if (sbuf)
		sbuf = pkg_sbuf_vprintf(sbuf, format, ap);
@@ -2301,10 +2266,12 @@ pkg_snprintf(char *str, size_t size, const char *format, ...)
int
pkg_asprintf(char **ret, const char *format, ...)
{
-
	struct sbuf	*sbuf = sbuf_new_auto();
+
	struct sbuf	*sbuf;
	int		 count;
	va_list		 ap;

+
	sbuf  = sbuf_new_auto();
+

	va_start(ap, format);
	if (sbuf)
		sbuf = pkg_sbuf_vprintf(sbuf, format, ap);
@@ -2323,7 +2290,6 @@ pkg_asprintf(char **ret, const char *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 ... Varargs list of struct pkg etc. supplying the data
 * @param format String with embedded %-escapes indicating what to output
@@ -2359,7 +2325,7 @@ pkg_sbuf_vprintf(struct sbuf *sbuf, const char *format, va_list ap)

	for (f = format; *f != '\0'; f++) {
		if (*f == '%') {
-
			f = process_format(sbuf, f, ap);
+
			f = process_format_main(sbuf, f, ap);
		} else if (*f == '\\' ) {
			f = process_escape(sbuf, f);
		} else {