2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include <sys/param.h>
34 #include <sys/limits.h>
39 #include <sys/ctype.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/systm.h>
44 #include <machine/_inttypes.h>
56 #include "bhnd_nvram_private.h"
57 #include "bhnd_nvram_valuevar.h"
60 #define bhnd_nv_hex2ascii(hex) hex2ascii(hex)
62 static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz";
63 #define bhnd_nv_hex2ascii(hex) (bhnd_nv_hex2ascii[hex])
67 * Maximum size, in bytes, of a string-encoded NVRAM integer value, not
68 * including any prefix (0x, 0, etc).
70 * We assume the largest possible encoding is the base-2 representation
71 * of a 64-bit integer.
73 #define NV_NUMSTR_MAX ((sizeof(uint64_t) * CHAR_BIT) + 1)
76 * Format a string representation of @p value using @p fmt, with, writing the
79 * @param value The value to be formatted.
80 * @param fmt The format string.
81 * @param[out] outp On success, the string will be written to this
82 * buffer. This argment may be NULL if the value is
84 * @param[in,out] olen The capacity of @p outp. On success, will be set
85 * to the actual number of bytes required for the
86 * requested string encoding (including a trailing
89 * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
92 * @retval EINVAL If @p fmt contains unrecognized format string
94 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
95 * is too small to hold the encoded value.
96 * @retval EFTYPE If value coercion from @p value to a single string
97 * value via @p fmt is unsupported.
98 * @retval ERANGE If value coercion of @p value would overflow (or
99 * underflow) the representation defined by @p fmt.
102 bhnd_nvram_val_printf(bhnd_nvram_val *value, const char *fmt, char *outp,
109 error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap);
117 * Format a string representation of the elements of @p value using @p fmt,
118 * writing the result to @p outp.
120 * @param value The value to be formatted.
121 * @param fmt The format string.
122 * @param[out] outp On success, the string will be written to this
123 * buffer. This argment may be NULL if the value is
125 * @param[in,out] olen The capacity of @p outp. On success, will be set
126 * to the actual number of bytes required for the
127 * requested string encoding (including a trailing
129 * @param ap Argument list.
131 * @par Format Strings
133 * Value format strings are similar, but not identical to, those used
136 * Format specifier format:
137 * %[repeat][flags][width][.precision][length modifier][specifier]
139 * The format specifier is interpreted as an encoding directive for an
140 * individual value element; each format specifier will fetch the next element
141 * from the value, encode the element as the appropriate type based on the
142 * length modifiers and specifier, and then format the result as a string.
144 * For example, given a string value of '0x000F', and a format specifier of
145 * '%#hhx', the value will be asked to encode its first element as
146 * BHND_NVRAM_TYPE_UINT8. String formatting will then be applied to the 8-bit
147 * unsigned integer representation, producing a string value of "0xF".
150 * - [digits] Repeatedly apply the format specifier to the input
151 * value's elements up to `digits` times. The delimiter
152 * must be passed as a string in the next variadic
154 * - [] Repeatedly apply the format specifier to the input
155 * value's elements until all elements have been. The
156 * processed. The delimiter must be passed as a string in
157 * the next variadic argument.
158 * - [*] Repeatedly apply the format specifier to the input
159 * value's elements. The repeat count is read from the
160 * next variadic argument as a size_t value
163 * - '#' use alternative form (e.g. 0x/0X prefixing of hex
166 * - '-' left adjust padding
167 * - '+' include a sign character
168 * - ' ' include a space in place of a sign character for
172 * - digits minimum field width.
173 * - * read the minimum field width from the next variadic
174 * argument as a ssize_t value. A negative value enables
176 * - .digits field precision.
177 * - .* read the field precision from the next variadic argument
178 * as a ssize_t value. A negative value enables left
182 * - 'hh', 'I8' Convert the value to an 8-bit signed or unsigned
184 * - 'h', 'I16' Convert the value to an 16-bit signed or unsigned
186 * - 'l', 'I32' Convert the value to an 32-bit signed or unsigned
188 * - 'll', 'j', 'I64' Convert the value to an 64-bit signed or unsigned
192 * - 'd', 'i' Convert and format as a signed decimal integer.
193 * - 'u' Convert and format as an unsigned decimal integer.
194 * - 'o' Convert and format as an unsigned octal integer.
195 * - 'x' Convert and format as an unsigned hexadecimal integer,
196 * using lowercase hex digits.
197 * - 'X' Convert and format as an unsigned hexadecimal integer,
198 * using uppercase hex digits.
199 * - 's' Convert and format as a string.
200 * - '%' Print a literal '%' character.
203 * @retval EINVAL If @p fmt contains unrecognized format string
205 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen
206 * is too small to hold the encoded value.
207 * @retval EFTYPE If value coercion from @p value to a single string
208 * value via @p fmt is unsupported.
209 * @retval ERANGE If value coercion of @p value would overflow (or
210 * underflow) the representation defined by @p fmt.
213 bhnd_nvram_val_vprintf(bhnd_nvram_val *value, const char *fmt, char *outp,
214 size_t *olen, va_list ap)
218 size_t limit, nbytes;
223 /* Determine output byte limit */
230 #define WRITE_CHAR(_c) do { \
231 if (limit > nbytes) \
232 *(outp + nbytes) = _c; \
234 if (nbytes == SIZE_MAX) \
239 /* Encode string value as per the format string */
240 for (const char *p = fmt; *p != '\0'; p++) {
242 size_t precision, width, delim_len;
244 bool alt_form, ladjust, have_precision;
245 char padc, signc, lenc;
256 have_precision = false;
262 /* Copy all input to output until we hit a format specifier */
268 /* Hit '%' -- is this followed by an escaped '%' literal? */
276 /* Parse repeat specifier */
280 /* Determine repeat count */
282 /* Repeat consumes all input */
283 repeat = bhnd_nvram_val_nelem(value);
284 } else if (*p == '*') {
285 /* Repeat is supplied as an argument */
286 repeat = va_arg(ap, size_t);
291 /* Repeat specified as argument */
292 repeat = strtoul(p, &endp, 10);
294 BHND_NV_LOG("error parsing repeat "
299 /* Advance past repeat count */
303 /* Advance past terminating ']' */
305 BHND_NV_LOG("error parsing repeat count at "
311 delim = va_arg(ap, const char *);
312 delim_len = strlen(delim);
334 /* Must not override '+' */
342 /* Non-flag character */
353 /* Parse minimum width */
357 /* Width is supplied as an argument */
358 arg = va_arg(ap, int);
360 /* Negative width argument is interpreted as
361 * '-' flag followed by positive width */
369 } else if (bhnd_nv_isdigit(*p)) {
373 /* Parse width value */
375 error = bhnd_nvram_parse_int(p, strlen(p), 10, &parsed,
376 &v, &len, BHND_NVRAM_TYPE_UINT32);
378 BHND_NV_LOG("error parsing width %s: %d\n", p,
383 /* Save width and advance input */
388 /* Parse precision */
394 have_precision = true;
399 /* Precision is specified as an argument */
400 arg = va_arg(ap, int);
402 /* Negative precision argument is interpreted
403 * as '-' flag followed by positive
411 } else if (!bhnd_nv_isdigit(*p)) {
412 /* Implicit precision of 0 */
415 /* Parse precision value */
417 error = bhnd_nvram_parse_int(p, strlen(p), 10,
419 BHND_NVRAM_TYPE_UINT32);
421 BHND_NV_LOG("error parsing width %s: "
426 /* Save precision and advance input */
432 /* Parse length modifiers */
443 /* Set initial length value */
446 } else if (lenc == *p && bits == 16) {
447 /* Modify previous length value */
450 BHND_NV_LOG("invalid length modifier "
458 /* Set initial length value */
461 } else if (lenc == *p && bits == 32) {
462 /* Modify previous length value */
465 BHND_NV_LOG("invalid length modifier "
472 /* Conflicts with all other length
473 * specifications, and may only occur once */
475 BHND_NV_LOG("invalid length modifier "
487 /* Conflicts with all other length
488 * specifications, and may only occur once */
490 BHND_NV_LOG("invalid length modifier "
497 /* Parse the length specifier value */
499 bits = strtoul(p, &endp, 10);
501 BHND_NV_LOG("invalid size specifier: "
506 /* Advance input past the parsed integer */
511 /* Non-length modifier character */
522 /* Parse conversion specifier and format the value(s) */
523 for (u_long n = 0; n < repeat; n++) {
524 bhnd_nvram_type arg_type;
528 bool is_signed, is_upper;
534 /* Fetch next element */
535 elem = bhnd_nvram_val_next(value, elem, &elen);
537 BHND_NV_LOG("format string references more "
538 "than %zu available value elements\n",
539 bhnd_nvram_val_nelem(value));
544 * If this is not the first value, append the delimiter.
549 nremain = limit - nbytes;
551 if (nremain >= delim_len)
552 memcpy(outp + nbytes, delim, delim_len);
554 /* Add delimiter length to the total byte count */
555 if (SIZE_MAX - nbytes < delim_len)
556 return (EFTYPE); /* overflows size_t */
561 /* Parse integer conversion specifiers */
587 /* Format argument */
589 #define NV_ENCODE_INT(_width) do { \
590 arg_type = (is_signed) ? BHND_NVRAM_TYPE_INT ## _width : \
591 BHND_NVRAM_TYPE_UINT ## _width; \
592 arg_size = sizeof(v.u ## _width); \
593 error = bhnd_nvram_val_encode_elem(value, elem, elen, \
594 &v.u ## _width, &arg_size, arg_type); \
596 BHND_NV_LOG("error encoding argument as %s: %d\n", \
597 bhnd_nvram_type_name(arg_type), error); \
602 if (v.i ## _width < 0) { \
604 numval = (int64_t)-(v.i ## _width); \
606 numval = (int64_t) (v.i ## _width); \
609 numval = v.u ## _width; \
618 char numbuf[NV_NUMSTR_MAX];
636 /* If precision is specified, it overrides
637 * (and behaves identically) to a zero-prefixed
639 if (have_precision) {
645 /* If zero-padding is used, value must be right
650 /* Request encode to the appropriate integer
651 * type, and then promote to common 64-bit
667 BHND_NV_LOG("invalid length specifier: "
673 /* If a precision of 0 is specified and the
674 * value is also zero, no characters should
676 if (have_precision && precision == 0 &&
682 /* Emit string representation to local buffer */
683 BHND_NV_ASSERT(base <= 16, ("invalid base"));
684 sptr = numbuf + nitems(numbuf) - 1;
685 for (slen = 0; slen < sizeof(numbuf); slen++) {
690 c = bhnd_nv_hex2ascii(n);
692 c = bhnd_nv_toupper(c);
697 numval /= (uint64_t)base;
706 /* Reserve space for 0/0x prefix? */
709 /* If 0, no prefix */
711 } else if (base == 8) {
712 arg_size += 1; /* 0 */
713 } else if (base == 16) {
714 arg_size += 2; /* 0x/0X */
718 /* Reserve space for ' ', '+', or '-' prefix? */
719 if (add_neg || signc != '\0') {
726 /* Right adjust (if using spaces) */
727 if (!ladjust && padc != '0') {
728 for (i = arg_size; i < width; i++)
738 } else if (base == 16) {
747 /* Right adjust (if using zeros) */
748 if (!ladjust && padc == '0') {
749 for (i = slen; i < width; i++)
753 /* Write the string to our output buffer */
754 if (limit > nbytes && limit - nbytes >= slen)
755 memcpy(outp + nbytes, sptr, slen);
757 /* Update the total byte count */
758 if (SIZE_MAX - nbytes < arg_size)
759 return (EFTYPE); /* overflows size_t */
764 for (i = arg_size; ladjust && i < width; i++)
774 /* Query the total length of the element when
775 * converted to a string */
776 arg_type = BHND_NVRAM_TYPE_STRING;
777 error = bhnd_nvram_val_encode_elem(value, elem,
778 elen, NULL, &arg_size, arg_type);
780 BHND_NV_LOG("error encoding argument "
782 bhnd_nvram_type_name(arg_type),
787 /* Do not include trailing NUL in the string
793 for (i = arg_size; !ladjust && i < width; i++)
796 /* Determine output positition and remaining
798 if (limit > nbytes) {
800 slen = limit - nbytes;
806 /* Encode the string to our output buffer */
807 error = bhnd_nvram_val_encode_elem(value, elem,
808 elen, s, &slen, arg_type);
809 if (error && error != ENOMEM) {
810 BHND_NV_LOG("error encoding argument "
812 bhnd_nvram_type_name(arg_type),
817 /* Update the total byte count */
818 if (SIZE_MAX - nbytes < arg_size)
819 return (EFTYPE); /* overflows size_t */
824 for (i = arg_size; ladjust && i < width; i++)
833 arg_type = BHND_NVRAM_TYPE_CHAR;
834 arg_size = bhnd_nvram_type_width(arg_type);
836 /* Encode as single character */
837 error = bhnd_nvram_val_encode_elem(value, elem,
838 elen, &c, &arg_size, arg_type);
840 BHND_NV_LOG("error encoding argument "
842 bhnd_nvram_type_name(arg_type),
847 BHND_NV_ASSERT(arg_size == sizeof(c),
848 ("invalid encoded size"));
851 for (i = arg_size; !ladjust && i < width; i++)
857 for (i = arg_size; ladjust && i < width; i++)
866 /* Append terminating NUL */
868 *(outp + nbytes) = '\0';
870 if (nbytes < SIZE_MAX)
875 /* Report required space */
877 if (limit < nbytes) {