/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1999 - 2014, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.haxx.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * * * Purpose: * A merge of Bjorn Reese's format() function and Daniel's dsprintf() * 1.0. A full blooded printf() clone with full support for $ * everywhere (parameters, widths and precisions) including variabled * sized parameters (like doubles, long longs, long doubles and even * void * in 64-bit architectures). * * Current restrictions: * - Max 128 parameters * - No 'long double' support. * * If you ever want truly portable and good *printf() clones, the project that * took on from here is named 'Trio' and you find more details on the trio web * page at https://daniel.haxx.se/trio/ */ /*------------------------------------------------------------------------/ / Universal string handler for user console interface /-------------------------------------------------------------------------/ / / Copyright (C) 2011, ChaN, all right reserved. / / * This software is a free software and there is NO WARRANTY. / * No restriction on use. You can use, modify and redistribute it for / personal, non-profit or commercial products UNDER YOUR RESPONSIBILITY. / * Redistributions of source code must retain the above copyright notice. / /-------------------------------------------------------------------------*/ #include #include #include #include struct nsprintf { char *buffer; size_t length; size_t max; }; struct asprintf { char *buffer; /* allocated buffer */ size_t len; /* length of string */ size_t alloc; /* length of alloc */ int fail; /* (!= 0) if an alloc has failed and thus the output is not the complete data */ }; #define OUTCHAR(x) \ do{ \ if(stream((unsigned char)(x), (FILE *)data) != -1) \ done++; \ else \ return done; /* return immediately on failure */ \ } while(0) static int dprintf_formatf( void *data, int(*stream)(int, FILE *), const char *fmt, va_list arp) { unsigned int r, i, j, w, f; unsigned long v; char s[16], c, d, *p; /* Number of characters written. */ int done = 0; for (;;) { c = *fmt++; /* Get a char */ if (!c) break; /* End of format? */ if (c != '%') { /* Pass through it if not a % sequense */ OUTCHAR(c); continue; } f = 0; c = *fmt++; /* Get first char of the sequense */ if (c == '0') { /* Flag: '0' padded */ f = 1; c = *fmt++; } else { if (c == '-') { /* Flag: left justified */ f = 2; c = *fmt++; } } for (w = 0; c >= '0' && c <= '9'; c = *fmt++) /* Minimum width */ w = w * 10 + c - '0'; if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ f |= 4; c = *fmt++; } else if (c == 'h') { /* Prefix: Size is short int */ f |= 16; c = *fmt++; } if (!c) break; /* End of format? */ d = c; if (d >= 'a') d -= 0x20; switch (d) { /* Type is... */ case 'S': /* String */ p = va_arg(arp, char*); for (j = 0; p[j]; j++); while (!(f & 2) && j++ < w) OUTCHAR(' '); for (; *p; p++) OUTCHAR(*p); while (j++ < w) OUTCHAR(' '); continue; case 'C': /* Character */ OUTCHAR((char)va_arg(arp, int)); continue; case 'B': /* Binary */ r = 2; break; case 'O': /* Octal */ r = 8; break; case 'D': /* Signed decimal */ case 'U': /* Unsigned decimal */ r = 10; break; case 'X': /* Hexdecimal */ r = 16; break; default: /* Unknown type (passthrough) */ OUTCHAR(c); continue; } /* Get an argument and put it in numeral */ v = (f & 4) ? va_arg(arp, long) : ((f & 16) ? ((d == 'D') ? (long)va_arg(arp, short) : (long)va_arg(arp, unsigned short)) : ((d == 'D') ? (long)va_arg(arp, int) : (long)va_arg(arp, unsigned int))); if (d == 'D' && (v & 0x80000000)) { v = 0 - v; f |= 8; } i = 0; do { d = (char)(v % r); v /= r; if (d > 9) d += (c == 'x') ? 0x27 : 0x07; s[i++] = d + '0'; } while (v && i < sizeof(s)); if (f & 8) s[i++] = '-'; j = i; d = (f & 1) ? '0' : ' '; while (!(f & 2) && j++ < w) OUTCHAR(d); do OUTCHAR(s[--i]); while (i); while (j++ < w) OUTCHAR(' '); } return done; } /* fputc() look-alike */ static int addbyter(int output, FILE *data) { struct nsprintf *infop=(struct nsprintf *)data; unsigned char outc = (unsigned char)output; if(infop->length < infop->max) { /* only do this if we haven't reached max length yet */ infop->buffer[0] = outc; /* store */ infop->buffer++; /* increase pointer */ infop->length++; /* we are now one byte larger */ return outc; /* fputc() returns like this on success */ } return -1; } int vsnprintf(char *buffer, size_t maxlength, const char *format, va_list ap_save) { int retcode; struct nsprintf info; info.buffer = buffer; info.length = 0; info.max = maxlength; retcode = dprintf_formatf(&info, addbyter, format, ap_save); if(info.max) { /* we terminate this with a zero byte */ if(info.max == info.length) /* we're at maximum, scrap the last letter */ info.buffer[-1] = 0; else info.buffer[0] = 0; } return retcode; } int snprintf(char *buffer, size_t maxlength, const char *format, ...) { int retcode; va_list ap_save; /* argument pointer */ va_start(ap_save, format); retcode = vsnprintf(buffer, maxlength, format, ap_save); va_end(ap_save); return retcode; } /* fputc() look-alike */ static int alloc_addbyter(int output, FILE *data) { struct asprintf *infop=(struct asprintf *)data; unsigned char outc = (unsigned char)output; if(!infop->buffer) { infop->buffer = malloc(32); if(!infop->buffer) { infop->fail = 1; return -1; /* fail */ } infop->alloc = 32; infop->len =0; } else if(infop->len+1 >= infop->alloc) { char *newptr; newptr = realloc(infop->buffer, infop->alloc*2); if(!newptr) { infop->fail = 1; return -1; /* fail */ } infop->buffer = newptr; infop->alloc *= 2; } infop->buffer[ infop->len ] = outc; infop->len++; return outc; /* fputc() returns like this on success */ } char *aprintf(const char *format, ...) { va_list ap_save; /* argument pointer */ int retcode; struct asprintf info; info.buffer = NULL; info.len = 0; info.alloc = 0; info.fail = 0; va_start(ap_save, format); retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); va_end(ap_save); if((-1 == retcode) || info.fail) { if(info.alloc) free(info.buffer); return NULL; } if(info.alloc) { info.buffer[info.len] = 0; /* we terminate this with a zero byte */ return info.buffer; } else return strdup(""); } char *vaprintf(const char *format, va_list ap_save) { int retcode; struct asprintf info; info.buffer = NULL; info.len = 0; info.alloc = 0; info.fail = 0; retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); if((-1 == retcode) || info.fail) { if(info.alloc) free(info.buffer); return NULL; } if(info.alloc) { info.buffer[info.len] = 0; /* we terminate this with a zero byte */ return info.buffer; } else return strdup(""); } int vasprintf(char **dst, const char *format, va_list ap_save) { int retcode; struct asprintf info; info.buffer = NULL; info.len = 0; info.alloc = 0; info.fail = 0; retcode = dprintf_formatf(&info, alloc_addbyter, format, ap_save); if ((-1 == retcode) || info.fail) { if (info.alloc) free(info.buffer); return -1; } if (info.alloc) { info.buffer[info.len] = 0; /* we terminate this with a zero byte */ *dst = info.buffer; return info.len; } else { *dst = strdup(""); return 0; } } static int storebuffer(int output, FILE *data) { char **buffer = (char **)data; unsigned char outc = (unsigned char)output; **buffer = outc; (*buffer)++; return outc; /* act like fputc() ! */ } int sprintf(char *buffer, const char *format, ...) { va_list ap_save; /* argument pointer */ int retcode; va_start(ap_save, format); retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); va_end(ap_save); *buffer=0; /* we terminate this with a zero byte */ return retcode; } int printf(const char *format, ...) { int retcode; va_list ap_save; /* argument pointer */ va_start(ap_save, format); retcode = dprintf_formatf(stdout, fputc, format, ap_save); va_end(ap_save); return retcode; } int fprintf(FILE *whereto, const char *format, ...) { int retcode; va_list ap_save; /* argument pointer */ va_start(ap_save, format); retcode = dprintf_formatf(whereto, fputc, format, ap_save); va_end(ap_save); return retcode; } int vsprintf(char *buffer, const char *format, va_list ap_save) { int retcode; retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save); *buffer=0; /* we terminate this with a zero byte */ return retcode; } int vprintf(const char *format, va_list ap_save) { return dprintf_formatf(stdout, fputc, format, ap_save); } int vfprintf(FILE *whereto, const char *format, va_list ap_save) { return dprintf_formatf(whereto, fputc, format, ap_save); }