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