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