]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/bhnd/nvram/bhnd_nvram_value_prf.c
MFV r353608: 10165 libzpool: passing argument 1 to restrict-qualified parameter
[FreeBSD/FreeBSD.git] / sys / dev / bhnd / nvram / bhnd_nvram_value_prf.c
1 /*-
2  * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
15  *
16  * NO WARRANTY
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.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/limits.h>
35 #include <sys/sbuf.h>
36
37 #ifdef _KERNEL
38
39 #include <sys/ctype.h>
40 #include <sys/kernel.h>
41 #include <sys/malloc.h>
42 #include <sys/systm.h>
43
44 #include <machine/_inttypes.h>
45
46 #else /* !_KERNEL */
47
48 #include <ctype.h>
49 #include <inttypes.h>
50 #include <errno.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 #endif /* _KERNEL */
55
56 #include "bhnd_nvram_private.h"
57 #include "bhnd_nvram_valuevar.h"
58
59 #ifdef _KERNEL
60 #define bhnd_nv_hex2ascii(hex)  hex2ascii(hex)
61 #else /* !_KERNEL */
62 static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz";
63 #define bhnd_nv_hex2ascii(hex)          (bhnd_nv_hex2ascii[hex])
64 #endif /* _KERNEL */
65
66 /**
67  * Maximum size, in bytes, of a string-encoded NVRAM integer value, not
68  * including any prefix (0x, 0, etc).
69  * 
70  * We assume the largest possible encoding is the base-2 representation
71  * of a 64-bit integer.
72  */
73 #define NV_NUMSTR_MAX   ((sizeof(uint64_t) * CHAR_BIT) + 1)
74
75 /**
76  * Format a string representation of @p value using @p fmt, with, writing the
77  * result to @p outp.
78  *
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
83  *                              not desired.
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
87  *                              NUL).
88  * 
89  * Refer to bhnd_nvram_val_vprintf() for full format string documentation.
90  *
91  * @retval 0            success
92  * @retval EINVAL       If @p fmt contains unrecognized format string
93  *                      specifiers.
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.
100  */
101 int
102 bhnd_nvram_val_printf(bhnd_nvram_val *value, const char *fmt, char *outp,
103     size_t *olen, ...)
104 {
105         va_list ap;
106         int     error;
107
108         va_start(ap, olen);
109         error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap);
110         va_end(ap);
111
112         return (error);
113 }
114
115
116 /**
117  * Format a string representation of the elements of @p value using @p fmt,
118  * writing the result to @p outp.
119  *
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
124  *                              not desired.
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
128  *                              NUL).
129  * @param               ap      Argument list.
130  *
131  * @par Format Strings
132  * 
133  * Value format strings are similar, but not identical to, those used
134  * by printf(3).
135  * 
136  * Format specifier format:
137  *     %[repeat][flags][width][.precision][length modifier][specifier]
138  *
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.
143  * 
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".
148  * 
149  * Repeat:
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
153  *                      argument.
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
161  * 
162  * Flags:
163  * - '#'                use alternative form (e.g. 0x/0X prefixing of hex
164  *                      strings).
165  * - '0'                zero padding
166  * - '-'                left adjust padding
167  * - '+'                include a sign character
168  * - ' '                include a space in place of a sign character for
169  *                      positive numbers.
170  * 
171  * Width/Precision:
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
175  *                      left adjustment.
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
179  *                      adjustment.
180  *
181  * Length Modifiers:
182  * - 'hh', 'I8'         Convert the value to an 8-bit signed or unsigned
183  *                      integer.
184  * - 'h', 'I16'         Convert the value to an 16-bit signed or unsigned
185  *                      integer.
186  * - 'l', 'I32'         Convert the value to an 32-bit signed or unsigned
187  *                      integer.
188  * - 'll', 'j', 'I64'   Convert the value to an 64-bit signed or unsigned
189  *                      integer.
190  * 
191  * Data Specifiers:
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.
201  *
202  * @retval 0            success
203  * @retval EINVAL       If @p fmt contains unrecognized format string
204  *                      specifiers.
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.
211  */
212 int
213 bhnd_nvram_val_vprintf(bhnd_nvram_val *value, const char *fmt, char *outp,
214     size_t *olen, va_list ap)
215 {
216         const void      *elem;
217         size_t           elen;
218         size_t           limit, nbytes;
219         int              error;
220
221         elem = NULL;
222
223         /* Determine output byte limit */
224         nbytes = 0;
225         if (outp != NULL)
226                 limit = *olen;
227         else
228                 limit = 0;
229
230 #define WRITE_CHAR(_c)  do {                    \
231         if (limit > nbytes)                     \
232                 *(outp + nbytes) = _c;          \
233                                                 \
234         if (nbytes == SIZE_MAX)                 \
235                 return (EFTYPE);                \
236         nbytes++;                               \
237 } while (0)
238
239         /* Encode string value as per the format string */
240         for (const char *p = fmt; *p != '\0'; p++) {
241                 const char      *delim;
242                 size_t           precision, width, delim_len;
243                 u_long           repeat, bits;
244                 bool             alt_form, ladjust, have_precision;
245                 char             padc, signc, lenc;
246
247                 padc = ' ';
248                 signc = '\0';
249                 lenc = '\0';
250                 delim = "";
251                 delim_len = 0;
252
253                 ladjust = false;
254                 alt_form = false;
255
256                 have_precision = false;
257                 precision = 1;
258                 bits = 32;
259                 width = 0;
260                 repeat = 1;
261
262                 /* Copy all input to output until we hit a format specifier */
263                 if (*p != '%') {
264                         WRITE_CHAR(*p);
265                         continue;
266                 }
267
268                 /* Hit '%' -- is this followed by an escaped '%' literal? */
269                 p++;
270                 if (*p == '%') {
271                         WRITE_CHAR('%');
272                         p++;
273                         continue;
274                 }
275
276                 /* Parse repeat specifier */
277                 if (*p == '[') {
278                         p++;
279                         
280                         /* Determine repeat count */
281                         if (*p == ']') {
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);
287                                 p++;
288                         } else {
289                                 char *endp;
290
291                                 /* Repeat specified as argument */
292                                 repeat = strtoul(p, &endp, 10);
293                                 if (p == endp) {
294                                         BHND_NV_LOG("error parsing repeat "
295                                                     "count at '%s'", p);
296                                         return (EINVAL);
297                                 }
298                                 
299                                 /* Advance past repeat count */
300                                 p = endp;
301                         }
302
303                         /* Advance past terminating ']' */
304                         if (*p != ']') {
305                                 BHND_NV_LOG("error parsing repeat count at "
306                                     "'%s'", p);
307                                 return (EINVAL);
308                         }
309                         p++;
310
311                         delim = va_arg(ap, const char *);
312                         delim_len = strlen(delim);
313                 }
314
315                 /* Parse flags */
316                 while (*p != '\0') {
317                         const char      *np;
318                         bool             stop;
319
320                         stop = false;
321                         np = p+1;
322         
323                         switch (*p) {
324                         case '#':
325                                 alt_form = true;
326                                 break;
327                         case '0':
328                                 padc = '0';
329                                 break;
330                         case '-':
331                                 ladjust = true;
332                                 break;
333                         case ' ':
334                                 /* Must not override '+' */
335                                 if (signc != '+')
336                                         signc = ' ';
337                                 break;
338                         case '+':
339                                 signc = '+';
340                                 break;
341                         default:
342                                 /* Non-flag character */
343                                 stop = true;
344                                 break;
345                         }
346
347                         if (stop)
348                                 break;
349                         else
350                                 p = np;
351                 }
352
353                 /* Parse minimum width */
354                 if (*p == '*') {
355                         ssize_t arg;
356
357                         /* Width is supplied as an argument */
358                         arg = va_arg(ap, int);
359
360                         /* Negative width argument is interpreted as
361                          * '-' flag followed by positive width */
362                         if (arg < 0) {
363                                 ladjust = true;
364                                 arg = -arg;
365                         }
366
367                         width = arg;
368                         p++;
369                 } else if (bhnd_nv_isdigit(*p)) {
370                         uint32_t        v;
371                         size_t          len, parsed;
372
373                         /* Parse width value */
374                         len = sizeof(v);
375                         error = bhnd_nvram_parse_int(p, strlen(p), 10, &parsed,
376                             &v, &len, BHND_NVRAM_TYPE_UINT32);
377                         if (error) {
378                                 BHND_NV_LOG("error parsing width %s: %d\n", p,
379                                     error);
380                                 return (EINVAL);
381                         }
382
383                         /* Save width and advance input */
384                         width = v;
385                         p += parsed;
386                 }
387
388                 /* Parse precision */
389                 if (*p == '.') {
390                         uint32_t        v;
391                         size_t          len, parsed;
392
393                         p++;
394                         have_precision = true;
395
396                         if (*p == '*') {
397                                 ssize_t arg;
398
399                                 /* Precision is specified as an argument */
400                                 arg = va_arg(ap, int);
401
402                                 /* Negative precision argument is interpreted
403                                  * as '-' flag followed by positive
404                                  * precision */
405                                 if (arg < 0) {
406                                         ladjust = true;
407                                         arg = -arg;
408                                 }
409
410                                 precision = arg;
411                         } else if (!bhnd_nv_isdigit(*p)) {
412                                 /* Implicit precision of 0 */
413                                 precision = 0;
414                         } else {
415                                 /* Parse precision value */
416                                 len = sizeof(v);
417                                 error = bhnd_nvram_parse_int(p, strlen(p), 10,
418                                     &parsed, &v, &len,
419                                     BHND_NVRAM_TYPE_UINT32);
420                                 if (error) {
421                                         BHND_NV_LOG("error parsing width %s: "
422                                             "%d\n", p, error);
423                                         return (EINVAL);
424                                 }
425
426                                 /* Save precision and advance input */
427                                 precision = v;
428                                 p += parsed;
429                         }
430                 }
431
432                 /* Parse length modifiers */
433                 while (*p != '\0') {
434                         const char      *np;
435                         bool             stop;
436                         
437                         stop = false;
438                         np = p+1;
439
440                         switch (*p) {
441                         case 'h':
442                                 if (lenc == '\0') {
443                                         /* Set initial length value */
444                                         lenc = *p;
445                                         bits = 16;
446                                 } else if (lenc == *p && bits == 16) {
447                                         /* Modify previous length value */
448                                         bits = 8;
449                                 } else {
450                                         BHND_NV_LOG("invalid length modifier "
451                                             "%c\n", *p);
452                                         return (EINVAL);
453                                 }
454                                 break;
455
456                         case 'l':
457                                 if (lenc == '\0') {
458                                         /* Set initial length value */
459                                         lenc = *p;
460                                         bits = 32;
461                                 } else if (lenc == *p && bits == 32) {
462                                         /* Modify previous length value */
463                                         bits = 64;
464                                 } else {
465                                         BHND_NV_LOG("invalid length modifier "
466                                             "%c\n", *p);
467                                         return (EINVAL);
468                                 }
469                                 break;
470
471                         case 'j':
472                                 /* Conflicts with all other length
473                                  * specifications, and may only occur once */
474                                 if (lenc != '\0') {
475                                         BHND_NV_LOG("invalid length modifier "
476                                             "%c\n", *p);
477                                         return (EINVAL);
478                                 }
479
480                                 lenc = *p;
481                                 bits = 64;
482                                 break;
483
484                         case 'I': {
485                                 char    *endp;
486
487                                 /* Conflicts with all other length
488                                  * specifications, and may only occur once */
489                                 if (lenc != '\0') {
490                                         BHND_NV_LOG("invalid length modifier "
491                                             "%c\n", *p);
492                                         return (EINVAL);
493                                 }
494
495                                 lenc = *p;
496
497                                 /* Parse the length specifier value */
498                                 p++;
499                                 bits = strtoul(p, &endp, 10);
500                                 if (p == endp) {
501                                         BHND_NV_LOG("invalid size specifier: "
502                                             "%s\n", p);
503                                         return (EINVAL);
504                                 }
505
506                                 /* Advance input past the parsed integer */
507                                 np = endp;
508                                 break;
509                         }
510                         default:
511                                 /* Non-length modifier character */
512                                 stop = true;
513                                 break;
514                         }
515
516                         if (stop)
517                                 break;
518                         else
519                                 p = np;
520                 }
521
522                 /* Parse conversion specifier and format the value(s) */
523                 for (u_long n = 0; n < repeat; n++) {
524                         bhnd_nvram_type arg_type;
525                         size_t          arg_size;
526                         size_t          i;
527                         u_long          base;
528                         bool            is_signed, is_upper;
529
530                         is_signed = false;
531                         is_upper = false;
532                         base = 0;
533
534                         /* Fetch next element */
535                         elem = bhnd_nvram_val_next(value, elem, &elen);
536                         if (elem == NULL) {
537                                 BHND_NV_LOG("format string references more "
538                                     "than %zu available value elements\n",
539                                     bhnd_nvram_val_nelem(value));
540                                 return (EINVAL);
541                         }
542
543                         /*
544                          * If this is not the first value, append the delimiter.
545                          */
546                         if (n > 0) {
547                                 size_t nremain = 0;
548                                 if (limit > nbytes)
549                                         nremain = limit - nbytes;
550         
551                                 if (nremain >= delim_len)
552                                         memcpy(outp + nbytes, delim, delim_len);
553
554                                 /* Add delimiter length to the total byte count */
555                                 if (SIZE_MAX - nbytes < delim_len)
556                                         return (EFTYPE); /* overflows size_t */
557
558                                 nbytes += delim_len;
559                         }
560
561                         /* Parse integer conversion specifiers */
562                         switch (*p) {
563                         case 'd':
564                         case 'i':
565                                 base = 10;
566                                 is_signed = true;
567                                 break;
568
569                         case 'u':
570                                 base = 10;
571                                 break;
572
573                         case 'o':
574                                 base = 8;
575                                 break;
576
577                         case 'x':
578                                 base = 16;
579                                 break;
580
581                         case 'X':
582                                 base = 16;
583                                 is_upper = true;
584                                 break;
585                         }
586
587                         /* Format argument */
588                         switch (*p) {
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);                       \
595         if (error) {                                                    \
596                 BHND_NV_LOG("error encoding argument as %s: %d\n",      \
597                      bhnd_nvram_type_name(arg_type), error);            \
598                 return (error);                                         \
599         }                                                               \
600                                                                         \
601         if (is_signed) {                                                \
602                 if (v.i ## _width < 0) {                                \
603                         add_neg = true;                                 \
604                         numval = (int64_t)-(v.i ## _width);             \
605                 } else {                                                \
606                         numval = (int64_t) (v.i ## _width);             \
607                 }                                                       \
608         } else {                                                        \
609                 numval = v.u ## _width;                                 \
610         }                                                               \
611 } while(0)
612                         case 'd':
613                         case 'i':
614                         case 'u':
615                         case 'o':
616                         case 'x':
617                         case 'X': {
618                                 char             numbuf[NV_NUMSTR_MAX];
619                                 char            *sptr;
620                                 uint64_t         numval;
621                                 size_t           slen;
622                                 bool             add_neg;
623                                 union {
624                                         uint8_t         u8;
625                                         uint16_t        u16;
626                                         uint32_t        u32;
627                                         uint64_t        u64;
628                                         int8_t          i8;
629                                         int16_t         i16;
630                                         int32_t         i32;
631                                         int64_t         i64;
632                                 } v;
633
634                                 add_neg = false;
635
636                                 /* If precision is specified, it overrides
637                                  * (and behaves identically) to a zero-prefixed
638                                  * minimum width */
639                                 if (have_precision) {
640                                         padc = '0';
641                                         width = precision;
642                                         ladjust = false;
643                                 }
644
645                                 /* If zero-padding is used, value must be right
646                                  * adjusted */
647                                 if (padc == '0')
648                                         ladjust = false;
649
650                                 /* Request encode to the appropriate integer
651                                  * type, and then promote to common 64-bit
652                                  * representation */
653                                 switch (bits) {
654                                 case 8:
655                                         NV_ENCODE_INT(8);
656                                         break;
657                                 case 16:
658                                         NV_ENCODE_INT(16);
659                                         break;
660                                 case 32:
661                                         NV_ENCODE_INT(32);
662                                         break;
663                                 case 64:
664                                         NV_ENCODE_INT(64);
665                                         break;
666                                 default:
667                                         BHND_NV_LOG("invalid length specifier: "
668                                             "%lu\n", bits);
669                                         return (EINVAL);
670                                 }
671 #undef  NV_ENCODE_INT
672
673                                 /* If a precision of 0 is specified and the
674                                  * value is also zero, no characters should
675                                  * be produced */
676                                 if (have_precision && precision == 0 &&
677                                     numval == 0)
678                                 {
679                                         break;
680                                 }
681
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++) {
686                                         char            c;
687                                         uint64_t        n;
688
689                                         n = numval % base;
690                                         c = bhnd_nv_hex2ascii(n);
691                                         if (is_upper)
692                                                 c = bhnd_nv_toupper(c);
693
694                                         sptr--;
695                                         *sptr = c;
696
697                                         numval /= (uint64_t)base;
698                                         if (numval == 0) {
699                                                 slen++;
700                                                 break;
701                                         }
702                                 }
703
704                                 arg_size = slen;
705
706                                 /* Reserve space for 0/0x prefix? */
707                                 if (alt_form) {
708                                         if (numval == 0) {
709                                                 /* If 0, no prefix */
710                                                 alt_form = false;
711                                         } else if (base == 8) {
712                                                 arg_size += 1; /* 0 */
713                                         } else if (base == 16) {
714                                                 arg_size += 2; /* 0x/0X */
715                                         }
716                                 }
717
718                                 /* Reserve space for ' ', '+', or '-' prefix? */
719                                 if (add_neg || signc != '\0') {
720                                         if (add_neg)
721                                                 signc = '-';
722
723                                         arg_size++;
724                                 }
725
726                                 /* Right adjust (if using spaces) */
727                                 if (!ladjust && padc != '0') {
728                                         for (i = arg_size;  i < width; i++)
729                                                 WRITE_CHAR(padc);
730                                 }
731
732                                 if (signc != '\0')
733                                         WRITE_CHAR(signc);
734
735                                 if (alt_form) {
736                                         if (base == 8) {
737                                                 WRITE_CHAR('0');
738                                         } else if (base == 16) {
739                                                 WRITE_CHAR('0');
740                                                 if (is_upper)
741                                                         WRITE_CHAR('X');
742                                                 else
743                                                         WRITE_CHAR('x');
744                                         }
745                                 }
746
747                                 /* Right adjust (if using zeros) */
748                                 if (!ladjust && padc == '0') {
749                                         for (i = slen;  i < width; i++)
750                                                 WRITE_CHAR(padc);
751                                 }
752
753                                 /* Write the string to our output buffer */
754                                 if (limit > nbytes && limit - nbytes >= slen)
755                                         memcpy(outp + nbytes, sptr, slen);
756
757                                 /* Update the total byte count */
758                                 if (SIZE_MAX - nbytes < arg_size)
759                                         return (EFTYPE); /* overflows size_t */
760
761                                 nbytes += arg_size;
762
763                                 /* Left adjust */
764                                 for (i = arg_size; ladjust && i < width; i++)
765                                         WRITE_CHAR(padc);
766
767                                 break;
768                         }
769
770                         case 's': {
771                                 char    *s;
772                                 size_t   slen;
773
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);
779                                 if (error) {
780                                         BHND_NV_LOG("error encoding argument "
781                                             "as %s: %d\n",
782                                             bhnd_nvram_type_name(arg_type),
783                                             error);
784                                         return (error);
785                                 }
786
787                                 /* Do not include trailing NUL in the string
788                                  * length */
789                                 if (arg_size > 0)
790                                         arg_size--;
791
792                                 /* Right adjust */
793                                 for (i = arg_size; !ladjust && i < width; i++)
794                                         WRITE_CHAR(padc);
795
796                                 /* Determine output positition and remaining
797                                  * buffer space */
798                                 if (limit > nbytes) {
799                                         s = outp + nbytes;
800                                         slen = limit - nbytes;
801                                 } else {
802                                         s = NULL;
803                                         slen = 0;
804                                 }
805
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 "
811                                             "as %s: %d\n",
812                                             bhnd_nvram_type_name(arg_type),
813                                             error);
814                                         return (error);
815                                 }
816
817                                 /* Update the total byte count */
818                                 if (SIZE_MAX - nbytes < arg_size)
819                                         return (EFTYPE); /* overflows size_t */
820
821                                 nbytes += arg_size;
822
823                                 /* Left adjust */
824                                 for (i = arg_size; ladjust && i < width; i++)
825                                         WRITE_CHAR(padc);
826
827                                 break;
828                         }
829
830                         case 'c': {
831                                 char c;
832
833                                 arg_type = BHND_NVRAM_TYPE_CHAR;
834                                 arg_size = bhnd_nvram_type_width(arg_type);
835
836                                 /* Encode as single character */
837                                 error = bhnd_nvram_val_encode_elem(value, elem,
838                                     elen, &c, &arg_size, arg_type);
839                                 if (error) {
840                                         BHND_NV_LOG("error encoding argument "
841                                             "as %s: %d\n",
842                                             bhnd_nvram_type_name(arg_type),
843                                             error);
844                                         return (error);
845                                 }
846
847                                 BHND_NV_ASSERT(arg_size == sizeof(c),
848                                     ("invalid encoded size"));
849
850                                 /* Right adjust */
851                                 for (i = arg_size; !ladjust && i < width; i++)
852                                         WRITE_CHAR(padc);
853
854                                 WRITE_CHAR(padc);
855
856                                 /* Left adjust */
857                                 for (i = arg_size; ladjust && i < width; i++)
858                                         WRITE_CHAR(padc);
859
860                                 break;
861                         }
862                         }
863                 }
864         }
865
866         /* Append terminating NUL */
867         if (limit > nbytes)
868                 *(outp + nbytes) = '\0';
869
870         if (nbytes < SIZE_MAX)
871                 nbytes++;
872         else
873                 return (EFTYPE);
874
875         /* Report required space */
876         *olen = nbytes;
877         if (limit < nbytes) {
878                 if (outp != NULL)
879                         return (ENOMEM);
880         }
881
882         return (0);
883 }