Rune - Further Object abstraction work
[rune.git] / classes / stdio / show.d
1 # STDIO/SHOW.D
2 #
3 # (c)Copyright 2020-2021, Matthew Dillon, All Rights Reserved.
4 #    See the COPYRIGHT file at the base of the Rune distribution.
5
6 # Show generated simple auto-formatted output
7 #
8 public method void
9 File.show(...)
10 {
11     int i;
12     int was_string = 1;
13
14     i = self.__varcount;
15     for (i = 0; i < self.__varcount; ++i) {
16         bool negative_ok;
17
18         if (!was_string)
19             this->fputc(' ');
20
21         /*
22          * Type comparisons ignore storage qualifiers by default.
23          * You can specify them in your cases to require particular
24          * qualifiers such as const.
25          */
26         switch(self.__vartype[i]) {
27         case typeof(int8_t):
28         case typeof(int16_t):
29         case typeof(int32_t):
30         case typeof(int64_t):
31             negative_ok = 1;
32             /* fall through */
33         case typeof(uint8_t):
34         case typeof(uint16_t):
35         case typeof(uint32_t):
36         case typeof(uint64_t):
37             /*
38              * Decode various integer types
39              */
40             int64_t v;
41             char buf[32];
42             usize_t j = arysize(buf);
43
44             switch(self.__vartype[i]) {
45             case typeof(int8_t):
46                 v = (int8_t)self.__vardata[i];
47                 break;
48             case typeof(int16_t):
49                 v = (int16_t)self.__vardata[i];
50                 break;
51             case typeof(int32_t):
52                 v = (int32_t)self.__vardata[i];
53                 break;
54             case typeof(int64_t):
55                 v = (int64_t)self.__vardata[i];
56                 break;
57             case typeof(uint8_t):
58                 v = (uint8_t)self.__vardata[i];
59                 break;
60             case typeof(uint16_t):
61                 v = (uint16_t)self.__vardata[i];
62                 break;
63             case typeof(uint32_t):
64                 v = (uint32_t)self.__vardata[i];
65                 break;
66             case typeof(uint64_t):
67                 v = (uint64_t)self.__vardata[i];
68                 break;
69             default:
70                 v = 0;
71                 break;
72             }
73
74             if (v == 0L) {
75                 buf[--j] = '0';
76             } else {
77                 uint64_t vv;
78                 int negative = 0;
79
80                 if (negative_ok && v < 0L) {
81                     v = -v;
82                     negative = 1;
83                 }
84                 vv = (uint64_t)v;
85                 while (vv != 0UL) {
86                     buf[--j] = (char)(vv % 10UL) + '0';
87                     vv = vv / 10UL;
88                 }
89                 if (negative != 0)
90                     buf[--j] = '-';
91             }
92             this->fwrite(&buf[j], arysize(buf) - (usize_t)j);
93             was_string = 0;
94             break;
95         case typeof(int *):
96             /* XXX doesn't work */
97             uint64_t v = (uint64_t)(int *)self.__vardata[i];
98             char buf[32];
99             int j = arysize(buf);
100             int vv;
101
102             while (v) {
103                 --j;
104                 vv = (int)v & 15;
105                 if (vv < 10)
106                     buf[j] = (char)vv + '0';
107                 else
108                     buf[j] = (char)vv + 'a' - (char)10;
109                 v = v >> 4;
110             }
111             buf[--j] = 'x';
112             buf[--j] = '0';
113             this->fwrite(&buf[j], arysize(buf) - (usize_t)j);
114             was_string = 0;
115             break;
116         case typeof(string_p):
117             string_p str = (string_p)self.__vardata[i];
118
119             if (str == NULL) {
120                 this->fputs("(null)");
121             } else {
122                 this->fputs(str);
123             }
124             was_string = 1;
125             break;
126         case typeof(string_rw_p):
127             /*
128              * Decode the standard string type
129              */
130             string_rw_p str = (string_rw_p)self.__vardata[i];
131
132             if (str == NULL) {
133                 this->fputs("(null)");
134             } else {
135                 this->fputs(str);
136             }
137             was_string = 1;
138             break;
139         case typeof(int8_t):
140         case typeof(char):
141             char c = (char)self.__vardata[i];
142
143             this->fputc(c);
144             was_string = 0;
145             break;
146         case typeof(bool):
147             if ((bool)self.__vardata[i]) {
148                 this->fputs("TRUE");
149             } else {
150                 this->fputs("FALSE");
151             }
152             was_string = 0;
153             break;
154         case typeof(float):
155             this->format_fp('f', 0, ' ', -1, 6, 6,
156                            (ldouble)(float)self.__vardata[i],
157                            (ldouble)float.epsilon);
158             was_string = 0;
159             break;
160         case typeof(double):
161             this->format_fp('f', 0, ' ', -1, 6, 15,
162                            (ldouble)(double)self.__vardata[i],
163                            (ldouble)double.epsilon);
164             was_string = 0;
165             break;
166         case typeof(ldouble):
167             this->format_fp('f', 0, ' ', -1, 6, 19,
168                            (ldouble)self.__vardata[i],
169                            ldouble.epsilon);
170             was_string = 0;
171             break;
172         default:
173             this->fputc('?');
174             was_string = 0;
175             break;
176         }
177     }
178     this->fputc('\n');
179 }
180
181 # Format is more printf-like, but without integer size specifiers.
182 # So e.g. %6.2f %08x %d %u %6u %06u %8.8s %*.*s and so forth.
183 #
184 #
185 const int STATE_SCAN    = 0;
186 const int STATE_W1      = 1;
187 const int STATE_W2      = 2;
188 const int STATE_CTL     = 3;
189
190 const int32_t FLAG_PL   = 0x0001;       # + (always show sign)
191 const int32_t FLAG_PL2  = 0x0002;       # + (always show sign)
192 const int32_t FLAG_LJ   = 0x0004;       # - (left justify)
193 const int32_t FLAG_ALT  = 0x0008;       # # (alternative form)
194 const int32_t FLAG_UP   = 0x0010;       #   (upper-case form)
195
196 public method void
197 File.format(string_p fmt, ...)
198 {
199     int state;
200     int32_t flags;
201     char zf;
202     int w1;
203     int w2;
204     int i;
205     string_p base;
206
207     base = fmt;
208     i = 0;
209     state = STATE_SCAN;
210
211     while (*base) {
212         if (*base == '%') {
213             ++base;
214             w1 = -1;
215             w2 = -1;
216             flags = 0;
217             zf = ' ';
218
219             switch(*base) {
220             case '#':
221                 flags |= FLAG_ALT;
222                 ++base;
223                 continue switch;
224             case '-':
225                 flags |= FLAG_LJ;
226                 zf = ' ';       # - overrides 0
227                 ++base;
228                     continue switch;
229             case ' ':
230                 flags |= FLAG_PL | FLAG_PL2;
231                 ++base;
232                 continue switch;
233             case '+':
234                 flags |= FLAG_PL;
235                 ++base;
236                 continue switch;
237             case '0':
238                 # left-justify overrides zero-fill.
239                 #
240                 ++base;
241                 if ((flags & FLAG_LJ) == 0)
242                     zf = '0';
243                 continue switch;
244             default:
245                 break;
246             }
247             state = STATE_W1;
248         }
249
250         switch(state) {
251         case STATE_SCAN:
252             string_p ptr;
253
254             for (ptr = base; *ptr && *ptr != '%'; ++ptr)
255                 ;
256             this->fwrite(base, ptr - base);
257             base = ptr;
258             break;
259         case STATE_W1:
260         case STATE_W2:
261             int t;
262
263             if (*base == '*') {
264                 switch(self.__vartype[i]) {
265                 case typeof(int8_t):
266                     t = (int8_t)self.__vardata[i];
267                     break;
268                 case typeof(int16_t):
269                     t = (int16_t)self.__vardata[i];
270                     break;
271                 case typeof(int32_t):
272                     t = (int32_t)self.__vardata[i];
273                     break;
274                 case typeof(int64_t):
275                     t = (int64_t)self.__vardata[i];
276                     break;
277                 case typeof(uint8_t):
278                     t = (uint8_t)self.__vardata[i];
279                     break;
280                 case typeof(uint16_t):
281                     t = (uint16_t)self.__vardata[i];
282                     break;
283                 case typeof(uint32_t):
284                     t = (uint32_t)self.__vardata[i];
285                     break;
286                 case typeof(uint64_t):
287                     t = (uint64_t)self.__vardata[i];
288                     break;
289                 default:
290                     this->format("(badw%d)", state);
291                     t = -1;
292                     break;
293                 }
294                 ++i;
295                 ++base;
296             } else if (*base >= '0' && *base <= '9') {
297                 t = 0;
298                 while (*base >= '0' && *base <= '9') {
299                     t = t * 10 + (int)(*base - '0');
300                     ++base;
301                 }
302             }
303             if (state == STATE_W1) {
304                 w1 = t;
305                 if (*base == '.') {
306                     state = STATE_W2;
307                     ++base;
308                 } else {
309                     state = STATE_CTL;
310                 }
311             } else {
312                 w2 = t;
313                 state = STATE_CTL;
314             }
315             break;
316         case STATE_CTL:
317             int neg_ok = 0;
318             int nbase = 16;
319
320             # Determine if we ran out of arguments.
321             #
322             if (i >= self.__varcount) {
323                 this->fputs("(noarg)");
324                 state = STATE_SCAN;
325                 ++base;
326                 break;
327             }
328
329             # Control designator.  Note that the format command
330             # can take any argument of the correct type but
331             # does not cast arguments to make them correct.
332             #
333             switch(*base) {
334             case 'c':
335                 char t;
336
337                 switch(self.__vartype[i]) {
338                 case typeof(int8_t):
339                     t = (int8_t)self.__vardata[i];
340                     break;
341                 case typeof(int16_t):
342                     t = (int16_t)self.__vardata[i];
343                     break;
344                 case typeof(int32_t):
345                     t = (int32_t)self.__vardata[i];
346                     break;
347                 case typeof(int64_t):
348                     t = (int64_t)self.__vardata[i];
349                     break;
350                 case typeof(uint8_t):
351                     t = (uint8_t)self.__vardata[i];
352                     break;
353                 case typeof(uint16_t):
354                     t = (uint16_t)self.__vardata[i];
355                     break;
356                 case typeof(uint32_t):
357                     t = (uint32_t)self.__vardata[i];
358                     break;
359                 case typeof(uint64_t):
360                     t = (uint64_t)self.__vardata[i];
361                     break;
362                 default:
363                     this->format("(badchar)");
364                     t = (char)-1;
365                     break;
366                 }
367                 this->fputc(t);
368                 break;
369             case 'd':
370                 neg_ok = 1;
371                 /* fall through */
372             case 'u':
373                 nbase = 10;
374                 /* fall through */
375             case 'o':
376                 if (*base == 'o')
377                         nbase = 8;
378                 /* fall through */
379             case 'X':
380                 if (*base == 'X')
381                         flags |= FLAG_UP;
382                 /* fall through */
383             case 'x':
384                 # Signed or unsigned integral values, based
385                 # on format above.  'd' is the only signed
386                 # integer printing format.
387                 #
388                 uint64_t t;
389
390                 switch(self.__vartype[i]) {
391                 case typeof(int8_t):
392                     t = (int8_t)self.__vardata[i];
393                     break;
394                 case typeof(int16_t):
395                     t = (int16_t)self.__vardata[i];
396                     break;
397                 case typeof(int32_t):
398                     t = (int32_t)self.__vardata[i];
399                     break;
400                 case typeof(int64_t):
401                     t = (int64_t)self.__vardata[i];
402                     break;
403                 case typeof(uint8_t):
404                     t = (uint8_t)self.__vardata[i];
405                     break;
406                 case typeof(uint16_t):
407                     t = (uint16_t)self.__vardata[i];
408                     break;
409                 case typeof(uint32_t):
410                     t = (uint32_t)self.__vardata[i];
411                     break;
412                 case typeof(uint64_t):
413                     t = (uint64_t)self.__vardata[i];
414                     break;
415                 default:
416                     this->format("(badint)");
417                     t = -1;
418                     nbase = 0;
419                     break;
420                 }
421                 if (nbase)
422                     this->format_int(nbase, neg_ok, flags, zf, w1, t);
423                 break;
424             case 'p':
425                 # Pointer address - caller must cast to
426                 # void * or const void *.
427                 #
428                 uint64_t t;
429
430                 t = (uint64_t)(intptr_t)self.__vardata[i];
431                 this->format_int(16, 0, FLAG_ALT, '0', 16, t);
432                 break;
433             case 'F':   /* standard float notation */
434             case 'E':   /* scientific notation */
435             case 'G':   /* choose f/e */
436             case 'A':   /* hex notation */
437             case 'R':   /* engineering notation */
438                 flags |= FLAG_UP;
439                 /* fall through */
440             case 'f':   /* standard float notation */
441             case 'e':   /* scientific notation */
442             case 'g':   /* choose f/e */
443             case 'a':   /* hex notation */
444             case 'r':   /* engineering notation */
445                 ldouble t;
446
447                 switch(self.__vartype[i]) {
448                 case typeof(float):
449                     t = (ldouble)(float)self.__vardata[i];
450                     if (w2 < 0)
451                         w2 = 6;
452                     this->format_fp(*base, flags, zf, w1, w2, 6, t,
453                                    (ldouble)float.epsilon);
454                     break;
455                 case typeof(double):
456                     t = (ldouble)(double)self.__vardata[i];
457                     if (w2 < 0)
458                         w2 = 6;
459                     this->format_fp(*base, flags, zf, w1, w2, 15, t,
460                                    (ldouble)double.epsilon);
461                     break;
462                 case typeof(ldouble):
463                     t = (ldouble)self.__vardata[i];
464                     if (w2 < 0)
465                         w2 = 6;
466                     this->format_fp(*base, flags, zf, w1, w2, 19, t,
467                                    (ldouble)ldouble.epsilon);
468                     break;
469                 default:
470                     this->format("(badflt)");
471                     break;
472                 }
473                 break;
474             case 's':
475                 switch(self.__vartype[i]) {
476                 case typeof(string_rw_p):
477                     string_p str = (string_p)(string_rw_p)self.__vardata[i];
478                     this->format_str(flags, zf, w1, w2, str);
479                     break;
480                 case typeof(string_p):
481                     string_p str = (string_p)self.__vardata[i];
482                     this->format_str(flags, zf, w1, w2, str);
483                     break;
484                 default:
485                     this->fputs("(badstr)");
486                     break;
487                 }
488                 break;
489             default:
490                 this->format("(badctl)");
491                 break;
492             }
493             ++base;
494             ++i;
495             state = STATE_SCAN;
496             break;
497         default:
498             /* NOT REACHED */
499             break;
500         }
501     }
502 }
503
504 # Format strings
505 #
506 public method
507 void
508 File.format_str(int32_t flags, char zf, int w1, int w2, string_p str)
509 {
510     int len;
511
512     # Handle null case (do not pad "(null)" for now)
513     #
514     if (str == NULL) {
515         this->fputs("(null)");
516         return;
517     }
518     len = Str.strlen(str);
519     if (w2 < 0)
520         w2 = len;
521
522     # Pad on the left when not left-justified
523     #
524     if (w2 < w1 && (flags & FLAG_LJ) == 0) {
525         while (w2 < w1) {
526             this->fputc(zf);
527             --w1;
528         }
529     }
530
531     # Output. w2 forces output to w2 length with space padding at end.
532     #
533     if (w2 <= len) {
534         this->fwrite(str, w2);
535     } else {
536         this->fwrite(str, len);
537         while (len < w2) {
538             this->fputc(' ');
539             ++len;
540         }
541     }
542
543     # Pad on the right when left-justified
544     #
545     if (w2 < w1 && (flags & FLAG_LJ)) {
546         while (w2 < w1) {
547             this->fputc(' ');
548             --w1;
549         }
550     }
551 }
552
553 # Format integers
554 #
555 public method
556 void
557 File.format_int(int nbase, int neg_ok, int32_t flags, char zf,
558             int w1, uint64_t val)
559 {
560     char buf[32];
561     usize_t bi = sizeof(buf);
562     usize_t len;
563     int didhexprefix = 0;
564
565     # Generate buffer
566     #
567     if (neg_ok && (int64_t)val < 0L) {
568         neg_ok = 1;             # flag negative value
569         val = -val;
570     } else {
571         neg_ok = 0;
572     }
573     while (val) {
574         if (nbase == 10) {
575             buf[--bi] = (char)(val % 10UL) + '0';
576             val = val / 10UL;
577         } else if (nbase == 16) {
578             if ((val & 15UL) < 10UL)
579                 buf[--bi] = (char)(val & 15UL) + '0';
580             else
581                 buf[--bi] = (char)(val & 15UL) + 'a' - 10UB;
582             val = val >> 4;
583         } else if (nbase == 8) {
584             buf[--bi] = (char)(val & 7UL) + '0';
585             val = val >> 3;
586         }
587     }
588     if (bi == sizeof(buf))
589         buf[--bi] = '0';
590
591     # Alternative form adjustments
592     #
593     if (flags & FLAG_ALT) {
594         if (nbase == 16) {
595             # Prefix with 0x or 0X for hex
596             #
597             if (flags & FLAG_UP)
598                 buf[--bi] = 'X';
599             else
600                 buf[--bi] = 'x';
601             buf[--bi] = '0';
602             didhexprefix = 1;
603         } else if (nbase == 8) {
604             # Require leading 0 for octal
605             #
606             if (buf[bi] != '0')
607                 buf[--bi] = '0';
608         }
609     }
610
611     len = sizeof(buf) - bi;
612
613     # prefix space-fill
614     #
615     if (zf != '0' && (flags & FLAG_LJ) == 0 && w1 >= 0) {
616         w1 = w1 - (int)len;
617         if (neg_ok || (flags & FLAG_PL))
618             --w1;
619         while (w1 > 0) {
620             this->fputc(zf);
621             --w1;
622         }
623     }
624
625     # plus or minus sign
626     #
627     if (neg_ok)
628         this->fputc('-');
629     else if (flags & FLAG_PL2)
630         this->fputc(' ');
631     else if (flags & FLAG_PL)
632         this->fputc('+');
633
634     # prefix zero-fill
635     #
636     if (zf == '0' && (flags & FLAG_LJ) == 0 && w1 >= 0) {
637         if (didhexprefix) {
638             this->fputc(buf[bi]);
639             this->fputc(buf[bi+1L]);
640             bi += 2L;
641             len -= 2L;
642         }
643         w1 = w1 - (int)len;
644         if (neg_ok || (flags & FLAG_PL))
645             --w1;
646         while (w1 > 0) {
647             this->fputc(zf);
648             --w1;
649         }
650     }
651
652     # Output buffer
653     #
654     this->fwrite(&buf[bi], len);
655
656     # Postfix space-fill
657     #
658     if ((flags & FLAG_LJ) && w1 >= 0) {
659         w1 = w1 - (int)len;
660         while (w1 > 0) {
661             this->fputc(' ');
662             --w1;
663         }
664     }
665 }
666
667 # Format floating point numbers
668 #
669 # Generally speaking use log10() and pow() to extract the base-10 exponent
670 # of a floating point number, then normalize it to 1.0-9.99* (except for 0)
671 # and use a simple int-conversion/subtract/multiply loop to extract the
672 # digits in base-10.
673 #
674 # To avoid base-10/base-2 representation artifacts, we also round up the
675 # absolute value by half a LSB at the requested precision.
676 #
677 public method
678 void
679 File.format_fp(char mode, int32_t flags, char zf, 
680            int w1, int w2, int realdig, ldouble val, ldouble epsilon)
681 {
682     char buf[32];
683     ldouble prec;
684     int bi;
685     int count;
686     int force_ee;
687     int stripzeros;
688     int isneg;
689     int xp;
690     int ee;
691
692     force_ee = 0;       # debugging safety, not needed in Rune
693     isneg = 0;  # debugging safety, not needed in Rune
694     ee = 0;             # debugging safety, not needed in Rune
695
696     # Handle negative values
697     #
698     if (val < 0.0X) {
699         val = -val;
700         isneg = 1;
701     }
702
703     # Calculate the exponent and normalize.
704     #
705     if (val >= 1.0X)
706         xp = (int)ldouble.log10(val);
707     else
708         xp = (int)ldouble.log10(val) - 1;
709     val = val * ldouble.pow(10.0X, (ldouble)-xp);
710
711     # Adjust by half an lsb to deal with boundary conditions, otherwise
712     # numbers may not print nicely.
713     #
714     # WARNING! At the very least adjust by epsilon.
715     #
716     prec = ldouble.pow(10.0X, (ldouble)-(xp + w2 + 1)) * 5.0X;
717     if (prec < epsilon * 5.0X)
718         prec = epsilon * 5.0X;
719     val = val + prec;
720
721     # Possible boundary condition due to adjustment.
722     #
723     if (val >= 10.0X) {
724         val = val / 10.0X;
725         ++xp;
726     }
727
728     # Dump digits to buffer
729     #
730     if (realdig > (int)sizeof(buf))
731         realdig = (int)sizeof(buf);
732     while (bi < realdig) {
733         char c = (char)(int)val;
734
735         buf[bi] = c + '0';
736         val = (val - (ldouble)c) * 10.0X;
737         ++bi;
738     }
739
740     # xp is the power of 10 exponent.  e.g. a value 1.000-9.999 will have
741     # xp == 0.
742     #
743     switch(mode) {
744     case 'F':
745     case 'f':
746         # Normal float display.
747         #
748         break;
749     case 'G':
750     case 'g':
751         # Best precision with zero stripping.
752         #
753         if ((flags & FLAG_ALT) == 0)
754             stripzeros = 1;
755         if (xp > -realdig || xp < realdig)
756             break;
757         force_ee = 1;
758         ee = xp;
759         xp = 0;
760         break;
761     case 'E':
762     case 'e':
763         # Scientific mode, d.dddpe+/-dd
764         #
765         force_ee = 1;
766         ee = xp;
767         xp = 0;
768         break;
769     case 'R':
770     case 'r':
771         # Engineering mode, exponent in multiples of 3 only
772         #
773         ee = xp / 3 * 3;
774         xp -= ee;
775         if (xp < 0) {
776             ee -= 3;
777             xp += 3;
778         }
779         break;
780     }
781
782     # Ok, we have our displayed exponent (ee).  Adjust (xp) to represent
783     # the number of digits in the buffer to the left of the decimal point
784     # to reduce confusion.
785     #
786     ++xp;
787
788     # If stripping fractional zeros attempt to truncate bi (so we get the
789     # rounded result), then strip zeros from there.  It's ok if the
790     # stripping digs into the left-of-decimal-point digits.
791     #
792     if (stripzeros) {
793         if (bi > xp + w2) {
794             bi = xp + w2;       # can go oob
795             if (bi < 1)         # fix if so
796                 bi = 1;
797         }
798         while (bi > 1 && buf[bi-1] == '0')
799             --bi;
800     }
801
802     # Now calculate the output space we will use so we can figure out
803     # what prefix padding to add.
804     #
805     count = 0;
806     if (isneg || (flags & FLAG_PL))     # plus, space, or minus
807         count = 1;
808
809     if (xp > 0)
810         count += xp;            # left of decimal
811     else
812         ++count;                # left of decimal a single 0
813
814     if (w2 == 0) {              # right of decimal (incl decimal pt)
815         if (flags & FLAG_ALT)   # force decimal point
816             ++count;
817     } else if (stripzeros) {
818         if (xp <= bi && bi - xp < w2) {
819             w2 = bi - xp;
820             if (w2 <= 0) {
821                 w2 = 0;                 # nothing left
822             } else {
823                 count += w2 + 1;        # something left
824             }
825         }
826     } else {
827         count += w2 + 1;
828     }
829
830     if (force_ee || ee) {
831         int tmp = ee;
832
833         if (tmp < 0)
834             tmp = -tmp;
835         count += 3;             # e+d
836         if (tmp >= 10)          # additional digits
837             ++count;
838         if (tmp >= 100)
839             ++count;
840         if (tmp >= 1000)
841             ++count;
842         if (tmp >= 10000)
843             ++count;
844     }
845
846     # Space-fill in front.
847     #
848     if (zf != '0' && (flags & FLAG_LJ) == 0 && count < w1) {
849         while (count < w1) {
850             ++count;
851             this->fputc(' ');
852         }
853     }
854
855     # Plus, minus, space, nothing
856     #
857     if (isneg)
858         this->fputc('-');
859     else if (flags & FLAG_PL2)
860         this->fputc(' ');
861     else if (flags & FLAG_PL)
862         this->fputc('+');
863
864     # Zero-pad in front.
865     #
866     if (zf == '0' && (flags & FLAG_LJ) == 0 && count < w1) {
867         while (count < w1) {
868             ++count;
869             this->fputc('0');
870         }
871     }
872
873     # Digits before decimal point
874     #
875     if (xp > 0) {
876         if (xp > bi) {
877             this->fwrite(buf, bi);
878             while (bi < xp) {
879                 this->fputc('0');
880                 ++bi;
881             }
882         } else {
883             this->fwrite(buf, xp);
884         }
885     } else {
886         this->fputc('0');
887     }
888
889     # Digits after decimal point
890     #
891     if (w2 == 0) {
892         if (flags & FLAG_ALT)           # force decimal point
893             this->fputc('.');
894     } else {
895         this->fputc('.');
896         while (w2 > 0) {
897             if (xp < 0) {
898                 this->fputc('0');
899                 ++xp;
900             } else if (xp < bi) {
901                 this->fputc(buf[xp]);
902                 ++xp;
903             } else if (!stripzeros) {
904                 this->fputc('0');
905             }
906             --w2;
907         }
908     }
909
910     # Exponent
911     #
912     if (force_ee || ee) {
913         if (flags & FLAG_UP)
914             this->format("E%+d", ee);
915         else
916             this->format("e%+d", ee);
917     }
918
919     # If left-justified space-fill remainder
920     #
921     if ((flags & FLAG_LJ) && count < w1) {
922         while (count < w1) {
923             ++count;
924             this->fputc(' ');
925         }
926     }
927 }