2 * Copyright (c) 1983, 1995, 1996 Eric P. Allman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 * Portions Copyright (c) 1996-2021, PostgreSQL Global Development Group
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * We used to use the platform's NL_ARGMAX here, but that's a bad idea,
40 * first because the point of this module is to remove platform dependencies
41 * not perpetuate them, and second because some platforms use ridiculously
42 * large values, leading to excessive stack consumption in dopr().
44 #define PG_NL_ARGMAX 31
48 * SNPRINTF, VSNPRINTF and friends
50 * These versions have been grabbed off the net. They have been
51 * cleaned up to compile properly and support for most of the C99
52 * specification has been added. Remaining unimplemented features are:
54 * 1. No locale support: the radix character is always '.' and the '
55 * (single quote) format flag is ignored.
57 * 2. No support for the "%n" format specification.
59 * 3. No support for wide characters ("lc" and "ls" formats).
61 * 4. No support for "long double" ("Lf" and related formats).
63 * 5. Space and '#' flags are not implemented.
65 * In addition, we support some extensions over C99:
67 * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
69 * 2. "%m" expands to the value of strerror(errno), where errno is the
70 * value that variable had at the start of the call. This is a glibc
71 * extension, but a very useful one.
74 * Historically the result values of sprintf/snprintf varied across platforms.
75 * This implementation now follows the C99 standard:
77 * 1. -1 is returned if an error is detected in the format string, or if
78 * a write to the target stream fails (as reported by fwrite). Note that
79 * overrunning snprintf's target buffer is *not* an error.
81 * 2. For successful writes to streams, the actual number of bytes written
82 * to the stream is returned.
84 * 3. For successful sprintf/snprintf, the number of bytes that would have
85 * been written to an infinite-size buffer (excluding the trailing '\0')
86 * is returned. snprintf will truncate its output to fit in the buffer
87 * (ensuring a trailing '\0' unless count == 0), but this is not reflected
88 * in the function result.
90 * snprintf buffer overrun can be detected by checking for function result
91 * greater than or equal to the supplied count.
94 /**************************************************************
96 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
97 * A bombproof version of doprnt (dopr) included.
98 * Sigh. This sort of thing is always nasty do deal with. Note that
99 * the version here does not include floating point. (now it does ... tgl)
100 **************************************************************/
102 /* Prevent recursion */
113 * Info about where the formatted output is going.
115 * dopr and subroutines will not write at/past bufend, but snprintf
116 * reserves one byte, ensuring it may place the trailing '\0' there.
118 * In snprintf, we use nchars to count the number of bytes dropped on the
119 * floor due to buffer overrun. The correct result of snprintf is thus
120 * (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
121 * seem: nchars is the number of emitted bytes that are not in the buffer now,
122 * either because we sent them to the stream or because we couldn't fit them
123 * into the buffer to begin with.)
127 char *bufptr
; /* next buffer output position */
128 char *bufstart
; /* first buffer element */
129 char *bufend
; /* last+1 buffer element, or NULL */
130 /* bufend == NULL is for sprintf, where we assume buf is big enough */
131 FILE *stream
; /* eventual output destination, or NULL */
132 int nchars
; /* # chars sent to stream, or dropped */
133 bool failed
; /* call is a failure; errno is set */
137 * Info about the type and value of a formatting parameter. Note that we
138 * don't currently support "long double", "wint_t", or "wchar_t *" data,
139 * nor the '%n' formatting code; else we'd need more types. Also, at this
140 * level we need not worry about signed vs unsigned values.
162 static void flushbuffer(PrintfTarget
*target
);
163 static void dopr(PrintfTarget
*target
, const char *format
, va_list args
);
167 * Externally visible entry points.
169 * All of these are just wrappers around dopr(). Note it's essential that
170 * they not change the value of "errno" before reaching dopr().
174 pg_vsnprintf(char *str
, size_t count
, const char *fmt
, va_list args
)
180 * C99 allows the case str == NULL when count == 0. Rather than
181 * special-casing this situation further down, we substitute a one-byte
182 * local buffer. Callers cannot tell, since the function result doesn't
190 target
.bufstart
= target
.bufptr
= str
;
191 target
.bufend
= str
+ count
- 1;
192 target
.stream
= NULL
;
194 target
.failed
= false;
195 dopr(&target
, fmt
, args
);
196 *(target
.bufptr
) = '\0';
197 return target
.failed
? -1 : (target
.bufptr
- target
.bufstart
202 pg_snprintf(char *str
, size_t count
, const char *fmt
,...)
208 len
= pg_vsnprintf(str
, count
, fmt
, args
);
214 pg_vsprintf(char *str
, const char *fmt
, va_list args
)
218 target
.bufstart
= target
.bufptr
= str
;
219 target
.bufend
= NULL
;
220 target
.stream
= NULL
;
221 target
.nchars
= 0; /* not really used in this case */
222 target
.failed
= false;
223 dopr(&target
, fmt
, args
);
224 *(target
.bufptr
) = '\0';
225 return target
.failed
? -1 : (target
.bufptr
- target
.bufstart
230 pg_sprintf(char *str
, const char *fmt
,...)
236 len
= pg_vsprintf(str
, fmt
, args
);
242 pg_vfprintf(FILE *stream
, const char *fmt
, va_list args
)
245 char buffer
[1024]; /* size is arbitrary */
252 target
.bufstart
= target
.bufptr
= buffer
;
253 target
.bufend
= buffer
+ sizeof(buffer
); /* use the whole buffer */
254 target
.stream
= stream
;
256 target
.failed
= false;
257 dopr(&target
, fmt
, args
);
258 /* dump any remaining buffer contents */
259 flushbuffer(&target
);
260 return target
.failed
? -1 : target
.nchars
;
264 pg_fprintf(FILE *stream
, const char *fmt
,...)
270 len
= pg_vfprintf(stream
, fmt
, args
);
276 pg_vprintf(const char *fmt
, va_list args
)
278 return pg_vfprintf(stdout
, fmt
, args
);
282 pg_printf(const char *fmt
,...)
288 len
= pg_vfprintf(stdout
, fmt
, args
);
294 * Attempt to write the entire buffer to target->stream; discard the entire
295 * buffer in any case. Call this only when target->stream is defined.
298 flushbuffer(PrintfTarget
*target
)
300 size_t nc
= target
->bufptr
- target
->bufstart
;
303 * Don't write anything if we already failed; this is to ensure we
304 * preserve the original failure's errno.
306 if (!target
->failed
&& nc
> 0)
310 written
= fwrite(target
->bufstart
, 1, nc
, target
->stream
);
311 target
->nchars
+= written
;
313 target
->failed
= true;
315 target
->bufptr
= target
->bufstart
;
319 static bool find_arguments(const char *format
, va_list args
,
320 PrintfArgValue
*argvalues
);
321 static void fmtstr(const char *value
, int leftjust
, int minlen
, int maxwidth
,
322 int pointflag
, PrintfTarget
*target
);
323 static void fmtptr(const void *value
, PrintfTarget
*target
);
324 static void fmtint(long long value
, char type
, int forcesign
,
325 int leftjust
, int minlen
, int zpad
, int precision
, int pointflag
,
326 PrintfTarget
*target
);
327 static void fmtchar(int value
, int leftjust
, int minlen
, PrintfTarget
*target
);
328 static void fmtfloat(double value
, char type
, int forcesign
,
329 int leftjust
, int minlen
, int zpad
, int precision
, int pointflag
,
330 PrintfTarget
*target
);
331 static void dostr(const char *str
, int slen
, PrintfTarget
*target
);
332 static void dopr_outch(int c
, PrintfTarget
*target
);
333 static void dopr_outchmulti(int c
, int slen
, PrintfTarget
*target
);
334 static int adjust_sign(int is_negative
, int forcesign
, int *signvalue
);
335 static int compute_padlen(int minlen
, int vallen
, int leftjust
);
336 static void leading_pad(int zpad
, int signvalue
, int *padlen
,
337 PrintfTarget
*target
);
338 static void trailing_pad(int padlen
, PrintfTarget
*target
);
341 * If strchrnul exists (it's a glibc-ism), it's a good bit faster than the
342 * equivalent manual loop. If it doesn't exist, provide a replacement.
344 * Note: glibc declares this as returning "char *", but that would require
345 * casting away const internally, so we don't follow that detail.
347 #ifndef HAVE_STRCHRNUL
349 static inline const char *
350 strchrnul(const char *s
, int c
)
352 while (*s
!= '\0' && *s
!= c
)
360 * glibc's <string.h> declares strchrnul only if _GNU_SOURCE is defined.
361 * While we typically use that on glibc platforms, configure will set
362 * HAVE_STRCHRNUL whether it's used or not. Fill in the missing declaration
363 * so that this file will compile cleanly with or without _GNU_SOURCE.
366 extern char *strchrnul(const char *s
, int c
);
369 #endif /* HAVE_STRCHRNUL */
373 * dopr(): the guts of *printf for all cases.
376 dopr(PrintfTarget
*target
, const char *format
, va_list args
)
378 int save_errno
= errno
;
379 const char *first_pct
= NULL
;
397 const char *strvalue
;
398 PrintfArgValue argvalues
[PG_NL_ARGMAX
+ 1];
401 * Initially, we suppose the format string does not use %n$. The first
402 * time we come to a conversion spec that has that, we'll call
403 * find_arguments() to check for consistent use of %n$ and fill the
404 * argvalues array with the argument values in the correct order.
408 while (*format
!= '\0')
410 /* Locate next conversion specifier */
413 /* Scan to next '%' or end of string */
414 const char *next_pct
= strchrnul(format
+ 1, '%');
416 /* Dump literal data we just scanned over */
417 dostr(format
, next_pct
- format
, target
);
421 if (*next_pct
== '\0')
427 * Remember start of first conversion spec; if we find %n$, then it's
428 * sufficient for find_arguments() to start here, without rescanning
429 * earlier literal text.
431 if (first_pct
== NULL
)
434 /* Process conversion spec starting at *format */
437 /* Fast path for conversion spec that is exactly %s */
441 strvalue
= va_arg(args
, char *);
442 if (strvalue
== NULL
)
444 dostr(strvalue
, strlen(strvalue
), target
);
450 fieldwidth
= precision
= zpad
= leftjust
= forcesign
= 0;
451 longflag
= longlongflag
= pointflag
= 0;
453 have_star
= afterstar
= false;
465 /* set zero padding if no nonzero digits yet */
466 if (accum
== 0 && !pointflag
)
478 accum
= accum
* 10 + (ch
- '0');
492 * We'll process value after reading n$. Note it's OK to
493 * assume have_dollar is set correctly, because in a valid
494 * format string the initial % must have had n$ if * does.
500 /* fetch and process value now */
501 int starval
= va_arg(args
, int);
514 fieldwidth
= starval
;
518 fieldwidth
= -fieldwidth
;
526 /* First dollar sign? */
529 /* Yup, so examine all conversion specs in format */
530 if (!find_arguments(first_pct
, args
, argvalues
))
536 /* fetch and process star value */
537 int starval
= argvalues
[accum
].i
;
550 fieldwidth
= starval
;
554 fieldwidth
= -fieldwidth
;
570 #if SIZEOF_SIZE_T == 8
571 #ifdef HAVE_LONG_INT_64
573 #elif defined(HAVE_LONG_LONG_INT_64)
576 #error "Don't know how to print 64bit integers"
579 /* assume size_t is same size as int */
598 numvalue
= argvalues
[fmtpos
].ll
;
600 numvalue
= argvalues
[fmtpos
].l
;
602 numvalue
= argvalues
[fmtpos
].i
;
607 numvalue
= va_arg(args
, long long);
609 numvalue
= va_arg(args
, long);
611 numvalue
= va_arg(args
, int);
613 fmtint(numvalue
, ch
, forcesign
, leftjust
, fieldwidth
, zpad
,
614 precision
, pointflag
, target
);
630 numvalue
= (unsigned long long) argvalues
[fmtpos
].ll
;
632 numvalue
= (unsigned long) argvalues
[fmtpos
].l
;
634 numvalue
= (unsigned int) argvalues
[fmtpos
].i
;
639 numvalue
= (unsigned long long) va_arg(args
, long long);
641 numvalue
= (unsigned long) va_arg(args
, long);
643 numvalue
= (unsigned int) va_arg(args
, int);
645 fmtint(numvalue
, ch
, forcesign
, leftjust
, fieldwidth
, zpad
,
646 precision
, pointflag
, target
);
657 cvalue
= (unsigned char) argvalues
[fmtpos
].i
;
659 cvalue
= (unsigned char) va_arg(args
, int);
660 fmtchar(cvalue
, leftjust
, fieldwidth
, target
);
671 strvalue
= argvalues
[fmtpos
].cptr
;
673 strvalue
= va_arg(args
, char *);
674 /* If string is NULL, silently substitute "(null)" */
675 if (strvalue
== NULL
)
677 fmtstr(strvalue
, leftjust
, fieldwidth
, precision
, pointflag
,
681 /* fieldwidth/leftjust are ignored ... */
683 strvalue
= argvalues
[fmtpos
].cptr
;
685 strvalue
= va_arg(args
, char *);
686 fmtptr((const void *) strvalue
, target
);
701 fvalue
= argvalues
[fmtpos
].d
;
703 fvalue
= va_arg(args
, double);
704 fmtfloat(fvalue
, ch
, forcesign
, leftjust
,
706 precision
, pointflag
,
711 char errbuf
[PG_STRERROR_R_BUFLEN
];
712 const char *errm
= strerror_r(save_errno
,
713 errbuf
, sizeof(errbuf
));
715 dostr(errm
, strlen(errm
), target
);
719 dopr_outch('%', target
);
724 * Anything else --- in particular, '\0' indicating end of
725 * format string --- is bogus.
730 /* Check for failure after each conversion spec */
739 target
->failed
= true;
743 * find_arguments(): sort out the arguments for a format spec with %n$
745 * If format is valid, return true and fill argvalues[i] with the value
746 * for the conversion spec that has %i$ or *i$. Else return false.
749 find_arguments(const char *format
, va_list args
,
750 PrintfArgValue
*argvalues
)
760 PrintfArgType argtypes
[PG_NL_ARGMAX
+ 1];
762 /* Initialize to "no dollar arguments known" */
764 MemSet(argtypes
, 0, sizeof(argtypes
));
767 * This loop must accept the same format strings as the one in dopr().
768 * However, we don't need to analyze them to the same level of detail.
770 * Since we're only called if there's a dollar-type spec somewhere, we can
771 * fail immediately if we find a non-dollar spec. Per the C99 standard,
772 * all argument references in the format string must be one or the other.
774 while (*format
!= '\0')
776 /* Locate next conversion specifier */
779 /* Unlike dopr, we can just quit if there's no more specifiers */
780 format
= strchr(format
+ 1, '%');
785 /* Process conversion spec starting at *format */
787 longflag
= longlongflag
= 0;
807 accum
= accum
* 10 + (ch
- '0');
814 return false; /* previous star missing dollar */
819 if (accum
<= 0 || accum
> PG_NL_ARGMAX
)
823 if (argtypes
[accum
] &&
824 argtypes
[accum
] != ATYPE_INT
)
826 argtypes
[accum
] = ATYPE_INT
;
827 last_dollar
= Max(last_dollar
, accum
);
841 #if SIZEOF_SIZE_T == 8
842 #ifdef HAVE_LONG_INT_64
844 #elif defined(HAVE_LONG_LONG_INT_64)
847 #error "Don't know how to print 64bit integers"
850 /* assume size_t is same size as int */
868 atype
= ATYPE_LONGLONG
;
873 if (argtypes
[fmtpos
] &&
874 argtypes
[fmtpos
] != atype
)
876 argtypes
[fmtpos
] = atype
;
877 last_dollar
= Max(last_dollar
, fmtpos
);
880 return false; /* non-dollar conversion spec */
885 if (argtypes
[fmtpos
] &&
886 argtypes
[fmtpos
] != ATYPE_INT
)
888 argtypes
[fmtpos
] = ATYPE_INT
;
889 last_dollar
= Max(last_dollar
, fmtpos
);
892 return false; /* non-dollar conversion spec */
898 if (argtypes
[fmtpos
] &&
899 argtypes
[fmtpos
] != ATYPE_CHARPTR
)
901 argtypes
[fmtpos
] = ATYPE_CHARPTR
;
902 last_dollar
= Max(last_dollar
, fmtpos
);
905 return false; /* non-dollar conversion spec */
914 if (argtypes
[fmtpos
] &&
915 argtypes
[fmtpos
] != ATYPE_DOUBLE
)
917 argtypes
[fmtpos
] = ATYPE_DOUBLE
;
918 last_dollar
= Max(last_dollar
, fmtpos
);
921 return false; /* non-dollar conversion spec */
927 return false; /* bogus format string */
931 * If we finish the spec with afterstar still set, there's a
932 * non-dollar star in there.
935 return false; /* non-dollar conversion spec */
939 * Format appears valid so far, so collect the arguments in physical
940 * order. (Since we rejected any non-dollar specs that would have
941 * collected arguments, we know that dopr() hasn't collected any yet.)
943 for (i
= 1; i
<= last_dollar
; i
++)
950 argvalues
[i
].i
= va_arg(args
, int);
953 argvalues
[i
].l
= va_arg(args
, long);
956 argvalues
[i
].ll
= va_arg(args
, long long);
959 argvalues
[i
].d
= va_arg(args
, double);
962 argvalues
[i
].cptr
= va_arg(args
, char *);
971 fmtstr(const char *value
, int leftjust
, int minlen
, int maxwidth
,
972 int pointflag
, PrintfTarget
*target
)
975 vallen
; /* amount to pad */
978 * If a maxwidth (precision) is specified, we must not fetch more bytes
982 vallen
= strnlen(value
, maxwidth
);
984 vallen
= strlen(value
);
986 padlen
= compute_padlen(minlen
, vallen
, leftjust
);
990 dopr_outchmulti(' ', padlen
, target
);
994 dostr(value
, vallen
, target
);
996 trailing_pad(padlen
, target
);
1000 fmtptr(const void *value
, PrintfTarget
*target
)
1005 /* we rely on regular C library's sprintf to do the basic conversion */
1006 vallen
= sprintf(convert
, "%p", value
);
1008 target
->failed
= true;
1010 dostr(convert
, vallen
, target
);
1014 fmtint(long long value
, char type
, int forcesign
, int leftjust
,
1015 int minlen
, int zpad
, int precision
, int pointflag
,
1016 PrintfTarget
*target
)
1018 unsigned long long base
;
1019 unsigned long long uvalue
;
1021 const char *cvt
= "0123456789abcdef";
1025 int padlen
; /* amount to pad */
1026 int zeropad
; /* extra leading zeroes */
1048 cvt
= "0123456789ABCDEF";
1053 return; /* keep compiler quiet */
1056 /* disable MSVC warning about applying unary minus to an unsigned value */
1058 #pragma warning(push)
1059 #pragma warning(disable: 4146)
1062 if (dosign
&& adjust_sign((value
< 0), forcesign
, &signvalue
))
1063 uvalue
= -(unsigned long long) value
;
1065 uvalue
= (unsigned long long) value
;
1067 #pragma warning(pop)
1071 * SUS: the result of converting 0 with an explicit precision of 0 is no
1074 if (value
== 0 && pointflag
&& precision
== 0)
1078 /* make integer string */
1081 convert
[sizeof(convert
) - (++vallen
)] = cvt
[uvalue
% base
];
1082 uvalue
= uvalue
/ base
;
1086 zeropad
= Max(0, precision
- vallen
);
1088 padlen
= compute_padlen(minlen
, vallen
+ zeropad
, leftjust
);
1090 leading_pad(zpad
, signvalue
, &padlen
, target
);
1093 dopr_outchmulti('0', zeropad
, target
);
1095 dostr(convert
+ sizeof(convert
) - vallen
, vallen
, target
);
1097 trailing_pad(padlen
, target
);
1101 fmtchar(int value
, int leftjust
, int minlen
, PrintfTarget
*target
)
1103 int padlen
; /* amount to pad */
1105 padlen
= compute_padlen(minlen
, 1, leftjust
);
1109 dopr_outchmulti(' ', padlen
, target
);
1113 dopr_outch(value
, target
);
1115 trailing_pad(padlen
, target
);
1119 fmtfloat(double value
, char type
, int forcesign
, int leftjust
,
1120 int minlen
, int zpad
, int precision
, int pointflag
,
1121 PrintfTarget
*target
)
1128 int zeropadlen
= 0; /* amount to pad with zeroes */
1129 int padlen
; /* amount to pad with spaces */
1132 * We rely on the regular C library's sprintf to do the basic conversion,
1133 * then handle padding considerations here.
1135 * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
1136 * too wildly more than that with other hardware. In "f" format, sprintf
1137 * could therefore generate at most 308 characters to the left of the
1138 * decimal point; while we need to allow the precision to get as high as
1139 * 308+17 to ensure that we don't truncate significant digits from very
1140 * small values. To handle both these extremes, we use a buffer of 1024
1141 * bytes and limit requested precision to 350 digits; this should prevent
1142 * buffer overrun even with non-IEEE math. If the original precision
1143 * request was more than 350, separately pad with zeroes.
1145 * We handle infinities and NaNs specially to ensure platform-independent
1148 if (precision
< 0) /* cover possible overflow of "accum" */
1150 prec
= Min(precision
, 350);
1154 strcpy(convert
, "NaN");
1156 /* no zero padding, regardless of precision spec */
1161 * Handle sign (NaNs have no sign, so we don't do this in the case
1162 * above). "value < 0.0" will not be true for IEEE minus zero, so we
1163 * detect that by looking for the case where value equals 0.0
1164 * according to == but not according to memcmp.
1166 static const double dzero
= 0.0;
1168 if (adjust_sign((value
< 0.0 ||
1170 memcmp(&value
, &dzero
, sizeof(double)) != 0)),
1171 forcesign
, &signvalue
))
1176 strcpy(convert
, "Infinity");
1178 /* no zero padding, regardless of precision spec */
1182 zeropadlen
= precision
- prec
;
1188 vallen
= sprintf(convert
, fmt
, prec
, value
);
1195 vallen
= sprintf(convert
, fmt
, value
);
1201 * Windows, alone among our supported platforms, likes to emit
1202 * three-digit exponent fields even when two digits would do. Hack
1203 * such results to look like the way everyone else does it.
1207 convert
[vallen
- 5] == 'e' &&
1208 convert
[vallen
- 3] == '0')
1210 convert
[vallen
- 3] = convert
[vallen
- 2];
1211 convert
[vallen
- 2] = convert
[vallen
- 1];
1217 padlen
= compute_padlen(minlen
, vallen
+ zeropadlen
, leftjust
);
1219 leading_pad(zpad
, signvalue
, &padlen
, target
);
1223 /* If 'e' or 'E' format, inject zeroes before the exponent */
1224 char *epos
= strrchr(convert
, 'e');
1227 epos
= strrchr(convert
, 'E');
1230 /* pad before exponent */
1231 dostr(convert
, epos
- convert
, target
);
1232 dopr_outchmulti('0', zeropadlen
, target
);
1233 dostr(epos
, vallen
- (epos
- convert
), target
);
1237 /* no exponent, pad after the digits */
1238 dostr(convert
, vallen
, target
);
1239 dopr_outchmulti('0', zeropadlen
, target
);
1244 /* no zero padding, just emit the number as-is */
1245 dostr(convert
, vallen
, target
);
1248 trailing_pad(padlen
, target
);
1252 target
->failed
= true;
1256 * Nonstandard entry point to print a double value efficiently.
1258 * This is approximately equivalent to strfromd(), but has an API more
1259 * adapted to what float8out() wants. The behavior is like snprintf()
1260 * with a format of "%.ng", where n is the specified precision.
1261 * However, the target buffer must be nonempty (i.e. count > 0), and
1262 * the precision is silently bounded to a sane range.
1265 pg_strfromd(char *str
, size_t count
, int precision
, double value
)
1267 PrintfTarget target
;
1273 /* Set up the target like pg_snprintf, but require nonempty buffer */
1275 target
.bufstart
= target
.bufptr
= str
;
1276 target
.bufend
= str
+ count
- 1;
1277 target
.stream
= NULL
;
1279 target
.failed
= false;
1282 * We bound precision to a reasonable range; the combination of this and
1283 * the knowledge that we're using "g" format without padding allows the
1284 * convert[] buffer to be reasonably small.
1288 else if (precision
> 32)
1292 * The rest is just an inlined version of the fmtfloat() logic above,
1293 * simplified using the knowledge that no padding is wanted.
1297 strcpy(convert
, "NaN");
1302 static const double dzero
= 0.0;
1306 memcmp(&value
, &dzero
, sizeof(double)) != 0))
1314 strcpy(convert
, "Infinity");
1324 vallen
= sprintf(convert
, fmt
, precision
, value
);
1327 target
.failed
= true;
1333 convert
[vallen
- 5] == 'e' &&
1334 convert
[vallen
- 3] == '0')
1336 convert
[vallen
- 3] = convert
[vallen
- 2];
1337 convert
[vallen
- 2] = convert
[vallen
- 1];
1345 dopr_outch(signvalue
, &target
);
1347 dostr(convert
, vallen
, &target
);
1350 *(target
.bufptr
) = '\0';
1351 return target
.failed
? -1 : (target
.bufptr
- target
.bufstart
1357 dostr(const char *str
, int slen
, PrintfTarget
*target
)
1359 /* fast path for common case of slen == 1 */
1362 dopr_outch(*str
, target
);
1370 if (target
->bufend
!= NULL
)
1371 avail
= target
->bufend
- target
->bufptr
;
1376 /* buffer full, can we dump to stream? */
1377 if (target
->stream
== NULL
)
1379 target
->nchars
+= slen
; /* no, lose the data */
1382 flushbuffer(target
);
1385 avail
= Min(avail
, slen
);
1386 memmove(target
->bufptr
, str
, avail
);
1387 target
->bufptr
+= avail
;
1394 dopr_outch(int c
, PrintfTarget
*target
)
1396 if (target
->bufend
!= NULL
&& target
->bufptr
>= target
->bufend
)
1398 /* buffer full, can we dump to stream? */
1399 if (target
->stream
== NULL
)
1401 target
->nchars
++; /* no, lose the data */
1404 flushbuffer(target
);
1406 *(target
->bufptr
++) = c
;
1410 dopr_outchmulti(int c
, int slen
, PrintfTarget
*target
)
1412 /* fast path for common case of slen == 1 */
1415 dopr_outch(c
, target
);
1423 if (target
->bufend
!= NULL
)
1424 avail
= target
->bufend
- target
->bufptr
;
1429 /* buffer full, can we dump to stream? */
1430 if (target
->stream
== NULL
)
1432 target
->nchars
+= slen
; /* no, lose the data */
1435 flushbuffer(target
);
1438 avail
= Min(avail
, slen
);
1439 memset(target
->bufptr
, c
, avail
);
1440 target
->bufptr
+= avail
;
1447 adjust_sign(int is_negative
, int forcesign
, int *signvalue
)
1461 compute_padlen(int minlen
, int vallen
, int leftjust
)
1465 padlen
= minlen
- vallen
;
1475 leading_pad(int zpad
, int signvalue
, int *padlen
, PrintfTarget
*target
)
1479 if (*padlen
> 0 && zpad
)
1483 dopr_outch(signvalue
, target
);
1489 dopr_outchmulti(zpad
, *padlen
, target
);
1493 maxpad
= (signvalue
!= 0);
1494 if (*padlen
> maxpad
)
1496 dopr_outchmulti(' ', *padlen
- maxpad
, target
);
1501 dopr_outch(signvalue
, target
);
1504 else if (*padlen
< 0)
1511 trailing_pad(int padlen
, PrintfTarget
*target
)
1514 dopr_outchmulti(' ', -padlen
, target
);