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