* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
- * Copyright (C) 1999 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al.
+ * Copyright (C) 1999 - 2017, Daniel Stenberg, <daniel@haxx.se>, 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 http://curl.haxx.se/docs/copyright.html.
+ * 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
*
* 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 http://daniel.haxx.se/trio/
+ * page at https://daniel.haxx.se/projects/trio/
*/
#include "curl_setup.h"
-
-#if defined(DJGPP) && (DJGPP_MINOR < 4)
-#undef _MPRINTF_REPLACE /* don't use x_was_used() here */
-#endif
-
#include <curl/mprintf.h>
#include "curl_memory.h"
/* The last #include file should be: */
#include "memdebug.h"
-#ifndef SIZEOF_LONG_DOUBLE
-#define SIZEOF_LONG_DOUBLE 0
-#endif
-
/*
* If SIZEOF_SIZE_T has not been defined, default to the size of long.
*/
-#ifndef SIZEOF_SIZE_T
-# define SIZEOF_SIZE_T CURL_SIZEOF_LONG
-#endif
-
#ifdef HAVE_LONGLONG
# define LONG_LONG_TYPE long long
# define HAVE_LONG_LONG_TYPE
#endif
/*
+ * Non-ANSI integer extensions
+ */
+
+#if (defined(__BORLANDC__) && (__BORLANDC__ >= 0x520)) || \
+ (defined(__WATCOMC__) && defined(__386__)) || \
+ (defined(__POCC__) && defined(_MSC_VER)) || \
+ (defined(_WIN32_WCE)) || \
+ (defined(__MINGW32__)) || \
+ (defined(_MSC_VER) && (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64))
+# define MP_HAVE_INT_EXTENSIONS
+#endif
+
+/*
* Max integer data types that mprintf.c is capable
*/
# define mp_uintmax_t unsigned long
#endif
-#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
+#define BUFFSIZE 326 /* buffer for long-to-str and float-to-str calcs, should
+ fit negative DBL_MAX (317 letters) */
#define MAX_PARAMETERS 128 /* lame static limit */
#ifdef __AMIGA__
} WHILE_FALSE
/* Data type to read from the arglist */
-typedef enum {
+typedef enum {
FORMAT_UNKNOWN = 0,
FORMAT_STRING,
FORMAT_PTR,
static long dprintf_DollarString(char *input, char **end)
{
- int number=0;
+ int number = 0;
while(ISDIGIT(*input)) {
number *= 10;
number += *input-'0';
return 0;
}
-static int dprintf_IsQualifierNoDollar(char c)
+static bool dprintf_IsQualifierNoDollar(const char *fmt)
{
- switch (c) {
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ if(!strncmp(fmt, "I32", 3) || !strncmp(fmt, "I64", 3)) {
+ return TRUE;
+ }
+#endif
+
+ switch(*fmt) {
case '-': case '+': case ' ': case '#': case '.':
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case 'h': case 'l': case 'L': case 'z': case 'q':
case '*': case 'O':
- return 1; /* true */
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ case 'I':
+#endif
+ return TRUE;
+
default:
- return 0; /* false */
+ return FALSE;
}
}
* Create an index with the type of each parameter entry and its
* value (may vary in size)
*
+ * Returns zero on success.
+ *
******************************************************************/
-static long dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos,
- va_list arglist)
+static int dprintf_Pass1(const char *format, va_stack_t *vto, char **endpos,
+ va_list arglist)
{
char *fmt = (char *)format;
int param_num = 0;
long width;
long precision;
int flags;
- long max_param=0;
+ long max_param = 0;
long i;
while(*fmt) {
/* Handle the flags */
- while(dprintf_IsQualifierNoDollar(*fmt)) {
- switch (*fmt++) {
+ while(dprintf_IsQualifierNoDollar(fmt)) {
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ if(!strncmp(fmt, "I32", 3)) {
+ flags |= FLAGS_LONG;
+ fmt += 3;
+ }
+ else if(!strncmp(fmt, "I64", 3)) {
+ flags |= FLAGS_LONGLONG;
+ fmt += 3;
+ }
+ else
+#endif
+
+ switch(*fmt++) {
case ' ':
flags |= FLAGS_SPACE;
break;
flags |= FLAGS_ALT;
break;
case '.':
- flags |= FLAGS_PREC;
if('*' == *fmt) {
/* The precision is picked from a specified parameter */
case 'h':
flags |= FLAGS_SHORT;
break;
+#if defined(MP_HAVE_INT_EXTENSIONS)
+ case 'I':
+#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
+ flags |= FLAGS_LONGLONG;
+#else
+ flags |= FLAGS_LONG;
+#endif
+ break;
+#endif
case 'l':
if(flags & FLAGS_LONG)
flags |= FLAGS_LONGLONG;
case 'z':
/* the code below generates a warning if -Wunreachable-code is
used */
-#if (SIZEOF_SIZE_T > CURL_SIZEOF_LONG)
+#if (SIZEOF_SIZE_T > SIZEOF_LONG)
flags |= FLAGS_LONGLONG;
#else
flags |= FLAGS_LONG;
#endif
break;
case 'O':
-#if (CURL_SIZEOF_CURL_OFF_T > CURL_SIZEOF_LONG)
+#if (SIZEOF_CURL_OFF_T > SIZEOF_LONG)
flags |= FLAGS_LONGLONG;
#else
flags |= FLAGS_LONG;
else
width = param_num;
if(width > max_param)
- max_param=width;
+ max_param = width;
break;
default:
break;
i = this_param - 1;
+ if((i < 0) || (i >= MAX_PARAMETERS))
+ /* out of allowed range */
+ return 1;
+
switch (*fmt) {
case 'S':
flags |= FLAGS_ALT;
if(flags & FLAGS_WIDTHPARAM) {
/* we have the width specified from a parameter, so we make that
parameter's info setup properly */
- vto[i].width = width - 1;
- i = width - 1;
- vto[i].type = FORMAT_WIDTH;
- vto[i].flags = FLAGS_NEW;
- vto[i].precision = vto[i].width = 0; /* can't use width or precision
- of width! */
+ long k = width - 1;
+ vto[i].width = k;
+ vto[k].type = FORMAT_WIDTH;
+ vto[k].flags = FLAGS_NEW;
+ /* can't use width or precision of width! */
+ vto[k].width = 0;
+ vto[k].precision = 0;
}
if(flags & FLAGS_PRECPARAM) {
/* we have the precision specified from a parameter, so we make that
parameter's info setup properly */
- vto[i].precision = precision - 1;
- i = precision - 1;
- vto[i].type = FORMAT_WIDTH;
- vto[i].flags = FLAGS_NEW;
- vto[i].precision = vto[i].width = 0; /* can't use width or precision
- of width! */
+ long k = precision - 1;
+ vto[i].precision = k;
+ vto[k].type = FORMAT_WIDTH;
+ vto[k].flags = FLAGS_NEW;
+ /* can't use width or precision of width! */
+ vto[k].width = 0;
+ vto[k].precision = 0;
}
*endpos++ = fmt + 1; /* end of this sequence */
}
}
/* Read the arg list parameters into our data list */
- for(i=0; i<max_param; i++) {
- if((i + 1 < max_param) && (vto[i + 1].type == FORMAT_WIDTH)) {
- /* Width/precision arguments must be read before the main argument
- * they are attached to
- */
- vto[i + 1].data.num.as_signed = (mp_intmax_t)va_arg(arglist, int);
+ for(i = 0; i<max_param; i++) {
+ /* Width/precision arguments must be read before the main argument
+ they are attached to */
+ if(vto[i].flags & FLAGS_WIDTHPARAM) {
+ vto[vto[i].width].data.num.as_signed =
+ (mp_intmax_t)va_arg(arglist, int);
+ }
+ if(vto[i].flags & FLAGS_PRECPARAM) {
+ vto[vto[i].precision].data.num.as_signed =
+ (mp_intmax_t)va_arg(arglist, int);
}
- switch (vto[i].type) {
+ switch(vto[i].type) {
case FORMAT_STRING:
vto[i].data.str = va_arg(arglist, char *);
break;
}
}
- return max_param;
+ return 0;
}
int done = 0;
long param; /* current parameter to read */
- long param_num=0; /* parameter counter */
+ long param_num = 0; /* parameter counter */
va_stack_t vto[MAX_PARAMETERS];
char *endpos[MAX_PARAMETERS];
va_stack_t *p;
+ /* 'workend' points to the final buffer byte position, but with an extra
+ byte as margin to avoid the (false?) warning Coverity gives us
+ otherwise */
+ char *workend = &work[sizeof(work) - 2];
+
/* Do the actual %-code parsing */
- dprintf_Pass1(format, vto, endpos, ap_save);
+ if(dprintf_Pass1(format, vto, endpos, ap_save))
+ return -1;
end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
created for us */
int is_neg;
/* Base of a number to be written. */
- long base;
+ unsigned long base;
/* Integral values to be written. */
mp_uintmax_t num;
/* Used to convert negative in positive. */
mp_intmax_t signed_num;
+ char *w;
+
if(*f != '%') {
/* This isn't a format spec, so write everything out until the next one
OR end of string is reached. */
/* If this is a positional parameter, the position must follow immediately
after the %, thus create a %<num>$ sequence */
- param=dprintf_DollarString(f, &f);
+ param = dprintf_DollarString(f, &f);
if(!param)
param = param_num;
p = &vto[param];
/* pick up the specified width */
- if(p->flags & FLAGS_WIDTHPARAM)
+ if(p->flags & FLAGS_WIDTHPARAM) {
width = (long)vto[p->width].data.num.as_signed;
+ param_num++; /* since the width is extracted from a parameter, we
+ must skip that to get to the next one properly */
+ if(width < 0) {
+ /* "A negative field width is taken as a '-' flag followed by a
+ positive field width." */
+ width = -width;
+ p->flags |= FLAGS_LEFT;
+ p->flags &= ~FLAGS_PAD_NIL;
+ }
+ }
else
width = p->width;
/* pick up the specified precision */
if(p->flags & FLAGS_PRECPARAM) {
prec = (long)vto[p->precision].data.num.as_signed;
- param_num++; /* since the precision is extraced from a parameter, we
+ param_num++; /* since the precision is extracted from a parameter, we
must skip that to get to the next one properly */
+ if(prec < 0)
+ /* "A negative precision is taken as if the precision were
+ omitted." */
+ prec = -1;
}
else if(p->flags & FLAGS_PREC)
prec = p->precision;
is_alt = (p->flags & FLAGS_ALT) ? 1 : 0;
- switch (p->type) {
+ switch(p->type) {
case FORMAT_INT:
num = p->data.num.as_unsigned;
if(p->flags & FLAGS_CHAR) {
number:
/* Number of base BASE. */
- {
- char *workend = &work[sizeof(work) - 1];
- char *w;
-
- /* Supply a default precision if none was given. */
- if(prec == -1)
- prec = 1;
-
- /* Put the number in WORK. */
- w = workend;
- while(num > 0) {
- *w-- = digits[num % base];
- num /= base;
- }
- width -= (long)(workend - w);
- prec -= (long)(workend - w);
- if(is_alt && base == 8 && prec <= 0) {
- *w-- = '0';
- --width;
- }
+ /* Supply a default precision if none was given. */
+ if(prec == -1)
+ prec = 1;
- if(prec > 0) {
- width -= prec;
- while(prec-- > 0)
- *w-- = '0';
- }
+ /* Put the number in WORK. */
+ w = workend;
+ while(num > 0) {
+ *w-- = digits[num % base];
+ num /= base;
+ }
+ width -= (long)(workend - w);
+ prec -= (long)(workend - w);
- if(is_alt && base == 16)
- width -= 2;
+ if(is_alt && base == 8 && prec <= 0) {
+ *w-- = '0';
+ --width;
+ }
- if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
- --width;
+ if(prec > 0) {
+ width -= prec;
+ while(prec-- > 0)
+ *w-- = '0';
+ }
- if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
- while(width-- > 0)
- OUTCHAR(' ');
+ if(is_alt && base == 16)
+ width -= 2;
- if(is_neg)
- OUTCHAR('-');
- else if(p->flags & FLAGS_SHOWSIGN)
- OUTCHAR('+');
- else if(p->flags & FLAGS_SPACE)
- OUTCHAR(' ');
+ if(is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
+ --width;
- if(is_alt && base == 16) {
- OUTCHAR('0');
- if(p->flags & FLAGS_UPPER)
- OUTCHAR('X');
- else
- OUTCHAR('x');
- }
+ if(!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
+ while(width-- > 0)
+ OUTCHAR(' ');
- if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
- while(width-- > 0)
- OUTCHAR('0');
+ if(is_neg)
+ OUTCHAR('-');
+ else if(p->flags & FLAGS_SHOWSIGN)
+ OUTCHAR('+');
+ else if(p->flags & FLAGS_SPACE)
+ OUTCHAR(' ');
+
+ if(is_alt && base == 16) {
+ OUTCHAR('0');
+ if(p->flags & FLAGS_UPPER)
+ OUTCHAR('X');
+ else
+ OUTCHAR('x');
+ }
- /* Write the number. */
- while(++w <= workend) {
- OUTCHAR(*w);
- }
+ if(!(p->flags & FLAGS_LEFT) && (p->flags & FLAGS_PAD_NIL))
+ while(width-- > 0)
+ OUTCHAR('0');
- if(p->flags & FLAGS_LEFT)
- while(width-- > 0)
- OUTCHAR(' ');
+ /* Write the number. */
+ while(++w <= workend) {
+ OUTCHAR(*w);
}
+
+ if(p->flags & FLAGS_LEFT)
+ while(width-- > 0)
+ OUTCHAR(' ');
break;
case FORMAT_STRING:
len = 0;
}
}
+ else if(prec != -1)
+ len = (size_t)prec;
else
len = strlen(str);
- if(prec != -1 && (size_t) prec < len)
- len = (size_t)prec;
- width -= (long)len;
+ width -= (len > LONG_MAX) ? LONG_MAX : (long)len;
if(p->flags & FLAGS_ALT)
OUTCHAR('"');
while(width-- > 0)
OUTCHAR(' ');
- while(len-- > 0)
+ while((len-- > 0) && *str)
OUTCHAR(*str++);
if(p->flags&FLAGS_LEFT)
while(width-- > 0)
*fptr = 0;
if(width >= 0) {
+ if(width >= (long)sizeof(work))
+ width = sizeof(work)-1;
/* RECURSIVE USAGE */
len = curl_msnprintf(fptr, left, "%ld", width);
fptr += len;
left -= len;
}
if(prec >= 0) {
+ /* for each digit in the integer part, we can have one less
+ precision */
+ size_t maxprec = sizeof(work) - 2;
+ double val = p->data.dnum;
+ while(val >= 10.0) {
+ val /= 10;
+ maxprec--;
+ }
+
+ if(prec > (long)maxprec)
+ prec = (long)maxprec-1;
/* RECURSIVE USAGE */
len = curl_msnprintf(fptr, left, ".%ld", prec);
fptr += len;
/* NOTE NOTE NOTE!! Not all sprintf implementations return number of
output characters */
(sprintf)(work, formatbuf, p->data.dnum);
-
- for(fptr=work; *fptr; fptr++)
+ DEBUGASSERT(strlen(work) <= sizeof(work));
+ for(fptr = work; *fptr; fptr++)
OUTCHAR(*fptr);
}
break;
/* fputc() look-alike */
static int addbyter(int output, FILE *data)
{
- struct nsprintf *infop=(struct nsprintf *)data;
+ struct nsprintf *infop = (struct nsprintf *)data;
unsigned char outc = (unsigned char)output;
if(infop->length < infop->max) {
info.max = maxlength;
retcode = dprintf_formatf(&info, addbyter, format, ap_save);
- if(info.max) {
+ if((retcode != -1) && info.max) {
/* we terminate this with a zero byte */
if(info.max == info.length)
/* we're at maximum, scrap the last letter */
/* fputc() look-alike */
static int alloc_addbyter(int output, FILE *data)
{
- struct asprintf *infop=(struct asprintf *)data;
+ struct asprintf *infop = (struct asprintf *)data;
unsigned char outc = (unsigned char)output;
if(!infop->buffer) {
return -1; /* fail */
}
infop->alloc = 32;
- infop->len =0;
+ infop->len = 0;
}
- else if(infop->len+1 >= infop->alloc) {
- char *newptr;
+ else if(infop->len + 1 >= infop->alloc) {
+ char *newptr = NULL;
+ size_t newsize = infop->alloc*2;
- newptr = realloc(infop->buffer, infop->alloc*2);
+ /* detect wrap-around or other overflow problems */
+ if(newsize > infop->alloc)
+ newptr = realloc(infop->buffer, newsize);
if(!newptr) {
infop->fail = 1;
return -1; /* fail */
}
infop->buffer = newptr;
- infop->alloc *= 2;
+ infop->alloc = newsize;
}
infop->buffer[ infop->len ] = outc;
info.buffer[info.len] = 0; /* we terminate this with a zero byte */
return info.buffer;
}
- else
- return strdup("");
+ return strdup("");
}
char *curl_mvaprintf(const char *format, va_list ap_save)
info.buffer[info.len] = 0; /* we terminate this with a zero byte */
return info.buffer;
}
- else
- return strdup("");
+ return strdup("");
}
static int storebuffer(int output, FILE *data)
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 */
+ *buffer = 0; /* we terminate this with a zero byte */
return retcode;
}
{
int retcode;
retcode = dprintf_formatf(&buffer, storebuffer, format, ap_save);
- *buffer=0; /* we terminate this with a zero byte */
+ *buffer = 0; /* we terminate this with a zero byte */
return retcode;
}