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 */
59static 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 */
98int
99bhnd_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 */
208int
209bhnd_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