mirror of
https://github.com/moparisthebest/curl
synced 2024-11-15 05:55:04 -05:00
44d84ac164
could very well cause a negate number get passed in and thus cause reading outside of the array usually used for this purpose. We avoid this by using the uppercase macro versions introduced just now that does some extra crazy typecasts to avoid byte codes > 127 to cause negative int values.
1223 lines
30 KiB
C
1223 lines
30 KiB
C
/****************************************************************************
|
|
*
|
|
* $Id$
|
|
*
|
|
*************************************************************************
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
|
|
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
|
|
* MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE AUTHORS AND
|
|
* CONTRIBUTORS ACCEPT NO RESPONSIBILITY IN ANY CONCEIVABLE MANNER.
|
|
*
|
|
* Purpose:
|
|
* A merge of Bjorn Reese's format() function and Daniel's dsprintf()
|
|
* 1.0. A full blooded printf() clone with full support for <num>$
|
|
* 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 http://daniel.haxx.se/trio/
|
|
*/
|
|
|
|
|
|
#include "setup.h"
|
|
#include <sys/types.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdarg.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
|
|
#if defined(DJGPP) && (DJGPP_MINOR < 4)
|
|
#undef _MPRINTF_REPLACE /* don't use x_was_used() here */
|
|
#endif
|
|
|
|
#include <curl/mprintf.h>
|
|
|
|
#ifndef SIZEOF_LONG_DOUBLE
|
|
#define SIZEOF_LONG_DOUBLE 0
|
|
#endif
|
|
|
|
#ifndef SIZEOF_SIZE_T
|
|
/* default to 4 bytes for size_t unless defined in the config.h */
|
|
#define SIZEOF_SIZE_T 4
|
|
#endif
|
|
|
|
#ifdef DPRINTF_DEBUG
|
|
#define HAVE_LONGLONG
|
|
#define LONG_LONG long long
|
|
#define ENABLE_64BIT
|
|
#endif
|
|
|
|
#include "memory.h"
|
|
/* The last #include file should be: */
|
|
#include "memdebug.h"
|
|
|
|
#define BUFFSIZE 256 /* buffer for long-to-str and float-to-str calcs */
|
|
#define MAX_PARAMETERS 128 /* lame static limit */
|
|
|
|
#undef TRUE
|
|
#undef FALSE
|
|
#undef BOOL
|
|
#ifdef __cplusplus
|
|
# define TRUE true
|
|
# define FALSE false
|
|
# define BOOL bool
|
|
#else
|
|
# define TRUE ((char)(1 == 1))
|
|
# define FALSE ((char)(0 == 1))
|
|
# define BOOL char
|
|
#endif
|
|
|
|
#ifdef _AMIGASF
|
|
# undef FORMAT_INT
|
|
#endif
|
|
|
|
/* Lower-case digits. */
|
|
static const char lower_digits[] = "0123456789abcdefghijklmnopqrstuvwxyz";
|
|
|
|
/* Upper-case digits. */
|
|
static const char upper_digits[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
|
|
#define OUTCHAR(x) \
|
|
do{ \
|
|
if(stream((unsigned char)(x), (FILE *)data) != -1) \
|
|
done++; \
|
|
else \
|
|
return done; /* return immediately on failure */ \
|
|
} while(0)
|
|
|
|
/* Data type to read from the arglist */
|
|
typedef enum {
|
|
FORMAT_UNKNOWN = 0,
|
|
FORMAT_STRING,
|
|
FORMAT_PTR,
|
|
FORMAT_INT,
|
|
FORMAT_INTPTR,
|
|
FORMAT_LONG,
|
|
FORMAT_LONGLONG,
|
|
FORMAT_DOUBLE,
|
|
FORMAT_LONGDOUBLE,
|
|
FORMAT_WIDTH /* For internal use */
|
|
} FormatType;
|
|
|
|
/* convertion and display flags */
|
|
enum {
|
|
FLAGS_NEW = 0,
|
|
FLAGS_SPACE = 1<<0,
|
|
FLAGS_SHOWSIGN = 1<<1,
|
|
FLAGS_LEFT = 1<<2,
|
|
FLAGS_ALT = 1<<3,
|
|
FLAGS_SHORT = 1<<4,
|
|
FLAGS_LONG = 1<<5,
|
|
FLAGS_LONGLONG = 1<<6,
|
|
FLAGS_LONGDOUBLE = 1<<7,
|
|
FLAGS_PAD_NIL = 1<<8,
|
|
FLAGS_UNSIGNED = 1<<9,
|
|
FLAGS_OCTAL = 1<<10,
|
|
FLAGS_HEX = 1<<11,
|
|
FLAGS_UPPER = 1<<12,
|
|
FLAGS_WIDTH = 1<<13, /* '*' or '*<num>$' used */
|
|
FLAGS_WIDTHPARAM = 1<<14, /* width PARAMETER was specified */
|
|
FLAGS_PREC = 1<<15, /* precision was specified */
|
|
FLAGS_PRECPARAM = 1<<16, /* precision PARAMETER was specified */
|
|
FLAGS_CHAR = 1<<17, /* %c story */
|
|
FLAGS_FLOATE = 1<<18, /* %e or %E */
|
|
FLAGS_FLOATG = 1<<19 /* %g or %G */
|
|
};
|
|
|
|
typedef struct {
|
|
FormatType type;
|
|
int flags;
|
|
long width; /* width OR width parameter number */
|
|
long precision; /* precision OR precision parameter number */
|
|
union {
|
|
char *str;
|
|
void *ptr;
|
|
long num;
|
|
#ifdef ENABLE_64BIT
|
|
LONG_LONG lnum;
|
|
#endif
|
|
double dnum;
|
|
} data;
|
|
} va_stack_t;
|
|
|
|
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 */
|
|
bool fail; /* TRUE if an alloc has failed and thus the output is not
|
|
the complete data */
|
|
};
|
|
|
|
int curl_msprintf(char *buffer, const char *format, ...);
|
|
|
|
static long dprintf_DollarString(char *input, char **end)
|
|
{
|
|
int number=0;
|
|
while(ISDIGIT(*input)) {
|
|
number *= 10;
|
|
number += *input-'0';
|
|
input++;
|
|
}
|
|
if(number && ('$'==*input++)) {
|
|
*end = input;
|
|
return number;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static BOOL dprintf_IsQualifierNoDollar(char c)
|
|
{
|
|
switch (c) {
|
|
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 TRUE;
|
|
default:
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#ifdef DPRINTF_DEBUG2
|
|
int dprintf_Pass1Report(va_stack_t *vto, int max)
|
|
{
|
|
int i;
|
|
char buffer[128];
|
|
int bit;
|
|
int flags;
|
|
|
|
for(i=0; i<max; i++) {
|
|
char *type;
|
|
switch(vto[i].type) {
|
|
case FORMAT_UNKNOWN:
|
|
type = "unknown";
|
|
break;
|
|
case FORMAT_STRING:
|
|
type ="string";
|
|
break;
|
|
case FORMAT_PTR:
|
|
type ="pointer";
|
|
break;
|
|
case FORMAT_INT:
|
|
type = "int";
|
|
break;
|
|
case FORMAT_LONG:
|
|
type = "long";
|
|
break;
|
|
case FORMAT_LONGLONG:
|
|
type = "long long";
|
|
break;
|
|
case FORMAT_DOUBLE:
|
|
type = "double";
|
|
break;
|
|
case FORMAT_LONGDOUBLE:
|
|
type = "long double";
|
|
break;
|
|
}
|
|
|
|
|
|
buffer[0]=0;
|
|
|
|
for(bit=0; bit<31; bit++) {
|
|
flags = vto[i].flags & (1<<bit);
|
|
|
|
if(flags & FLAGS_SPACE)
|
|
strcat(buffer, "space ");
|
|
else if(flags & FLAGS_SHOWSIGN)
|
|
strcat(buffer, "plus ");
|
|
else if(flags & FLAGS_LEFT)
|
|
strcat(buffer, "left ");
|
|
else if(flags & FLAGS_ALT)
|
|
strcat(buffer, "alt ");
|
|
else if(flags & FLAGS_SHORT)
|
|
strcat(buffer, "short ");
|
|
else if(flags & FLAGS_LONG)
|
|
strcat(buffer, "long ");
|
|
else if(flags & FLAGS_LONGLONG)
|
|
strcat(buffer, "longlong ");
|
|
else if(flags & FLAGS_LONGDOUBLE)
|
|
strcat(buffer, "longdouble ");
|
|
else if(flags & FLAGS_PAD_NIL)
|
|
strcat(buffer, "padnil ");
|
|
else if(flags & FLAGS_UNSIGNED)
|
|
strcat(buffer, "unsigned ");
|
|
else if(flags & FLAGS_OCTAL)
|
|
strcat(buffer, "octal ");
|
|
else if(flags & FLAGS_HEX)
|
|
strcat(buffer, "hex ");
|
|
else if(flags & FLAGS_UPPER)
|
|
strcat(buffer, "upper ");
|
|
else if(flags & FLAGS_WIDTH)
|
|
strcat(buffer, "width ");
|
|
else if(flags & FLAGS_WIDTHPARAM)
|
|
strcat(buffer, "widthparam ");
|
|
else if(flags & FLAGS_PREC)
|
|
strcat(buffer, "precision ");
|
|
else if(flags & FLAGS_PRECPARAM)
|
|
strcat(buffer, "precparam ");
|
|
else if(flags & FLAGS_CHAR)
|
|
strcat(buffer, "char ");
|
|
else if(flags & FLAGS_FLOATE)
|
|
strcat(buffer, "floate ");
|
|
else if(flags & FLAGS_FLOATG)
|
|
strcat(buffer, "floatg ");
|
|
}
|
|
printf("REPORT: %d. %s [%s]\n", i, type, buffer);
|
|
|
|
}
|
|
|
|
|
|
}
|
|
#endif
|
|
|
|
/******************************************************************
|
|
*
|
|
* Pass 1:
|
|
* Create an index with the type of each parameter entry and its
|
|
* value (may vary in size)
|
|
*
|
|
******************************************************************/
|
|
|
|
static long dprintf_Pass1(char *format, va_stack_t *vto, char **endpos,
|
|
va_list arglist)
|
|
{
|
|
char *fmt = format;
|
|
int param_num = 0;
|
|
long this_param;
|
|
long width;
|
|
long precision;
|
|
int flags;
|
|
long max_param=0;
|
|
long i;
|
|
|
|
while (*fmt) {
|
|
if (*fmt++ == '%') {
|
|
if (*fmt == '%') {
|
|
fmt++;
|
|
continue; /* while */
|
|
}
|
|
|
|
flags = FLAGS_NEW;
|
|
|
|
/* Handle the positional case (N$) */
|
|
|
|
param_num++;
|
|
|
|
this_param = dprintf_DollarString(fmt, &fmt);
|
|
if (0 == this_param)
|
|
/* we got no positional, get the next counter */
|
|
this_param = param_num;
|
|
|
|
if (this_param > max_param)
|
|
max_param = this_param;
|
|
|
|
/*
|
|
* The parameter with number 'i' should be used. Next, we need
|
|
* to get SIZE and TYPE of the parameter. Add the information
|
|
* to our array.
|
|
*/
|
|
|
|
width = 0;
|
|
precision = 0;
|
|
|
|
/* Handle the flags */
|
|
|
|
while (dprintf_IsQualifierNoDollar(*fmt)) {
|
|
switch (*fmt++) {
|
|
case ' ':
|
|
flags |= FLAGS_SPACE;
|
|
break;
|
|
case '+':
|
|
flags |= FLAGS_SHOWSIGN;
|
|
break;
|
|
case '-':
|
|
flags |= FLAGS_LEFT;
|
|
flags &= ~FLAGS_PAD_NIL;
|
|
break;
|
|
case '#':
|
|
flags |= FLAGS_ALT;
|
|
break;
|
|
case '.':
|
|
flags |= FLAGS_PREC;
|
|
if ('*' == *fmt) {
|
|
/* The precision is picked from a specified parameter */
|
|
|
|
flags |= FLAGS_PRECPARAM;
|
|
fmt++;
|
|
param_num++;
|
|
|
|
i = dprintf_DollarString(fmt, &fmt);
|
|
if (i)
|
|
precision = i;
|
|
else
|
|
precision = param_num;
|
|
|
|
if (precision > max_param)
|
|
max_param = precision;
|
|
}
|
|
else {
|
|
flags |= FLAGS_PREC;
|
|
precision = strtol(fmt, &fmt, 10);
|
|
}
|
|
break;
|
|
case 'h':
|
|
flags |= FLAGS_SHORT;
|
|
break;
|
|
case 'l':
|
|
if (flags & FLAGS_LONG)
|
|
flags |= FLAGS_LONGLONG;
|
|
else
|
|
flags |= FLAGS_LONG;
|
|
break;
|
|
case 'L':
|
|
flags |= FLAGS_LONGDOUBLE;
|
|
break;
|
|
case 'q':
|
|
flags |= FLAGS_LONGLONG;
|
|
break;
|
|
case 'z':
|
|
/* the code below generates a warning if -Wunreachable-code is
|
|
used */
|
|
#if SIZEOF_SIZE_T>4
|
|
flags |= FLAGS_LONGLONG;
|
|
#else
|
|
flags |= FLAGS_LONG;
|
|
#endif
|
|
break;
|
|
case 'O':
|
|
#if SIZEOF_CURL_OFF_T > 4
|
|
flags |= FLAGS_LONGLONG;
|
|
#else
|
|
flags |= FLAGS_LONG;
|
|
#endif
|
|
break;
|
|
case '0':
|
|
if (!(flags & FLAGS_LEFT))
|
|
flags |= FLAGS_PAD_NIL;
|
|
/* FALLTHROUGH */
|
|
case '1': case '2': case '3': case '4':
|
|
case '5': case '6': case '7': case '8': case '9':
|
|
flags |= FLAGS_WIDTH;
|
|
width = strtol(fmt-1, &fmt, 10);
|
|
break;
|
|
case '*': /* Special case */
|
|
flags |= FLAGS_WIDTHPARAM;
|
|
param_num++;
|
|
|
|
i = dprintf_DollarString(fmt, &fmt);
|
|
if(i)
|
|
width = i;
|
|
else
|
|
width = param_num;
|
|
if(width > max_param)
|
|
max_param=width;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
} /* switch */
|
|
|
|
/* Handle the specifier */
|
|
|
|
i = this_param - 1;
|
|
|
|
switch (*fmt) {
|
|
case 'S':
|
|
flags |= FLAGS_ALT;
|
|
/* FALLTHROUGH */
|
|
case 's':
|
|
vto[i].type = FORMAT_STRING;
|
|
break;
|
|
case 'n':
|
|
vto[i].type = FORMAT_INTPTR;
|
|
break;
|
|
case 'p':
|
|
vto[i].type = FORMAT_PTR;
|
|
break;
|
|
case 'd': case 'i':
|
|
vto[i].type = FORMAT_INT;
|
|
break;
|
|
case 'u':
|
|
vto[i].type = FORMAT_INT;
|
|
flags |= FLAGS_UNSIGNED;
|
|
break;
|
|
case 'o':
|
|
vto[i].type = FORMAT_INT;
|
|
flags |= FLAGS_OCTAL;
|
|
break;
|
|
case 'x':
|
|
vto[i].type = FORMAT_INT;
|
|
flags |= FLAGS_HEX;
|
|
break;
|
|
case 'X':
|
|
vto[i].type = FORMAT_INT;
|
|
flags |= FLAGS_HEX|FLAGS_UPPER;
|
|
break;
|
|
case 'c':
|
|
vto[i].type = FORMAT_INT;
|
|
flags |= FLAGS_CHAR;
|
|
break;
|
|
case 'f':
|
|
vto[i].type = FORMAT_DOUBLE;
|
|
break;
|
|
case 'e':
|
|
vto[i].type = FORMAT_DOUBLE;
|
|
flags |= FLAGS_FLOATE;
|
|
break;
|
|
case 'E':
|
|
vto[i].type = FORMAT_DOUBLE;
|
|
flags |= FLAGS_FLOATE|FLAGS_UPPER;
|
|
break;
|
|
case 'g':
|
|
vto[i].type = FORMAT_DOUBLE;
|
|
flags |= FLAGS_FLOATG;
|
|
break;
|
|
case 'G':
|
|
vto[i].type = FORMAT_DOUBLE;
|
|
flags |= FLAGS_FLOATG|FLAGS_UPPER;
|
|
break;
|
|
default:
|
|
vto[i].type = FORMAT_UNKNOWN;
|
|
break;
|
|
} /* switch */
|
|
|
|
vto[i].flags = flags;
|
|
vto[i].width = width;
|
|
vto[i].precision = precision;
|
|
|
|
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! */
|
|
}
|
|
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! */
|
|
}
|
|
*endpos++ = fmt + 1; /* end of this sequence */
|
|
}
|
|
}
|
|
|
|
#ifdef DPRINTF_DEBUG2
|
|
dprintf_Pass1Report(vto, max_param);
|
|
#endif
|
|
|
|
/* 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 = va_arg(arglist, int);
|
|
}
|
|
|
|
switch (vto[i].type)
|
|
{
|
|
case FORMAT_STRING:
|
|
vto[i].data.str = va_arg(arglist, char *);
|
|
break;
|
|
|
|
case FORMAT_INTPTR:
|
|
case FORMAT_UNKNOWN:
|
|
case FORMAT_PTR:
|
|
vto[i].data.ptr = va_arg(arglist, void *);
|
|
break;
|
|
|
|
case FORMAT_INT:
|
|
#ifdef ENABLE_64BIT
|
|
if(vto[i].flags & FLAGS_LONGLONG)
|
|
vto[i].data.lnum = va_arg(arglist, LONG_LONG);
|
|
else
|
|
#endif
|
|
if(vto[i].flags & FLAGS_LONG)
|
|
vto[i].data.num = va_arg(arglist, long);
|
|
else
|
|
vto[i].data.num = va_arg(arglist, int);
|
|
break;
|
|
|
|
case FORMAT_DOUBLE:
|
|
vto[i].data.dnum = va_arg(arglist, double);
|
|
break;
|
|
|
|
case FORMAT_WIDTH:
|
|
/* Argument has been read. Silently convert it into an integer
|
|
* for later use
|
|
*/
|
|
vto[i].type = FORMAT_INT;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return max_param;
|
|
|
|
}
|
|
|
|
static int dprintf_formatf(
|
|
void *data, /* untouched by format(), just sent to the stream() function in
|
|
the second argument */
|
|
/* function pointer called for each output character */
|
|
int (*stream)(int, FILE *),
|
|
const char *format, /* %-formatted string */
|
|
va_list ap_save) /* list of parameters */
|
|
{
|
|
/* Base-36 digits for numbers. */
|
|
const char *digits = lower_digits;
|
|
|
|
/* Pointer into the format string. */
|
|
char *f;
|
|
|
|
/* Number of characters written. */
|
|
int done = 0;
|
|
|
|
long param; /* current parameter to read */
|
|
long param_num=0; /* parameter counter */
|
|
|
|
va_stack_t vto[MAX_PARAMETERS];
|
|
char *endpos[MAX_PARAMETERS];
|
|
char **end;
|
|
|
|
char work[BUFFSIZE];
|
|
|
|
va_stack_t *p;
|
|
|
|
/* Do the actual %-code parsing */
|
|
dprintf_Pass1((char *)format, vto, endpos, ap_save);
|
|
|
|
end = &endpos[0]; /* the initial end-position from the list dprintf_Pass1()
|
|
created for us */
|
|
|
|
f = (char *)format;
|
|
while (*f != '\0') {
|
|
/* Format spec modifiers. */
|
|
char alt;
|
|
|
|
/* Width of a field. */
|
|
long width;
|
|
|
|
/* Precision of a field. */
|
|
long prec;
|
|
|
|
/* Decimal integer is negative. */
|
|
char is_neg;
|
|
|
|
/* Base of a number to be written. */
|
|
long base;
|
|
|
|
/* Integral values to be written. */
|
|
#ifdef ENABLE_64BIT
|
|
unsigned LONG_LONG num;
|
|
#else
|
|
unsigned long num;
|
|
#endif
|
|
long signed_num;
|
|
|
|
if (*f != '%') {
|
|
/* This isn't a format spec, so write everything out until the next one
|
|
OR end of string is reached. */
|
|
do {
|
|
OUTCHAR(*f);
|
|
} while(*++f && ('%' != *f));
|
|
continue;
|
|
}
|
|
|
|
++f;
|
|
|
|
/* Check for "%%". Note that although the ANSI standard lists
|
|
'%' as a conversion specifier, it says "The complete format
|
|
specification shall be `%%'," so we can avoid all the width
|
|
and precision processing. */
|
|
if (*f == '%') {
|
|
++f;
|
|
OUTCHAR('%');
|
|
continue;
|
|
}
|
|
|
|
/* If this is a positional parameter, the position must follow imediately
|
|
after the %, thus create a %<num>$ sequence */
|
|
param=dprintf_DollarString(f, &f);
|
|
|
|
if(!param)
|
|
param = param_num;
|
|
else
|
|
--param;
|
|
|
|
param_num++; /* increase this always to allow "%2$s %1$s %s" and then the
|
|
third %s will pick the 3rd argument */
|
|
|
|
p = &vto[param];
|
|
|
|
/* pick up the specified width */
|
|
if(p->flags & FLAGS_WIDTHPARAM)
|
|
width = vto[p->width].data.num;
|
|
else
|
|
width = p->width;
|
|
|
|
/* pick up the specified precision */
|
|
if(p->flags & FLAGS_PRECPARAM)
|
|
prec = vto[p->precision].data.num;
|
|
else if(p->flags & FLAGS_PREC)
|
|
prec = p->precision;
|
|
else
|
|
prec = -1;
|
|
|
|
alt = (p->flags & FLAGS_ALT)?TRUE:FALSE;
|
|
|
|
switch (p->type) {
|
|
case FORMAT_INT:
|
|
num = p->data.num;
|
|
if(p->flags & FLAGS_CHAR) {
|
|
/* Character. */
|
|
if (!(p->flags & FLAGS_LEFT))
|
|
while (--width > 0)
|
|
OUTCHAR(' ');
|
|
OUTCHAR((char) num);
|
|
if (p->flags & FLAGS_LEFT)
|
|
while (--width > 0)
|
|
OUTCHAR(' ');
|
|
break;
|
|
}
|
|
if(p->flags & FLAGS_UNSIGNED) {
|
|
/* Decimal unsigned integer. */
|
|
base = 10;
|
|
goto unsigned_number;
|
|
}
|
|
if(p->flags & FLAGS_OCTAL) {
|
|
/* Octal unsigned integer. */
|
|
base = 8;
|
|
goto unsigned_number;
|
|
}
|
|
if(p->flags & FLAGS_HEX) {
|
|
/* Hexadecimal unsigned integer. */
|
|
|
|
digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
|
|
base = 16;
|
|
goto unsigned_number;
|
|
}
|
|
|
|
/* Decimal integer. */
|
|
base = 10;
|
|
|
|
#ifdef ENABLE_64BIT
|
|
if(p->flags & FLAGS_LONGLONG) {
|
|
/* long long */
|
|
is_neg = p->data.lnum < 0;
|
|
num = is_neg ? (- p->data.lnum) : p->data.lnum;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
signed_num = (long) num;
|
|
is_neg = signed_num < 0;
|
|
num = is_neg ? (- signed_num) : signed_num;
|
|
}
|
|
goto number;
|
|
|
|
unsigned_number:
|
|
/* Unsigned number of base BASE. */
|
|
is_neg = 0;
|
|
|
|
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 (alt && base == 8 && prec <= 0) {
|
|
*w-- = '0';
|
|
--width;
|
|
}
|
|
|
|
if (prec > 0) {
|
|
width -= prec;
|
|
while (prec-- > 0)
|
|
*w-- = '0';
|
|
}
|
|
|
|
if (alt && base == 16)
|
|
width -= 2;
|
|
|
|
if (is_neg || (p->flags & FLAGS_SHOWSIGN) || (p->flags & FLAGS_SPACE))
|
|
--width;
|
|
|
|
if (!(p->flags & FLAGS_LEFT) && !(p->flags & FLAGS_PAD_NIL))
|
|
while (width-- > 0)
|
|
OUTCHAR(' ');
|
|
|
|
if (is_neg)
|
|
OUTCHAR('-');
|
|
else if (p->flags & FLAGS_SHOWSIGN)
|
|
OUTCHAR('+');
|
|
else if (p->flags & FLAGS_SPACE)
|
|
OUTCHAR(' ');
|
|
|
|
if (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('0');
|
|
|
|
/* Write the number. */
|
|
while (++w <= workend) {
|
|
OUTCHAR(*w);
|
|
}
|
|
|
|
if (p->flags & FLAGS_LEFT)
|
|
while (width-- > 0)
|
|
OUTCHAR(' ');
|
|
}
|
|
break;
|
|
|
|
case FORMAT_STRING:
|
|
/* String. */
|
|
{
|
|
static const char null[] = "(nil)";
|
|
const char *str;
|
|
size_t len;
|
|
|
|
str = (char *) p->data.str;
|
|
if ( str == NULL) {
|
|
/* Write null[] if there's space. */
|
|
if (prec == -1 || prec >= (long) sizeof(null) - 1) {
|
|
str = null;
|
|
len = sizeof(null) - 1;
|
|
/* Disable quotes around (nil) */
|
|
p->flags &= (~FLAGS_ALT);
|
|
}
|
|
else {
|
|
str = "";
|
|
len = 0;
|
|
}
|
|
}
|
|
else
|
|
len = strlen(str);
|
|
|
|
if (prec != -1 && (size_t) prec < len)
|
|
len = prec;
|
|
width -= (long)len;
|
|
|
|
if (p->flags & FLAGS_ALT)
|
|
OUTCHAR('"');
|
|
|
|
if (!(p->flags&FLAGS_LEFT))
|
|
while (width-- > 0)
|
|
OUTCHAR(' ');
|
|
|
|
while (len-- > 0)
|
|
OUTCHAR(*str++);
|
|
if (p->flags&FLAGS_LEFT)
|
|
while (width-- > 0)
|
|
OUTCHAR(' ');
|
|
|
|
if (p->flags & FLAGS_ALT)
|
|
OUTCHAR('"');
|
|
}
|
|
break;
|
|
|
|
case FORMAT_PTR:
|
|
/* Generic pointer. */
|
|
{
|
|
void *ptr;
|
|
ptr = (void *) p->data.ptr;
|
|
if (ptr != NULL) {
|
|
/* If the pointer is not NULL, write it as a %#x spec. */
|
|
base = 16;
|
|
digits = (p->flags & FLAGS_UPPER)? upper_digits : lower_digits;
|
|
alt = 1;
|
|
num = (size_t) ptr;
|
|
is_neg = 0;
|
|
goto number;
|
|
}
|
|
else {
|
|
/* Write "(nil)" for a nil pointer. */
|
|
static const char strnil[] = "(nil)";
|
|
const char *point;
|
|
|
|
width -= sizeof(strnil) - 1;
|
|
if (p->flags & FLAGS_LEFT)
|
|
while (width-- > 0)
|
|
OUTCHAR(' ');
|
|
for (point = strnil; *point != '\0'; ++point)
|
|
OUTCHAR(*point);
|
|
if (! (p->flags & FLAGS_LEFT))
|
|
while (width-- > 0)
|
|
OUTCHAR(' ');
|
|
}
|
|
}
|
|
break;
|
|
|
|
case FORMAT_DOUBLE:
|
|
{
|
|
char formatbuf[32]="%";
|
|
char *fptr;
|
|
size_t left = sizeof(formatbuf)-strlen(formatbuf);
|
|
int len;
|
|
|
|
width = -1;
|
|
if (p->flags & FLAGS_WIDTH)
|
|
width = p->width;
|
|
else if (p->flags & FLAGS_WIDTHPARAM)
|
|
width = vto[p->width].data.num;
|
|
|
|
prec = -1;
|
|
if (p->flags & FLAGS_PREC)
|
|
prec = p->precision;
|
|
else if (p->flags & FLAGS_PRECPARAM)
|
|
prec = vto[p->precision].data.num;
|
|
|
|
if (p->flags & FLAGS_LEFT)
|
|
strcat(formatbuf, "-");
|
|
if (p->flags & FLAGS_SHOWSIGN)
|
|
strcat(formatbuf, "+");
|
|
if (p->flags & FLAGS_SPACE)
|
|
strcat(formatbuf, " ");
|
|
if (p->flags & FLAGS_ALT)
|
|
strcat(formatbuf, "#");
|
|
|
|
fptr=&formatbuf[strlen(formatbuf)];
|
|
|
|
if(width >= 0) {
|
|
/* RECURSIVE USAGE */
|
|
len = curl_msnprintf(fptr, left, "%ld", width);
|
|
fptr += len;
|
|
left -= len;
|
|
}
|
|
if(prec >= 0) {
|
|
/* RECURSIVE USAGE */
|
|
len = curl_msnprintf(fptr, left, ".%ld", prec);
|
|
fptr += len;
|
|
left -= len;
|
|
}
|
|
if (p->flags & FLAGS_LONG)
|
|
*fptr++ = 'l';
|
|
|
|
if (p->flags & FLAGS_FLOATE)
|
|
*fptr++ = p->flags&FLAGS_UPPER ? 'E':'e';
|
|
else if (p->flags & FLAGS_FLOATG)
|
|
*fptr++ = p->flags & FLAGS_UPPER ? 'G' : 'g';
|
|
else
|
|
*fptr++ = 'f';
|
|
|
|
*fptr = 0; /* and a final zero termination */
|
|
|
|
/* NOTE NOTE NOTE!! Not all sprintf() implementations returns number
|
|
of output characters */
|
|
(sprintf)(work, formatbuf, p->data.dnum);
|
|
|
|
for(fptr=work; *fptr; fptr++)
|
|
OUTCHAR(*fptr);
|
|
}
|
|
break;
|
|
|
|
case FORMAT_INTPTR:
|
|
/* Answer the count of characters written. */
|
|
#ifdef ENABLE_64BIT
|
|
if (p->flags & FLAGS_LONGLONG)
|
|
*(LONG_LONG *) p->data.ptr = (LONG_LONG)done;
|
|
else
|
|
#endif
|
|
if (p->flags & FLAGS_LONG)
|
|
*(long *) p->data.ptr = (long)done;
|
|
else if (!(p->flags & FLAGS_SHORT))
|
|
*(int *) p->data.ptr = (int)done;
|
|
else
|
|
*(short *) p->data.ptr = (short)done;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
f = *end++; /* goto end of %-code */
|
|
|
|
}
|
|
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 curl_mvsnprintf(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 curl_msnprintf(char *buffer, size_t maxlength, const char *format, ...)
|
|
{
|
|
int retcode;
|
|
va_list ap_save; /* argument pointer */
|
|
va_start(ap_save, format);
|
|
retcode = curl_mvsnprintf(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=(char *)malloc(32);
|
|
if(!infop->buffer) {
|
|
infop->fail = TRUE;
|
|
return -1; /* fail */
|
|
}
|
|
infop->alloc = 32;
|
|
infop->len =0;
|
|
}
|
|
else if(infop->len+1 >= infop->alloc) {
|
|
char *newptr;
|
|
|
|
newptr = (char *)realloc(infop->buffer, infop->alloc*2);
|
|
|
|
if(!newptr) {
|
|
infop->fail = TRUE;
|
|
return -1;
|
|
}
|
|
infop->buffer = newptr;
|
|
infop->alloc *= 2;
|
|
}
|
|
|
|
infop->buffer[ infop->len ] = outc;
|
|
|
|
infop->len++;
|
|
|
|
return outc; /* fputc() returns like this on success */
|
|
}
|
|
|
|
char *curl_maprintf(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 = FALSE;
|
|
|
|
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 *curl_mvaprintf(const char *format, va_list ap_save)
|
|
{
|
|
int retcode;
|
|
struct asprintf info;
|
|
|
|
info.buffer = NULL;
|
|
info.len = 0;
|
|
info.alloc = 0;
|
|
info.fail = FALSE;
|
|
|
|
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("");
|
|
}
|
|
|
|
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 curl_msprintf(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 curl_mprintf(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 curl_mfprintf(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 curl_mvsprintf(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 curl_mvprintf(const char *format, va_list ap_save)
|
|
{
|
|
return dprintf_formatf(stdout, fputc, format, ap_save);
|
|
}
|
|
|
|
int curl_mvfprintf(FILE *whereto, const char *format, va_list ap_save)
|
|
{
|
|
return dprintf_formatf(whereto, fputc, format, ap_save);
|
|
}
|
|
|
|
#ifdef DPRINTF_DEBUG
|
|
int main()
|
|
{
|
|
char buffer[129];
|
|
char *ptr;
|
|
#ifdef ENABLE_64BIT
|
|
long long one=99;
|
|
long long two=100;
|
|
long long test = 0x1000000000LL;
|
|
curl_mprintf("%lld %lld %lld\n", one, two, test);
|
|
#endif
|
|
|
|
curl_mprintf("%3d %5d\n", 10, 1998);
|
|
|
|
ptr=curl_maprintf("test this then baby %s%s%s%s%s%s %d %d %d loser baby get a hit in yer face now!", "", "pretty long string pretty long string pretty long string pretty long string pretty long string", "/", "/", "/", "pretty long string", 1998, 1999, 2001);
|
|
|
|
puts(ptr);
|
|
|
|
memset(ptr, 55, strlen(ptr)+1);
|
|
|
|
free(ptr);
|
|
|
|
#if 1
|
|
curl_mprintf(buffer, "%s %s %d", "daniel", "stenberg", 19988);
|
|
puts(buffer);
|
|
|
|
curl_mfprintf(stderr, "%s %#08x\n", "dummy", 65);
|
|
|
|
printf("%s %#08x\n", "dummy", 65);
|
|
{
|
|
double tryout = 3.14156592;
|
|
curl_mprintf(buffer, "%.2g %G %f %e %E", tryout, tryout, tryout, tryout, tryout);
|
|
puts(buffer);
|
|
printf("%.2g %G %f %e %E\n", tryout, tryout, tryout, tryout, tryout);
|
|
}
|
|
#endif
|
|
|
|
return 0;
|
|
}
|
|
|
|
#endif
|