aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c')
-rw-r--r--sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c883
1 files changed, 883 insertions, 0 deletions
diff --git a/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c b/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c
new file mode 100644
index 000000000000..ad57fd609050
--- /dev/null
+++ b/sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c
@@ -0,0 +1,883 @@
+/*-
+ * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * without modification.
+ * 2. Redistributions in binary form must reproduce at minimum a disclaimer
+ * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
+ * redistribution must be conditioned upon including a substantially
+ * similar Disclaimer requirement for further binary redistribution.
+ *
+ * NO WARRANTY
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
+ * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGES.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/sbuf.h>
+
+#ifdef _KERNEL
+
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+
+#include <machine/_inttypes.h>
+
+#else /* !_KERNEL */
+
+#include <ctype.h>
+#include <inttypes.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+
+#endif /* _KERNEL */
+
+#include "bhnd_nvram_private.h"
+#include "bhnd_nvram_valuevar.h"
+
+#ifdef _KERNEL
+#define bhnd_nv_hex2ascii(hex) hex2ascii(hex)
+#else /* !_KERNEL */
+static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz";
+#define bhnd_nv_hex2ascii(hex) (bhnd_nv_hex2ascii[hex])
+#endif /* _KERNEL */
+
+/**
+ * Maximum size, in bytes, of a string-encoded NVRAM integer value, not
+ * including any prefix (0x, 0, etc).
+ *
+ * We assume the largest possible encoding is the base-2 representation
+ * of a 64-bit integer.
+ */
+#define NV_NUMSTR_MAX ((sizeof(uint64_t) * CHAR_BIT) + 1)
+
+/**
+ * Format a string representation of @p value using @p fmt, with, writing the
+ * result to @p outp.
+ *
+ * @param value The value to be formatted.
+ * @param fmt The format string.
+ * @param[out] outp On success, the string will be written to this
+ * buffer. This argment may be NULL if the value is
+ * not desired.
+ * @param[in,out] olen The capacity of @p outp. On success, will be set
+ * to the actual number of bytes required for the
+ * requested string encoding (including a trailing
+ * NUL).
+ *
+ * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
+ *
+ * @retval 0 success
+ * @retval EINVAL If @p fmt contains unrecognized format string
+ * specifiers.
+ * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
+ * is too small to hold the encoded value.
+ * @retval EFTYPE If value coercion from @p value to a single string
+ * value via @p fmt is unsupported.
+ * @retval ERANGE If value coercion of @p value would overflow (or
+ * underflow) the representation defined by @p fmt.
+ */
+int
+bhnd_nvram_val_printf(bhnd_nvram_val_t *value, const char *fmt, char *outp,
+ size_t *olen, ...)
+{
+ va_list ap;
+ int error;
+
+ va_start(ap, olen);
+ error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap);
+ va_end(ap);
+
+ return (error);
+}
+
+
+/**
+ * Format a string representation of the elements of @p value using @p fmt,
+ * writing the result to @p outp.
+ *
+ * @param value The value to be formatted.
+ * @param fmt The format string.
+ * @param[out] outp On success, the string will be written to this
+ * buffer. This argment may be NULL if the value is
+ * not desired.
+ * @param[in,out] olen The capacity of @p outp. On success, will be set
+ * to the actual number of bytes required for the
+ * requested string encoding (including a trailing
+ * NUL).
+ * @param ap Argument list.
+ *
+ * @par Format Strings
+ *
+ * Value format strings are similar, but not identical to, those used
+ * by printf(3).
+ *
+ * Format specifier format:
+ * %[repeat][flags][width][.precision][length modifier][specifier]
+ *
+ * The format specifier is interpreted as an encoding directive for an
+ * individual value element; each format specifier will fetch the next element
+ * from the value, encode the element as the appropriate type based on the
+ * length modifiers and specifier, and then format the result as a string.
+ *
+ * For example, given a string value of '0x000F', and a format specifier of
+ * '%#hhx', the value will be asked to encode its first element as
+ * BHND_NVRAM_TYPE_UINT8. String formatting will then be applied to the 8-bit
+ * unsigned integer representation, producing a string value of "0xF".
+ *
+ * Repeat:
+ * - [digits] Repeatedly apply the format specifier to the input
+ * value's elements up to `digits` times. The delimiter
+ * must be passed as a string in the next variadic
+ * argument.
+ * - [] Repeatedly apply the format specifier to the input
+ * value's elements until all elements have been. The
+ * processed. The delimiter must be passed as a string in
+ * the next variadic argument.
+ * - [*] Repeatedly apply the format specifier to the input
+ * value's elements. The repeat count is read from the
+ * next variadic argument as a size_t value
+ *
+ * Flags:
+ * - '#' use alternative form (e.g. 0x/0X prefixing of hex
+ * strings).
+ * - '0' zero padding
+ * - '-' left adjust padding
+ * - '+' include a sign character
+ * - ' ' include a space in place of a sign character for
+ * positive numbers.
+ *
+ * Width/Precision:
+ * - digits minimum field width.
+ * - * read the minimum field width from the next variadic
+ * argument as a ssize_t value. A negative value enables
+ * left adjustment.
+ * - .digits field precision.
+ * - .* read the field precision from the next variadic argument
+ * as a ssize_t value. A negative value enables left
+ * adjustment.
+ *
+ * Length Modifiers:
+ * - 'hh', 'I8' Convert the value to an 8-bit signed or unsigned
+ * integer.
+ * - 'h', 'I16' Convert the value to an 16-bit signed or unsigned
+ * integer.
+ * - 'l', 'I32' Convert the value to an 32-bit signed or unsigned
+ * integer.
+ * - 'll', 'j', 'I64' Convert the value to an 64-bit signed or unsigned
+ * integer.
+ *
+ * Data Specifiers:
+ * - 'd', 'i' Convert and format as a signed decimal integer.
+ * - 'u' Convert and format as an unsigned decimal integer.
+ * - 'o' Convert and format as an unsigned octal integer.
+ * - 'x' Convert and format as an unsigned hexadecimal integer,
+ * using lowercase hex digits.
+ * - 'X' Convert and format as an unsigned hexadecimal integer,
+ * using uppercase hex digits.
+ * - 's' Convert and format as a string.
+ * - '%' Print a literal '%' character.
+ *
+ * @retval 0 success
+ * @retval EINVAL If @p fmt contains unrecognized format string
+ * specifiers.
+ * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
+ * is too small to hold the encoded value.
+ * @retval EFTYPE If value coercion from @p value to a single string
+ * value via @p fmt is unsupported.
+ * @retval ERANGE If value coercion of @p value would overflow (or
+ * underflow) the representation defined by @p fmt.
+ */
+int
+bhnd_nvram_val_vprintf(bhnd_nvram_val_t *value, const char *fmt, char *outp,
+ size_t *olen, va_list ap)
+{
+ const void *elem;
+ size_t elen;
+ size_t limit, nbytes;
+ int error;
+
+ elem = NULL;
+
+ /* Determine output byte limit */
+ nbytes = 0;
+ if (outp != NULL)
+ limit = *olen;
+ else
+ limit = 0;
+
+#define WRITE_CHAR(_c) do { \
+ if (limit > nbytes) \
+ *(outp + nbytes) = _c; \
+ \
+ if (nbytes == SIZE_MAX) \
+ return (EFTYPE); \
+ nbytes++; \
+} while (0)
+
+ /* Encode string value as per the format string */
+ for (const char *p = fmt; *p != '\0'; p++) {
+ const char *delim;
+ size_t precision, width, delim_len;
+ u_long repeat, bits;
+ bool alt_form, ladjust, have_precision;
+ char padc, signc, lenc;
+
+ padc = ' ';
+ signc = '\0';
+ lenc = '\0';
+ delim = "";
+ delim_len = 0;
+
+ ladjust = false;
+ alt_form = false;
+
+ have_precision = false;
+ precision = 1;
+ bits = 32;
+ width = 0;
+ repeat = 1;
+
+ /* Copy all input to output until we hit a format specifier */
+ if (*p != '%') {
+ WRITE_CHAR(*p);
+ continue;
+ }
+
+ /* Hit '%' -- is this followed by an escaped '%' literal? */
+ p++;
+ if (*p == '%') {
+ WRITE_CHAR('%');
+ p++;
+ continue;
+ }
+
+ /* Parse repeat specifier */
+ if (*p == '[') {
+ p++;
+
+ /* Determine repeat count */
+ if (*p == ']') {
+ /* Repeat consumes all input */
+ repeat = bhnd_nvram_val_nelem(value);
+ } else if (*p == '*') {
+ /* Repeat is supplied as an argument */
+ repeat = va_arg(ap, size_t);
+ p++;
+ } else {
+ char *endp;
+
+ /* Repeat specified as argument */
+ repeat = strtoul(p, &endp, 10);
+ if (p == endp) {
+ BHND_NV_LOG("error parsing repeat "
+ "count at '%s'", p);
+ return (EINVAL);
+ }
+
+ /* Advance past repeat count */
+ p = endp;
+ }
+
+ /* Advance past terminating ']' */
+ if (*p != ']') {
+ BHND_NV_LOG("error parsing repeat count at "
+ "'%s'", p);
+ return (EINVAL);
+ }
+ p++;
+
+ delim = va_arg(ap, const char *);
+ delim_len = strlen(delim);
+ }
+
+ /* Parse flags */
+ while (*p != '\0') {
+ const char *np;
+ bool stop;
+
+ stop = false;
+ np = p+1;
+
+ switch (*p) {
+ case '#':
+ alt_form = true;
+ break;
+ case '0':
+ padc = '0';
+ break;
+ case '-':
+ ladjust = true;
+ break;
+ case ' ':
+ /* Must not override '+' */
+ if (signc != '+')
+ signc = ' ';
+ break;
+ case '+':
+ signc = '+';
+ break;
+ default:
+ /* Non-flag character */
+ stop = true;
+ break;
+ }
+
+ if (stop)
+ break;
+ else
+ p = np;
+ }
+
+ /* Parse minimum width */
+ if (*p == '*') {
+ ssize_t arg;
+
+ /* Width is supplied as an argument */
+ arg = va_arg(ap, int);
+
+ /* Negative width argument is interpreted as
+ * '-' flag followed by positive width */
+ if (arg < 0) {
+ ladjust = true;
+ arg = -arg;
+ }
+
+ width = arg;
+ p++;
+ } else if (bhnd_nv_isdigit(*p)) {
+ uint32_t v;
+ size_t len, parsed;
+
+ /* Parse width value */
+ len = sizeof(v);
+ error = bhnd_nvram_parse_int(p, strlen(p), 10, &parsed,
+ &v, &len, BHND_NVRAM_TYPE_UINT32);
+ if (error) {
+ BHND_NV_LOG("error parsing width %s: %d\n", p,
+ error);
+ return (EINVAL);
+ }
+
+ /* Save width and advance input */
+ width = v;
+ p += parsed;
+ }
+
+ /* Parse precision */
+ if (*p == '.') {
+ uint32_t v;
+ size_t len, parsed;
+
+ p++;
+ have_precision = true;
+
+ if (*p == '*') {
+ ssize_t arg;
+
+ /* Precision is specified as an argument */
+ arg = va_arg(ap, int);
+
+ /* Negative precision argument is interpreted
+ * as '-' flag followed by positive
+ * precision */
+ if (arg < 0) {
+ ladjust = true;
+ arg = -arg;
+ }
+
+ precision = arg;
+ } else if (!bhnd_nv_isdigit(*p)) {
+ /* Implicit precision of 0 */
+ precision = 0;
+ } else {
+ /* Parse precision value */
+ len = sizeof(v);
+ error = bhnd_nvram_parse_int(p, strlen(p), 10,
+ &parsed, &v, &len,
+ BHND_NVRAM_TYPE_UINT32);
+ if (error) {
+ BHND_NV_LOG("error parsing width %s: "
+ "%d\n", p, error);
+ return (EINVAL);
+ }
+
+ /* Save precision and advance input */
+ precision = v;
+ p += parsed;
+ }
+ }
+
+ /* Parse length modifiers */
+ while (*p != '\0') {
+ const char *np;
+ bool stop;
+
+ stop = false;
+ np = p+1;
+
+ switch (*p) {
+ case 'h':
+ if (lenc == '\0') {
+ /* Set initial length value */
+ lenc = *p;
+ bits = 16;
+ } else if (lenc == *p && bits == 16) {
+ /* Modify previous length value */
+ bits = 8;
+ } else {
+ BHND_NV_LOG("invalid length modifier "
+ "%c\n", *p);
+ return (EINVAL);
+ }
+ break;
+
+ case 'l':
+ if (lenc == '\0') {
+ /* Set initial length value */
+ lenc = *p;
+ bits = 32;
+ } else if (lenc == *p && bits == 32) {
+ /* Modify previous length value */
+ bits = 64;
+ } else {
+ BHND_NV_LOG("invalid length modifier "
+ "%c\n", *p);
+ return (EINVAL);
+ }
+ break;
+
+ case 'j':
+ /* Conflicts with all other length
+ * specifications, and may only occur once */
+ if (lenc != '\0') {
+ BHND_NV_LOG("invalid length modifier "
+ "%c\n", *p);
+ return (EINVAL);
+ }
+
+ lenc = *p;
+ bits = 64;
+ break;
+
+ case 'I': {
+ char *endp;
+
+ /* Conflicts with all other length
+ * specifications, and may only occur once */
+ if (lenc != '\0') {
+ BHND_NV_LOG("invalid length modifier "
+ "%c\n", *p);
+ return (EINVAL);
+ }
+
+ lenc = *p;
+
+ /* Parse the length specifier value */
+ p++;
+ bits = strtoul(p, &endp, 10);
+ if (p == endp) {
+ BHND_NV_LOG("invalid size specifier: "
+ "%s\n", p);
+ return (EINVAL);
+ }
+
+ /* Advance input past the parsed integer */
+ np = endp;
+ break;
+ }
+ default:
+ /* Non-length modifier character */
+ stop = true;
+ break;
+ }
+
+ if (stop)
+ break;
+ else
+ p = np;
+ }
+
+ /* Parse conversion specifier and format the value(s) */
+ for (u_long n = 0; n < repeat; n++) {
+ bhnd_nvram_type arg_type;
+ size_t arg_size;
+ size_t i;
+ u_long base;
+ bool is_signed, is_upper;
+
+ is_signed = false;
+ is_upper = false;
+ base = 0;
+
+ /* Fetch next element */
+ elem = bhnd_nvram_val_next(value, elem, &elen);
+ if (elem == NULL) {
+ BHND_NV_LOG("format string references more "
+ "than %zu available value elements\n",
+ bhnd_nvram_val_nelem(value));
+ return (EINVAL);
+ }
+
+ /*
+ * If this is not the first value, append the delimiter.
+ */
+ if (n > 0) {
+ size_t nremain = 0;
+ if (limit > nbytes)
+ nremain = limit - nbytes;
+
+ if (nremain >= delim_len)
+ memcpy(outp + nbytes, delim, delim_len);
+
+ /* Add delimiter length to the total byte count */
+ if (SIZE_MAX - nbytes < delim_len)
+ return (EFTYPE); /* overflows size_t */
+
+ nbytes += delim_len;
+ }
+
+ /* Parse integer conversion specifiers */
+ switch (*p) {
+ case 'd':
+ case 'i':
+ base = 10;
+ is_signed = true;
+ break;
+
+ case 'u':
+ base = 10;
+ break;
+
+ case 'o':
+ base = 8;
+ break;
+
+ case 'x':
+ base = 16;
+ break;
+
+ case 'X':
+ base = 16;
+ is_upper = true;
+ break;
+ }
+
+ /* Format argument */
+ switch (*p) {
+#define NV_ENCODE_INT(_width) do { \
+ arg_type = (is_signed) ? BHND_NVRAM_TYPE_INT ## _width : \
+ BHND_NVRAM_TYPE_UINT ## _width; \
+ arg_size = sizeof(v.u ## _width); \
+ error = bhnd_nvram_val_encode_elem(value, elem, elen, \
+ &v.u ## _width, &arg_size, arg_type); \
+ if (error) { \
+ BHND_NV_LOG("error encoding argument as %s: %d\n", \
+ bhnd_nvram_type_name(arg_type), error); \
+ return (error); \
+ } \
+ \
+ if (is_signed) { \
+ if (v.i ## _width < 0) { \
+ add_neg = true; \
+ numval = (int64_t)-(v.i ## _width); \
+ } else { \
+ numval = (int64_t) (v.i ## _width); \
+ } \
+ } else { \
+ numval = v.u ## _width; \
+ } \
+} while(0)
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X': {
+ char numbuf[NV_NUMSTR_MAX];
+ char *sptr;
+ uint64_t numval;
+ size_t slen;
+ bool add_neg;
+ union {
+ uint8_t u8;
+ uint16_t u16;
+ uint32_t u32;
+ uint64_t u64;
+ int8_t i8;
+ int16_t i16;
+ int32_t i32;
+ int64_t i64;
+ } v;
+
+ add_neg = false;
+
+ /* If precision is specified, it overrides
+ * (and behaves identically) to a zero-prefixed
+ * minimum width */
+ if (have_precision) {
+ padc = '0';
+ width = precision;
+ ladjust = false;
+ }
+
+ /* If zero-padding is used, value must be right
+ * adjusted */
+ if (padc == '0')
+ ladjust = false;
+
+ /* Request encode to the appropriate integer
+ * type, and then promote to common 64-bit
+ * representation */
+ switch (bits) {
+ case 8:
+ NV_ENCODE_INT(8);
+ break;
+ case 16:
+ NV_ENCODE_INT(16);
+ break;
+ case 32:
+ NV_ENCODE_INT(32);
+ break;
+ case 64:
+ NV_ENCODE_INT(64);
+ break;
+ default:
+ BHND_NV_LOG("invalid length specifier: "
+ "%lu\n", bits);
+ return (EINVAL);
+ }
+#undef NV_ENCODE_INT
+
+ /* If a precision of 0 is specified and the
+ * value is also zero, no characters should
+ * be produced */
+ if (have_precision && precision == 0 &&
+ numval == 0)
+ {
+ break;
+ }
+
+ /* Emit string representation to local buffer */
+ BHND_NV_ASSERT(base <= 16, ("invalid base"));
+ sptr = numbuf + nitems(numbuf) - 1;
+ for (slen = 0; slen < sizeof(numbuf); slen++) {
+ char c;
+ uint64_t n;
+
+ n = numval % base;
+ c = bhnd_nv_hex2ascii(n);
+ if (is_upper)
+ c = bhnd_nv_toupper(c);
+
+ sptr--;
+ *sptr = c;
+
+ numval /= (uint64_t)base;
+ if (numval == 0) {
+ slen++;
+ break;
+ }
+ }
+
+ arg_size = slen;
+
+ /* Reserve space for 0/0x prefix? */
+ if (alt_form) {
+ if (numval == 0) {
+ /* If 0, no prefix */
+ alt_form = false;
+ } else if (base == 8) {
+ arg_size += 1; /* 0 */
+ } else if (base == 16) {
+ arg_size += 2; /* 0x/0X */
+ }
+ }
+
+ /* Reserve space for ' ', '+', or '-' prefix? */
+ if (add_neg || signc != '\0') {
+ if (add_neg)
+ signc = '-';
+
+ arg_size++;
+ }
+
+ /* Right adjust (if using spaces) */
+ if (!ladjust && padc != '0') {
+ for (i = arg_size; i < width; i++)
+ WRITE_CHAR(padc);
+ }
+
+ if (signc != '\0')
+ WRITE_CHAR(signc);
+
+ if (alt_form) {
+ if (base == 8) {
+ WRITE_CHAR('0');
+ } else if (base == 16) {
+ WRITE_CHAR('0');
+ if (is_upper)
+ WRITE_CHAR('X');
+ else
+ WRITE_CHAR('x');
+ }
+ }
+
+ /* Right adjust (if using zeros) */
+ if (!ladjust && padc == '0') {
+ for (i = slen; i < width; i++)
+ WRITE_CHAR(padc);
+ }
+
+ /* Write the string to our output buffer */
+ if (limit > nbytes && limit - nbytes >= slen)
+ memcpy(outp + nbytes, sptr, slen);
+
+ /* Update the total byte count */
+ if (SIZE_MAX - nbytes < arg_size)
+ return (EFTYPE); /* overflows size_t */
+
+ nbytes += arg_size;
+
+ /* Left adjust */
+ for (i = arg_size; ladjust && i < width; i++)
+ WRITE_CHAR(padc);
+
+ break;
+ }
+
+ case 's': {
+ char *s;
+ size_t slen;
+
+ /* Query the total length of the element when
+ * converted to a string */
+ arg_type = BHND_NVRAM_TYPE_STRING;
+ error = bhnd_nvram_val_encode_elem(value, elem,
+ elen, NULL, &arg_size, arg_type);
+ if (error) {
+ BHND_NV_LOG("error encoding argument "
+ "as %s: %d\n",
+ bhnd_nvram_type_name(arg_type),
+ error);
+ return (error);
+ }
+
+ /* Do not include trailing NUL in the string
+ * length */
+ if (arg_size > 0)
+ arg_size--;
+
+ /* Right adjust */
+ for (i = arg_size; !ladjust && i < width; i++)
+ WRITE_CHAR(padc);
+
+ /* Determine output positition and remaining
+ * buffer space */
+ if (limit > nbytes) {
+ s = outp + nbytes;
+ slen = limit - nbytes;
+ } else {
+ s = NULL;
+ slen = 0;
+ }
+
+ /* Encode the string to our output buffer */
+ error = bhnd_nvram_val_encode_elem(value, elem,
+ elen, s, &slen, arg_type);
+ if (error && error != ENOMEM) {
+ BHND_NV_LOG("error encoding argument "
+ "as %s: %d\n",
+ bhnd_nvram_type_name(arg_type),
+ error);
+ return (error);
+ }
+
+ /* Update the total byte count */
+ if (SIZE_MAX - nbytes < arg_size)
+ return (EFTYPE); /* overflows size_t */
+
+ nbytes += arg_size;
+
+ /* Left adjust */
+ for (i = arg_size; ladjust && i < width; i++)
+ WRITE_CHAR(padc);
+
+ break;
+ }
+
+ case 'c': {
+ char c;
+
+ arg_type = BHND_NVRAM_TYPE_CHAR;
+ arg_size = bhnd_nvram_value_size(arg_type, NULL,
+ 0, 1);
+
+ /* Encode as single character */
+ error = bhnd_nvram_val_encode_elem(value, elem,
+ elen, &c, &arg_size, arg_type);
+ if (error) {
+ BHND_NV_LOG("error encoding argument "
+ "as %s: %d\n",
+ bhnd_nvram_type_name(arg_type),
+ error);
+ return (error);
+ }
+
+ BHND_NV_ASSERT(arg_size == sizeof(c),
+ ("invalid encoded size"));
+
+ /* Right adjust */
+ for (i = arg_size; !ladjust && i < width; i++)
+ WRITE_CHAR(padc);
+
+ WRITE_CHAR(padc);
+
+ /* Left adjust */
+ for (i = arg_size; ladjust && i < width; i++)
+ WRITE_CHAR(padc);
+
+ break;
+ }
+ }
+ }
+ }
+
+ /* Append terminating NUL */
+ if (limit > nbytes)
+ *(outp + nbytes) = '\0';
+
+ if (nbytes < SIZE_MAX)
+ nbytes++;
+ else
+ return (EFTYPE);
+
+ /* Report required space */
+ *olen = nbytes;
+ if (limit < nbytes) {
+ if (outp != NULL)
+ return (ENOMEM);
+ }
+
+ return (0);
+}