xref: /freebsd/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c (revision fdafd315ad0d0f28a11b9fb4476a9ab059c62b92)
177cb4d3eSLandon J. Fuller /*-
277cb4d3eSLandon J. Fuller  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
377cb4d3eSLandon J. Fuller  * All rights reserved.
477cb4d3eSLandon J. Fuller  *
577cb4d3eSLandon J. Fuller  * Redistribution and use in source and binary forms, with or without
677cb4d3eSLandon J. Fuller  * modification, are permitted provided that the following conditions
777cb4d3eSLandon J. Fuller  * are met:
877cb4d3eSLandon J. Fuller  * 1. Redistributions of source code must retain the above copyright
977cb4d3eSLandon J. Fuller  *    notice, this list of conditions and the following disclaimer,
1077cb4d3eSLandon J. Fuller  *    without modification.
1177cb4d3eSLandon J. Fuller  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
1277cb4d3eSLandon J. Fuller  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
1377cb4d3eSLandon J. Fuller  *    redistribution must be conditioned upon including a substantially
1477cb4d3eSLandon J. Fuller  *    similar Disclaimer requirement for further binary redistribution.
1577cb4d3eSLandon J. Fuller  *
1677cb4d3eSLandon J. Fuller  * NO WARRANTY
1777cb4d3eSLandon J. Fuller  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1877cb4d3eSLandon J. Fuller  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1977cb4d3eSLandon J. Fuller  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
2077cb4d3eSLandon J. Fuller  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
2177cb4d3eSLandon J. Fuller  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
2277cb4d3eSLandon J. Fuller  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2377cb4d3eSLandon J. Fuller  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2477cb4d3eSLandon J. Fuller  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
2577cb4d3eSLandon J. Fuller  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2677cb4d3eSLandon J. Fuller  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
2777cb4d3eSLandon J. Fuller  * THE POSSIBILITY OF SUCH DAMAGES.
2877cb4d3eSLandon J. Fuller  */
2977cb4d3eSLandon J. Fuller 
3077cb4d3eSLandon J. Fuller #include <sys/param.h>
31*91898857SMark Johnston #include <sys/limits.h>
3277cb4d3eSLandon J. Fuller #include <sys/sbuf.h>
3377cb4d3eSLandon J. Fuller 
3477cb4d3eSLandon J. Fuller #ifdef _KERNEL
3577cb4d3eSLandon J. Fuller 
3677cb4d3eSLandon J. Fuller #include <sys/ctype.h>
3777cb4d3eSLandon J. Fuller #include <sys/kernel.h>
3877cb4d3eSLandon J. Fuller #include <sys/malloc.h>
3977cb4d3eSLandon J. Fuller #include <sys/systm.h>
4077cb4d3eSLandon J. Fuller 
4177cb4d3eSLandon J. Fuller #include <machine/_inttypes.h>
4277cb4d3eSLandon J. Fuller 
4377cb4d3eSLandon J. Fuller #else /* !_KERNEL */
4477cb4d3eSLandon J. Fuller 
4577cb4d3eSLandon J. Fuller #include <ctype.h>
4677cb4d3eSLandon J. Fuller #include <inttypes.h>
4777cb4d3eSLandon J. Fuller #include <errno.h>
4877cb4d3eSLandon J. Fuller #include <stdlib.h>
4977cb4d3eSLandon J. Fuller #include <string.h>
5077cb4d3eSLandon J. Fuller 
5177cb4d3eSLandon J. Fuller #endif /* _KERNEL */
5277cb4d3eSLandon J. Fuller 
5377cb4d3eSLandon J. Fuller #include "bhnd_nvram_private.h"
5477cb4d3eSLandon J. Fuller #include "bhnd_nvram_valuevar.h"
5577cb4d3eSLandon J. Fuller 
5677cb4d3eSLandon J. Fuller #ifdef _KERNEL
5777cb4d3eSLandon J. Fuller #define	bhnd_nv_hex2ascii(hex)	hex2ascii(hex)
5877cb4d3eSLandon J. Fuller #else /* !_KERNEL */
5977cb4d3eSLandon J. Fuller static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz";
6077cb4d3eSLandon J. Fuller #define	bhnd_nv_hex2ascii(hex)		(bhnd_nv_hex2ascii[hex])
6177cb4d3eSLandon J. Fuller #endif /* _KERNEL */
6277cb4d3eSLandon J. Fuller 
6377cb4d3eSLandon J. Fuller /**
6477cb4d3eSLandon J. Fuller  * Maximum size, in bytes, of a string-encoded NVRAM integer value, not
6577cb4d3eSLandon J. Fuller  * including any prefix (0x, 0, etc).
6677cb4d3eSLandon J. Fuller  *
6777cb4d3eSLandon J. Fuller  * We assume the largest possible encoding is the base-2 representation
6877cb4d3eSLandon J. Fuller  * of a 64-bit integer.
6977cb4d3eSLandon J. Fuller  */
7077cb4d3eSLandon J. Fuller #define NV_NUMSTR_MAX	((sizeof(uint64_t) * CHAR_BIT) + 1)
7177cb4d3eSLandon J. Fuller 
7277cb4d3eSLandon J. Fuller /**
7377cb4d3eSLandon J. Fuller  * Format a string representation of @p value using @p fmt, with, writing the
7477cb4d3eSLandon J. Fuller  * result to @p outp.
7577cb4d3eSLandon J. Fuller  *
7677cb4d3eSLandon J. Fuller  * @param		value	The value to be formatted.
7777cb4d3eSLandon J. Fuller  * @param		fmt	The format string.
7877cb4d3eSLandon J. Fuller  * @param[out]		outp	On success, the string will be written to this
7977cb4d3eSLandon J. Fuller  *				buffer. This argment may be NULL if the value is
8077cb4d3eSLandon J. Fuller  *				not desired.
8177cb4d3eSLandon J. Fuller  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
8277cb4d3eSLandon J. Fuller  *				to the actual number of bytes required for the
8377cb4d3eSLandon J. Fuller  *				requested string encoding (including a trailing
8477cb4d3eSLandon J. Fuller  *				NUL).
8577cb4d3eSLandon J. Fuller  *
8677cb4d3eSLandon J. Fuller  * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
8777cb4d3eSLandon J. Fuller  *
8877cb4d3eSLandon J. Fuller  * @retval 0		success
8977cb4d3eSLandon J. Fuller  * @retval EINVAL	If @p fmt contains unrecognized format string
9077cb4d3eSLandon J. Fuller  *			specifiers.
9177cb4d3eSLandon J. Fuller  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
9277cb4d3eSLandon J. Fuller  *			is too small to hold the encoded value.
9377cb4d3eSLandon J. Fuller  * @retval EFTYPE	If value coercion from @p value to a single string
9477cb4d3eSLandon J. Fuller  *			value via @p fmt is unsupported.
9577cb4d3eSLandon J. Fuller  * @retval ERANGE	If value coercion of @p value would overflow (or
9677cb4d3eSLandon J. Fuller  *			underflow) the representation defined by @p fmt.
9777cb4d3eSLandon J. Fuller  */
9877cb4d3eSLandon J. Fuller int
bhnd_nvram_val_printf(bhnd_nvram_val * value,const char * fmt,char * outp,size_t * olen,...)9958efe686SLandon J. Fuller bhnd_nvram_val_printf(bhnd_nvram_val *value, const char *fmt, char *outp,
10077cb4d3eSLandon J. Fuller     size_t *olen, ...)
10177cb4d3eSLandon J. Fuller {
10277cb4d3eSLandon J. Fuller 	va_list	ap;
10377cb4d3eSLandon J. Fuller 	int	error;
10477cb4d3eSLandon J. Fuller 
10577cb4d3eSLandon J. Fuller 	va_start(ap, olen);
10677cb4d3eSLandon J. Fuller 	error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap);
10777cb4d3eSLandon J. Fuller 	va_end(ap);
10877cb4d3eSLandon J. Fuller 
10977cb4d3eSLandon J. Fuller 	return (error);
11077cb4d3eSLandon J. Fuller }
11177cb4d3eSLandon J. Fuller 
11277cb4d3eSLandon J. Fuller /**
11377cb4d3eSLandon J. Fuller  * Format a string representation of the elements of @p value using @p fmt,
11477cb4d3eSLandon J. Fuller  * writing the result to @p outp.
11577cb4d3eSLandon J. Fuller  *
11677cb4d3eSLandon J. Fuller  * @param		value	The value to be formatted.
11777cb4d3eSLandon J. Fuller  * @param		fmt	The format string.
11877cb4d3eSLandon J. Fuller  * @param[out]		outp	On success, the string will be written to this
11977cb4d3eSLandon J. Fuller  *				buffer. This argment may be NULL if the value is
12077cb4d3eSLandon J. Fuller  *				not desired.
12177cb4d3eSLandon J. Fuller  * @param[in,out]	olen	The capacity of @p outp. On success, will be set
12277cb4d3eSLandon J. Fuller  *				to the actual number of bytes required for the
12377cb4d3eSLandon J. Fuller  *				requested string encoding (including a trailing
12477cb4d3eSLandon J. Fuller  *				NUL).
12577cb4d3eSLandon J. Fuller  * @param		ap	Argument list.
12677cb4d3eSLandon J. Fuller  *
12777cb4d3eSLandon J. Fuller  * @par Format Strings
12877cb4d3eSLandon J. Fuller  *
12977cb4d3eSLandon J. Fuller  * Value format strings are similar, but not identical to, those used
13077cb4d3eSLandon J. Fuller  * by printf(3).
13177cb4d3eSLandon J. Fuller  *
13277cb4d3eSLandon J. Fuller  * Format specifier format:
13377cb4d3eSLandon J. Fuller  *     %[repeat][flags][width][.precision][length modifier][specifier]
13477cb4d3eSLandon J. Fuller  *
13577cb4d3eSLandon J. Fuller  * The format specifier is interpreted as an encoding directive for an
13677cb4d3eSLandon J. Fuller  * individual value element; each format specifier will fetch the next element
13777cb4d3eSLandon J. Fuller  * from the value, encode the element as the appropriate type based on the
13877cb4d3eSLandon J. Fuller  * length modifiers and specifier, and then format the result as a string.
13977cb4d3eSLandon J. Fuller  *
14077cb4d3eSLandon J. Fuller  * For example, given a string value of '0x000F', and a format specifier of
14177cb4d3eSLandon J. Fuller  * '%#hhx', the value will be asked to encode its first element as
14277cb4d3eSLandon J. Fuller  * BHND_NVRAM_TYPE_UINT8. String formatting will then be applied to the 8-bit
14377cb4d3eSLandon J. Fuller  * unsigned integer representation, producing a string value of "0xF".
14477cb4d3eSLandon J. Fuller  *
14577cb4d3eSLandon J. Fuller  * Repeat:
14677cb4d3eSLandon J. Fuller  * - [digits]		Repeatedly apply the format specifier to the input
14777cb4d3eSLandon J. Fuller  *			value's elements up to `digits` times. The delimiter
14877cb4d3eSLandon J. Fuller  *			must be passed as a string in the next variadic
14977cb4d3eSLandon J. Fuller  *			argument.
15077cb4d3eSLandon J. Fuller  * - []			Repeatedly apply the format specifier to the input
15177cb4d3eSLandon J. Fuller  *			value's elements until all elements have been. The
15277cb4d3eSLandon J. Fuller  *			processed. The delimiter must be passed as a string in
15377cb4d3eSLandon J. Fuller  *			the next variadic argument.
15477cb4d3eSLandon J. Fuller  * - [*]		Repeatedly apply the format specifier to the input
15577cb4d3eSLandon J. Fuller  *			value's elements. The repeat count is read from the
15677cb4d3eSLandon J. Fuller  *			next variadic argument as a size_t value
15777cb4d3eSLandon J. Fuller  *
15877cb4d3eSLandon J. Fuller  * Flags:
15977cb4d3eSLandon J. Fuller  * - '#'		use alternative form (e.g. 0x/0X prefixing of hex
16077cb4d3eSLandon J. Fuller  *			strings).
16177cb4d3eSLandon J. Fuller  * - '0'		zero padding
16277cb4d3eSLandon J. Fuller  * - '-'		left adjust padding
16377cb4d3eSLandon J. Fuller  * - '+'		include a sign character
16477cb4d3eSLandon J. Fuller  * - ' '		include a space in place of a sign character for
16577cb4d3eSLandon J. Fuller  *			positive numbers.
16677cb4d3eSLandon J. Fuller  *
16777cb4d3eSLandon J. Fuller  * Width/Precision:
16877cb4d3eSLandon J. Fuller  * - digits		minimum field width.
16977cb4d3eSLandon J. Fuller  * - *			read the minimum field width from the next variadic
17077cb4d3eSLandon J. Fuller  *			argument as a ssize_t value. A negative value enables
17177cb4d3eSLandon J. Fuller  *			left adjustment.
17277cb4d3eSLandon J. Fuller  * - .digits		field precision.
17377cb4d3eSLandon J. Fuller  * - .*			read the field precision from the next variadic argument
17477cb4d3eSLandon J. Fuller  *			as a ssize_t value. A negative value enables left
17577cb4d3eSLandon J. Fuller  *			adjustment.
17677cb4d3eSLandon J. Fuller  *
17777cb4d3eSLandon J. Fuller  * Length Modifiers:
17877cb4d3eSLandon J. Fuller  * - 'hh', 'I8'		Convert the value to an 8-bit signed or unsigned
17977cb4d3eSLandon J. Fuller  *			integer.
18077cb4d3eSLandon J. Fuller  * - 'h', 'I16'		Convert the value to an 16-bit signed or unsigned
18177cb4d3eSLandon J. Fuller  *			integer.
18277cb4d3eSLandon J. Fuller  * - 'l', 'I32'		Convert the value to an 32-bit signed or unsigned
18377cb4d3eSLandon J. Fuller  *			integer.
18477cb4d3eSLandon J. Fuller  * - 'll', 'j', 'I64'	Convert the value to an 64-bit signed or unsigned
18577cb4d3eSLandon J. Fuller  *			integer.
18677cb4d3eSLandon J. Fuller  *
18777cb4d3eSLandon J. Fuller  * Data Specifiers:
18877cb4d3eSLandon J. Fuller  * - 'd', 'i'		Convert and format as a signed decimal integer.
18977cb4d3eSLandon J. Fuller  * - 'u'		Convert and format as an unsigned decimal integer.
19077cb4d3eSLandon J. Fuller  * - 'o'		Convert and format as an unsigned octal integer.
19177cb4d3eSLandon J. Fuller  * - 'x'		Convert and format as an unsigned hexadecimal integer,
19277cb4d3eSLandon J. Fuller  *			using lowercase hex digits.
19377cb4d3eSLandon J. Fuller  * - 'X'		Convert and format as an unsigned hexadecimal integer,
19477cb4d3eSLandon J. Fuller  *			using uppercase hex digits.
19577cb4d3eSLandon J. Fuller  * - 's'		Convert and format as a string.
19677cb4d3eSLandon J. Fuller  * - '%'		Print a literal '%' character.
19777cb4d3eSLandon J. Fuller  *
19877cb4d3eSLandon J. Fuller  * @retval 0		success
19977cb4d3eSLandon J. Fuller  * @retval EINVAL	If @p fmt contains unrecognized format string
20077cb4d3eSLandon J. Fuller  *			specifiers.
20177cb4d3eSLandon J. Fuller  * @retval ENOMEM	If the @p outp is non-NULL, and the provided @p olen
20277cb4d3eSLandon J. Fuller  *			is too small to hold the encoded value.
20377cb4d3eSLandon J. Fuller  * @retval EFTYPE	If value coercion from @p value to a single string
20477cb4d3eSLandon J. Fuller  *			value via @p fmt is unsupported.
20577cb4d3eSLandon J. Fuller  * @retval ERANGE	If value coercion of @p value would overflow (or
20677cb4d3eSLandon J. Fuller  *			underflow) the representation defined by @p fmt.
20777cb4d3eSLandon J. Fuller  */
20877cb4d3eSLandon J. Fuller int
bhnd_nvram_val_vprintf(bhnd_nvram_val * value,const char * fmt,char * outp,size_t * olen,va_list ap)20958efe686SLandon J. Fuller bhnd_nvram_val_vprintf(bhnd_nvram_val *value, const char *fmt, char *outp,
21077cb4d3eSLandon J. Fuller     size_t *olen, va_list ap)
21177cb4d3eSLandon J. Fuller {
21277cb4d3eSLandon J. Fuller 	const void	*elem;
21377cb4d3eSLandon J. Fuller 	size_t		 elen;
21477cb4d3eSLandon J. Fuller 	size_t		 limit, nbytes;
21577cb4d3eSLandon J. Fuller 	int		 error;
21677cb4d3eSLandon J. Fuller 
21777cb4d3eSLandon J. Fuller 	elem = NULL;
21877cb4d3eSLandon J. Fuller 
21977cb4d3eSLandon J. Fuller 	/* Determine output byte limit */
22077cb4d3eSLandon J. Fuller 	nbytes = 0;
22177cb4d3eSLandon J. Fuller 	if (outp != NULL)
22277cb4d3eSLandon J. Fuller 		limit = *olen;
22377cb4d3eSLandon J. Fuller 	else
22477cb4d3eSLandon J. Fuller 		limit = 0;
22577cb4d3eSLandon J. Fuller 
22677cb4d3eSLandon J. Fuller #define	WRITE_CHAR(_c)	do {			\
22777cb4d3eSLandon J. Fuller 	if (limit > nbytes)			\
22877cb4d3eSLandon J. Fuller 		*(outp + nbytes) = _c;		\
22977cb4d3eSLandon J. Fuller 						\
23077cb4d3eSLandon J. Fuller 	if (nbytes == SIZE_MAX)			\
23177cb4d3eSLandon J. Fuller 		return (EFTYPE);		\
23277cb4d3eSLandon J. Fuller 	nbytes++;				\
23377cb4d3eSLandon J. Fuller } while (0)
23477cb4d3eSLandon J. Fuller 
23577cb4d3eSLandon J. Fuller 	/* Encode string value as per the format string */
23677cb4d3eSLandon J. Fuller 	for (const char *p = fmt; *p != '\0'; p++) {
23777cb4d3eSLandon J. Fuller 		const char	*delim;
23877cb4d3eSLandon J. Fuller 		size_t		 precision, width, delim_len;
23977cb4d3eSLandon J. Fuller 		u_long		 repeat, bits;
24077cb4d3eSLandon J. Fuller 		bool		 alt_form, ladjust, have_precision;
24177cb4d3eSLandon J. Fuller 		char		 padc, signc, lenc;
24277cb4d3eSLandon J. Fuller 
24377cb4d3eSLandon J. Fuller 		padc = ' ';
24477cb4d3eSLandon J. Fuller 		signc = '\0';
24577cb4d3eSLandon J. Fuller 		lenc = '\0';
24677cb4d3eSLandon J. Fuller 		delim = "";
24777cb4d3eSLandon J. Fuller 		delim_len = 0;
24877cb4d3eSLandon J. Fuller 
24977cb4d3eSLandon J. Fuller 		ladjust = false;
25077cb4d3eSLandon J. Fuller 		alt_form = false;
25177cb4d3eSLandon J. Fuller 
25277cb4d3eSLandon J. Fuller 		have_precision = false;
25377cb4d3eSLandon J. Fuller 		precision = 1;
25477cb4d3eSLandon J. Fuller 		bits = 32;
25577cb4d3eSLandon J. Fuller 		width = 0;
25677cb4d3eSLandon J. Fuller 		repeat = 1;
25777cb4d3eSLandon J. Fuller 
25877cb4d3eSLandon J. Fuller 		/* Copy all input to output until we hit a format specifier */
25977cb4d3eSLandon J. Fuller 		if (*p != '%') {
26077cb4d3eSLandon J. Fuller 			WRITE_CHAR(*p);
26177cb4d3eSLandon J. Fuller 			continue;
26277cb4d3eSLandon J. Fuller 		}
26377cb4d3eSLandon J. Fuller 
26477cb4d3eSLandon J. Fuller 		/* Hit '%' -- is this followed by an escaped '%' literal? */
26577cb4d3eSLandon J. Fuller 		p++;
26677cb4d3eSLandon J. Fuller 		if (*p == '%') {
26777cb4d3eSLandon J. Fuller 			WRITE_CHAR('%');
26877cb4d3eSLandon J. Fuller 			p++;
26977cb4d3eSLandon J. Fuller 			continue;
27077cb4d3eSLandon J. Fuller 		}
27177cb4d3eSLandon J. Fuller 
27277cb4d3eSLandon J. Fuller 		/* Parse repeat specifier */
27377cb4d3eSLandon J. Fuller 		if (*p == '[') {
27477cb4d3eSLandon J. Fuller 			p++;
27577cb4d3eSLandon J. Fuller 
27677cb4d3eSLandon J. Fuller 			/* Determine repeat count */
27777cb4d3eSLandon J. Fuller 			if (*p == ']') {
27877cb4d3eSLandon J. Fuller 				/* Repeat consumes all input */
27977cb4d3eSLandon J. Fuller 				repeat = bhnd_nvram_val_nelem(value);
28077cb4d3eSLandon J. Fuller 			} else if (*p == '*') {
28177cb4d3eSLandon J. Fuller 				/* Repeat is supplied as an argument */
28277cb4d3eSLandon J. Fuller 				repeat = va_arg(ap, size_t);
28377cb4d3eSLandon J. Fuller 				p++;
28477cb4d3eSLandon J. Fuller 			} else {
28577cb4d3eSLandon J. Fuller 				char *endp;
28677cb4d3eSLandon J. Fuller 
28777cb4d3eSLandon J. Fuller 				/* Repeat specified as argument */
28877cb4d3eSLandon J. Fuller 				repeat = strtoul(p, &endp, 10);
28977cb4d3eSLandon J. Fuller 				if (p == endp) {
29077cb4d3eSLandon J. Fuller 					BHND_NV_LOG("error parsing repeat "
29177cb4d3eSLandon J. Fuller 						    "count at '%s'", p);
29277cb4d3eSLandon J. Fuller 					return (EINVAL);
29377cb4d3eSLandon J. Fuller 				}
29477cb4d3eSLandon J. Fuller 
29577cb4d3eSLandon J. Fuller 				/* Advance past repeat count */
29677cb4d3eSLandon J. Fuller 				p = endp;
29777cb4d3eSLandon J. Fuller 			}
29877cb4d3eSLandon J. Fuller 
29977cb4d3eSLandon J. Fuller 			/* Advance past terminating ']' */
30077cb4d3eSLandon J. Fuller 			if (*p != ']') {
30177cb4d3eSLandon J. Fuller 				BHND_NV_LOG("error parsing repeat count at "
30277cb4d3eSLandon J. Fuller 				    "'%s'", p);
30377cb4d3eSLandon J. Fuller 				return (EINVAL);
30477cb4d3eSLandon J. Fuller 			}
30577cb4d3eSLandon J. Fuller 			p++;
30677cb4d3eSLandon J. Fuller 
30777cb4d3eSLandon J. Fuller 			delim = va_arg(ap, const char *);
30877cb4d3eSLandon J. Fuller 			delim_len = strlen(delim);
30977cb4d3eSLandon J. Fuller 		}
31077cb4d3eSLandon J. Fuller 
31177cb4d3eSLandon J. Fuller 		/* Parse flags */
31277cb4d3eSLandon J. Fuller 		while (*p != '\0') {
31377cb4d3eSLandon J. Fuller 			const char	*np;
31477cb4d3eSLandon J. Fuller 			bool		 stop;
31577cb4d3eSLandon J. Fuller 
31677cb4d3eSLandon J. Fuller 			stop = false;
31777cb4d3eSLandon J. Fuller 			np = p+1;
31877cb4d3eSLandon J. Fuller 
31977cb4d3eSLandon J. Fuller 			switch (*p) {
32077cb4d3eSLandon J. Fuller 			case '#':
32177cb4d3eSLandon J. Fuller 				alt_form = true;
32277cb4d3eSLandon J. Fuller 				break;
32377cb4d3eSLandon J. Fuller 			case '0':
32477cb4d3eSLandon J. Fuller 				padc = '0';
32577cb4d3eSLandon J. Fuller 				break;
32677cb4d3eSLandon J. Fuller 			case '-':
32777cb4d3eSLandon J. Fuller 				ladjust = true;
32877cb4d3eSLandon J. Fuller 				break;
32977cb4d3eSLandon J. Fuller 			case ' ':
33077cb4d3eSLandon J. Fuller 				/* Must not override '+' */
33177cb4d3eSLandon J. Fuller 				if (signc != '+')
33277cb4d3eSLandon J. Fuller 					signc = ' ';
33377cb4d3eSLandon J. Fuller 				break;
33477cb4d3eSLandon J. Fuller 			case '+':
33577cb4d3eSLandon J. Fuller 				signc = '+';
33677cb4d3eSLandon J. Fuller 				break;
33777cb4d3eSLandon J. Fuller 			default:
33877cb4d3eSLandon J. Fuller 				/* Non-flag character */
33977cb4d3eSLandon J. Fuller 				stop = true;
34077cb4d3eSLandon J. Fuller 				break;
34177cb4d3eSLandon J. Fuller 			}
34277cb4d3eSLandon J. Fuller 
34377cb4d3eSLandon J. Fuller 			if (stop)
34477cb4d3eSLandon J. Fuller 				break;
34577cb4d3eSLandon J. Fuller 			else
34677cb4d3eSLandon J. Fuller 				p = np;
34777cb4d3eSLandon J. Fuller 		}
34877cb4d3eSLandon J. Fuller 
34977cb4d3eSLandon J. Fuller 		/* Parse minimum width */
35077cb4d3eSLandon J. Fuller 		if (*p == '*') {
35177cb4d3eSLandon J. Fuller 			ssize_t arg;
35277cb4d3eSLandon J. Fuller 
35377cb4d3eSLandon J. Fuller 			/* Width is supplied as an argument */
35477cb4d3eSLandon J. Fuller 			arg = va_arg(ap, int);
35577cb4d3eSLandon J. Fuller 
35677cb4d3eSLandon J. Fuller 			/* Negative width argument is interpreted as
35777cb4d3eSLandon J. Fuller 			 * '-' flag followed by positive width */
35877cb4d3eSLandon J. Fuller 			if (arg < 0) {
35977cb4d3eSLandon J. Fuller 				ladjust = true;
36077cb4d3eSLandon J. Fuller 				arg = -arg;
36177cb4d3eSLandon J. Fuller 			}
36277cb4d3eSLandon J. Fuller 
36377cb4d3eSLandon J. Fuller 			width = arg;
36477cb4d3eSLandon J. Fuller 			p++;
36577cb4d3eSLandon J. Fuller 		} else if (bhnd_nv_isdigit(*p)) {
36677cb4d3eSLandon J. Fuller 			uint32_t	v;
36777cb4d3eSLandon J. Fuller 			size_t		len, parsed;
36877cb4d3eSLandon J. Fuller 
36977cb4d3eSLandon J. Fuller 			/* Parse width value */
37077cb4d3eSLandon J. Fuller 			len = sizeof(v);
37177cb4d3eSLandon J. Fuller 			error = bhnd_nvram_parse_int(p, strlen(p), 10, &parsed,
37277cb4d3eSLandon J. Fuller 			    &v, &len, BHND_NVRAM_TYPE_UINT32);
37377cb4d3eSLandon J. Fuller 			if (error) {
37477cb4d3eSLandon J. Fuller 				BHND_NV_LOG("error parsing width %s: %d\n", p,
37577cb4d3eSLandon J. Fuller 				    error);
37677cb4d3eSLandon J. Fuller 				return (EINVAL);
37777cb4d3eSLandon J. Fuller 			}
37877cb4d3eSLandon J. Fuller 
37977cb4d3eSLandon J. Fuller 			/* Save width and advance input */
38077cb4d3eSLandon J. Fuller 			width = v;
38177cb4d3eSLandon J. Fuller 			p += parsed;
38277cb4d3eSLandon J. Fuller 		}
38377cb4d3eSLandon J. Fuller 
38477cb4d3eSLandon J. Fuller 		/* Parse precision */
38577cb4d3eSLandon J. Fuller 		if (*p == '.') {
38677cb4d3eSLandon J. Fuller 			uint32_t	v;
38777cb4d3eSLandon J. Fuller 			size_t		len, parsed;
38877cb4d3eSLandon J. Fuller 
38977cb4d3eSLandon J. Fuller 			p++;
39077cb4d3eSLandon J. Fuller 			have_precision = true;
39177cb4d3eSLandon J. Fuller 
39277cb4d3eSLandon J. Fuller 			if (*p == '*') {
39377cb4d3eSLandon J. Fuller 				ssize_t arg;
39477cb4d3eSLandon J. Fuller 
39577cb4d3eSLandon J. Fuller 				/* Precision is specified as an argument */
39677cb4d3eSLandon J. Fuller 				arg = va_arg(ap, int);
39777cb4d3eSLandon J. Fuller 
39877cb4d3eSLandon J. Fuller 				/* Negative precision argument is interpreted
39977cb4d3eSLandon J. Fuller 				 * as '-' flag followed by positive
40077cb4d3eSLandon J. Fuller 				 * precision */
40177cb4d3eSLandon J. Fuller 				if (arg < 0) {
40277cb4d3eSLandon J. Fuller 					ladjust = true;
40377cb4d3eSLandon J. Fuller 					arg = -arg;
40477cb4d3eSLandon J. Fuller 				}
40577cb4d3eSLandon J. Fuller 
40677cb4d3eSLandon J. Fuller 				precision = arg;
40777cb4d3eSLandon J. Fuller 			} else if (!bhnd_nv_isdigit(*p)) {
40877cb4d3eSLandon J. Fuller 				/* Implicit precision of 0 */
40977cb4d3eSLandon J. Fuller 				precision = 0;
41077cb4d3eSLandon J. Fuller 			} else {
41177cb4d3eSLandon J. Fuller 				/* Parse precision value */
41277cb4d3eSLandon J. Fuller 				len = sizeof(v);
41377cb4d3eSLandon J. Fuller 				error = bhnd_nvram_parse_int(p, strlen(p), 10,
41477cb4d3eSLandon J. Fuller 				    &parsed, &v, &len,
41577cb4d3eSLandon J. Fuller 				    BHND_NVRAM_TYPE_UINT32);
41677cb4d3eSLandon J. Fuller 				if (error) {
41777cb4d3eSLandon J. Fuller 					BHND_NV_LOG("error parsing width %s: "
41877cb4d3eSLandon J. Fuller 					    "%d\n", p, error);
41977cb4d3eSLandon J. Fuller 					return (EINVAL);
42077cb4d3eSLandon J. Fuller 				}
42177cb4d3eSLandon J. Fuller 
42277cb4d3eSLandon J. Fuller 				/* Save precision and advance input */
42377cb4d3eSLandon J. Fuller 				precision = v;
42477cb4d3eSLandon J. Fuller 				p += parsed;
42577cb4d3eSLandon J. Fuller 			}
42677cb4d3eSLandon J. Fuller 		}
42777cb4d3eSLandon J. Fuller 
42877cb4d3eSLandon J. Fuller 		/* Parse length modifiers */
42977cb4d3eSLandon J. Fuller 		while (*p != '\0') {
43077cb4d3eSLandon J. Fuller 			const char	*np;
43177cb4d3eSLandon J. Fuller 			bool		 stop;
43277cb4d3eSLandon J. Fuller 
43377cb4d3eSLandon J. Fuller 			stop = false;
43477cb4d3eSLandon J. Fuller 			np = p+1;
43577cb4d3eSLandon J. Fuller 
43677cb4d3eSLandon J. Fuller 			switch (*p) {
43777cb4d3eSLandon J. Fuller 			case 'h':
43877cb4d3eSLandon J. Fuller 				if (lenc == '\0') {
43977cb4d3eSLandon J. Fuller 					/* Set initial length value */
44077cb4d3eSLandon J. Fuller 					lenc = *p;
44177cb4d3eSLandon J. Fuller 					bits = 16;
44277cb4d3eSLandon J. Fuller 				} else if (lenc == *p && bits == 16) {
44377cb4d3eSLandon J. Fuller 					/* Modify previous length value */
44477cb4d3eSLandon J. Fuller 					bits = 8;
44577cb4d3eSLandon J. Fuller 				} else {
44677cb4d3eSLandon J. Fuller 					BHND_NV_LOG("invalid length modifier "
44777cb4d3eSLandon J. Fuller 					    "%c\n", *p);
44877cb4d3eSLandon J. Fuller 					return (EINVAL);
44977cb4d3eSLandon J. Fuller 				}
45077cb4d3eSLandon J. Fuller 				break;
45177cb4d3eSLandon J. Fuller 
45277cb4d3eSLandon J. Fuller 			case 'l':
45377cb4d3eSLandon J. Fuller 				if (lenc == '\0') {
45477cb4d3eSLandon J. Fuller 					/* Set initial length value */
45577cb4d3eSLandon J. Fuller 					lenc = *p;
45677cb4d3eSLandon J. Fuller 					bits = 32;
45777cb4d3eSLandon J. Fuller 				} else if (lenc == *p && bits == 32) {
45877cb4d3eSLandon J. Fuller 					/* Modify previous length value */
45977cb4d3eSLandon J. Fuller 					bits = 64;
46077cb4d3eSLandon J. Fuller 				} else {
46177cb4d3eSLandon J. Fuller 					BHND_NV_LOG("invalid length modifier "
46277cb4d3eSLandon J. Fuller 					    "%c\n", *p);
46377cb4d3eSLandon J. Fuller 					return (EINVAL);
46477cb4d3eSLandon J. Fuller 				}
46577cb4d3eSLandon J. Fuller 				break;
46677cb4d3eSLandon J. Fuller 
46777cb4d3eSLandon J. Fuller 			case 'j':
46877cb4d3eSLandon J. Fuller 				/* Conflicts with all other length
46977cb4d3eSLandon J. Fuller 				 * specifications, and may only occur once */
47077cb4d3eSLandon J. Fuller 				if (lenc != '\0') {
47177cb4d3eSLandon J. Fuller 					BHND_NV_LOG("invalid length modifier "
47277cb4d3eSLandon J. Fuller 					    "%c\n", *p);
47377cb4d3eSLandon J. Fuller 					return (EINVAL);
47477cb4d3eSLandon J. Fuller 				}
47577cb4d3eSLandon J. Fuller 
47677cb4d3eSLandon J. Fuller 				lenc = *p;
47777cb4d3eSLandon J. Fuller 				bits = 64;
47877cb4d3eSLandon J. Fuller 				break;
47977cb4d3eSLandon J. Fuller 
48077cb4d3eSLandon J. Fuller 			case 'I': {
48177cb4d3eSLandon J. Fuller 				char	*endp;
48277cb4d3eSLandon J. Fuller 
48377cb4d3eSLandon J. Fuller 				/* Conflicts with all other length
48477cb4d3eSLandon J. Fuller 				 * specifications, and may only occur once */
48577cb4d3eSLandon J. Fuller 				if (lenc != '\0') {
48677cb4d3eSLandon J. Fuller 					BHND_NV_LOG("invalid length modifier "
48777cb4d3eSLandon J. Fuller 					    "%c\n", *p);
48877cb4d3eSLandon J. Fuller 					return (EINVAL);
48977cb4d3eSLandon J. Fuller 				}
49077cb4d3eSLandon J. Fuller 
49177cb4d3eSLandon J. Fuller 				lenc = *p;
49277cb4d3eSLandon J. Fuller 
49377cb4d3eSLandon J. Fuller 				/* Parse the length specifier value */
49477cb4d3eSLandon J. Fuller 				p++;
49577cb4d3eSLandon J. Fuller 				bits = strtoul(p, &endp, 10);
49677cb4d3eSLandon J. Fuller 				if (p == endp) {
49777cb4d3eSLandon J. Fuller 					BHND_NV_LOG("invalid size specifier: "
49877cb4d3eSLandon J. Fuller 					    "%s\n", p);
49977cb4d3eSLandon J. Fuller 					return (EINVAL);
50077cb4d3eSLandon J. Fuller 				}
50177cb4d3eSLandon J. Fuller 
50277cb4d3eSLandon J. Fuller 				/* Advance input past the parsed integer */
50377cb4d3eSLandon J. Fuller 				np = endp;
50477cb4d3eSLandon J. Fuller 				break;
50577cb4d3eSLandon J. Fuller 			}
50677cb4d3eSLandon J. Fuller 			default:
50777cb4d3eSLandon J. Fuller 				/* Non-length modifier character */
50877cb4d3eSLandon J. Fuller 				stop = true;
50977cb4d3eSLandon J. Fuller 				break;
51077cb4d3eSLandon J. Fuller 			}
51177cb4d3eSLandon J. Fuller 
51277cb4d3eSLandon J. Fuller 			if (stop)
51377cb4d3eSLandon J. Fuller 				break;
51477cb4d3eSLandon J. Fuller 			else
51577cb4d3eSLandon J. Fuller 				p = np;
51677cb4d3eSLandon J. Fuller 		}
51777cb4d3eSLandon J. Fuller 
51877cb4d3eSLandon J. Fuller 		/* Parse conversion specifier and format the value(s) */
51977cb4d3eSLandon J. Fuller 		for (u_long n = 0; n < repeat; n++) {
52077cb4d3eSLandon J. Fuller 			bhnd_nvram_type	arg_type;
52177cb4d3eSLandon J. Fuller 			size_t		arg_size;
52277cb4d3eSLandon J. Fuller 			size_t		i;
52377cb4d3eSLandon J. Fuller 			u_long		base;
52477cb4d3eSLandon J. Fuller 			bool		is_signed, is_upper;
52577cb4d3eSLandon J. Fuller 
52677cb4d3eSLandon J. Fuller 			is_signed = false;
52777cb4d3eSLandon J. Fuller 			is_upper = false;
52877cb4d3eSLandon J. Fuller 			base = 0;
52977cb4d3eSLandon J. Fuller 
53077cb4d3eSLandon J. Fuller 			/* Fetch next element */
53177cb4d3eSLandon J. Fuller 			elem = bhnd_nvram_val_next(value, elem, &elen);
53277cb4d3eSLandon J. Fuller 			if (elem == NULL) {
53377cb4d3eSLandon J. Fuller 				BHND_NV_LOG("format string references more "
53477cb4d3eSLandon J. Fuller 				    "than %zu available value elements\n",
53577cb4d3eSLandon J. Fuller 				    bhnd_nvram_val_nelem(value));
53677cb4d3eSLandon J. Fuller 				return (EINVAL);
53777cb4d3eSLandon J. Fuller 			}
53877cb4d3eSLandon J. Fuller 
53977cb4d3eSLandon J. Fuller 			/*
54077cb4d3eSLandon J. Fuller 			 * If this is not the first value, append the delimiter.
54177cb4d3eSLandon J. Fuller 			 */
54277cb4d3eSLandon J. Fuller 			if (n > 0) {
54377cb4d3eSLandon J. Fuller 				size_t nremain = 0;
54477cb4d3eSLandon J. Fuller 				if (limit > nbytes)
54577cb4d3eSLandon J. Fuller 					nremain = limit - nbytes;
54677cb4d3eSLandon J. Fuller 
54777cb4d3eSLandon J. Fuller 				if (nremain >= delim_len)
54877cb4d3eSLandon J. Fuller 					memcpy(outp + nbytes, delim, delim_len);
54977cb4d3eSLandon J. Fuller 
55077cb4d3eSLandon J. Fuller 				/* Add delimiter length to the total byte count */
55177cb4d3eSLandon J. Fuller 				if (SIZE_MAX - nbytes < delim_len)
55277cb4d3eSLandon J. Fuller 					return (EFTYPE); /* overflows size_t */
55377cb4d3eSLandon J. Fuller 
55477cb4d3eSLandon J. Fuller 				nbytes += delim_len;
55577cb4d3eSLandon J. Fuller 			}
55677cb4d3eSLandon J. Fuller 
55777cb4d3eSLandon J. Fuller 			/* Parse integer conversion specifiers */
55877cb4d3eSLandon J. Fuller 			switch (*p) {
55977cb4d3eSLandon J. Fuller 			case 'd':
56077cb4d3eSLandon J. Fuller 			case 'i':
56177cb4d3eSLandon J. Fuller 				base = 10;
56277cb4d3eSLandon J. Fuller 				is_signed = true;
56377cb4d3eSLandon J. Fuller 				break;
56477cb4d3eSLandon J. Fuller 
56577cb4d3eSLandon J. Fuller 			case 'u':
56677cb4d3eSLandon J. Fuller 				base = 10;
56777cb4d3eSLandon J. Fuller 				break;
56877cb4d3eSLandon J. Fuller 
56977cb4d3eSLandon J. Fuller 			case 'o':
57077cb4d3eSLandon J. Fuller 				base = 8;
57177cb4d3eSLandon J. Fuller 				break;
57277cb4d3eSLandon J. Fuller 
57377cb4d3eSLandon J. Fuller 			case 'x':
57477cb4d3eSLandon J. Fuller 				base = 16;
57577cb4d3eSLandon J. Fuller 				break;
57677cb4d3eSLandon J. Fuller 
57777cb4d3eSLandon J. Fuller 			case 'X':
57877cb4d3eSLandon J. Fuller 				base = 16;
57977cb4d3eSLandon J. Fuller 				is_upper = true;
58077cb4d3eSLandon J. Fuller 				break;
58177cb4d3eSLandon J. Fuller 			}
58277cb4d3eSLandon J. Fuller 
58377cb4d3eSLandon J. Fuller 			/* Format argument */
58477cb4d3eSLandon J. Fuller 			switch (*p) {
58577cb4d3eSLandon J. Fuller #define	NV_ENCODE_INT(_width) do { 					\
58677cb4d3eSLandon J. Fuller 	arg_type = (is_signed) ? BHND_NVRAM_TYPE_INT ## _width :	\
58777cb4d3eSLandon J. Fuller 	    BHND_NVRAM_TYPE_UINT ## _width;				\
58877cb4d3eSLandon J. Fuller 	arg_size = sizeof(v.u ## _width);				\
58977cb4d3eSLandon J. Fuller 	error = bhnd_nvram_val_encode_elem(value, elem, elen,		\
59077cb4d3eSLandon J. Fuller 	    &v.u ## _width, &arg_size, arg_type);			\
59177cb4d3eSLandon J. Fuller 	if (error) {							\
59277cb4d3eSLandon J. Fuller 		BHND_NV_LOG("error encoding argument as %s: %d\n",	\
59377cb4d3eSLandon J. Fuller 		     bhnd_nvram_type_name(arg_type), error);		\
59477cb4d3eSLandon J. Fuller 		return (error);						\
59577cb4d3eSLandon J. Fuller 	}								\
59677cb4d3eSLandon J. Fuller 									\
59777cb4d3eSLandon J. Fuller 	if (is_signed) {						\
59877cb4d3eSLandon J. Fuller 		if (v.i ## _width < 0) {				\
59977cb4d3eSLandon J. Fuller 			add_neg = true;					\
60077cb4d3eSLandon J. Fuller 			numval = (int64_t)-(v.i ## _width);		\
60177cb4d3eSLandon J. Fuller 		} else {						\
60277cb4d3eSLandon J. Fuller 			numval = (int64_t) (v.i ## _width);		\
60377cb4d3eSLandon J. Fuller 		}							\
60477cb4d3eSLandon J. Fuller 	} else {							\
60577cb4d3eSLandon J. Fuller 		numval = v.u ## _width;					\
60677cb4d3eSLandon J. Fuller 	}								\
60777cb4d3eSLandon J. Fuller } while(0)
60877cb4d3eSLandon J. Fuller 			case 'd':
60977cb4d3eSLandon J. Fuller 			case 'i':
61077cb4d3eSLandon J. Fuller 			case 'u':
61177cb4d3eSLandon J. Fuller 			case 'o':
61277cb4d3eSLandon J. Fuller 			case 'x':
61377cb4d3eSLandon J. Fuller 			case 'X': {
61477cb4d3eSLandon J. Fuller 				char		 numbuf[NV_NUMSTR_MAX];
61577cb4d3eSLandon J. Fuller 				char		*sptr;
61677cb4d3eSLandon J. Fuller 				uint64_t	 numval;
61777cb4d3eSLandon J. Fuller 				size_t		 slen;
61877cb4d3eSLandon J. Fuller 				bool		 add_neg;
61977cb4d3eSLandon J. Fuller 				union {
62077cb4d3eSLandon J. Fuller 					uint8_t		u8;
62177cb4d3eSLandon J. Fuller 					uint16_t	u16;
62277cb4d3eSLandon J. Fuller 					uint32_t	u32;
62377cb4d3eSLandon J. Fuller 					uint64_t	u64;
62477cb4d3eSLandon J. Fuller 					int8_t		i8;
62577cb4d3eSLandon J. Fuller 					int16_t		i16;
62677cb4d3eSLandon J. Fuller 					int32_t		i32;
62777cb4d3eSLandon J. Fuller 					int64_t		i64;
62877cb4d3eSLandon J. Fuller 				} v;
62977cb4d3eSLandon J. Fuller 
63077cb4d3eSLandon J. Fuller 				add_neg = false;
63177cb4d3eSLandon J. Fuller 
63277cb4d3eSLandon J. Fuller 				/* If precision is specified, it overrides
63377cb4d3eSLandon J. Fuller 				 * (and behaves identically) to a zero-prefixed
63477cb4d3eSLandon J. Fuller 				 * minimum width */
63577cb4d3eSLandon J. Fuller 				if (have_precision) {
63677cb4d3eSLandon J. Fuller 					padc = '0';
63777cb4d3eSLandon J. Fuller 					width = precision;
63877cb4d3eSLandon J. Fuller 					ladjust = false;
63977cb4d3eSLandon J. Fuller 				}
64077cb4d3eSLandon J. Fuller 
64177cb4d3eSLandon J. Fuller 				/* If zero-padding is used, value must be right
64277cb4d3eSLandon J. Fuller 				 * adjusted */
64377cb4d3eSLandon J. Fuller 				if (padc == '0')
64477cb4d3eSLandon J. Fuller 					ladjust = false;
64577cb4d3eSLandon J. Fuller 
64677cb4d3eSLandon J. Fuller 				/* Request encode to the appropriate integer
64777cb4d3eSLandon J. Fuller 				 * type, and then promote to common 64-bit
64877cb4d3eSLandon J. Fuller 				 * representation */
64977cb4d3eSLandon J. Fuller 				switch (bits) {
65077cb4d3eSLandon J. Fuller 				case 8:
65177cb4d3eSLandon J. Fuller 					NV_ENCODE_INT(8);
65277cb4d3eSLandon J. Fuller 					break;
65377cb4d3eSLandon J. Fuller 				case 16:
65477cb4d3eSLandon J. Fuller 					NV_ENCODE_INT(16);
65577cb4d3eSLandon J. Fuller 					break;
65677cb4d3eSLandon J. Fuller 				case 32:
65777cb4d3eSLandon J. Fuller 					NV_ENCODE_INT(32);
65877cb4d3eSLandon J. Fuller 					break;
65977cb4d3eSLandon J. Fuller 				case 64:
66077cb4d3eSLandon J. Fuller 					NV_ENCODE_INT(64);
66177cb4d3eSLandon J. Fuller 					break;
66277cb4d3eSLandon J. Fuller 				default:
66377cb4d3eSLandon J. Fuller 					BHND_NV_LOG("invalid length specifier: "
66477cb4d3eSLandon J. Fuller 					    "%lu\n", bits);
66577cb4d3eSLandon J. Fuller 					return (EINVAL);
66677cb4d3eSLandon J. Fuller 				}
66777cb4d3eSLandon J. Fuller #undef	NV_ENCODE_INT
66877cb4d3eSLandon J. Fuller 
66977cb4d3eSLandon J. Fuller 				/* If a precision of 0 is specified and the
67077cb4d3eSLandon J. Fuller 				 * value is also zero, no characters should
67177cb4d3eSLandon J. Fuller 				 * be produced */
67277cb4d3eSLandon J. Fuller 				if (have_precision && precision == 0 &&
67377cb4d3eSLandon J. Fuller 				    numval == 0)
67477cb4d3eSLandon J. Fuller 				{
67577cb4d3eSLandon J. Fuller 					break;
67677cb4d3eSLandon J. Fuller 				}
67777cb4d3eSLandon J. Fuller 
67877cb4d3eSLandon J. Fuller 				/* Emit string representation to local buffer */
67977cb4d3eSLandon J. Fuller 				BHND_NV_ASSERT(base <= 16, ("invalid base"));
68077cb4d3eSLandon J. Fuller 				sptr = numbuf + nitems(numbuf) - 1;
68177cb4d3eSLandon J. Fuller 				for (slen = 0; slen < sizeof(numbuf); slen++) {
68277cb4d3eSLandon J. Fuller 					char		c;
68377cb4d3eSLandon J. Fuller 					uint64_t	n;
68477cb4d3eSLandon J. Fuller 
68577cb4d3eSLandon J. Fuller 					n = numval % base;
68677cb4d3eSLandon J. Fuller 					c = bhnd_nv_hex2ascii(n);
68777cb4d3eSLandon J. Fuller 					if (is_upper)
68877cb4d3eSLandon J. Fuller 						c = bhnd_nv_toupper(c);
68977cb4d3eSLandon J. Fuller 
69077cb4d3eSLandon J. Fuller 					sptr--;
69177cb4d3eSLandon J. Fuller 					*sptr = c;
69277cb4d3eSLandon J. Fuller 
69377cb4d3eSLandon J. Fuller 					numval /= (uint64_t)base;
69477cb4d3eSLandon J. Fuller 					if (numval == 0) {
69577cb4d3eSLandon J. Fuller 						slen++;
69677cb4d3eSLandon J. Fuller 						break;
69777cb4d3eSLandon J. Fuller 					}
69877cb4d3eSLandon J. Fuller 				}
69977cb4d3eSLandon J. Fuller 
70077cb4d3eSLandon J. Fuller 				arg_size = slen;
70177cb4d3eSLandon J. Fuller 
70277cb4d3eSLandon J. Fuller 				/* Reserve space for 0/0x prefix? */
70377cb4d3eSLandon J. Fuller 				if (alt_form) {
70477cb4d3eSLandon J. Fuller 					if (numval == 0) {
70577cb4d3eSLandon J. Fuller 						/* If 0, no prefix */
70677cb4d3eSLandon J. Fuller 						alt_form = false;
70777cb4d3eSLandon J. Fuller 					} else if (base == 8) {
70877cb4d3eSLandon J. Fuller 						arg_size += 1; /* 0 */
70977cb4d3eSLandon J. Fuller 					} else if (base == 16) {
71077cb4d3eSLandon J. Fuller 						arg_size += 2; /* 0x/0X */
71177cb4d3eSLandon J. Fuller 					}
71277cb4d3eSLandon J. Fuller 				}
71377cb4d3eSLandon J. Fuller 
71477cb4d3eSLandon J. Fuller 				/* Reserve space for ' ', '+', or '-' prefix? */
71577cb4d3eSLandon J. Fuller 				if (add_neg || signc != '\0') {
71677cb4d3eSLandon J. Fuller 					if (add_neg)
71777cb4d3eSLandon J. Fuller 						signc = '-';
71877cb4d3eSLandon J. Fuller 
71977cb4d3eSLandon J. Fuller 					arg_size++;
72077cb4d3eSLandon J. Fuller 				}
72177cb4d3eSLandon J. Fuller 
72277cb4d3eSLandon J. Fuller 				/* Right adjust (if using spaces) */
72377cb4d3eSLandon J. Fuller 				if (!ladjust && padc != '0') {
72477cb4d3eSLandon J. Fuller 					for (i = arg_size;  i < width; i++)
72577cb4d3eSLandon J. Fuller 						WRITE_CHAR(padc);
72677cb4d3eSLandon J. Fuller 				}
72777cb4d3eSLandon J. Fuller 
72877cb4d3eSLandon J. Fuller 				if (signc != '\0')
72977cb4d3eSLandon J. Fuller 					WRITE_CHAR(signc);
73077cb4d3eSLandon J. Fuller 
73177cb4d3eSLandon J. Fuller 				if (alt_form) {
73277cb4d3eSLandon J. Fuller 					if (base == 8) {
73377cb4d3eSLandon J. Fuller 						WRITE_CHAR('0');
73477cb4d3eSLandon J. Fuller 					} else if (base == 16) {
73577cb4d3eSLandon J. Fuller 						WRITE_CHAR('0');
73677cb4d3eSLandon J. Fuller 						if (is_upper)
73777cb4d3eSLandon J. Fuller 							WRITE_CHAR('X');
73877cb4d3eSLandon J. Fuller 						else
73977cb4d3eSLandon J. Fuller 							WRITE_CHAR('x');
74077cb4d3eSLandon J. Fuller 					}
74177cb4d3eSLandon J. Fuller 				}
74277cb4d3eSLandon J. Fuller 
74377cb4d3eSLandon J. Fuller 				/* Right adjust (if using zeros) */
74477cb4d3eSLandon J. Fuller 				if (!ladjust && padc == '0') {
74577cb4d3eSLandon J. Fuller 					for (i = slen;  i < width; i++)
74677cb4d3eSLandon J. Fuller 						WRITE_CHAR(padc);
74777cb4d3eSLandon J. Fuller 				}
74877cb4d3eSLandon J. Fuller 
74977cb4d3eSLandon J. Fuller 				/* Write the string to our output buffer */
75077cb4d3eSLandon J. Fuller 				if (limit > nbytes && limit - nbytes >= slen)
75177cb4d3eSLandon J. Fuller 					memcpy(outp + nbytes, sptr, slen);
75277cb4d3eSLandon J. Fuller 
75377cb4d3eSLandon J. Fuller 				/* Update the total byte count */
75477cb4d3eSLandon J. Fuller 				if (SIZE_MAX - nbytes < arg_size)
75577cb4d3eSLandon J. Fuller 					return (EFTYPE); /* overflows size_t */
75677cb4d3eSLandon J. Fuller 
75777cb4d3eSLandon J. Fuller 				nbytes += arg_size;
75877cb4d3eSLandon J. Fuller 
75977cb4d3eSLandon J. Fuller 				/* Left adjust */
76077cb4d3eSLandon J. Fuller 				for (i = arg_size; ladjust && i < width; i++)
76177cb4d3eSLandon J. Fuller 					WRITE_CHAR(padc);
76277cb4d3eSLandon J. Fuller 
76377cb4d3eSLandon J. Fuller 				break;
76477cb4d3eSLandon J. Fuller 			}
76577cb4d3eSLandon J. Fuller 
76677cb4d3eSLandon J. Fuller 			case 's': {
76777cb4d3eSLandon J. Fuller 				char	*s;
76877cb4d3eSLandon J. Fuller 				size_t	 slen;
76977cb4d3eSLandon J. Fuller 
77077cb4d3eSLandon J. Fuller 				/* Query the total length of the element when
77177cb4d3eSLandon J. Fuller 				 * converted to a string */
77277cb4d3eSLandon J. Fuller 				arg_type = BHND_NVRAM_TYPE_STRING;
77377cb4d3eSLandon J. Fuller 				error = bhnd_nvram_val_encode_elem(value, elem,
77477cb4d3eSLandon J. Fuller 				    elen, NULL, &arg_size, arg_type);
77577cb4d3eSLandon J. Fuller 				if (error) {
77677cb4d3eSLandon J. Fuller 					BHND_NV_LOG("error encoding argument "
77777cb4d3eSLandon J. Fuller 					    "as %s: %d\n",
77877cb4d3eSLandon J. Fuller 					    bhnd_nvram_type_name(arg_type),
77977cb4d3eSLandon J. Fuller 					    error);
78077cb4d3eSLandon J. Fuller 					return (error);
78177cb4d3eSLandon J. Fuller 				}
78277cb4d3eSLandon J. Fuller 
78377cb4d3eSLandon J. Fuller 				/* Do not include trailing NUL in the string
78477cb4d3eSLandon J. Fuller 				 * length */
78577cb4d3eSLandon J. Fuller 				if (arg_size > 0)
78677cb4d3eSLandon J. Fuller 					arg_size--;
78777cb4d3eSLandon J. Fuller 
78877cb4d3eSLandon J. Fuller 				/* Right adjust */
78977cb4d3eSLandon J. Fuller 				for (i = arg_size; !ladjust && i < width; i++)
79077cb4d3eSLandon J. Fuller 					WRITE_CHAR(padc);
79177cb4d3eSLandon J. Fuller 
79277cb4d3eSLandon J. Fuller 				/* Determine output positition and remaining
79377cb4d3eSLandon J. Fuller 				 * buffer space */
79477cb4d3eSLandon J. Fuller 				if (limit > nbytes) {
79577cb4d3eSLandon J. Fuller 					s = outp + nbytes;
79677cb4d3eSLandon J. Fuller 					slen = limit - nbytes;
79777cb4d3eSLandon J. Fuller 				} else {
79877cb4d3eSLandon J. Fuller 					s = NULL;
79977cb4d3eSLandon J. Fuller 					slen = 0;
80077cb4d3eSLandon J. Fuller 				}
80177cb4d3eSLandon J. Fuller 
80277cb4d3eSLandon J. Fuller 				/* Encode the string to our output buffer */
80377cb4d3eSLandon J. Fuller 				error = bhnd_nvram_val_encode_elem(value, elem,
80477cb4d3eSLandon J. Fuller 				    elen, s, &slen, arg_type);
80577cb4d3eSLandon J. Fuller 				if (error && error != ENOMEM) {
80677cb4d3eSLandon J. Fuller 					BHND_NV_LOG("error encoding argument "
80777cb4d3eSLandon J. Fuller 					    "as %s: %d\n",
80877cb4d3eSLandon J. Fuller 					    bhnd_nvram_type_name(arg_type),
80977cb4d3eSLandon J. Fuller 					    error);
81077cb4d3eSLandon J. Fuller 					return (error);
81177cb4d3eSLandon J. Fuller 				}
81277cb4d3eSLandon J. Fuller 
81377cb4d3eSLandon J. Fuller 				/* Update the total byte count */
81477cb4d3eSLandon J. Fuller 				if (SIZE_MAX - nbytes < arg_size)
81577cb4d3eSLandon J. Fuller 					return (EFTYPE); /* overflows size_t */
81677cb4d3eSLandon J. Fuller 
81777cb4d3eSLandon J. Fuller 				nbytes += arg_size;
81877cb4d3eSLandon J. Fuller 
81977cb4d3eSLandon J. Fuller 				/* Left adjust */
82077cb4d3eSLandon J. Fuller 				for (i = arg_size; ladjust && i < width; i++)
82177cb4d3eSLandon J. Fuller 					WRITE_CHAR(padc);
82277cb4d3eSLandon J. Fuller 
82377cb4d3eSLandon J. Fuller 				break;
82477cb4d3eSLandon J. Fuller 			}
82577cb4d3eSLandon J. Fuller 
82677cb4d3eSLandon J. Fuller 			case 'c': {
82777cb4d3eSLandon J. Fuller 				char c;
82877cb4d3eSLandon J. Fuller 
82977cb4d3eSLandon J. Fuller 				arg_type = BHND_NVRAM_TYPE_CHAR;
8309be0790dSLandon J. Fuller 				arg_size = bhnd_nvram_type_width(arg_type);
83177cb4d3eSLandon J. Fuller 
83277cb4d3eSLandon J. Fuller 				/* Encode as single character */
83377cb4d3eSLandon J. Fuller 				error = bhnd_nvram_val_encode_elem(value, elem,
83477cb4d3eSLandon J. Fuller 				    elen, &c, &arg_size, arg_type);
83577cb4d3eSLandon J. Fuller 				if (error) {
83677cb4d3eSLandon J. Fuller 					BHND_NV_LOG("error encoding argument "
83777cb4d3eSLandon J. Fuller 					    "as %s: %d\n",
83877cb4d3eSLandon J. Fuller 					    bhnd_nvram_type_name(arg_type),
83977cb4d3eSLandon J. Fuller 					    error);
84077cb4d3eSLandon J. Fuller 					return (error);
84177cb4d3eSLandon J. Fuller 				}
84277cb4d3eSLandon J. Fuller 
84377cb4d3eSLandon J. Fuller 				BHND_NV_ASSERT(arg_size == sizeof(c),
84477cb4d3eSLandon J. Fuller 				    ("invalid encoded size"));
84577cb4d3eSLandon J. Fuller 
84677cb4d3eSLandon J. Fuller 				/* Right adjust */
84777cb4d3eSLandon J. Fuller 				for (i = arg_size; !ladjust && i < width; i++)
84877cb4d3eSLandon J. Fuller 					WRITE_CHAR(padc);
84977cb4d3eSLandon J. Fuller 
85077cb4d3eSLandon J. Fuller 				WRITE_CHAR(padc);
85177cb4d3eSLandon J. Fuller 
85277cb4d3eSLandon J. Fuller 				/* Left adjust */
85377cb4d3eSLandon J. Fuller 				for (i = arg_size; ladjust && i < width; i++)
85477cb4d3eSLandon J. Fuller 					WRITE_CHAR(padc);
85577cb4d3eSLandon J. Fuller 
85677cb4d3eSLandon J. Fuller 				break;
85777cb4d3eSLandon J. Fuller 			}
85877cb4d3eSLandon J. Fuller 			}
85977cb4d3eSLandon J. Fuller 		}
86077cb4d3eSLandon J. Fuller 	}
86177cb4d3eSLandon J. Fuller 
86277cb4d3eSLandon J. Fuller 	/* Append terminating NUL */
86377cb4d3eSLandon J. Fuller 	if (limit > nbytes)
86477cb4d3eSLandon J. Fuller 		*(outp + nbytes) = '\0';
86577cb4d3eSLandon J. Fuller 
86677cb4d3eSLandon J. Fuller 	if (nbytes < SIZE_MAX)
86777cb4d3eSLandon J. Fuller 		nbytes++;
86877cb4d3eSLandon J. Fuller 	else
86977cb4d3eSLandon J. Fuller 		return (EFTYPE);
87077cb4d3eSLandon J. Fuller 
87177cb4d3eSLandon J. Fuller 	/* Report required space */
87277cb4d3eSLandon J. Fuller 	*olen = nbytes;
87377cb4d3eSLandon J. Fuller 	if (limit < nbytes) {
87477cb4d3eSLandon J. Fuller 		if (outp != NULL)
87577cb4d3eSLandon J. Fuller 			return (ENOMEM);
87677cb4d3eSLandon J. Fuller 	}
87777cb4d3eSLandon J. Fuller 
87877cb4d3eSLandon J. Fuller 	return (0);
87977cb4d3eSLandon J. Fuller }
880