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