1 /*- 2 * Copyright (c) 2015-2016 Landon Fuller <landonf@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer, 10 * without modification. 11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer 12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 13 * redistribution must be conditioned upon including a substantially 14 * similar Disclaimer requirement for further binary redistribution. 15 * 16 * NO WARRANTY 17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 * THE POSSIBILITY OF SUCH DAMAGES. 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/limits.h> 35 #include <sys/sbuf.h> 36 37 #ifdef _KERNEL 38 39 #include <sys/ctype.h> 40 #include <sys/kernel.h> 41 #include <sys/malloc.h> 42 #include <sys/systm.h> 43 44 #include <machine/_inttypes.h> 45 46 #else /* !_KERNEL */ 47 48 #include <ctype.h> 49 #include <inttypes.h> 50 #include <errno.h> 51 #include <stdlib.h> 52 #include <string.h> 53 54 #endif /* _KERNEL */ 55 56 #include "bhnd_nvram_private.h" 57 #include "bhnd_nvram_valuevar.h" 58 59 #ifdef _KERNEL 60 #define bhnd_nv_hex2ascii(hex) hex2ascii(hex) 61 #else /* !_KERNEL */ 62 static char const bhnd_nv_hex2ascii[] = "0123456789abcdefghijklmnopqrstuvwxyz"; 63 #define bhnd_nv_hex2ascii(hex) (bhnd_nv_hex2ascii[hex]) 64 #endif /* _KERNEL */ 65 66 /** 67 * Maximum size, in bytes, of a string-encoded NVRAM integer value, not 68 * including any prefix (0x, 0, etc). 69 * 70 * We assume the largest possible encoding is the base-2 representation 71 * of a 64-bit integer. 72 */ 73 #define NV_NUMSTR_MAX ((sizeof(uint64_t) * CHAR_BIT) + 1) 74 75 /** 76 * Format a string representation of @p value using @p fmt, with, writing the 77 * result to @p outp. 78 * 79 * @param value The value to be formatted. 80 * @param fmt The format string. 81 * @param[out] outp On success, the string will be written to this 82 * buffer. This argment may be NULL if the value is 83 * not desired. 84 * @param[in,out] olen The capacity of @p outp. On success, will be set 85 * to the actual number of bytes required for the 86 * requested string encoding (including a trailing 87 * NUL). 88 * 89 * Refer to bhnd_nvram_val_vprintf() for full format string documentation. 90 * 91 * @retval 0 success 92 * @retval EINVAL If @p fmt contains unrecognized format string 93 * specifiers. 94 * @retval ENOMEM If the @p outp is non-NULL, and the provided @p olen 95 * is too small to hold the encoded value. 96 * @retval EFTYPE If value coercion from @p value to a single string 97 * value via @p fmt is unsupported. 98 * @retval ERANGE If value coercion of @p value would overflow (or 99 * underflow) the representation defined by @p fmt. 100 */ 101 int 102 bhnd_nvram_val_printf(bhnd_nvram_val *value, const char *fmt, char *outp, 103 size_t *olen, ...) 104 { 105 va_list ap; 106 int error; 107 108 va_start(ap, olen); 109 error = bhnd_nvram_val_vprintf(value, fmt, outp, olen, ap); 110 va_end(ap); 111 112 return (error); 113 } 114 115 /** 116 * 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 } 883