void BetterStream::_vprintf (unsigned char in_progmem, const char *fmt, va_list ap) { unsigned char c; /* holds a char from the format string */ unsigned char flags; unsigned char width; unsigned char prec; unsigned char buf[11]; /* size for -1 in octal, without '\0' */ for (;;) { /* * Process non-format characters */ for (;;) { c = GETBYTE (in_progmem, 1, fmt); if (!c) return; if (c == '%') { c = GETBYTE (in_progmem, 1, fmt); if (c != '%') break; } /* emit cr before lf to make most terminals happy */ if (c == '\n') write('\r'); write(c); } flags = 0; width = 0; prec = 0; /* * Process format adjustment characters, precision, width. */ do { if (flags < FL_WIDTH) { switch (c) { case '0': flags |= FL_ZFILL; continue; case '+': flags |= FL_PLUS; /* FALLTHROUGH */ case ' ': flags |= FL_SPACE; continue; case '-': flags |= FL_LPAD; continue; case '#': flags |= FL_ALT; continue; } } if (flags < FL_LONG) { if (c >= '0' && c <= '9') { c -= '0'; if (flags & FL_PREC) { prec = 10*prec + c; continue; } width = 10*width + c; flags |= FL_WIDTH; continue; } if (c == '.') { if (flags & FL_PREC) return; flags |= FL_PREC; continue; } if (c == 'l') { flags |= FL_LONG; continue; } if (c == 'h') continue; } break; } while ( (c = GETBYTE (in_progmem, 1, fmt)) != 0); /* * Handle floating-point formats E, F, G, e, f, g. */ if (c >= 'E' && c <= 'G') { flags |= FL_FLTUPP; c += 'e' - 'E'; goto flt_oper; } else if (c >= 'e' && c <= 'g') { int exp; /* exponent of master decimal digit */ int n; unsigned char vtype; /* result of float value parse */ unsigned char sign; /* sign character (or 0) */ unsigned char ndigs; flags &= ~FL_FLTUPP; flt_oper: if (!(flags & FL_PREC)) prec = 6; flags &= ~(FL_FLTEXP | FL_FLTFIX); if (c == 'e') flags |= FL_FLTEXP; else if (c == 'f') flags |= FL_FLTFIX; else if (prec > 0) prec -= 1; if (flags & FL_FLTFIX) { vtype = 7; /* 'prec' arg for 'ftoa_engine' */ ndigs = prec < 60 ? prec + 1 : 60; } else { if (prec > 7) prec = 7; vtype = prec; ndigs = 0; } exp = __ftoa_engine (va_arg(ap,double), (char *)buf, vtype, ndigs); vtype = buf[0]; sign = 0; if ((vtype & FTOA_MINUS) && !(vtype & FTOA_NAN)) sign = '-'; else if (flags & FL_PLUS) sign = '+'; else if (flags & FL_SPACE) sign = ' '; if (vtype & (FTOA_NAN | FTOA_INF)) { const char *p; ndigs = sign ? 4 : 3; if (width > ndigs) { width -= ndigs; if (!(flags & FL_LPAD)) { do { write(' '); } while (--width); } } else { width = 0; } if (sign) write(sign); p = PSTR("inf"); if (vtype & FTOA_NAN) p = PSTR("nan"); while ( (ndigs = pgm_read_byte((const prog_char *)p)) != 0) { if (flags & FL_FLTUPP) ndigs += 'I' - 'i'; write(ndigs); p++; } goto tail; } /* Output format adjustment, number of decimal digits in buf[] */ if (flags & FL_FLTFIX) { ndigs += exp; if ((vtype & FTOA_CARRY) && buf[1] == '1') ndigs -= 1; if ((signed char)ndigs < 1) ndigs = 1; else if (ndigs > 8) ndigs = 8; } else if (!(flags & FL_FLTEXP)) { /* 'g(G)' format */ if (exp <= prec && exp >= -4) flags |= FL_FLTFIX; while (prec && buf[1+prec] == '0') prec--; if (flags & FL_FLTFIX) { ndigs = prec + 1; /* number of digits in buf */ prec = prec > exp ? prec - exp : 0; /* fractional part length */ } } /* Conversion result length, width := free space length */ if (flags & FL_FLTFIX) n = (exp>0 ? exp+1 : 1); else n = 5; /* 1e+00 */ if (sign) n += 1; if (prec) n += prec + 1; width = width > n ? width - n : 0; /* Output before first digit */ if (!(flags & (FL_LPAD | FL_ZFILL))) { while (width) { write(' '); width--; } } if (sign) write(sign); if (!(flags & FL_LPAD)) { while (width) { write('0'); width--; } } if (flags & FL_FLTFIX) { /* 'f' format */ n = exp > 0 ? exp : 0; /* exponent of left digit */ do { if (n == -1) write('.'); flags = (n <= exp && n > exp - ndigs) ? buf[exp - n + 1] : '0'; if (--n < -prec) break; write(flags); } while (1); if (n == exp && (buf[1] > '5' || (buf[1] == '5' && !(vtype & FTOA_CARRY))) ) { flags = '1'; } write(flags); } else { /* 'e(E)' format */ /* mantissa */ if (buf[1] != '1') vtype &= ~FTOA_CARRY; write(buf[1]); if (prec) { write('.'); sign = 2; do { write(buf[sign++]); } while (--prec); } /* exponent */ write(flags & FL_FLTUPP ? 'E' : 'e'); ndigs = '+'; if (exp < 0 || (exp == 0 && (vtype & FTOA_CARRY) != 0)) { exp = -exp; ndigs = '-'; } write(ndigs); for (ndigs = '0'; exp >= 10; exp -= 10) ndigs += 1; write(ndigs); write('0' + exp); } goto tail; } /* * Handle string formats c, s, S. */ { const char * pnt; size_t size; switch (c) { case 'c': buf[0] = va_arg (ap, int); pnt = (char *)buf; size = 1; goto no_pgmstring; case 's': pnt = va_arg (ap, char *); size = strnlen (pnt, (flags & FL_PREC) ? prec : ~0); no_pgmstring: flags &= ~FL_PGMSTRING; goto str_lpad; case 'S': // pgmstring: // not yet used pnt = va_arg (ap, char *); size = strnlen_P (pnt, (flags & FL_PREC) ? prec : ~0); flags |= FL_PGMSTRING; str_lpad: if (!(flags & FL_LPAD)) { while (size < width) { write(' '); width--; } } while (size) { write(GETBYTE (flags, FL_PGMSTRING, pnt)); if (width) width -= 1; size -= 1; } goto tail; } } /* * Handle integer formats variations for d/i, u, o, p, x, X. */ if (c == 'd' || c == 'i') { long x = (flags & FL_LONG) ? va_arg(ap,long) : va_arg(ap,int); flags &= ~(FL_NEGATIVE | FL_ALT); if (x < 0) { x = -x; flags |= FL_NEGATIVE; } c = __ultoa_invert (x, (char *)buf, 10) - (char *)buf; } else {
int vfprintf (FILE * stream, const char *fmt, va_list ap) { unsigned char c; /* holds a char from the format string */ unsigned char flags; unsigned char buf[11]; /* size for -1 in octal, without '\0' */ stream->len = 0; if ((stream->flags & __SWR) == 0) return EOF; for (;;) { for (;;) { c = *fmt++; if (!c) goto ret; if (c == '%') { c = *fmt++; if (c != '%') break; } putc (c, stream); } for (flags = 0; !(flags & FL_LONG); /* 'll' will detect as error */ c = *fmt++) { if (c && strchr(" +-.0123456789h", c)) continue; if (c == '#') { flags |= FL_ALT; continue; } if (c == 'l') { flags |= FL_LONG; continue; } break; } /* Only a format character is valid. */ if (c && strchr("EFGefg", c)) { (void) va_arg (ap, double); putc ('?', stream); continue; } { const char * pnt; switch (c) { case 'c': putc (va_arg (ap, int), stream); continue; case 'S': /* FALLTHROUGH */ case 's': pnt = va_arg (ap, char *); while ( (c = *pnt++) != 0) putc (c, stream); continue; } } if (c == 'd' || c == 'i') { long x = (flags & FL_LONG) ? va_arg(ap,long) : va_arg(ap,int); flags &= ~FL_ALT; if (x < 0) { x = -x; /* `putc ('-', stream)' will considarably inlarge stack size. So flag is used. */ flags |= FL_NEGATIVE; } c = __ultoa_invert (x, (char *)buf, 10) - (char *)buf; } else {