1 | // This file is part of SmallBASIC |
2 | // |
3 | // formating numbers and strings |
4 | // |
5 | // This program is distributed under the terms of the GPL v2.0 or later |
6 | // Download the GNU Public License (GPL) from www.gnu.org |
7 | // |
8 | // Copyright(C) 2000 Nicholas Christopoulos |
9 | |
10 | #include "common/sys.h" |
11 | #include "common/str.h" |
12 | #include "common/fmt.h" |
13 | #include "common/device.h" |
14 | #include "common/pproc.h" |
15 | #include "common/messages.h" |
16 | #include "common/blib_math.h" |
17 | |
18 | #if defined OS_PREC64 |
19 | // limits for use with 64bit integer or 64bit fp algorithm |
20 | #define FMT_xMIN 1e-8 |
21 | #define FMT_xMAX 1e+14 |
22 | #define FMT_RND 14 |
23 | #define FMT_xRND 1e+14 |
24 | #define FMT_xRND2 1e+13 |
25 | #else |
26 | // limits for use with 32bit integer algorithm |
27 | #define FMT_xMIN 1e-8 // lowest limit to use the exp. format |
28 | #define FMT_xMAX 1e+9 // highest limit to use the exp. format |
29 | #define FMT_RND 9 // rounding on x digits |
30 | #define FMT_xRND 1e+9 // 1 * 10 ^ FMT_RND |
31 | #define FMT_xRND2 1e+8 // 1 * 10 ^ (FMT_RND-1) |
32 | #endif |
33 | |
34 | // PRINT USING; format-list |
35 | #define MAX_FMT_N 128 |
36 | |
37 | void bestfta_p(var_num_t x, char *dest, var_num_t minx, var_num_t maxx); |
38 | void fmt_nmap(int dir, char *dest, char *fmt, char *src); |
39 | void fmt_omap(char *dest, const char *fmt); |
40 | int fmt_cdig(char *fmt); |
41 | char *fmt_getnumfmt(char *dest, char *source); |
42 | char *fmt_getstrfmt(char *dest, char *source); |
43 | void fmt_addfmt(const char *fmt, int type); |
44 | void fmt_printL(int output, intptr_t handle); |
45 | |
46 | typedef struct { |
47 | char *fmt; // the format or a string |
48 | int type; // 0 = string, 1 = numeric format, 2 = string format |
49 | } fmt_node_t; |
50 | |
51 | static fmt_node_t fmt_stack[MAX_FMT_N]; // the list |
52 | static int fmt_count; // number of elements in the list |
53 | static int fmt_cur; // next format element to be used |
54 | |
55 | /* |
56 | * tables of powers :) |
57 | */ |
58 | static double nfta_eplus[] = { |
59 | 1e+8, 1e+16, 1e+24, 1e+32, 1e+40, 1e+48, 1e+56, 1e+64, // 8 |
60 | 1e+72, 1e+80, 1e+88, 1e+96, 1e+104, 1e+112, 1e+120, 1e+128, // 16 |
61 | 1e+136, 1e+144, 1e+152, 1e+160, 1e+168, 1e+176, 1e+184, 1e+192, // 24 |
62 | 1e+200, 1e+208, 1e+216, 1e+224, 1e+232, 1e+240, 1e+248, 1e+256, // 32 |
63 | 1e+264, 1e+272, 1e+280, 1e+288, 1e+296, 1e+304 // 38 |
64 | }; |
65 | |
66 | static double nfta_eminus[] = { |
67 | 1e-8, 1e-16, 1e-24, 1e-32, 1e-40, 1e-48, 1e-56, 1e-64, // 8 |
68 | 1e-72, 1e-80, 1e-88, 1e-96, 1e-104, 1e-112, 1e-120, 1e-128, // 16 |
69 | 1e-136, 1e-144, 1e-152, 1e-160, 1e-168, 1e-176, 1e-184, 1e-192, // 24 |
70 | 1e-200, 1e-208, 1e-216, 1e-224, 1e-232, 1e-240, 1e-248, 1e-256, // 32 |
71 | 1e-264, 1e-272, 1e-280, 1e-288, 1e-296, 1e-304 // 38 |
72 | }; |
73 | |
74 | /* |
75 | * Part of floating point to string (by using integers) algorithm |
76 | * where x any number 2^31 > x >= 0 |
77 | */ |
78 | void fptoa(var_num_t x, char *dest) { |
79 | dest[0] = '\0'; |
80 | sprintf(dest, VAR_INT_NUM_FMT, x); |
81 | } |
82 | |
83 | /* |
84 | * Convert to text then remove righmost zeroes from the string |
85 | */ |
86 | void fptoa_rmzeros(var_num_t x, char *dest) { |
87 | fptoa(x, dest); |
88 | int end = strlen(dest); |
89 | while (end > 0 && dest[end - 1] == '0') { |
90 | end--; |
91 | } |
92 | dest[end] = '\0'; |
93 | } |
94 | |
95 | /* |
96 | * best float to string (lib) |
97 | * |
98 | * This is the real float-to-string routine. |
99 | * It used by the routines: |
100 | * bestfta(double x, char *dest) |
101 | * expfta(double x, char *dest) |
102 | */ |
103 | void bestfta_p(var_num_t x, char *dest, var_num_t minx, var_num_t maxx) { |
104 | var_num_t ipart, fpart, fdif; |
105 | var_int_t power = 0; |
106 | int sign, i; |
107 | char *d = dest; |
108 | char buf[64]; |
109 | |
110 | memset(buf, 0, sizeof(buf)); |
111 | |
112 | if (fabsl(x) == 0.0) { |
113 | strcpy(dest, "0" ); |
114 | return; |
115 | } |
116 | |
117 | // find sign |
118 | sign = sgn(x); |
119 | if (sign < 0) { |
120 | *d++ = '-'; |
121 | } |
122 | x = fabsl(x); |
123 | |
124 | if (x >= 1E308) { |
125 | *d = '\0'; |
126 | strcat(d, WORD_INF); |
127 | return; |
128 | } else if (x <= 1E-307) { |
129 | *d = '\0'; |
130 | strcat(d, "0" ); |
131 | return; |
132 | } |
133 | |
134 | // find power |
135 | if (x < minx) { |
136 | for (i = 37; i >= 0; i--) { |
137 | if (x < nfta_eminus[i]) { |
138 | x *= nfta_eplus[i]; |
139 | power = -((i + 1) * 8); |
140 | } else { |
141 | break; |
142 | } |
143 | } |
144 | |
145 | while (x < 1.0 && power > -307) { |
146 | x *= 10.0; |
147 | power--; |
148 | } |
149 | } else if (x > maxx) { |
150 | for (i = 37; i >= 0; i--) { |
151 | if (x > nfta_eplus[i]) { |
152 | x /= nfta_eplus[i]; |
153 | power = ((i + 1) * 8); |
154 | } else { |
155 | break; |
156 | } |
157 | } |
158 | |
159 | while (x >= 10.0 && power < 308) { |
160 | x /= 10.0; |
161 | power++; |
162 | } |
163 | } |
164 | |
165 | // format left part |
166 | ipart = fabsl(fint(x)); |
167 | fpart = fround(frac(x), FMT_RND) * FMT_xRND; |
168 | if (fpart >= FMT_xRND) { // rounding bug |
169 | ipart = ipart + 1.0; |
170 | if (ipart >= maxx) { |
171 | ipart = ipart / 10.0; |
172 | power++; |
173 | } |
174 | fpart = 0.0; |
175 | } |
176 | |
177 | fptoa(ipart, buf); |
178 | strcpy(d, buf); |
179 | d += strlen(buf); |
180 | |
181 | if (fpart > 0.0) { |
182 | // format right part |
183 | *d++ = '.'; |
184 | |
185 | fdif = frac(x) * FMT_xRND; |
186 | if (fdif < fpart) { |
187 | // rounded value has greater precision |
188 | fdif = fpart; |
189 | } |
190 | |
191 | while (fdif < FMT_xRND2) { |
192 | fdif *= 10; |
193 | *d++ = '0'; |
194 | } |
195 | |
196 | fptoa_rmzeros(fpart, buf); |
197 | strcpy(d, buf); |
198 | d += strlen(buf); |
199 | } |
200 | |
201 | if (power) { |
202 | // add the power |
203 | *d++ = 'E'; |
204 | if (power > 0) { |
205 | *d++ = '+'; |
206 | } |
207 | fptoa(power, buf); |
208 | strcpy(d, buf); |
209 | d += strlen(buf); |
210 | } |
211 | |
212 | // finish |
213 | *d = '\0'; |
214 | } |
215 | |
216 | /* |
217 | * best float to string (user) |
218 | */ |
219 | void bestfta(var_num_t x, char *dest) { |
220 | bestfta_p(x, dest, FMT_xMIN, FMT_xMAX); |
221 | } |
222 | |
223 | /* |
224 | * float to string (user, E mode) |
225 | */ |
226 | void expfta(var_num_t x, char *dest) { |
227 | bestfta_p(x, dest, 1.0, 1.0); |
228 | if (strchr(dest, 'E') == NULL) { |
229 | strcat(dest, "E+0" ); |
230 | } |
231 | } |
232 | |
233 | /* |
234 | * format: map number to format |
235 | * |
236 | * dir = direction, 1 = left to right, -1 right to left |
237 | */ |
238 | void fmt_nmap(int dir, char *dest, char *fmt, char *src) { |
239 | char *p, *d, *s; |
240 | |
241 | *dest = '\0'; |
242 | if (dir > 0) { |
243 | // |
244 | // left to right |
245 | // |
246 | p = fmt; |
247 | d = dest; |
248 | s = src; |
249 | while (*p) { |
250 | switch (*p) { |
251 | case '#': |
252 | case '^': |
253 | if (*s) { |
254 | *d++ = *s++; |
255 | } |
256 | break; |
257 | case '0': |
258 | if (*s) { |
259 | *d++ = *s++; |
260 | } else { |
261 | *d++ = '0'; |
262 | } |
263 | break; |
264 | default: |
265 | *d++ = *p; |
266 | } |
267 | p++; |
268 | } |
269 | *d = '\0'; |
270 | } else { |
271 | // |
272 | // right to left |
273 | // |
274 | p = fmt + (strlen(fmt) - 1); |
275 | d = dest + (strlen(fmt) - 1); |
276 | *(d + 1) = '\0'; |
277 | s = src + (strlen(src) - 1); |
278 | while (p >= fmt) { |
279 | switch (*p) { |
280 | case '#': |
281 | case '^': |
282 | if (s >= src) { |
283 | *d-- = *s--; |
284 | } else { |
285 | *d-- = ' '; |
286 | } |
287 | break; |
288 | case '0': |
289 | if (s >= src) { |
290 | *d-- = *s--; |
291 | } else { |
292 | *d-- = '0'; |
293 | } |
294 | break; |
295 | default: |
296 | if (*p == ',') { |
297 | if (s >= src) { |
298 | if (*s == '-') { |
299 | *d-- = *s--; |
300 | } else { |
301 | *d-- = *p; |
302 | } |
303 | } else { |
304 | *d-- = ' '; |
305 | } |
306 | } else { |
307 | *d-- = *p; |
308 | } |
309 | } |
310 | p--; |
311 | } |
312 | } |
313 | } |
314 | |
315 | /* |
316 | * format: map number-overflow to format |
317 | */ |
318 | void fmt_omap(char *dest, const char *fmt) { |
319 | char *p = (char *) fmt; |
320 | char *d = dest; |
321 | |
322 | while (*p) { |
323 | switch (*p) { |
324 | case '#': |
325 | case '0': |
326 | case '^': |
327 | *d++ = '*'; |
328 | break; |
329 | default: |
330 | *d++ = *p; |
331 | } |
332 | |
333 | p++; |
334 | } |
335 | *d = '\0'; |
336 | } |
337 | |
338 | /* |
339 | * format: count digits |
340 | */ |
341 | int fmt_cdig(char *fmt) { |
342 | char *p = fmt; |
343 | int count = 0; |
344 | |
345 | while (*p) { |
346 | switch (*p) { |
347 | case '#': |
348 | case '0': |
349 | case '^': |
350 | count++; |
351 | break; |
352 | } |
353 | p++; |
354 | } |
355 | |
356 | return count; |
357 | } |
358 | |
359 | /* |
360 | * format: format a number |
361 | * |
362 | * symbols: |
363 | * # = digit or space |
364 | * 0 = digit or zero |
365 | * ^ = exponential digit/format |
366 | * . = decimal point |
367 | * , = thousands |
368 | * - = minus for negative |
369 | * + = sign of number |
370 | */ |
371 | char *format_num(const char *fmt_cnst, var_num_t x) { |
372 | char *p, *fmt; |
373 | char left[64], right[64]; |
374 | char lbuf[64] ; |
375 | int lc = 0, sign = 0; |
376 | |
377 | char *dest = malloc(128); |
378 | |
379 | // backup of format |
380 | fmt = malloc(strlen(fmt_cnst) + 1); |
381 | strcpy(fmt, fmt_cnst); |
382 | |
383 | // check sign |
384 | if (strchr(fmt, '-') || strchr(fmt, '+')) { |
385 | sign = 1; |
386 | if (x < 0.0) { |
387 | sign = -1; |
388 | x = -x; |
389 | } |
390 | } |
391 | |
392 | if (strchr(fmt_cnst, '^')) { |
393 | // |
394 | // E format |
395 | // |
396 | lc = fmt_cdig(fmt); |
397 | if (lc < 4) { |
398 | fmt_omap(dest, fmt); |
399 | free(fmt); |
400 | return dest; |
401 | } |
402 | |
403 | // convert |
404 | expfta(x, dest); |
405 | |
406 | // format |
407 | p = strchr(dest, 'E'); |
408 | if (p) { |
409 | *p = '\0'; |
410 | strlcpy(left, dest, sizeof(left)); |
411 | strlcpy(right, p + 1, sizeof(right)); |
412 | int lsz = strlen(left); |
413 | int rsz = strlen(right) + 1; |
414 | |
415 | if (lc < rsz + 1) { |
416 | fmt_omap(dest, fmt); |
417 | free(fmt); |
418 | return dest; |
419 | } |
420 | |
421 | if (lc < lsz + rsz + 1) { |
422 | left[lc - rsz] = '\0'; |
423 | } |
424 | strlcpy(lbuf, left, sizeof(lbuf)); |
425 | strlcat(lbuf, "E" , sizeof(lbuf)); |
426 | strlcat(lbuf, right, sizeof(lbuf)); |
427 | fmt_nmap(-1, dest, fmt, lbuf); |
428 | } else { |
429 | strlcpy(left, dest, sizeof(left)); |
430 | fmt_nmap(-1, dest, fmt, left); |
431 | } |
432 | } else { |
433 | // |
434 | // normal format |
435 | // |
436 | |
437 | // rounding |
438 | p = strchr(fmt, '.'); |
439 | if (p) { |
440 | x = fround(x, fmt_cdig(p + 1)); |
441 | } else { |
442 | x = fround(x, 0); |
443 | } |
444 | |
445 | // convert |
446 | bestfta(x, dest); |
447 | if (strchr(dest, 'E')) { |
448 | fmt_omap(dest, fmt); |
449 | free(fmt); |
450 | return dest; |
451 | } |
452 | |
453 | // left & right parts |
454 | left[0] = right[0] = '\0'; |
455 | p = strchr(dest, '.'); |
456 | if (p) { |
457 | *p = '\0'; |
458 | strlcpy(right, p + 1, sizeof(right)); |
459 | } |
460 | strlcpy(left, dest, sizeof(left)); |
461 | |
462 | // map format |
463 | char rbuf[64]; |
464 | int dp = 0; |
465 | rbuf[0] = lbuf[0] = '\0'; |
466 | p = strchr(fmt, '.'); |
467 | if (p) { |
468 | dp = 1; |
469 | *p = '\0'; |
470 | fmt_nmap(1, rbuf, p + 1, right); |
471 | } |
472 | |
473 | lc = fmt_cdig(fmt); |
474 | if (lc < strlen(left)) { |
475 | fmt_omap(dest, fmt_cnst); |
476 | free(fmt); |
477 | return dest; |
478 | } |
479 | fmt_nmap(-1, lbuf, fmt, left); |
480 | |
481 | strcpy(dest, lbuf); |
482 | if (dp) { |
483 | strcat(dest, "." ); |
484 | strcat(dest, rbuf); |
485 | } |
486 | } |
487 | |
488 | // sign in format |
489 | if (sign) { // 24/6 Snoopy42 modifications |
490 | char *e; |
491 | |
492 | e = strchr(dest, 'E'); |
493 | if (e) { // special treatment for E format |
494 | p = strchr(dest, '+'); |
495 | if (p && p < e) { // the sign bust be before the E |
496 | *p = (sign > 0) ? '+' : '-'; |
497 | } |
498 | p = strchr(dest, '-'); |
499 | if (p && p < e) { |
500 | *p = (sign > 0) ? ' ' : '-'; |
501 | } |
502 | } else { // no E format |
503 | p = strchr(dest, '+'); |
504 | if (p) { |
505 | *p = (sign > 0) ? '+' : '-'; |
506 | } |
507 | p = strchr(dest, '-'); |
508 | if (p) { |
509 | *p = (sign > 0) ? ' ' : '-'; |
510 | } |
511 | } |
512 | } |
513 | |
514 | // cleanup |
515 | free(fmt); |
516 | return dest; |
517 | } |
518 | |
519 | /* |
520 | * format: format a string |
521 | * |
522 | * symbols: |
523 | * & the whole string |
524 | * ! the first char |
525 | * \\ segment |
526 | */ |
527 | char *format_str(const char *fmt_cnst, const char *str) { |
528 | if (strchr(fmt_cnst, '&')) { |
529 | int size = strlen(str) + 1; |
530 | char *dest = malloc(size); |
531 | strcpy(dest, str); |
532 | return dest; |
533 | } |
534 | if (strchr(fmt_cnst, '!')) { |
535 | char *dest = malloc(2); |
536 | dest[0] = str[0]; |
537 | dest[1] = '\0'; |
538 | return dest; |
539 | } |
540 | |
541 | // segment |
542 | int ps = 0; |
543 | int pe = 0; |
544 | int lc = 0; |
545 | int count = 0; |
546 | const int fmtlen = strlen(fmt_cnst); |
547 | const int srclen = strlen(str); |
548 | char *p = (char *)fmt_cnst; |
549 | char *ss = NULL; |
550 | while (*p) { |
551 | if (*p == '\\' && lc != '_') { |
552 | if (count == 0) { |
553 | ss = p; |
554 | ps = (int) (p - fmt_cnst); |
555 | count++; |
556 | } else { |
557 | pe = p - fmt_cnst; |
558 | count++; |
559 | break; |
560 | } |
561 | } else if (count) { |
562 | count++; |
563 | } |
564 | lc = *p; |
565 | p++; |
566 | } |
567 | |
568 | char *dest = malloc(fmtlen + 1); |
569 | memset(dest, ' ', fmtlen - 1); |
570 | dest[fmtlen] = '\0'; |
571 | char *d = dest; |
572 | if (ps) { |
573 | memcpy(d, fmt_cnst, ps); |
574 | d += ps; |
575 | } |
576 | |
577 | // convert |
578 | if (ss) { |
579 | int i, j; |
580 | for (i = j = 0; i < count; i++) { |
581 | switch (ss[i]) { |
582 | case '\\': |
583 | case ' ': |
584 | if (j < srclen) { |
585 | d[i] = str[j]; |
586 | j++; |
587 | } else { |
588 | d[i] = ' '; |
589 | } |
590 | break; |
591 | default: |
592 | d[i] = ss[i]; |
593 | } |
594 | } |
595 | } |
596 | |
597 | d += count; |
598 | *d = '\0'; |
599 | if (*(fmt_cnst + pe + 1) != '\0') { |
600 | strcat(dest, fmt_cnst + pe + 1); |
601 | } |
602 | return dest; |
603 | } |
604 | |
605 | /* |
606 | * get numeric format |
607 | */ |
608 | char *fmt_getnumfmt(char *dest, char *source) { |
609 | int dp = 0, sign = 0, exitf = 0; |
610 | char *p = source; |
611 | char *d = dest; |
612 | |
613 | while (*p) { |
614 | switch (*p) { |
615 | case '^': |
616 | case '#': |
617 | case '0': |
618 | case ',': |
619 | *d++ = *p; |
620 | break; |
621 | case '-': |
622 | case '+': |
623 | sign++; |
624 | if (sign > 1) |
625 | exitf = 1; |
626 | else |
627 | *d++ = *p; |
628 | break; |
629 | case '.': |
630 | dp++; |
631 | if (dp > 1) { |
632 | exitf = 1; |
633 | } else { |
634 | *d++ = *p; |
635 | } |
636 | break; |
637 | default: |
638 | exitf = 1; |
639 | } |
640 | |
641 | if (exitf) { |
642 | break; |
643 | } |
644 | p++; |
645 | } |
646 | |
647 | *d = '\0'; |
648 | return p; |
649 | } |
650 | |
651 | /* |
652 | * get string format |
653 | */ |
654 | char *fmt_getstrfmt(char *dest, char *source) { |
655 | char *p = source; |
656 | char *d = dest; |
657 | |
658 | if (source[0] == '&' || source[0] == '!') { |
659 | *d++ = *source; |
660 | *d++ = '\0'; |
661 | return p + 1; |
662 | } |
663 | |
664 | while (*p) { |
665 | *d++ = *p++; |
666 | if (*p == '\\') { |
667 | *d++ = *p++; |
668 | break; |
669 | } |
670 | } |
671 | |
672 | *d = '\0'; |
673 | return p; |
674 | } |
675 | |
676 | /* |
677 | * add format node |
678 | */ |
679 | void fmt_addfmt(const char *fmt, int type) { |
680 | fmt_node_t *node = &fmt_stack[fmt_count]; |
681 | fmt_count++; |
682 | if (fmt_count >= MAX_FMT_N) { |
683 | panic("Maximum format-node reached" ); |
684 | } |
685 | node->fmt = malloc(strlen(fmt) + 1); |
686 | strcpy(node->fmt, fmt); |
687 | node->type = type; |
688 | } |
689 | |
690 | /* |
691 | * cleanup format-list |
692 | */ |
693 | void free_format() { |
694 | for (int i = 0; i < fmt_count; i++) { |
695 | fmt_node_t *node = &fmt_stack[i]; |
696 | free(node->fmt); |
697 | } |
698 | |
699 | fmt_count = fmt_cur = 0; |
700 | } |
701 | |
702 | /* |
703 | * The final format - create the format-list |
704 | * (that list it will be used later by fmt_printN and fmt_printS) |
705 | * |
706 | * '_' the next character is not belongs to format (simple string) |
707 | */ |
708 | void build_format(const char *fmt_cnst) { |
709 | char buf[1024]; |
710 | |
711 | free_format(); |
712 | |
713 | // backup of format |
714 | char *fmt = malloc(strlen(fmt_cnst) + 1); |
715 | strcpy(fmt, fmt_cnst); |
716 | |
717 | char *p = fmt; |
718 | char *b = buf; |
719 | int nc = 0; |
720 | while (*p) { |
721 | switch (*p) { |
722 | case '_': |
723 | // store prev. buf |
724 | *b = '\0'; |
725 | if (strlen(buf)) { |
726 | fmt_addfmt(buf, 0); |
727 | } |
728 | // store the new |
729 | buf[0] = *(p + 1); |
730 | buf[1] = '\0'; |
731 | fmt_addfmt(buf, 0); |
732 | b = buf; |
733 | p++; |
734 | break; |
735 | case '-': |
736 | case '+': |
737 | case '^': |
738 | case '0': |
739 | case '#': |
740 | // store prev. buf |
741 | *b = '\0'; |
742 | if (strlen(buf)) { |
743 | fmt_addfmt(buf, 0); |
744 | } |
745 | // get num-fmt |
746 | p = fmt_getnumfmt(buf, p); |
747 | fmt_addfmt(buf, 1); |
748 | b = buf; |
749 | nc = 1; |
750 | break; |
751 | case '&': |
752 | case '!': |
753 | case '\\': |
754 | // store prev. buf |
755 | *b = '\0'; |
756 | if (strlen(buf)) { |
757 | fmt_addfmt(buf, 0); |
758 | } |
759 | // get str-fmt |
760 | p = fmt_getstrfmt(buf, p); |
761 | fmt_addfmt(buf, 2); |
762 | b = buf; |
763 | nc = 1; |
764 | break; |
765 | default: |
766 | *b++ = *p; |
767 | } |
768 | |
769 | if (*p) { |
770 | if (nc) { // do not advance |
771 | nc = 0; |
772 | } else { |
773 | p++; |
774 | } |
775 | } |
776 | } |
777 | |
778 | // store prev. buf |
779 | *b = '\0'; |
780 | if (strlen(buf)) { |
781 | fmt_addfmt(buf, 0); |
782 | } |
783 | // cleanup |
784 | free(fmt); |
785 | } |
786 | |
787 | /* |
788 | * print simple strings (parts of format) |
789 | */ |
790 | void fmt_printL(int output, intptr_t handle) { |
791 | if (fmt_count == 0) { |
792 | return; |
793 | } else { |
794 | fmt_node_t *node; |
795 | do { |
796 | node = &fmt_stack[fmt_cur]; |
797 | if (node->type == 0) { |
798 | pv_write(node->fmt, output, handle); |
799 | fmt_cur++; |
800 | if (fmt_cur >= fmt_count) { |
801 | fmt_cur = 0; |
802 | } |
803 | } |
804 | } while (node->type == 0 && fmt_cur != 0); |
805 | } |
806 | } |
807 | |
808 | /* |
809 | * print formated number |
810 | */ |
811 | void fmt_printN(var_num_t x, int output, intptr_t handle) { |
812 | if (fmt_count == 0) { |
813 | rt_raise(ERR_FORMAT_INVALID_FORMAT); |
814 | } else { |
815 | fmt_printL(output, handle); |
816 | fmt_node_t *node = &fmt_stack[fmt_cur]; |
817 | fmt_cur++; |
818 | if (fmt_cur >= fmt_count) { |
819 | fmt_cur = 0; |
820 | } |
821 | if (node->type == 1) { |
822 | char *buf = format_num(node->fmt, x); |
823 | pv_write(buf, output, handle); |
824 | free(buf); |
825 | if (fmt_cur != 0) { |
826 | fmt_printL(output, handle); |
827 | } |
828 | } else { |
829 | rt_raise(ERR_FORMAT_INVALID_FORMAT); |
830 | } |
831 | } |
832 | } |
833 | |
834 | /* |
835 | * print formated string |
836 | */ |
837 | void fmt_printS(const char *str, int output, intptr_t handle) { |
838 | if (fmt_count == 0) { |
839 | rt_raise(ERR_FORMAT_INVALID_FORMAT); |
840 | } else { |
841 | fmt_printL(output, handle); |
842 | fmt_node_t *node = &fmt_stack[fmt_cur]; |
843 | fmt_cur++; |
844 | if (fmt_cur >= fmt_count) { |
845 | fmt_cur = 0; |
846 | } |
847 | if (node->type == 2) { |
848 | char *buf = format_str(node->fmt, str); |
849 | pv_write(buf, output, handle); |
850 | free(buf); |
851 | if (fmt_cur != 0) { |
852 | fmt_printL(output, handle); |
853 | } |
854 | } else { |
855 | rt_raise(ERR_FORMAT_INVALID_FORMAT); |
856 | } |
857 | } |
858 | } |
859 | |
860 | |