Commit 3311c766 authored by Tom Lane's avatar Tom Lane

Fix a rather sizable number of problems in our homegrown snprintf, such as

incorrect implementation of argument reordering, arbitrary limit of output
size for sprintf and fprintf, willingness to access more bytes than "%.Ns"
specification allows, wrong formatting of LONGLONG_MIN, various field-padding
bugs and omissions.  I believe it now accurately implements a subset of
the Single Unix Spec requirements (remaining unimplemented features are
documented, too).  Bruce Momjian and Tom Lane.
parent 99552287
...@@ -30,23 +30,42 @@ ...@@ -30,23 +30,42 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE. * SUCH DAMAGE.
*
* $PostgreSQL: pgsql/src/port/snprintf.c,v 1.30 2005/12/05 02:39:38 tgl Exp $
*/ */
#include "c.h" #include "c.h"
#include <limits.h>
#ifndef WIN32 #ifndef WIN32
#include <sys/ioctl.h> #include <sys/ioctl.h>
#endif #endif
#include <sys/param.h> #include <sys/param.h>
#ifndef NL_ARGMAX
#define NL_ARGMAX 16
#endif
/* /*
** SNPRINTF, VSNPRINT -- counted versions of printf * SNPRINTF, VSNPRINTF and friends
** *
** These versions have been grabbed off the net. They have been * These versions have been grabbed off the net. They have been
** cleaned up to compile properly and support for .precision and * cleaned up to compile properly and support for most of the Single
** %lx has been added. * Unix Specification has been added. Remaining unimplemented features
*/ * are:
*
* 1. No locale support: the radix character is always '.' and the '
* (single quote) format flag is ignored.
*
* 2. No support for the "%n" format specification.
*
* 3. No support for wide characters ("lc" and "ls" formats).
*
* 4. No support for "long double" ("Lf" and related formats).
*
* 5. Space and '#' flags are not implemented.
*/
/************************************************************** /**************************************************************
* Original: * Original:
...@@ -54,18 +73,8 @@ ...@@ -54,18 +73,8 @@
* A bombproof version of doprnt (dopr) included. * A bombproof version of doprnt (dopr) included.
* Sigh. This sort of thing is always nasty do deal with. Note that * Sigh. This sort of thing is always nasty do deal with. Note that
* the version here does not include floating point. (now it does ... tgl) * the version here does not include floating point. (now it does ... tgl)
*
* snprintf() is used instead of sprintf() as it does limit checks
* for string length. This covers a nasty loophole.
*
* The other functions are there to prevent NULL pointers from
* causing nasty effects.
**************************************************************/ **************************************************************/
/*static char _id[] = "$PostgreSQL: pgsql/src/port/snprintf.c,v 1.29 2005/10/15 02:49:51 momjian Exp $";*/
static void dopr(char *buffer, const char *format, va_list args, char *end);
/* Prevent recursion */ /* Prevent recursion */
#undef vsnprintf #undef vsnprintf
#undef snprintf #undef snprintf
...@@ -73,17 +82,66 @@ static void dopr(char *buffer, const char *format, va_list args, char *end); ...@@ -73,17 +82,66 @@ static void dopr(char *buffer, const char *format, va_list args, char *end);
#undef fprintf #undef fprintf
#undef printf #undef printf
/* Info about where the formatted output is going */
typedef struct
{
char *bufptr; /* next buffer output position */
char *bufstart; /* first buffer element */
char *bufend; /* last buffer element, or NULL */
/* bufend == NULL is for sprintf, where we assume buf is big enough */
FILE *stream; /* eventual output destination, or NULL */
int nchars; /* # chars already sent to stream */
} PrintfTarget;
/*
* Info about the type and value of a formatting parameter. Note that we
* don't currently support "long double", "wint_t", or "wchar_t *" data,
* nor the '%n' formatting code; else we'd need more types. Also, at this
* level we need not worry about signed vs unsigned values.
*/
typedef enum
{
ATYPE_NONE = 0,
ATYPE_INT,
ATYPE_LONG,
ATYPE_LONGLONG,
ATYPE_DOUBLE,
ATYPE_CHARPTR
} PrintfArgType;
typedef union
{
int i;
long l;
int64 ll;
double d;
char *cptr;
} PrintfArgValue;
static void flushbuffer(PrintfTarget *target);
static int dopr(PrintfTarget *target, const char *format, va_list args);
int int
pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args) pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
{ {
char *end; PrintfTarget target;
str[0] = '\0'; if (str == NULL || count == 0)
end = str + count - 1; return 0;
dopr(str, fmt, args, end); target.bufstart = target.bufptr = str;
if (count > 0) target.bufend = str + count - 1;
end[0] = '\0'; target.stream = NULL;
return strlen(str); /* target.nchars is unused in this case */
if (dopr(&target, fmt, args))
{
*(target.bufptr) = '\0';
errno = EINVAL; /* bad format */
return -1;
}
*(target.bufptr) = '\0';
return target.bufptr - target.bufstart;
} }
int int
...@@ -98,34 +156,73 @@ pg_snprintf(char *str, size_t count, const char *fmt,...) ...@@ -98,34 +156,73 @@ pg_snprintf(char *str, size_t count, const char *fmt,...)
return len; return len;
} }
static int
pg_vsprintf(char *str, const char *fmt, va_list args)
{
PrintfTarget target;
if (str == NULL)
return 0;
target.bufstart = target.bufptr = str;
target.bufend = NULL;
target.stream = NULL;
/* target.nchars is unused in this case */
if (dopr(&target, fmt, args))
{
*(target.bufptr) = '\0';
errno = EINVAL; /* bad format */
return -1;
}
*(target.bufptr) = '\0';
return target.bufptr - target.bufstart;
}
int int
pg_sprintf(char *str, const char *fmt,...) pg_sprintf(char *str, const char *fmt,...)
{ {
int len; int len;
va_list args; va_list args;
char buffer[4096];
va_start(args, fmt); va_start(args, fmt);
len = pg_vsnprintf(buffer, (size_t) 4096, fmt, args); len = pg_vsprintf(str, fmt, args);
va_end(args); va_end(args);
/* limit output to string */
StrNCpy(str, buffer, (len + 1 < 4096) ? len + 1 : 4096);
return len; return len;
} }
static int
pg_vfprintf(FILE *stream, const char *fmt, va_list args)
{
PrintfTarget target;
char buffer[1024]; /* size is arbitrary */
if (stream == NULL)
{
errno = EINVAL;
return -1;
}
target.bufstart = target.bufptr = buffer;
target.bufend = buffer + sizeof(buffer) - 1;
target.stream = stream;
target.nchars = 0;
if (dopr(&target, fmt, args))
{
errno = EINVAL; /* bad format */
return -1;
}
/* dump any remaining buffer contents */
flushbuffer(&target);
return target.nchars;
}
int int
pg_fprintf(FILE *stream, const char *fmt,...) pg_fprintf(FILE *stream, const char *fmt,...)
{ {
int len; int len;
va_list args; va_list args;
char buffer[4096];
char *p;
va_start(args, fmt); va_start(args, fmt);
len = pg_vsnprintf(buffer, (size_t) 4096, fmt, args); len = pg_vfprintf(stream, fmt, args);
va_end(args); va_end(args);
for (p = buffer; *p; p++)
putc(*p, stream);
return len; return len;
} }
...@@ -134,528 +231,655 @@ pg_printf(const char *fmt,...) ...@@ -134,528 +231,655 @@ pg_printf(const char *fmt,...)
{ {
int len; int len;
va_list args; va_list args;
char buffer[4096];
char *p;
va_start(args, fmt); va_start(args, fmt);
len = pg_vsnprintf(buffer, (size_t) 4096, fmt, args); len = pg_vfprintf(stdout, fmt, args);
va_end(args); va_end(args);
for (p = buffer; *p; p++)
putchar(*p);
return len; return len;
} }
static int adjust_sign(int is_negative, int forcesign, int *signvalue); /* call this only when stream is defined */
static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen); static void
static void leading_pad(int zpad, int *signvalue, int *padlen, char *end, flushbuffer(PrintfTarget *target)
char **output); {
static void trailing_pad(int *padlen, char *end, char **output); size_t nc = target->bufptr - target->bufstart;
if (nc > 0)
target->nchars += fwrite(target->bufstart, 1, nc, target->stream);
target->bufptr = target->bufstart;
}
static void fmtstr(char *value, int leftjust, int minlen, int maxwidth, static void fmtstr(char *value, int leftjust, int minlen, int maxwidth,
char *end, char **output); int pointflag, PrintfTarget *target);
static void fmtint(int64 value, int base, int dosign, int forcesign, static void fmtptr(void *value, PrintfTarget *target);
int leftjust, int minlen, int zpad, char *end, char **output); static void fmtint(int64 value, char type, int forcesign,
int leftjust, int minlen, int zpad, int precision, int pointflag,
PrintfTarget *target);
static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target);
static void fmtfloat(double value, char type, int forcesign, static void fmtfloat(double value, char type, int forcesign,
int leftjust, int minlen, int zpad, int precision, int pointflag, char *end, int leftjust, int minlen, int zpad, int precision, int pointflag,
char **output); PrintfTarget *target);
static void dostr(char *str, int cut, char *end, char **output); static void dostr(const char *str, int slen, PrintfTarget *target);
static void dopr_outch(int c, char *end, char **output); static void dopr_outch(int c, PrintfTarget *target);
static int adjust_sign(int is_negative, int forcesign, int *signvalue);
#define FMTSTR 1 static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen);
#define FMTNUM 2 static void leading_pad(int zpad, int *signvalue, int *padlen,
#define FMTNUM_U 3 PrintfTarget *target);
#define FMTFLOAT 4 static void trailing_pad(int *padlen, PrintfTarget *target);
#define FMTCHAR 5
#define FMTWIDTH 6
#define FMTLEN 7
/* /*
* dopr(): poor man's version of doprintf * dopr(): poor man's version of doprintf
*/ */
static int
static void dopr(PrintfTarget *target, const char *format, va_list args)
dopr(char *buffer, const char *format, va_list args, char *end)
{ {
const char *format_start = format;
int ch; int ch;
bool have_dollar;
bool have_non_dollar;
bool have_star;
bool afterstar;
int accum;
int longlongflag; int longlongflag;
int longflag; int longflag;
int pointflag; int pointflag;
int maxwidth;
int leftjust; int leftjust;
int minlen; int fieldwidth;
int precision;
int zpad; int zpad;
int forcesign; int forcesign;
int last_dollar;
int fmtpos;
int cvalue;
int64 numvalue;
double fvalue;
char *strvalue;
int i; int i;
const char *format_save; PrintfArgType argtypes[NL_ARGMAX+1];
const char *fmtbegin; PrintfArgValue argvalues[NL_ARGMAX+1];
int fmtpos = 1;
int realpos = 0;
int precision;
int position;
char *output;
int nargs = 1;
const char *p;
struct fmtpar
{
const char *fmtbegin;
const char *fmtend;
void *value;
int64 numvalue;
double fvalue;
int charvalue;
int leftjust;
int minlen;
int zpad;
int maxwidth;
int base;
int dosign;
int forcesign;
char type;
int precision;
int pointflag;
char func;
int realpos;
int longflag;
int longlongflag;
} *fmtpar, **fmtparptr;
/* /*
* Create enough structures to hold all arguments. This overcounts, eg * Parse the format string to determine whether there are %n$ format
* not all '*' characters are necessarily arguments, but it's not worth * specs, and identify the types and order of the format parameters.
* being exact.
*/ */
for (p = format; *p != '\0'; p++) have_dollar = have_non_dollar = false;
if (*p == '%' || *p == '*') last_dollar = 0;
nargs++; MemSet(argtypes, 0, sizeof(argtypes));
/* Need to use malloc() because memory system might not be started yet. */ while ((ch = *format++) != '\0')
if ((fmtpar = malloc(sizeof(struct fmtpar) * nargs)) == NULL)
{ {
fprintf(stderr, _("out of memory\n")); if (ch != '%')
exit(1); continue;
longflag = longlongflag = pointflag = 0;
fmtpos = accum = 0;
afterstar = false;
nextch1:
ch = *format++;
if (ch == '\0')
break; /* illegal, but we don't complain */
switch (ch)
{
case '-':
case '+':
goto nextch1;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
accum = accum * 10 + (ch - '0');
goto nextch1;
case '.':
pointflag = 1;
accum = 0;
goto nextch1;
case '*':
if (afterstar)
have_non_dollar = true; /* multiple stars */
afterstar = true;
accum = 0;
goto nextch1;
case '$':
have_dollar = true;
if (accum <= 0 || accum > NL_ARGMAX)
return -1;
if (afterstar)
{
if (argtypes[accum] &&
argtypes[accum] != ATYPE_INT)
return -1;
argtypes[accum] = ATYPE_INT;
last_dollar = Max(last_dollar, accum);
afterstar = false;
}
else
fmtpos = accum;
accum = 0;
goto nextch1;
case 'l':
if (longflag)
longlongflag = 1;
else
longflag = 1;
goto nextch1;
case 'h':
case '\'':
/* ignore these */
goto nextch1;
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
if (fmtpos)
{
PrintfArgType atype;
if (longlongflag)
atype = ATYPE_LONGLONG;
else if (longflag)
atype = ATYPE_LONG;
else
atype = ATYPE_INT;
if (argtypes[fmtpos] &&
argtypes[fmtpos] != atype)
return -1;
argtypes[fmtpos] = atype;
last_dollar = Max(last_dollar, fmtpos);
}
else
have_non_dollar = true;
break;
case 'c':
if (fmtpos)
{
if (argtypes[fmtpos] &&
argtypes[fmtpos] != ATYPE_INT)
return -1;
argtypes[fmtpos] = ATYPE_INT;
last_dollar = Max(last_dollar, fmtpos);
}
else
have_non_dollar = true;
break;
case 's':
case 'p':
if (fmtpos)
{
if (argtypes[fmtpos] &&
argtypes[fmtpos] != ATYPE_CHARPTR)
return -1;
argtypes[fmtpos] = ATYPE_CHARPTR;
last_dollar = Max(last_dollar, fmtpos);
}
else
have_non_dollar = true;
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (fmtpos)
{
if (argtypes[fmtpos] &&
argtypes[fmtpos] != ATYPE_DOUBLE)
return -1;
argtypes[fmtpos] = ATYPE_DOUBLE;
last_dollar = Max(last_dollar, fmtpos);
}
else
have_non_dollar = true;
break;
case '%':
break;
}
/*
* If we finish the spec with afterstar still set, there's a
* non-dollar star in there.
*/
if (afterstar)
have_non_dollar = true;
} }
if ((fmtparptr = malloc(sizeof(struct fmtpar *) * nargs)) == NULL)
/* Per spec, you use either all dollar or all not. */
if (have_dollar && have_non_dollar)
return -1;
/*
* In dollar mode, collect the arguments in physical order.
*/
for (i = 1; i <= last_dollar; i++)
{ {
fprintf(stderr, _("out of memory\n")); switch (argtypes[i])
exit(1); {
case ATYPE_NONE:
return -1; /* invalid format */
case ATYPE_INT:
argvalues[i].i = va_arg(args, int);
break;
case ATYPE_LONG:
argvalues[i].l = va_arg(args, long);
break;
case ATYPE_LONGLONG:
argvalues[i].ll = va_arg(args, int64);
break;
case ATYPE_DOUBLE:
argvalues[i].d = va_arg(args, double);
break;
case ATYPE_CHARPTR:
argvalues[i].cptr = va_arg(args, char *);
break;
}
} }
format_save = format; /*
* At last we can parse the format for real.
output = buffer; */
while ((ch = *format++)) format = format_start;
while ((ch = *format++) != '\0')
{ {
if (ch != '%')
{
dopr_outch(ch, target);
continue;
}
fieldwidth = precision = zpad = leftjust = forcesign = 0;
longflag = longlongflag = pointflag = 0;
fmtpos = accum = 0;
have_star = afterstar = false;
nextch2:
ch = *format++;
if (ch == '\0')
break; /* illegal, but we don't complain */
switch (ch) switch (ch)
{ {
case '%': case '-':
leftjust = minlen = zpad = forcesign = maxwidth = 0; leftjust = 1;
longflag = longlongflag = pointflag = 0; goto nextch2;
fmtbegin = format - 1; case '+':
realpos = 0; forcesign = 1;
position = precision = 0; goto nextch2;
nextch: case '0':
ch = *format++; /* set zero padding if no nonzero digits yet */
switch (ch) if (accum == 0 && !pointflag)
zpad = '0';
/* FALL THRU */
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
accum = accum * 10 + (ch - '0');
goto nextch2;
case '.':
if (have_star)
have_star = false;
else
fieldwidth = accum;
pointflag = 1;
accum = 0;
goto nextch2;
case '*':
if (have_dollar)
{ {
case '\0': /* process value after reading n$ */
goto performpr; afterstar = true;
case '-': }
leftjust = 1; else
goto nextch; {
case '+': /* fetch and process value now */
forcesign = 1; int starval = va_arg(args, int);
goto nextch;
case '0': /* set zero padding if minlen not set */ if (pointflag)
if (minlen == 0 && !pointflag) {
zpad = '0'; precision = starval;
case '1': if (precision < 0)
case '2': precision = 0;
case '3': }
case '4': else
case '5': {
case '6': fieldwidth = starval;
case '7': if (fieldwidth < 0)
case '8':
case '9':
if (!pointflag)
{
minlen = minlen * 10 + ch - '0';
position = position * 10 + ch - '0';
}
else
{ {
maxwidth = maxwidth * 10 + ch - '0'; leftjust = 1;
precision = precision * 10 + ch - '0'; fieldwidth = -fieldwidth;
} }
goto nextch; }
case '$': }
realpos = position; have_star = true;
minlen = 0; accum = 0;
goto nextch; goto nextch2;
case '*': case '$':
MemSet(&fmtpar[fmtpos], 0, sizeof(fmtpar[fmtpos])); if (afterstar)
if (!pointflag) {
fmtpar[fmtpos].func = FMTLEN; /* fetch and process star value */
else int starval = argvalues[accum].i;
fmtpar[fmtpos].func = FMTWIDTH;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos; if (pointflag)
fmtpos++; {
goto nextch; precision = starval;
case '.': if (precision < 0)
pointflag = 1; precision = 0;
goto nextch; }
case 'l': else
if (longflag) {
longlongflag = 1; fieldwidth = starval;
else if (fieldwidth < 0)
longflag = 1;
goto nextch;
case 'h':
/* ignore */
goto nextch;
#ifdef NOT_USED
/*
* We might export this to client apps so we should
* support 'qd' and 'I64d'(MinGW) also in case the
* native version does.
*/
case 'q':
longlongflag = 1;
longflag = 1;
goto nextch;
case 'I':
if (*format == '6' && *(format + 1) == '4')
{ {
format += 2; leftjust = 1;
longlongflag = 1; fieldwidth = -fieldwidth;
longflag = 1;
goto nextch;
} }
break; }
#endif afterstar = false;
case 'u':
case 'U':
fmtpar[fmtpos].longflag = longflag;
fmtpar[fmtpos].longlongflag = longlongflag;
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].base = 10;
fmtpar[fmtpos].dosign = 0;
fmtpar[fmtpos].forcesign = forcesign;
fmtpar[fmtpos].leftjust = leftjust;
fmtpar[fmtpos].minlen = minlen;
fmtpar[fmtpos].zpad = zpad;
fmtpar[fmtpos].func = FMTNUM_U;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case 'o':
case 'O':
fmtpar[fmtpos].longflag = longflag;
fmtpar[fmtpos].longlongflag = longlongflag;
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].base = 8;
fmtpar[fmtpos].dosign = 0;
fmtpar[fmtpos].forcesign = forcesign;
fmtpar[fmtpos].leftjust = leftjust;
fmtpar[fmtpos].minlen = minlen;
fmtpar[fmtpos].zpad = zpad;
fmtpar[fmtpos].func = FMTNUM_U;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case 'd':
case 'D':
fmtpar[fmtpos].longflag = longflag;
fmtpar[fmtpos].longlongflag = longlongflag;
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].base = 10;
fmtpar[fmtpos].dosign = 1;
fmtpar[fmtpos].forcesign = forcesign;
fmtpar[fmtpos].leftjust = leftjust;
fmtpar[fmtpos].minlen = minlen;
fmtpar[fmtpos].zpad = zpad;
fmtpar[fmtpos].func = FMTNUM;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case 'x':
fmtpar[fmtpos].longflag = longflag;
fmtpar[fmtpos].longlongflag = longlongflag;
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].base = 16;
fmtpar[fmtpos].dosign = 0;
fmtpar[fmtpos].forcesign = forcesign;
fmtpar[fmtpos].leftjust = leftjust;
fmtpar[fmtpos].minlen = minlen;
fmtpar[fmtpos].zpad = zpad;
fmtpar[fmtpos].func = FMTNUM_U;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case 'X':
fmtpar[fmtpos].longflag = longflag;
fmtpar[fmtpos].longlongflag = longlongflag;
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].base = -16;
fmtpar[fmtpos].dosign = 1;
fmtpar[fmtpos].forcesign = forcesign;
fmtpar[fmtpos].leftjust = leftjust;
fmtpar[fmtpos].minlen = minlen;
fmtpar[fmtpos].zpad = zpad;
fmtpar[fmtpos].func = FMTNUM_U;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case 's':
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].leftjust = leftjust;
fmtpar[fmtpos].minlen = minlen;
fmtpar[fmtpos].zpad = zpad;
fmtpar[fmtpos].maxwidth = maxwidth;
fmtpar[fmtpos].func = FMTSTR;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case 'c':
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].func = FMTCHAR;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
fmtpar[fmtpos].fmtbegin = fmtbegin;
fmtpar[fmtpos].fmtend = format;
fmtpar[fmtpos].type = ch;
fmtpar[fmtpos].forcesign = forcesign;
fmtpar[fmtpos].leftjust = leftjust;
fmtpar[fmtpos].minlen = minlen;
fmtpar[fmtpos].zpad = zpad;
fmtpar[fmtpos].precision = precision;
fmtpar[fmtpos].pointflag = pointflag;
fmtpar[fmtpos].func = FMTFLOAT;
fmtpar[fmtpos].realpos = realpos ? realpos : fmtpos;
fmtpos++;
break;
case '%':
break;
default:
dostr("???????", 0, end, &output);
} }
else
fmtpos = accum;
accum = 0;
goto nextch2;
case 'l':
if (longflag)
longlongflag = 1;
else
longflag = 1;
goto nextch2;
case 'h':
case '\'':
/* ignore these */
goto nextch2;
case 'd':
case 'i':
if (!have_star)
{
if (pointflag)
precision = accum;
else
fieldwidth = accum;
}
if (have_dollar)
{
if (longlongflag)
numvalue = argvalues[fmtpos].ll;
else if (longflag)
numvalue = argvalues[fmtpos].l;
else
numvalue = argvalues[fmtpos].i;
}
else
{
if (longlongflag)
numvalue = va_arg(args, int64);
else if (longflag)
numvalue = va_arg(args, long);
else
numvalue = va_arg(args, int);
}
fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
precision, pointflag, target);
break; break;
default: case 'o':
dopr_outch(ch, end, &output); case 'u':
break; case 'x':
} case 'X':
} if (!have_star)
{
performpr: if (pointflag)
/* reorder pointers */ precision = accum;
for (i = 1; i < fmtpos; i++) else
fmtparptr[i] = &fmtpar[fmtpar[i].realpos]; fieldwidth = accum;
}
/* assign values */ if (have_dollar)
for (i = 1; i < fmtpos; i++)
{
switch (fmtparptr[i]->func)
{
case FMTSTR:
fmtparptr[i]->value = va_arg(args, char *);
break;
case FMTNUM:
if (fmtparptr[i]->longflag)
{ {
if (fmtparptr[i]->longlongflag) if (longlongflag)
fmtparptr[i]->numvalue = va_arg(args, int64); numvalue = (uint64) argvalues[fmtpos].ll;
else if (longflag)
numvalue = (unsigned long) argvalues[fmtpos].l;
else else
fmtparptr[i]->numvalue = va_arg(args, long); numvalue = (unsigned int) argvalues[fmtpos].i;
} }
else else
fmtparptr[i]->numvalue = va_arg(args, int); {
if (longlongflag)
numvalue = (uint64) va_arg(args, int64);
else if (longflag)
numvalue = (unsigned long) va_arg(args, long);
else
numvalue = (unsigned int) va_arg(args, int);
}
fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
precision, pointflag, target);
break; break;
case FMTNUM_U: case 'c':
if (fmtparptr[i]->longflag) if (!have_star)
{ {
if (fmtparptr[i]->longlongflag) if (pointflag)
fmtparptr[i]->numvalue = va_arg(args, uint64); precision = accum;
else else
fmtparptr[i]->numvalue = va_arg(args, unsigned long); fieldwidth = accum;
} }
if (have_dollar)
cvalue = (unsigned char) argvalues[fmtpos].i;
else else
fmtparptr[i]->numvalue = va_arg(args, unsigned int); cvalue = (unsigned char) va_arg(args, int);
fmtchar(cvalue, leftjust, fieldwidth, target);
break; break;
case FMTFLOAT: case 's':
fmtparptr[i]->fvalue = va_arg(args, double); if (!have_star)
{
if (pointflag)
precision = accum;
else
fieldwidth = accum;
}
if (have_dollar)
strvalue = argvalues[fmtpos].cptr;
else
strvalue = va_arg(args, char *);
fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
target);
break; break;
case FMTCHAR: case 'p':
fmtparptr[i]->charvalue = va_arg(args, int); /* fieldwidth/leftjust are ignored ... */
if (have_dollar)
strvalue = argvalues[fmtpos].cptr;
else
strvalue = va_arg(args, char *);
fmtptr((void *) strvalue, target);
break; break;
case FMTLEN: case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (!have_star)
{ {
int minlen = va_arg(args, int); if (pointflag)
int leftjust = 0; precision = accum;
else
if (minlen < 0) fieldwidth = accum;
{
minlen = -minlen;
leftjust = 1;
}
if (i + 1 < fmtpos && fmtparptr[i + 1]->func != FMTWIDTH)
{
fmtparptr[i + 1]->minlen = minlen;
fmtparptr[i + 1]->leftjust |= leftjust;
}
/* For "%*.*f", use the second arg */
if (i + 2 < fmtpos && fmtparptr[i + 1]->func == FMTWIDTH)
{
fmtparptr[i + 2]->minlen = minlen;
fmtparptr[i + 2]->leftjust |= leftjust;
}
} }
if (have_dollar)
fvalue = argvalues[fmtpos].d;
else
fvalue = va_arg(args, double);
fmtfloat(fvalue, ch, forcesign, leftjust,
fieldwidth, zpad,
precision, pointflag,
target);
break; break;
case FMTWIDTH: case '%':
if (i + 1 < fmtpos) dopr_outch('%', target);
fmtparptr[i + 1]->maxwidth = fmtparptr[i + 1]->precision =
va_arg(args, int);
break; break;
} }
} }
/* do the output */ return 0;
output = buffer; }
format = format_save;
while ((ch = *format++)) static size_t
{ pg_strnlen(const char *str, size_t maxlen)
for (i = 1; i < fmtpos; i++) {
{ const char *p = str;
if (ch == '%' && *format == '%')
{
format++;
continue;
}
if (fmtpar[i].fmtbegin == format - 1)
{
switch (fmtparptr[i]->func)
{
case FMTSTR:
fmtstr(fmtparptr[i]->value, fmtparptr[i]->leftjust,
fmtparptr[i]->minlen, fmtparptr[i]->maxwidth,
end, &output);
break;
case FMTNUM:
case FMTNUM_U:
fmtint(fmtparptr[i]->numvalue, fmtparptr[i]->base,
fmtparptr[i]->dosign, fmtparptr[i]->forcesign,
fmtparptr[i]->leftjust, fmtparptr[i]->minlen,
fmtparptr[i]->zpad, end, &output);
break;
case FMTFLOAT:
fmtfloat(fmtparptr[i]->fvalue, fmtparptr[i]->type,
fmtparptr[i]->forcesign, fmtparptr[i]->leftjust,
fmtparptr[i]->minlen, fmtparptr[i]->zpad,
fmtparptr[i]->precision, fmtparptr[i]->pointflag,
end, &output);
break;
case FMTCHAR:
dopr_outch(fmtparptr[i]->charvalue, end, &output);
break;
}
format = fmtpar[i].fmtend;
goto nochar;
}
}
dopr_outch(ch, end, &output);
nochar:
/* nothing */
; /* semicolon required because a goto has to be
* attached to a statement */
}
*output = '\0';
free(fmtpar); while (maxlen-- > 0 && *p)
free(fmtparptr); p++;
return p - str;
} }
static void static void
fmtstr(char *value, int leftjust, int minlen, int maxwidth, char *end, fmtstr(char *value, int leftjust, int minlen, int maxwidth,
char **output) int pointflag, PrintfTarget *target)
{ {
int padlen, int padlen,
vallen; /* amount to pad */ vallen; /* amount to pad */
if (value == NULL) /*
value = "<NULL>"; * If a maxwidth (precision) is specified, we must not fetch more bytes
* than that.
vallen = strlen(value); */
if (maxwidth && vallen > maxwidth) if (pointflag)
vallen = maxwidth; vallen = pg_strnlen(value, maxwidth);
else
vallen = strlen(value);
adjust_padlen(minlen, vallen, leftjust, &padlen); adjust_padlen(minlen, vallen, leftjust, &padlen);
while (padlen > 0) while (padlen > 0)
{ {
dopr_outch(' ', end, output); dopr_outch(' ', target);
--padlen; --padlen;
} }
dostr(value, maxwidth, end, output);
trailing_pad(&padlen, end, output); dostr(value, vallen, target);
trailing_pad(&padlen, target);
} }
static void static void
fmtint(int64 value, int base, int dosign, int forcesign, int leftjust, fmtptr(void *value, PrintfTarget *target)
int minlen, int zpad, char *end, char **output)
{ {
int vallen;
char convert[64];
/* we rely on regular C library's sprintf to do the basic conversion */
vallen = sprintf(convert, "%p", value);
dostr(convert, vallen, target);
}
static void
fmtint(int64 value, char type, int forcesign, int leftjust,
int minlen, int zpad, int precision, int pointflag,
PrintfTarget *target)
{
uint64 base;
int dosign;
const char *cvt = "0123456789abcdef";
int signvalue = 0; int signvalue = 0;
char convert[64]; char convert[64];
int vallen = 0; int vallen = 0;
int padlen = 0; /* amount to pad */ int padlen = 0; /* amount to pad */
int caps = 0; int zeropad; /* extra leading zeroes */
/* Handle +/- and %X (uppercase hex) */ switch (type)
{
case 'd':
case 'i':
base = 10;
dosign = 1;
break;
case 'o':
base = 8;
dosign = 0;
break;
case 'u':
base = 10;
dosign = 0;
break;
case 'x':
base = 16;
dosign = 0;
break;
case 'X':
cvt = "0123456789ABCDEF";
base = 16;
dosign = 0;
break;
default:
return; /* keep compiler quiet */
}
/* Handle +/- */
if (dosign && adjust_sign((value < 0), forcesign, &signvalue)) if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
value = -value; value = -value;
if (base < 0)
/*
* SUS: the result of converting 0 with an explicit precision of 0 is no
* characters
*/
if (value == 0 && pointflag && precision == 0)
vallen = 0;
else
{ {
caps = 1; /* make integer string */
base = -base; uint64 uvalue = (uint64) value;
do
{
convert[vallen++] = cvt[uvalue % base];
uvalue = uvalue / base;
} while (uvalue);
} }
/* make integer string */ zeropad = Max(0, precision - vallen);
do
{
convert[vallen++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
[value % (unsigned) base];
value = (value / (unsigned) base);
} while (value);
convert[vallen] = 0;
adjust_padlen(minlen, vallen, leftjust, &padlen); adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen);
leading_pad(zpad, &signvalue, &padlen, target);
leading_pad(zpad, &signvalue, &padlen, end, output); while (zeropad-- > 0)
dopr_outch('0', target);
while (vallen > 0) while (vallen > 0)
dopr_outch(convert[--vallen], end, output); dopr_outch(convert[--vallen], target);
trailing_pad(&padlen, end, output); trailing_pad(&padlen, target);
}
static void
fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
{
int padlen = 0; /* amount to pad */
adjust_padlen(minlen, 1, leftjust, &padlen);
while (padlen > 0)
{
dopr_outch(' ', target);
--padlen;
}
dopr_outch(value, target);
trailing_pad(&padlen, target);
} }
static void static void
fmtfloat(double value, char type, int forcesign, int leftjust, fmtfloat(double value, char type, int forcesign, int leftjust,
int minlen, int zpad, int precision, int pointflag, char *end, int minlen, int zpad, int precision, int pointflag,
char **output) PrintfTarget *target)
{ {
int signvalue = 0; int signvalue = 0;
int vallen; int vallen;
...@@ -676,37 +900,51 @@ fmtfloat(double value, char type, int forcesign, int leftjust, ...@@ -676,37 +900,51 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
adjust_padlen(minlen, vallen, leftjust, &padlen); adjust_padlen(minlen, vallen, leftjust, &padlen);
leading_pad(zpad, &signvalue, &padlen, end, output); leading_pad(zpad, &signvalue, &padlen, target);
dostr(convert, 0, end, output); dostr(convert, vallen, target);
trailing_pad(&padlen, end, output); trailing_pad(&padlen, target);
} }
static void static void
dostr(char *str, int cut, char *end, char **output) dostr(const char *str, int slen, PrintfTarget *target)
{ {
if (cut) while (slen > 0)
while (*str && cut-- > 0) {
dopr_outch(*str++, end, output); int avail;
else
while (*str) if (target->bufend != NULL)
dopr_outch(*str++, end, output); avail = target->bufend - target->bufptr;
else
avail = slen;
if (avail <= 0)
{
/* buffer full, can we dump to stream? */
if (target->stream == NULL)
return; /* no, lose the data */
flushbuffer(target);
continue;
}
avail = Min(avail, slen);
memmove(target->bufptr, str, avail);
target->bufptr += avail;
str += avail;
slen -= avail;
}
} }
static void static void
dopr_outch(int c, char *end, char **output) dopr_outch(int c, PrintfTarget *target)
{ {
#ifdef NOT_USED if (target->bufend != NULL && target->bufptr >= target->bufend)
if (iscntrl((unsigned char) c) && c != '\n' && c != '\t')
{ {
c = '@' + (c & 0x1F); /* buffer full, can we dump to stream? */
if (end == 0 || *output < end) if (target->stream == NULL)
*(*output)++ = '^'; return; /* no, lose the data */
flushbuffer(target);
} }
#endif *(target->bufptr++) = c;
if (end == 0 || *output < end)
*(*output)++ = c;
} }
...@@ -731,49 +969,49 @@ adjust_padlen(int minlen, int vallen, int leftjust, int *padlen) ...@@ -731,49 +969,49 @@ adjust_padlen(int minlen, int vallen, int leftjust, int *padlen)
if (*padlen < 0) if (*padlen < 0)
*padlen = 0; *padlen = 0;
if (leftjust) if (leftjust)
*padlen = -*padlen; *padlen = -(*padlen);
} }
static void static void
leading_pad(int zpad, int *signvalue, int *padlen, char *end, char **output) leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target)
{ {
if (*padlen > 0 && zpad) if (*padlen > 0 && zpad)
{ {
if (*signvalue) if (*signvalue)
{ {
dopr_outch(*signvalue, end, output); dopr_outch(*signvalue, target);
--*padlen; --(*padlen);
*signvalue = 0; *signvalue = 0;
} }
while (*padlen > 0) while (*padlen > 0)
{ {
dopr_outch(zpad, end, output); dopr_outch(zpad, target);
--*padlen; --(*padlen);
} }
} }
while (*padlen > 0 + (*signvalue != 0)) while (*padlen > (*signvalue != 0))
{ {
dopr_outch(' ', end, output); dopr_outch(' ', target);
--*padlen; --(*padlen);
} }
if (*signvalue) if (*signvalue)
{ {
dopr_outch(*signvalue, end, output); dopr_outch(*signvalue, target);
if (*padlen > 0) if (*padlen > 0)
--* padlen; --(*padlen);
if (padlen < 0) else if (*padlen < 0)
++padlen; ++(*padlen);
} }
} }
static void static void
trailing_pad(int *padlen, char *end, char **output) trailing_pad(int *padlen, PrintfTarget *target)
{ {
while (*padlen < 0) while (*padlen < 0)
{ {
dopr_outch(' ', end, output); dopr_outch(' ', target);
++*padlen; ++(*padlen);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment