1 /* 2 * Copyright (C) 2020 Linux Studio Plugins Project <https://lsp-plug.in/> 3 * (C) 2020 Vladimir Sadovnikov <sadko4u@gmail.com> 4 * 5 * This file is part of lsp-plugins 6 * Created on: 25 февр. 2020 г. 7 * 8 * lsp-plugins is free software: you can redistribute it and/or modify 9 * it under the terms of the GNU Lesser General Public License as published by 10 * the Free Software Foundation, either version 3 of the License, or 11 * any later version. 12 * 13 * lsp-plugins is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 * GNU Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public License 19 * along with lsp-plugins. If not, see <https://www.gnu.org/licenses/>. 20 */ 21 22 #include <core/calc/format.h> 23 #include <core/calc/Tokenizer.h> 24 #include <core/io/InStringSequence.h> 25 #include <core/io/OutStringSequence.h> 26 #include <core/stdlib/stdio.h> 27 28 namespace lsp 29 { 30 namespace calc 31 { format(io::IOutSequence * out,const char * fmt,const Parameters * r)32 status_t format(io::IOutSequence *out, const char *fmt, const Parameters *r) 33 { 34 if ((out == NULL) || (fmt == NULL)) 35 return STATUS_BAD_ARGUMENTS; 36 37 io::InStringSequence xfmt; 38 status_t res = xfmt.wrap(fmt); 39 if (res != STATUS_OK) 40 { 41 xfmt.close(); 42 return res; 43 } 44 45 res = format(out, &xfmt, r); 46 if (res != STATUS_OK) 47 { 48 xfmt.close(); 49 return res; 50 } 51 52 return xfmt.close(); 53 } 54 format(io::IOutSequence * out,const LSPString * fmt,const Parameters * r)55 status_t format(io::IOutSequence *out, const LSPString *fmt, const Parameters *r) 56 { 57 if ((out == NULL) || (fmt == NULL)) 58 return STATUS_BAD_ARGUMENTS; 59 60 io::InStringSequence xfmt; 61 status_t res = xfmt.wrap(fmt); 62 if (res != STATUS_OK) 63 { 64 xfmt.close(); 65 return res; 66 } 67 68 res = format(out, &xfmt, r); 69 if (res != STATUS_OK) 70 { 71 xfmt.close(); 72 return res; 73 } 74 75 return xfmt.close(); 76 } 77 format(LSPString * out,io::IInSequence * fmt,const Parameters * r)78 status_t format(LSPString *out, io::IInSequence *fmt, const Parameters *r) 79 { 80 if ((out == NULL) || (fmt == NULL)) 81 return STATUS_BAD_ARGUMENTS; 82 83 io::OutStringSequence xout; 84 out->set_length(0); 85 status_t res = xout.wrap(out, false); 86 if (res != STATUS_OK) 87 { 88 xout.close(); 89 return res; 90 } 91 92 res = format(&xout, fmt, r); 93 if (res != STATUS_OK) 94 { 95 xout.close(); 96 return res; 97 } 98 99 return xout.close(); 100 } 101 format(LSPString * out,const char * fmt,const Parameters * r)102 status_t format(LSPString *out, const char *fmt, const Parameters *r) 103 { 104 if ((out == NULL) || (fmt == NULL)) 105 return STATUS_BAD_ARGUMENTS; 106 107 io::OutStringSequence xout; 108 out->set_length(0); 109 status_t res = xout.wrap(out, false); 110 if (res != STATUS_OK) 111 { 112 xout.close(); 113 return res; 114 } 115 116 res = format(&xout, fmt, r); 117 if (res != STATUS_OK) 118 { 119 xout.close(); 120 return res; 121 } 122 123 return xout.close(); 124 } 125 format(LSPString * out,const LSPString * fmt,const Parameters * r)126 status_t format(LSPString *out, const LSPString *fmt, const Parameters *r) 127 { 128 if ((out == NULL) || (fmt == NULL)) 129 return STATUS_BAD_ARGUMENTS; 130 131 io::OutStringSequence xout; 132 out->set_length(0); 133 status_t res = xout.wrap(out, false); 134 if (res != STATUS_OK) 135 { 136 xout.close(); 137 return res; 138 } 139 140 res = format(&xout, fmt, r); 141 if (res != STATUS_OK) 142 { 143 xout.close(); 144 return res; 145 } 146 147 return xout.close(); 148 } 149 150 enum align_type_t 151 { 152 AL_NONE, // No alignment 153 AL_LEFT, // '<' - pad formatted value from right 154 AL_RIGHT, // '>' - pad formatted value from left 155 AL_MIDDLE, // '|' - pad formatted value from left and right 156 AL_FROM_LEFT, // '>|' - pad formatted value from left and right, prefer right alignment 157 AL_FROM_RIGHT, // '|<' - pad formatted value from left and right, prefer left alignment 158 AL_TO_LEFT, // '<|' - take 1/4 from left, 3/4 from right 159 AL_TO_RIGHT, // '|>' - take 1/4 from right, 3/4 from left 160 }; 161 162 enum fmt_flags_t 163 { 164 F_NAME = 1 << 0, 165 F_INDEX = 1 << 1, 166 F_TYPE = 1 << 2, 167 F_WIDTH = 1 << 3, 168 F_FRAC = 1 << 4, 169 F_SIGN = 1 << 5, 170 F_LPAD = 1 << 6, 171 F_RPAD = 1 << 7 172 }; 173 174 typedef struct fmt_spec_t 175 { 176 LSPString buf; // Buffer to store full specifier 177 LSPString name; // Name of the parameter 178 size_t index; // Index of parameter 179 size_t flags; // Format flags 180 lsp_wchar_t lpad; // Padding left 181 lsp_wchar_t rpad; // Padding right 182 align_type_t align; // Alignment 183 lsp_wchar_t type; // Type 184 size_t width; // Width 185 size_t frac; // Fraction 186 } fmt_spec_t; 187 init_spec(fmt_spec_t * spec,size_t index)188 void init_spec(fmt_spec_t *spec, size_t index) 189 { 190 spec->buf.clear(); 191 spec->name.clear(); 192 spec->index = index; 193 spec->flags = 0; 194 spec->lpad = ' '; 195 spec->rpad = ' '; 196 spec->align = AL_NONE; 197 spec->type = 0; 198 spec->width = 0; 199 spec->frac = 0; 200 } 201 read_specifier(io::IOutSequence * out,io::IInSequence * fmt,fmt_spec_t * spec)202 status_t read_specifier(io::IOutSequence *out, io::IInSequence *fmt, fmt_spec_t *spec) 203 { 204 lsp_swchar_t c; 205 status_t res = STATUS_OK; 206 207 // Read format specifier as string 208 while (true) 209 { 210 // Read next character 211 c = fmt->read(); 212 if (c < 0) 213 { 214 if (c != -STATUS_EOF) 215 return -c; 216 217 // Format specifier has been interrupted 218 if ((res = out->write('{')) != STATUS_OK) 219 return res; 220 if ((res = out->write(&spec->buf)) != STATUS_OK) 221 return res; 222 return STATUS_BAD_FORMAT; 223 } 224 225 // End of format specifier? 226 if (c == '}') 227 break; 228 229 // Append character and repeat 230 spec->buf.append(c); 231 } 232 233 // now parse format specifier 234 size_t len = spec->buf.length(); 235 for (size_t i=0; i<len; ) 236 { 237 c = spec->buf.char_at(i++); 238 239 switch (c) 240 { 241 case '@': // Name 242 // Prevent from duplicate definitions 243 if (spec->flags & (F_NAME | F_INDEX)) 244 { 245 res = STATUS_BAD_FORMAT; 246 break; 247 } 248 249 // Check first character 250 c = (i < len) ? spec->buf.char_at(i++) : 0; 251 if (Tokenizer::is_identifier_first(c)) 252 { 253 // Append character 254 spec->flags |= F_NAME; 255 if (!spec->name.append(c)) 256 { 257 res = STATUS_NO_MEM; 258 break; 259 } 260 261 // Check other characters 262 for (; i < len; ++i) 263 { 264 c = spec->buf.char_at(i); 265 if (!Tokenizer::is_identifier_next(c)) 266 break; 267 else if (!spec->name.append(c)) 268 { 269 res = STATUS_NO_MEM; 270 break; 271 } 272 } 273 } 274 else 275 res = STATUS_BAD_FORMAT; 276 277 break; 278 case '[': // Index 279 // Prevent from duplicate definitions 280 if (spec->flags & (F_NAME | F_INDEX)) 281 { 282 res = STATUS_BAD_FORMAT; 283 break; 284 } 285 286 // Read decimal index 287 spec->index = 0; 288 for ( ; i < len; ++i) 289 { 290 c = spec->buf.char_at(i); 291 if ((c < '0') || (c > '9')) 292 break; 293 294 spec->index = spec->index * 10 + (c - '0'); 295 spec->flags |= F_INDEX; 296 } 297 298 // Require final ']' 299 if ((spec->flags & F_INDEX) && (i < len)) 300 { 301 c = spec->buf.char_at(i++); 302 if (c != ']') 303 res = STATUS_BAD_FORMAT; 304 } 305 else 306 res = STATUS_BAD_FORMAT; 307 308 break; 309 310 case '^': // Left padding 311 if ((!(spec->flags & F_LPAD)) && (i < len)) 312 { 313 spec->flags |= F_LPAD; 314 spec->lpad = spec->buf.char_at(i++); 315 } 316 else 317 res = STATUS_BAD_FORMAT; 318 break; 319 320 case '$': // Right padding 321 if ((!(spec->flags & F_RPAD)) && (i < len)) 322 { 323 spec->flags |= F_RPAD; 324 spec->rpad = spec->buf.char_at(i++); 325 } 326 else 327 res = STATUS_BAD_FORMAT; 328 break; 329 330 case '>': // Right alignment 331 if (spec->align == AL_NONE) 332 { 333 spec->align = AL_RIGHT; 334 if ((i < len) && (spec->buf.char_at(i) == '|')) // '>|' 335 { 336 spec->align = AL_FROM_LEFT; 337 ++i; 338 } 339 } 340 else 341 res = STATUS_BAD_FORMAT; 342 break; 343 344 case '<': // Left alignment 345 if (spec->align == AL_NONE) 346 { 347 spec->align = AL_LEFT; 348 if ((i < len) && (spec->buf.char_at(i) == '|')) // '<|' 349 { 350 spec->align = AL_TO_LEFT; 351 ++i; 352 } 353 } 354 else 355 res = STATUS_BAD_FORMAT; 356 break; 357 358 case '|': // Middle alignment 359 if (spec->align == AL_NONE) 360 { 361 spec->align = AL_MIDDLE; 362 if (i < len) 363 { 364 // Lookup additional modes 365 c = spec->buf.char_at(i); 366 if (c == '<') // '|<' 367 { 368 spec->align = AL_FROM_RIGHT; 369 ++i; 370 } 371 else if (c == '>') // '|>' 372 { 373 spec->align = AL_TO_RIGHT; 374 ++i; 375 } 376 } 377 } 378 else 379 res = STATUS_BAD_FORMAT; 380 break; 381 382 case '%': // Type specifier 383 // Prevent from duplicate definition 384 if (spec->flags & F_TYPE) 385 { 386 res = STATUS_BAD_FORMAT; 387 break; 388 } 389 spec->flags |= F_TYPE; 390 391 // Lookup for sign 392 if ((i < len) && (spec->buf.char_at(i) == '+')) 393 { 394 spec->flags |= F_SIGN; 395 ++i; 396 } 397 398 // Read width 399 for ( ; i < len; ++i) 400 { 401 c = spec->buf.char_at(i); 402 if ((c < '0') || (c > '9')) 403 break; 404 405 spec->flags |= F_WIDTH; 406 spec->width = spec->width * 10 + (c - '0'); 407 } 408 409 // Lookup for dot 410 if ((i < len) && (spec->buf.char_at(i) == '.')) 411 { 412 // Read fraction 413 while ((++i) < len) 414 { 415 c = spec->buf.char_at(i); 416 if ((c < '0') || (c > '9')) 417 break; 418 419 spec->flags |= F_FRAC; 420 spec->frac = spec->frac * 10 + (c - '0'); 421 } 422 423 // Check that fraction part is present 424 if (!(spec->flags & F_FRAC)) 425 { 426 res = STATUS_BAD_FORMAT; 427 break; 428 } 429 } 430 431 // Read type specifier 432 if (i >= len) 433 { 434 res = STATUS_BAD_FORMAT; 435 break; 436 } 437 438 c = spec->buf.char_at(i++); 439 switch (c) 440 { 441 case 'i': case 'd': case 'u': // decimals 442 case 'b': case 'o': case 'x': case 'X': // octals, binaries, hexadecimals 443 case 'f': case 'F': case 'e': case 'E': // floating-points 444 case 's': // string 445 spec->type = c; 446 break; 447 case 'l': // boolean, bOOLEAN 448 c = (i < len) ? spec->buf.char_at(i) : 0; 449 if (c == 'l') 450 { 451 ++i; 452 spec->type = 'l'; 453 } 454 else if (c == 'L') 455 { 456 ++i; 457 spec->type = 'z'; 458 } 459 else 460 spec->type = 'l'; 461 break; 462 463 case 'L': // BOOLEAN, Boolean 464 c = (i < len) ? spec->buf.char_at(i) : 0; 465 if (c == 'l') 466 { 467 ++i; 468 spec->type = 'Z'; 469 } 470 else if (c == 'L') 471 { 472 ++i; 473 spec->type = 'L'; 474 } 475 else 476 spec->type = 'L'; 477 break; 478 479 case 't': // text, tEXT 480 c = (i < len) ? spec->buf.char_at(i) : 0; 481 if (c == 't') 482 { 483 ++i; 484 spec->type = 't'; 485 } 486 else if (c == 'T') 487 { 488 ++i; 489 spec->type = 'y'; 490 } 491 else 492 spec->type = 't'; 493 break; 494 case 'T': // TEXT, Text 495 c = (i < len) ? spec->buf.char_at(i) : 0; 496 if (c == 't') 497 { 498 ++i; 499 spec->type = 'Y'; 500 } 501 else if (c == 'T') 502 { 503 ++i; 504 spec->type = 'T'; 505 } 506 else 507 spec->type = 'T'; 508 break; 509 default: 510 // No type specifier, just width 511 --i; 512 break; 513 } 514 515 break; 516 517 default: 518 res = STATUS_BAD_FORMAT; 519 break; 520 } 521 522 // Check result 523 if (res == STATUS_BAD_FORMAT) 524 { 525 if ((res = out->write('{')) != STATUS_OK) 526 return res; 527 if ((res = out->write(&spec->buf)) != STATUS_OK) 528 return res; 529 if ((res = out->write('}')) != STATUS_OK) 530 return res; 531 res = STATUS_BAD_FORMAT; 532 break; 533 } 534 else if (res != STATUS_OK) 535 break; 536 } 537 538 return res; 539 } 540 check_specials(fmt_spec_t * spec,value_t * v)541 status_t check_specials(fmt_spec_t *spec, value_t *v) 542 { 543 if (v->type == VT_NULL) 544 return (spec->buf.set_ascii("<null>")) ? STATUS_SKIP : STATUS_NO_MEM; 545 else if (v->type == VT_UNDEF) 546 return (spec->buf.set_ascii("<undef>")) ? STATUS_SKIP : STATUS_NO_MEM; 547 return STATUS_OK; 548 } 549 int_to_dec(fmt_spec_t * spec,value_t * v)550 status_t int_to_dec(fmt_spec_t *spec, value_t *v) 551 { 552 status_t res = check_specials(spec, v); 553 if (res != STATUS_OK) 554 return (res == STATUS_SKIP) ? STATUS_OK : res; 555 556 ssize_t x = v->v_int; 557 do 558 { 559 lsp_swchar_t rem = x % 10; 560 if (!spec->buf.append(lsp_wchar_t((rem >= 0) ? '0' + rem : '0' - rem))) 561 return STATUS_NO_MEM; 562 x /= 10; 563 } while (x); 564 565 if (v->v_int < 0) 566 res = (spec->buf.append('-')) ? STATUS_OK : STATUS_NO_MEM; 567 else if (spec->flags & F_SIGN) 568 res = (spec->buf.append('+')) ? STATUS_OK : STATUS_NO_MEM; 569 570 if (res != STATUS_OK) 571 return res; 572 573 spec->buf.reverse(); 574 return STATUS_OK; 575 } 576 uint_to_dec(fmt_spec_t * spec,value_t * v)577 status_t uint_to_dec(fmt_spec_t *spec, value_t *v) 578 { 579 status_t res = check_specials(spec, v); 580 if (res != STATUS_OK) 581 return (res == STATUS_SKIP) ? STATUS_OK : res; 582 583 size_t x = v->v_int; 584 do 585 { 586 if (!spec->buf.append(lsp_wchar_t((x % 10) + '0'))) 587 return STATUS_NO_MEM; 588 x /= 10; 589 } while (x); 590 591 if (res != STATUS_OK) 592 return res; 593 594 spec->buf.reverse(); 595 return STATUS_OK; 596 } 597 int_to_bin(fmt_spec_t * spec,value_t * v)598 status_t int_to_bin(fmt_spec_t *spec, value_t *v) 599 { 600 status_t res = check_specials(spec, v); 601 if (res != STATUS_OK) 602 return (res == STATUS_SKIP) ? STATUS_OK : res; 603 604 size_t x = v->v_int; 605 do 606 { 607 if (!spec->buf.append(lsp_wchar_t((x & 1) + '0'))) 608 return STATUS_NO_MEM; 609 x >>= 1; 610 } while (x); 611 612 if (res != STATUS_OK) 613 return res; 614 615 spec->buf.reverse(); 616 return STATUS_OK; 617 } 618 int_to_oct(fmt_spec_t * spec,value_t * v)619 status_t int_to_oct(fmt_spec_t *spec, value_t *v) 620 { 621 status_t res = check_specials(spec, v); 622 if (res != STATUS_OK) 623 return (res == STATUS_SKIP) ? STATUS_OK : res; 624 625 size_t x = v->v_int; 626 do 627 { 628 if (!spec->buf.append(lsp_wchar_t((x & 0x7) + '0'))) 629 return STATUS_NO_MEM; 630 x >>= 3; 631 } while (x); 632 633 if (res != STATUS_OK) 634 return res; 635 636 spec->buf.reverse(); 637 return STATUS_OK; 638 } 639 640 static const char *hex_table = "0123456789abcdef0123456789ABCDEF"; 641 int_to_hex(fmt_spec_t * spec,value_t * v)642 status_t int_to_hex(fmt_spec_t *spec, value_t *v) 643 { 644 status_t res = check_specials(spec, v); 645 if (res != STATUS_OK) 646 return (res == STATUS_SKIP) ? STATUS_OK : res; 647 648 const char *table = (spec->type == 'X') ? &hex_table[16] : hex_table; 649 size_t x = v->v_int; 650 do 651 { 652 if (!spec->buf.append(table[x & 0xf])) 653 return STATUS_NO_MEM; 654 x >>= 4; 655 } while (x); 656 657 if (res != STATUS_OK) 658 return res; 659 660 spec->buf.reverse(); 661 return STATUS_OK; 662 } 663 float_to_str(fmt_spec_t * spec,value_t * v)664 status_t float_to_str(fmt_spec_t *spec, value_t *v) 665 { 666 status_t res = check_specials(spec, v); 667 if (res != STATUS_OK) 668 return (res == STATUS_SKIP) ? STATUS_OK : res; 669 670 if (isnan(v->v_float)) 671 res = (spec->buf.set_ascii("nan")) ? STATUS_OK : STATUS_NO_MEM; 672 else if (isinf(v->v_float)) 673 { 674 if (v->v_float < 0) 675 { 676 v->v_float = INFINITY; 677 res = (spec->buf.set_ascii("-inf")) ? STATUS_OK : STATUS_NO_MEM; 678 } 679 else if (spec->flags & F_SIGN) 680 res = (spec->buf.set_ascii("+inf")) ? STATUS_OK : STATUS_NO_MEM; 681 else 682 res = (spec->buf.set_ascii("inf")) ? STATUS_OK : STATUS_NO_MEM; 683 } 684 else 685 { 686 char fmt[64]; 687 if (spec->flags & F_FRAC) 688 ::snprintf(fmt, sizeof(fmt), "%%.%d%c", int(spec->frac), char(spec->type)); 689 else 690 ::snprintf(fmt, sizeof(fmt), "%%.6%c", char(spec->type)); 691 692 fmt[63] = '\0'; 693 res = spec->buf.fmt_ascii(fmt, v->v_float) ? STATUS_OK : STATUS_NO_MEM; 694 if ((res == STATUS_OK) && (spec->flags & F_SIGN) && (v->v_float > 0)) 695 res = (spec->buf.prepend(lsp_wchar_t('+'))) ? STATUS_OK : STATUS_NO_MEM; 696 } 697 698 return res; 699 } 700 text_to_str(fmt_spec_t * spec,value_t * v)701 status_t text_to_str(fmt_spec_t *spec, value_t *v) 702 { 703 status_t res = check_specials(spec, v); 704 if (res != STATUS_OK) 705 return (res == STATUS_SKIP) ? STATUS_OK : res; 706 707 if (!spec->buf.set(v->v_str)) 708 return STATUS_NO_MEM; 709 710 // Additional modifications? 711 switch (spec->type) 712 { 713 case 't': spec->buf.tolower(); break; 714 case 'T': spec->buf.toupper(); break; 715 case 'y': 716 if (spec->buf.length() > 0) 717 spec->buf.tolower(0, 1); 718 if (spec->buf.length() > 1) 719 spec->buf.toupper(1); 720 break; 721 case 'Y': 722 if (spec->buf.length() > 0) 723 spec->buf.toupper(0, 1); 724 if (spec->buf.length() > 1) 725 spec->buf.tolower(1); 726 break; 727 } 728 729 return STATUS_OK; 730 } 731 bool_to_str(fmt_spec_t * spec,value_t * v)732 status_t bool_to_str(fmt_spec_t *spec, value_t *v) 733 { 734 status_t res = check_specials(spec, v); 735 if (res != STATUS_OK) 736 return (res == STATUS_SKIP) ? STATUS_OK : res; 737 738 bool success = true; 739 switch (spec->type) 740 { 741 case 'l': success = spec->buf.set_ascii((v->v_bool) ? "true" : "false"); break; 742 case 'L': success = spec->buf.set_ascii((v->v_bool) ? "TRUE" : "FALSE"); break; 743 case 'z': success = spec->buf.set_ascii((v->v_bool) ? "tRUE" : "fALSE"); break; 744 case 'Z': success = spec->buf.set_ascii((v->v_bool) ? "True" : "False"); break; 745 } 746 return (success) ? STATUS_OK : STATUS_NO_MEM; 747 } 748 emit_parameter(io::IOutSequence * out,fmt_spec_t * spec,const Parameters * r)749 status_t emit_parameter(io::IOutSequence *out, fmt_spec_t *spec, const Parameters *r) 750 { 751 value_t v; 752 init_value(&v); 753 status_t res = STATUS_OK; 754 755 // Fetch the value if resolver is present 756 if (r != NULL) 757 { 758 if (spec->flags & F_NAME) 759 res = r->get(&spec->name, &v); 760 else 761 res = r->get(spec->index, &v); 762 763 if (res != STATUS_OK) 764 { 765 destroy_value(&v); 766 return res; 767 } 768 } 769 770 // Cast the value's type 771 spec->buf.clear(); 772 773 switch (spec->type) 774 { 775 case 'i': case 'd': 776 res = cast_value(&v, VT_INT); 777 if (res == STATUS_OK) 778 res = int_to_dec(spec, &v); 779 break; 780 case 'u': 781 res = cast_value(&v, VT_INT); 782 if (res == STATUS_OK) 783 res = uint_to_dec(spec, &v); 784 break; 785 case 'b': 786 res = cast_value(&v, VT_INT); 787 if (res == STATUS_OK) 788 res = int_to_bin(spec, &v); 789 break; 790 case 'o': 791 res = cast_value(&v, VT_INT); 792 if (res == STATUS_OK) 793 res = int_to_oct(spec, &v); 794 break; 795 case 'x': case 'X': 796 res = cast_value(&v, VT_INT); 797 if (res == STATUS_OK) 798 res = int_to_hex(spec, &v); 799 break; 800 case 'e': case 'E': 801 case 'f': case 'F': 802 res = cast_value(&v, VT_FLOAT); 803 if (res == STATUS_OK) 804 res = float_to_str(spec, &v); 805 break; 806 case 's': case 't': case 'T': case 'y': case 'Y': 807 res = cast_value(&v, VT_STRING); 808 if (res == STATUS_OK) 809 res = text_to_str(spec, &v); 810 break; 811 case 'l': case 'L': case 'z': case 'Z': 812 res = cast_value(&v, VT_BOOL); 813 if (res == STATUS_OK) 814 res = bool_to_str(spec, &v); 815 break; 816 default: 817 res = cast_value(&v, VT_STRING); 818 if (res == STATUS_OK) 819 res = text_to_str(spec, &v); 820 break; 821 } 822 823 if (res != STATUS_OK) 824 { 825 destroy_value(&v); 826 return res; 827 } 828 829 // Compute padding 830 ssize_t lpad = 0, rpad = 0, pad = spec->width - spec->buf.length(); 831 if ((spec->flags & F_WIDTH) && (pad > 0)) 832 { 833 switch (spec->align) 834 { 835 case AL_LEFT: 836 rpad = pad; 837 break; 838 case AL_RIGHT: 839 lpad = pad; 840 break; 841 case AL_MIDDLE: 842 case AL_FROM_LEFT: 843 lpad = pad >> 1; 844 rpad = pad - lpad; 845 break; 846 case AL_FROM_RIGHT: 847 rpad = pad >> 1; 848 lpad = pad - rpad; 849 break; 850 case AL_TO_LEFT: 851 lpad = pad >> 2; 852 rpad = pad - lpad; 853 break; 854 case AL_TO_RIGHT: 855 rpad = pad >> 2; 856 lpad = pad - rpad; 857 break; 858 default: 859 break; 860 } 861 } 862 863 // Emit value 864 while (lpad--) 865 { 866 if ((res = out->write(spec->lpad)) != STATUS_OK) 867 { 868 destroy_value(&v); 869 return res; 870 } 871 } 872 if ((res = out->write(&spec->buf)) != STATUS_OK) 873 { 874 destroy_value(&v); 875 return res; 876 } 877 while (rpad--) 878 { 879 if ((res = out->write(spec->rpad)) != STATUS_OK) 880 { 881 destroy_value(&v); 882 return res; 883 } 884 } 885 886 destroy_value(&v); 887 return res; 888 } 889 format(io::IOutSequence * out,io::IInSequence * fmt,const Parameters * r)890 status_t format(io::IOutSequence *out, io::IInSequence *fmt, const Parameters *r) 891 { 892 if ((out == NULL) || (fmt == NULL)) 893 return STATUS_BAD_ARGUMENTS; 894 895 status_t res; 896 size_t index = 0; 897 bool protector = false; 898 fmt_spec_t spec; 899 init_spec(&spec, index); 900 901 while (true) 902 { 903 // Read character 904 lsp_swchar_t c = fmt->read(); 905 if (c < 0) 906 return (c != -STATUS_EOF) ? -c : STATUS_OK; 907 908 switch (c) 909 { 910 case '\\': 911 if (protector) 912 { 913 if ((res = out->write('\\')) != STATUS_OK) 914 return res; 915 } 916 protector = !protector; 917 break; 918 919 case '{': 920 if (protector) 921 { 922 if ((res = out->write('{')) != STATUS_OK) 923 return res; 924 protector = false; 925 } 926 else 927 { 928 // Read specifier and format the value 929 res = read_specifier(out, fmt, &spec); 930 if (res == STATUS_OK) 931 { 932 if ((res = emit_parameter(out, &spec, r)) != STATUS_OK) 933 return res; 934 935 // Reset specifier 936 if (!(spec.flags & (F_NAME | F_INDEX))) 937 ++index; 938 } 939 else if (res != STATUS_BAD_FORMAT) 940 return res; 941 init_spec(&spec, index); 942 } 943 break; 944 945 default: 946 if ((res = out->write(c)) != STATUS_OK) 947 return res; 948 break; 949 } // c 950 } 951 } 952 } 953 } 954 955