* Copyright (c) 1983, 1995, 1996 Eric P. Allman
* Copyright (c) 1988, 1993
* The Regents of the University of California. All rights reserved.
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* 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
* SUCH DAMAGE.
*
* src/port/snprintf.c
*/
#include "c.h"
#include <limits.h>
#include <math.h>
#include <utils/elog.h>
#ifndef WIN32
#include <sys/ioctl.h>
#endif
#include <sys/param.h>
#ifdef WIN32
#include <float.h>
#endif
* We used to use the platform's NL_ARGMAX here, but that's a bad idea,
* first because the point of this module is to remove platform dependencies
* not perpetuate them, and second because some platforms use ridiculously
* large values, leading to excessive stack consumption in dopr().
*/
#define PG_NL_ARGMAX 31
* SNPRINTF, VSNPRINTF and friends
*
* These versions have been grabbed off the net. They have been
* cleaned up to compile properly and support for most of the C99
* 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.
*
*
* Historically the result values of sprintf/snprintf varied across platforms.
* This implementation now follows the C99 standard:
*
* 1. -1 is returned if an error is detected in the format string, or if
* a write to the target stream fails (as reported by fwrite). Note that
* overrunning snprintf's target buffer is *not* an error.
*
* 2. For successful writes to streams, the actual number of bytes written
* to the stream is returned.
*
* 3. For successful sprintf/snprintf, the number of bytes that would have
* been written to an infinite-size buffer (excluding the trailing '\0')
* is returned. snprintf will truncate its output to fit in the buffer
* (ensuring a trailing '\0' unless count == 0), but this is not reflected
* in the function result.
*
* snprintf buffer overrun can be detected by checking for function result
* greater than or equal to the supplied count.
*/
* Original:
* Patrick Powell Tue Apr 11 09:48:21 PDT 1995
* A bombproof version of doprnt (dopr) included.
* 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)
**************************************************************/
#undef vfprintf
#undef fprintf
#undef printf
* Info about where the formatted output is going.
*
* dopr and subroutines will not write at/past bufend, but snprintf
* reserves one byte, ensuring it may place the trailing '\0' there.
*
* In snprintf, we use nchars to count the number of bytes dropped on the
* floor due to buffer overrun. The correct result of snprintf is thus
* (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
* seem: nchars is the number of emitted bytes that are not in the buffer now,
* either because we sent them to the stream or because we couldn't fit them
* into the buffer to begin with.)
*/
typedef struct {
char* bufptr;
char* bufstart;
char* bufend;
FILE* stream;
int nchars;
} 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);
void pg_do_point_flag(int* pointflag, int starval, int* precision, int* fieldwidth, int* leftjust)
{
if (*pointflag) {
*precision = starval;
if (*precision < 0) {
*precision = 0;
*pointflag = 0;
}
} else {
*fieldwidth = starval;
if (*fieldwidth < 0) {
*leftjust = 1;
*fieldwidth = -(*fieldwidth);
}
}
}
int pg_vsprintf_inner(char* str, size_t count, const char* fmt, va_list args, char action)
{
PrintfTarget target;
if (action == 'n') {
char onebyte[1];
* C99 allows the case str == NULL when count == 0. Rather than
* special-casing this situation further down, we substitute a one-byte
* local buffer. Callers cannot tell, since the function result doesn't
* depend on count.
*/
if (count == 0) {
str = onebyte;
count = 1;
}
target.bufstart = target.bufptr = str;
target.bufend = str + count - 1;
} else {
target.bufstart = target.bufptr = str;
target.bufend = NULL;
}
target.stream = NULL;
target.nchars = 0;
if (dopr(&target, fmt, args)) {
*(target.bufptr) = '\0';
errno = EINVAL;
return -1;
}
*(target.bufptr) = '\0';
return target.bufptr - target.bufstart + target.nchars;
}
int pg_vsnprintf(char* str, size_t count, const char* fmt, va_list args)
{
return pg_vsprintf_inner(str, count, fmt, args, 'n');
}
int pg_snprintf(char* str, size_t count, const char* fmt, ...)
{
int len;
va_list args;
va_start(args, fmt);
len = pg_vsnprintf(str, count, fmt, args);
va_end(args);
return len;
}
static int pg_vsprintf(char* str, const char* fmt, va_list args)
{
return pg_vsprintf_inner(str, 0, fmt, args, 's');
}
int pg_sprintf(char* str, const char* fmt, ...)
{
int len;
va_list args;
va_start(args, fmt);
len = pg_vsprintf(str, fmt, args);
va_end(args);
return len;
}
int pg_vfprintf(FILE* stream, const char* fmt, va_list args)
{
PrintfTarget target;
char buffer[1024];
if (stream == NULL) {
errno = EINVAL;
return -1;
}
target.bufstart = target.bufptr = buffer;
target.bufend = buffer + sizeof(buffer);
target.stream = stream;
target.nchars = 0;
if (dopr(&target, fmt, args)) {
errno = EINVAL;
return -1;
}
flushbuffer(&target);
return target.nchars;
}
int pg_fprintf(FILE* stream, const char* fmt, ...)
{
int len;
va_list args;
va_start(args, fmt);
len = pg_vfprintf(stream, fmt, args);
va_end(args);
return len;
}
int pg_printf(const char* fmt, ...)
{
int len;
va_list args;
va_start(args, fmt);
len = pg_vfprintf(stdout, fmt, args);
va_end(args);
return len;
}
static void flushbuffer(PrintfTarget* target)
{
size_t nc = target->bufptr - target->bufstart;
* Don't write anything if we already failed; this is to ensure we
* preserve the original failure's errno.
*/
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, int pointflag, PrintfTarget* target);
static void fmtptr(void* value, PrintfTarget* target);
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, int leftjust, int minlen, int zpad, int precision,
int pointflag, PrintfTarget* target);
static void dostr(const char* str, int slen, PrintfTarget* target);
static void dopr_outch(int c, PrintfTarget* target);
static int adjust_sign(int is_negative, int forcesign, int* signvalue);
static void adjust_padlen(int minlen, int vallen, int leftjust, int* padlen);
static void leading_pad(int zpad, int* signvalue, int* padlen, PrintfTarget* target);
static void trailing_pad(int* padlen, PrintfTarget* target);
* dopr(): poor man's version of doprintf
*/
static int dopr(PrintfTarget* target, const char* format, va_list args)
{
const char* format_start = format;
int ch;
bool have_dollar = false;
bool have_non_dollar = false;
bool have_star = false;
bool afterstar = false;
int accum;
int longlongflag;
int longflag;
int pointflag;
int leftjust;
int fieldwidth;
int precision;
int zpad;
int forcesign;
int last_dollar;
int fmtpos;
int cvalue;
int64 numvalue;
double fvalue;
char* strvalue = NULL;
int i;
PrintfArgType argtypes[PG_NL_ARGMAX + 1];
PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
* Parse the format string to determine whether there are %n$ format
* specs, and identify the types and order of the format parameters.
*/
have_dollar = have_non_dollar = false;
last_dollar = 0;
errno_t rc = memset_s(argtypes, sizeof(argtypes), 0, sizeof(argtypes));
securec_check(rc, "\0", "\0");
while ((ch = *format++) != '\0') {
if (ch != '%') {
continue;
}
longflag = longlongflag = pointflag = 0;
fmtpos = accum = 0;
afterstar = false;
nextch1:
ch = *format++;
if (ch == '\0') {
break;
}
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;
}
afterstar = true;
accum = 0;
goto nextch1;
case '$':
have_dollar = true;
if (accum <= 0 || accum > PG_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 '\'':
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;
default:
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 (have_dollar && have_non_dollar) {
return -1;
}
* In dollar mode, collect the arguments in physical order.
*/
for (i = 1; i <= last_dollar; i++) {
switch (argtypes[i]) {
case ATYPE_NONE:
return -1;
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;
default:
break;
}
}
* At last we can parse the format for real.
*/
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;
}
switch (ch) {
case '-':
leftjust = 1;
goto nextch2;
case '+':
forcesign = 1;
goto nextch2;
case '0':
if (accum == 0 && !pointflag) {
zpad = '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 nextch2;
case '.':
if (have_star) {
have_star = false;
} else {
fieldwidth = accum;
}
pointflag = 1;
accum = 0;
goto nextch2;
case '*':
if (have_dollar) {
afterstar = true;
} else {
int starval = va_arg(args, int);
pg_do_point_flag(&pointflag, starval, &precision, &fieldwidth, &leftjust);
}
have_star = true;
accum = 0;
goto nextch2;
case '$':
if (afterstar) {
int starval = argvalues[accum].i;
pg_do_point_flag(&pointflag, starval, &precision, &fieldwidth, &leftjust);
afterstar = false;
} else {
fmtpos = accum;
}
accum = 0;
goto nextch2;
case 'l':
if (longflag) {
longlongflag = 1;
} else {
longflag = 1;
}
goto nextch2;
case 'h':
case '\'':
goto nextch2;
case 'd':
case 'i':
if (!have_star) {
pointflag ? (precision = accum) : (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;
case 'o':
case 'u':
case 'x':
case 'X':
if (!have_star) {
pointflag ? (precision = accum) : (fieldwidth = accum);
}
if (have_dollar) {
if (longlongflag) {
numvalue = (uint64)argvalues[fmtpos].ll;
} else if (longflag) {
numvalue = (unsigned long)argvalues[fmtpos].l;
} else {
numvalue = (unsigned int)argvalues[fmtpos].i;
}
} else {
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;
case 'c':
if (!have_star) {
if (pointflag) {
precision = accum;
} else {
fieldwidth = accum;
}
}
if (have_dollar) {
cvalue = (unsigned char)argvalues[fmtpos].i;
} else {
cvalue = (unsigned char)va_arg(args, int);
}
fmtchar(cvalue, leftjust, fieldwidth, target);
break;
case 's':
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;
case 'p':
if (have_dollar) {
strvalue = argvalues[fmtpos].cptr;
} else {
strvalue = va_arg(args, char*);
}
fmtptr((void*)strvalue, target);
break;
case 'e':
case 'E':
case 'f':
case 'g':
case 'G':
if (!have_star) {
if (pointflag) {
precision = accum;
} else {
fieldwidth = accum;
}
}
if (have_dollar) {
fvalue = argvalues[fmtpos].d;
} else {
fvalue = va_arg(args, double);
}
fmtfloat(fvalue, ch, forcesign, leftjust, fieldwidth, zpad, precision, pointflag, target);
break;
case '%':
dopr_outch('%', target);
break;
default:
break;
}
}
return 0;
}
static size_t pg_strnlen(const char* str, size_t maxlen)
{
const char* p = str;
while (maxlen-- > 0 && *p) {
p++;
}
return p - str;
}
static void fmtstr(char* value, int leftjust, int minlen, int maxwidth, int pointflag, PrintfTarget* target)
{
int padlen, vallen;
* If a maxwidth (precision) is specified, we must not fetch more bytes
* than that.
*/
if (pointflag) {
vallen = pg_strnlen(value, maxwidth);
} else {
vallen = strlen(value);
}
adjust_padlen(minlen, vallen, leftjust, &padlen);
while (padlen > 0) {
dopr_outch(' ', target);
--padlen;
}
dostr(value, vallen, target);
trailing_pad(&padlen, target);
}
static void fmtptr(void* value, PrintfTarget* target)
{
int vallen;
char convert[64];
vallen = sprintf_s(convert, sizeof(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;
char convert[64];
int vallen = 0;
int padlen = 0;
int zeropad;
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;
}
if (dosign && adjust_sign((value < 0), forcesign, &signvalue)) {
value = -value;
}
* SUS: the result of converting 0 with an explicit precision of 0 is no
* characters
*/
if (value == 0 && pointflag && precision == 0) {
vallen = 0;
} else {
uint64 uvalue = (uint64)value;
do {
convert[vallen++] = cvt[uvalue % base];
uvalue = uvalue / base;
} while (uvalue);
}
zeropad = Max(0, precision - vallen);
adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen);
leading_pad(zpad, &signvalue, &padlen, target);
while (zeropad-- > 0) {
dopr_outch('0', target);
}
while (vallen > 0) {
dopr_outch(convert[--vallen], target);
}
trailing_pad(&padlen, target);
}
static void fmtchar(int value, int leftjust, int minlen, PrintfTarget* target)
{
int padlen = 0;
adjust_padlen(minlen, 1, leftjust, &padlen);
while (padlen > 0) {
dopr_outch(' ', target);
--padlen;
}
dopr_outch(value, target);
trailing_pad(&padlen, target);
}
static void fmtfloat(double value, char type, int forcesign, int leftjust, int minlen, int zpad, int precision,
int pointflag, PrintfTarget* target)
{
int signvalue = 0;
int prec;
int vallen;
char fmt[32];
char convert[1024];
int zeropadlen = 0;
int padlen = 0;
int nRet = 0;
* We rely on the regular C library's sprintf to do the basic conversion,
* then handle padding considerations here.
*
* The dynamic range of "double" is about 1E+-308 for IEEE math, and not
* too wildly more than that with other hardware. In "f" format, sprintf
* could therefore generate at most 308 characters to the left of the
* decimal point; while we need to allow the precision to get as high as
* 308+17 to ensure that we don't truncate significant digits from very
* small values. To handle both these extremes, we use a buffer of 1024
* bytes and limit requested precision to 350 digits; this should prevent
* buffer overrun even with non-IEEE math. If the original precision
* request was more than 350, separately pad with zeroes.
*/
if (precision < 0) {
precision = 0;
}
prec = Min(precision, 350);
if (pointflag) {
nRet = sprintf_s(fmt, sizeof(fmt), "%%.%d%c", prec, type);
securec_check_c(nRet, "\0", "\0");
zeropadlen = precision - prec;
} else {
nRet = sprintf_s(fmt, sizeof(fmt), "%%%c", type);
securec_check_c(nRet, "\0", "\0");
}
if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) {
value = -value;
}
vallen = sprintf_s(convert, sizeof(convert), fmt, value);
if (zeropadlen > 0 && !isdigit((unsigned char)convert[vallen - 1])) {
zeropadlen = 0;
}
adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen);
leading_pad(zpad, &signvalue, &padlen, target);
if (zeropadlen > 0) {
char* epos = strrchr(convert, 'e');
if (epos == NULL) {
epos = strrchr(convert, 'E');
}
if (epos != NULL) {
dostr(convert, epos - convert, target);
while (zeropadlen-- > 0) {
dopr_outch('0', target);
}
dostr(epos, vallen - (epos - convert), target);
} else {
dostr(convert, vallen, target);
while (zeropadlen-- > 0) {
dopr_outch('0', target);
}
}
} else {
dostr(convert, vallen, target);
}
trailing_pad(&padlen, target);
}
static void dostr(const char* str, int slen, PrintfTarget* target)
{
while (slen > 0) {
int avail;
if (target->bufend != NULL) {
avail = target->bufend - target->bufptr;
} else {
avail = slen;
}
if (avail <= 0) {
if (target->stream == NULL) {
target->nchars += slen;
return;
}
flushbuffer(target);
continue;
}
avail = Min(avail, slen);
errno_t rc = memmove_s(target->bufptr, avail, str, avail);
securec_check_c(rc, "\0", "\0");
target->bufptr += avail;
str += avail;
slen -= avail;
}
}
static void dopr_outch(int c, PrintfTarget* target)
{
if (target->bufend != NULL && target->bufptr >= target->bufend) {
if (target->stream == NULL) {
target->nchars++;
return;
}
flushbuffer(target);
}
*(target->bufptr++) = c;
}
static int adjust_sign(int is_negative, int forcesign, int* signvalue)
{
if (is_negative) {
*signvalue = '-';
return true;
} else if (forcesign) {
*signvalue = '+';
}
return false;
}
static void adjust_padlen(int minlen, int vallen, int leftjust, int* padlen)
{
*padlen = minlen - vallen;
if (*padlen < 0) {
*padlen = 0;
}
if (leftjust) {
*padlen = -(*padlen);
}
}
static void leading_pad(int zpad, int* signvalue, int* padlen, PrintfTarget* target)
{
if (*padlen > 0 && zpad) {
if (*signvalue) {
dopr_outch(*signvalue, target);
--(*padlen);
*signvalue = 0;
}
while (*padlen > 0) {
dopr_outch(zpad, target);
--(*padlen);
}
}
while (*padlen > (*signvalue != 0)) {
dopr_outch(' ', target);
--(*padlen);
}
if (*signvalue) {
dopr_outch(*signvalue, target);
if (*padlen > 0) {
--(*padlen);
} else if (*padlen < 0) {
++(*padlen);
}
}
}
static void trailing_pad(int* padlen, PrintfTarget* target)
{
while (*padlen < 0) {
dopr_outch(' ', target);
++(*padlen);
}
}