/* File: tinyprintf.c Copyright (C) 2004 Kustaa Nyholm Copyright (C) 2010 CJlano Copyright (C) 2011 Petteri Aimonen 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 */ /* This is a smaller implementation of printf-family of functions, * based on tinyprintf code by Kustaa Nyholm. * The formats supported by this implementation are: 'd' 'u' 'c' 's' 'x' 'X'. * Zero padding and field width are also supported. * If the library is compiled with 'PRINTF_SUPPORT_LONG' defined then the * long specifier is also supported. * Otherwise it is ignored, so on 32 bit platforms there is no point to use * PRINTF_SUPPORT_LONG because int == long. */ #include typedef void (*putcf) (void *, char); struct param { int width; /**< field width */ char lz; /**< Leading zeros */ char sign; /**< The sign to display (if any) */ char alt; /**< alternate form */ char base; /**< number base (e.g.: 8, 10, 16) */ char uc; /**< Upper case (for base16 only) */ char *bf; /**< Buffer to output */ }; #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 a2i(char ch, const char **src, int base, int *nump) { const char *p = *src; 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; } 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, before alternate or sign */ if (!p->lz) { 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); } void tfp_format(void *putp, putcf putf, const char *fmt, va_list va) { struct param p; #ifdef PRINTF_LONG_SUPPORT char bf[23]; #else char bf[12]; #endif p.bf = bf; char ch; while ((ch = *(fmt++))) { if (ch != '%') { putf(putp, ch); } else { /* Init parameter struct */ p.lz = 0; p.alt = 0; p.width = 0; p.sign = 0; #ifdef PRINTF_LONG_SUPPORT char lng = 0; #endif /* Flags */ while ((ch = *(fmt++))) { switch (ch) { case '0': p.lz = 1; continue; case '#': p.alt = 1; continue; default: break; } break; } /* Width */ if (ch >= '0' && ch <= '9') { ch = a2i(ch, &fmt, 10, &(p.width)); } if (ch == 'l') { ch = *(fmt++); #ifdef PRINTF_LONG_SUPPORT lng = 1; #endif } switch (ch) { case 0: goto abort; case 'u': p.base = 10; #ifdef PRINTF_LONG_SUPPORT if (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 if (lng) li2a(va_arg(va, unsigned long int), &p); else #endif i2a(va_arg(va, int), &p); putchw(putp, putf, &p); break; case 'x': case 'X': p.base = 16; p.uc = (ch == 'X'); #ifdef PRINTF_LONG_SUPPORT if (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); default: break; } } } abort:; } // Output to FILE* through fputc struct file_putp_state { FILE *file; size_t written; }; static void file_putp(void *p, char c) { struct file_putp_state* state = p; fputc(c, state->file); state->written++; } int vfprintf(FILE *f, const char *fmt, va_list va) { struct file_putp_state state = {f, 0}; tfp_format(&state, file_putp, fmt, va); return state.written; } int fprintf(FILE *f, const char *fmt, ...) { va_list va; va_start(va, fmt); int rv = vfprintf(f, fmt, va); va_end(va); return rv; } int printf(const char *fmt, ...) { va_list va; va_start(va, fmt); int rv = vfprintf(stdout, fmt, va); va_end(va); return rv; } // Output to buffer, with bounds checking struct buf_putp_state { char *buffer; size_t written; size_t buflen; }; static void buf_putp(void *p, char c) { struct buf_putp_state* state = p; if (state->written + 1 >= state->buflen) { state->written++; // Just count the chars } else { state->written++; *(state->buffer++) = c; } } int vsnprintf(char *str, size_t size, const char *fmt, va_list va) { struct buf_putp_state state = {str, 0, size}; tfp_format(&state, buf_putp, fmt, va); *(state.buffer) = '\0'; return state.written; } int snprintf(char *str, size_t size, const char *fmt, ...) { va_list va; va_start(va, fmt); int rv = vsnprintf(str, size, fmt, va); va_end(va); return rv; }