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