/* File: tinyprintf.c Copyright (C) 2004 Kustaa Nyholm This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "tinystdio.h" #include #include #include /* * Configuration */ /* Enable long int support */ #define PRINTF_LONG_SUPPORT /* Enable long long int support (implies long int support) */ #define PRINTF_LONG_LONG_SUPPORT /* Enable %z (size_t) support */ #define PRINTF_SIZE_T_SUPPORT /* * Configuration adjustments */ #ifdef PRINTF_LONG_LONG_SUPPORT # define PRINTF_LONG_SUPPORT #endif /* __SIZEOF___ defined at least by gcc */ #ifdef __SIZEOF_POINTER__ # define SIZEOF_POINTER __SIZEOF_POINTER__ #endif #ifdef __SIZEOF_LONG_LONG__ # define SIZEOF_LONG_LONG __SIZEOF_LONG_LONG__ #endif #ifdef __SIZEOF_LONG__ # define SIZEOF_LONG __SIZEOF_LONG__ #endif #ifdef __SIZEOF_INT__ # define SIZEOF_INT __SIZEOF_INT__ #endif #ifdef __GNUC__ # define _TFP_GCC_NO_INLINE_ __attribute__ ((noinline)) #else # define _TFP_GCC_NO_INLINE_ #endif /* * Implementation */ struct param { char lz:1; /**< Leading zeros */ char alt:1; /**< alternate form */ char uc:1; /**< Upper case (for base16 only) */ char align_left:1; /**< 0 == align right (default), 1 == align left */ int width; /**< field width */ char sign; /**< The sign to display (if any) */ unsigned int base; /**< number base (e.g.: 8, 10, 16) */ char *bf; /**< Buffer to output */ char prec; }; #ifdef PRINTF_LONG_LONG_SUPPORT static void _TFP_GCC_NO_INLINE_ ulli2a( unsigned long long int num, struct param *p) { int n = 0; unsigned long long int d = 1; char *bf = p->bf; while (num / d >= p->base) d *= p->base; while (d != 0) { int dgt = num / d; num %= d; d /= p->base; if (n || dgt > 0 || d == 0) { *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); ++n; } } *bf = 0; } static void lli2a(long long int num, struct param *p) { if (num < 0) { num = -num; p->sign = '-'; } ulli2a(num, p); } #endif #ifdef PRINTF_LONG_SUPPORT static void uli2a(unsigned long int num, struct param *p) { int n = 0; unsigned long int d = 1; char *bf = p->bf; while (num / d >= p->base) d *= p->base; while (d != 0) { int dgt = num / d; num %= d; d /= p->base; if (n || dgt > 0 || d == 0) { *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); ++n; } } *bf = 0; } static void li2a(long num, struct param *p) { if (num < 0) { num = -num; p->sign = '-'; } uli2a(num, p); } #endif static void ui2a(unsigned int num, struct param *p) { int n = 0; unsigned int d = 1; char *bf = p->bf; while (num / d >= p->base) d *= p->base; while (d != 0) { int dgt = num / d; num %= d; d /= p->base; if (n || dgt > 0 || d == 0) { *bf++ = dgt + (dgt < 10 ? '0' : (p->uc ? 'A' : 'a') - 10); ++n; } } *bf = 0; } static void i2a(int num, struct param *p) { if (num < 0) { num = -num; p->sign = '-'; } ui2a(num, p); } static int a2d(char ch) { if (ch >= '0' && ch <= '9') return ch - '0'; else if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; else if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; else return -1; } static char a2u(char ch, const char **src, int base, unsigned int *nump) { const char *p = *src; unsigned int num = 0; int digit; while ((digit = a2d(ch)) >= 0) { if (digit > base) break; num = num * base + digit; ch = *p++; } *src = p; *nump = num; return ch; } void float_to_s(double a, char buffer[]) { if (a < 0) { a = -a; tfp_sprintf(buffer, "-%d.%d", (int)a, (int)((a - (int)a)*1000)); } else tfp_sprintf(buffer, "%d.%d", (int)a, (int)((a - (int)a)*1000)); } double s_to_float(char* str) { int neg; double fvalue = 0; int pos; if (*str == '-') { neg = 1; str++; } else neg = 0; int point_flag = 0; int exp = 0; for (fvalue = 0, pos = 0; *str != 0 ; str++, pos++) { if (*str == '.') { point_flag = 1; str++; } if ('0' <= *str && *str <= '9') fvalue = fvalue*10 + (int)(*str - '0'); else break; if (point_flag == 1) exp++; } if (pos == 0) return 0; for (pos = 0; pos < exp; pos++) fvalue = fvalue/10.0; return neg ? -fvalue : fvalue; } static void putchw(void *putp, putcf putf, struct param *p) { char ch; int n = p->width; char *bf = p->bf; /* Number of filling characters */ while (*bf++ && n > 0) n--; if (p->sign) n--; if (p->alt && p->base == 16) n -= 2; else if (p->alt && p->base == 8) n--; /* Fill with space to align to the right, before alternate or sign */ if (!p->lz && !p->align_left) { while (n-- > 0) putf(putp, ' '); } /* print sign */ if (p->sign) putf(putp, p->sign); /* Alternate */ if (p->alt && p->base == 16) { putf(putp, '0'); putf(putp, (p->uc ? 'X' : 'x')); } else if (p->alt && p->base == 8) { putf(putp, '0'); } /* Fill with zeros, after alternate or sign */ if (p->lz) { while (n-- > 0) putf(putp, '0'); } /* Put actual buffer */ bf = p->bf; while ((ch = *bf++)) putf(putp, ch); /* Fill with space to align to the left, after string */ if (!p->lz && p->align_left) { while (n-- > 0) putf(putp, ' '); } } void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) { struct param p; double fval; int temp_buffer[10]; int fpart; int fiter; int ffactor; int sign; #ifdef PRINTF_LONG_SUPPORT char bf[23]; /* long = 64b on some architectures */ #else char bf[12]; /* int = 32b on some architectures */ #endif char ch; p.bf = bf; while ((ch = *(fmt++))) { if (ch != '%') { putf(putp, ch); } else { #ifdef PRINTF_LONG_SUPPORT char lng = 0; /* 1 for long, 2 for long long */ #endif /* Init parameter struct */ p.lz = 0; p.alt = 0; p.width = 0; p.align_left = 0; p.sign = 0; p.prec = 2; /* Flags */ while ((ch = *(fmt++))) { switch (ch) { case '-': p.align_left = 1; continue; case '0': p.lz = 1; continue; case '#': p.alt = 1; continue; case '+': p.sign = 1; continue; default: break; } break; } /* Width */ if (ch >= '0' && ch <= '9') { ch = a2u(ch, &fmt, 10, &(p.width)); } /* We accept 'x.y' format but don't support it completely: * we ignore the 'y' digit => this ignores 0-fill * size and makes it == width (ie. 'x') */ if (ch == '.') { //p.lz = 1; /* zero-padding */ /* ignore actual 0-fill size: */ ch = *(fmt++); if (ch >= '0' && ch <= '9') p.prec = ch - '0'; do { ch = *(fmt++); } while (ch >= '0' && ch <= '9'); } #ifdef PRINTF_SIZE_T_SUPPORT # ifdef PRINTF_LONG_SUPPORT if (ch == 'z') { ch = *(fmt++); if (sizeof(size_t) == sizeof(unsigned long int)) lng = 1; # ifdef PRINTF_LONG_LONG_SUPPORT else if (sizeof(size_t) == sizeof(unsigned long long int)) lng = 2; # endif } else # endif #endif #ifdef PRINTF_LONG_SUPPORT if (ch == 'l') { ch = *(fmt++); lng = 1; #ifdef PRINTF_LONG_LONG_SUPPORT if (ch == 'l') { ch = *(fmt++); lng = 2; } #endif } #endif switch (ch) { case 0: goto abort; case 'u': p.base = 10; #ifdef PRINTF_LONG_SUPPORT #ifdef PRINTF_LONG_LONG_SUPPORT if (2 == lng) ulli2a(va_arg(va, unsigned long long int), &p); else #endif if (1 == lng) uli2a(va_arg(va, unsigned long int), &p); else #endif ui2a(va_arg(va, unsigned int), &p); putchw(putp, putf, &p); break; case 'd': case 'i': p.base = 10; #ifdef PRINTF_LONG_SUPPORT #ifdef PRINTF_LONG_LONG_SUPPORT if (2 == lng) lli2a(va_arg(va, long long int), &p); else #endif if (1 == lng) li2a(va_arg(va, long int), &p); else #endif i2a(va_arg(va, int), &p); putchw(putp, putf, &p); break; #ifdef SIZEOF_POINTER case 'p': p.alt = 1; # if defined(SIZEOF_INT) && SIZEOF_POINTER <= SIZEOF_INT lng = 0; # elif defined(SIZEOF_LONG) && SIZEOF_POINTER <= SIZEOF_LONG lng = 1; # elif defined(SIZEOF_LONG_LONG) && SIZEOF_POINTER <= SIZEOF_LONG_LONG lng = 2; # endif #endif case 'x': case 'X': p.base = 16; p.uc = (ch == 'X')?1:0; #ifdef PRINTF_LONG_SUPPORT #ifdef PRINTF_LONG_LONG_SUPPORT if (2 == lng) ulli2a(va_arg(va, unsigned long long int), &p); else #endif if (1 == lng) uli2a(va_arg(va, unsigned long int), &p); else #endif ui2a(va_arg(va, unsigned int), &p); putchw(putp, putf, &p); break; case 'o': p.base = 8; ui2a(va_arg(va, unsigned int), &p); putchw(putp, putf, &p); break; case 'c': putf(putp, (char)(va_arg(va, int))); break; case 's': p.bf = va_arg(va, char *); putchw(putp, putf, &p); p.bf = bf; break; case '%': putf(putp, ch); break; case 'f': case 'F': fval = va_arg(va, double); sign = 0; if (fval < 0) { sign = 1; p.width--; fval = - fval; } else if (p.sign) { sign = 2; p.width--; } fpart = (int)fval; fiter = 0; while (fpart != 0) { temp_buffer[fiter++] = fpart % 10; fpart = fpart / 10; } fiter--; if (fiter == -1) p.width--; /* Leading zeros */ if (p.lz) { if (sign == 1) putf(putp, '-'); else if (sign == 2) putf(putp, '+'); while (p.width-- > p.prec + fiter + 2) { putf(putp, '0'); } } else { while (p.width-- > p.prec + fiter + 2) { putf(putp, ' '); } if (sign == 1) putf(putp, '-'); else if (sign == 2) putf(putp, '+'); } if (fiter == -1) putf(putp, '0'); while (fiter > -1) { putf(putp, '0' + (temp_buffer[fiter--])); } putf(putp, '.'); ffactor = 1; while (p.prec-- > 0) { ffactor *= 10; fpart = (int)((fval - (int)fval)*ffactor); if (fpart == 0) putf(putp, '0'); } fiter = 0; while (fpart != 0) { temp_buffer[fiter++] = fpart % 10; fpart = fpart / 10; } fiter--; while (fiter > -1) { putf(putp, '0' + (temp_buffer[fiter--])); } break; default: break; } } } abort:; } #if TINYPRINTF_DEFINE_TFP_PRINTF static putcf stdout_putf; static void *stdout_putp; void init_printf(void *putp, putcf putf) { stdout_putf = putf; stdout_putp = putp; } void tfp_printf(char *fmt, ...) { va_list va; va_start(va, fmt); tfp_format(stdout_putp, stdout_putf, fmt, va); va_end(va); } #endif #if TINYPRINTF_DEFINE_TFP_SPRINTF struct _vsnprintf_putcf_data { size_t dest_capacity; char *dest; size_t num_chars; }; static void _vsnprintf_putcf(void *p, char c) { struct _vsnprintf_putcf_data *data = (struct _vsnprintf_putcf_data*)p; if (data->num_chars < data->dest_capacity) data->dest[data->num_chars] = c; data->num_chars ++; } int tfp_vsnprintf(char *str, size_t size, const char *format, va_list ap) { struct _vsnprintf_putcf_data data; if (size < 1) return 0; data.dest = str; data.dest_capacity = size-1; data.num_chars = 0; tfp_format(&data, _vsnprintf_putcf, format, ap); if (data.num_chars < data.dest_capacity) data.dest[data.num_chars] = '\0'; else data.dest[data.dest_capacity] = '\0'; return data.num_chars; } int tfp_snprintf(char *str, size_t size, const char *format, ...) { va_list ap; int retval; va_start(ap, format); retval = tfp_vsnprintf(str, size, format, ap); va_end(ap); return retval; } struct _vsprintf_putcf_data { char *dest; size_t num_chars; }; static void _vsprintf_putcf(void *p, char c) { struct _vsprintf_putcf_data *data = (struct _vsprintf_putcf_data*)p; data->dest[data->num_chars++] = c; } int tfp_vsprintf(char *str, const char *format, va_list ap) { struct _vsprintf_putcf_data data; data.dest = str; data.num_chars = 0; tfp_format(&data, _vsprintf_putcf, format, ap); data.dest[data.num_chars] = '\0'; return data.num_chars; } int tfp_sprintf(char *str, const char *format, ...) { va_list ap; int retval; va_start(ap, format); retval = tfp_vsprintf(str, format, ap); va_end(ap); return retval; } int isdigit (char c) { if ((c>='0') && (c<='9')) return 1; return 0; } int tfp_vsscanf(const char* str, const char* format, ...) { va_list ap; int value, tmp, width; float fvalue; double Fvalue; int count = 0; int pos; char neg, fmt_code; const char* pf; const char *tc; char tmp_str[20]; va_start(ap, format); for (pf = format, count = 0; *format != 0 && *str != 0; format++, str++) { while (*format == ' ' && *format != 0) format++; if (*format == 0) break; while (*str == ' ' && *str != 0) str++; if (*str == 0) break; if (*format == '%') { format++; if (*format == 'n') { if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { fmt_code = 'x'; str += 2; } else if (str[0] == 'b') { fmt_code = 'b'; str++; } else fmt_code = 'd'; } else if (*format >= '1' && *format <= '9') { for (tc = format; isdigit (*format); format++); strncpy(tmp_str, tc, format - tc); tmp_str[format - tc] = '\0'; width = atoi(tmp_str); fmt_code = *format; } else fmt_code = *format; switch (fmt_code) { case 'x': case 'X': for (value = 0, pos = 0; *str != 0; str++, pos++) { if ('0' <= *str && *str <= '9') tmp = *str - '0'; else if ('a' <= *str && *str <= 'f') tmp = *str - 'a' + 10; else if ('A' <= *str && *str <= 'F') tmp = *str - 'A' + 10; else break; value *= 16; value += tmp; } if (pos == 0) return count; *(va_arg(ap, int*)) = value; count++; break; case 'b': for (value = 0, pos = 0; *str != 0; str++, pos++) { if (*str != '0' && *str != '1') break; value *= 2; value += *str - '0'; } if (pos == 0) return count; *(va_arg(ap, int*)) = value; count++; break; case 'd': if (*str == '-') { neg = 1; str++; } else neg = 0; if (!width) { for (value = 0, pos = 0; *str != 0; str++, pos++) { if ('0' <= *str && *str <= '9') value = value*10 + (int)(*str - '0'); else{ break; } } } else{ for (value = 0, pos = 0; pos != width; str++, pos++) { if ('0' <= *str && *str <= '9') value = value*10 + (int)(*str - '0'); else{ break; } } str--; } if (pos == 0) return count; *(va_arg(ap, int*)) = neg ? -value : value; count++; break; case 'f': if (*str == '-') { neg = 1; str++; } else neg = 0; int point_flag = 0; int exp = 0; for (fvalue = 0, pos = 0; *str != 0 ; str++, pos++) { if (*str == '.') { point_flag = 1; str++; } if ('0' <= *str && *str <= '9') fvalue = fvalue*10 + (int)(*str - '0'); else break; if (point_flag == 1) exp++; } if (pos == 0) return count; for (pos = 0; pos < exp; pos++) fvalue = fvalue/10.0; *(va_arg(ap, float*)) = neg ? -fvalue : fvalue; count++; break; case 'F': if (*str == '-') { neg = 1; str++; } else neg = 0; int Fpoint_flag = 0; int Fexp = 0; for (Fvalue = 0, pos = 0; *str != 0 ; str++, pos++) { if (*str == '.') { Fpoint_flag = 1; str++; } if ('0' <= *str && *str <= '9') Fvalue = Fvalue*10 + (int)(*str - '0'); else break; if (Fpoint_flag == 1) Fexp++; } if (pos == 0) return count; for (pos = 0; pos < Fexp; pos++) Fvalue = Fvalue/10.0; *(va_arg(ap, double*)) = neg ? -Fvalue : Fvalue; count++; break; case 'c': *(va_arg(ap, char*)) = *str; count++; break; case 's': pos = 0; char* tab = va_arg(ap, char**); while (*str != ' ' && *str != 0) *(tab++) = *str++; *tab = 0; count++; break; default: return count; } } else { if (*format != *str) break; } } va_end(ap); return count; } #endif