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-2024, 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
)
759 int last_dollar
= 0; /* Init to "no dollar arguments known" */
760 PrintfArgType argtypes
[PG_NL_ARGMAX
+ 1] = {0};
763 * This loop must accept the same format strings as the one in dopr().
764 * However, we don't need to analyze them to the same level of detail.
766 * Since we're only called if there's a dollar-type spec somewhere, we can
767 * fail immediately if we find a non-dollar spec. Per the C99 standard,
768 * all argument references in the format string must be one or the other.
770 while (*format
!= '\0')
772 /* Locate next conversion specifier */
775 /* Unlike dopr, we can just quit if there's no more specifiers */
776 format
= strchr(format
+ 1, '%');
781 /* Process conversion spec starting at *format */
783 longflag
= longlongflag
= 0;
803 accum
= accum
* 10 + (ch
- '0');
810 return false; /* previous star missing dollar */
815 if (accum
<= 0 || accum
> PG_NL_ARGMAX
)
819 if (argtypes
[accum
] &&
820 argtypes
[accum
] != ATYPE_INT
)
822 argtypes
[accum
] = ATYPE_INT
;
823 last_dollar
= Max(last_dollar
, accum
);
837 #if SIZEOF_SIZE_T == 8
838 #ifdef HAVE_LONG_INT_64
840 #elif defined(HAVE_LONG_LONG_INT_64)
843 #error "Don't know how to print 64bit integers"
846 /* assume size_t is same size as int */
864 atype
= ATYPE_LONGLONG
;
869 if (argtypes
[fmtpos
] &&
870 argtypes
[fmtpos
] != atype
)
872 argtypes
[fmtpos
] = atype
;
873 last_dollar
= Max(last_dollar
, fmtpos
);
876 return false; /* non-dollar conversion spec */
881 if (argtypes
[fmtpos
] &&
882 argtypes
[fmtpos
] != ATYPE_INT
)
884 argtypes
[fmtpos
] = ATYPE_INT
;
885 last_dollar
= Max(last_dollar
, fmtpos
);
888 return false; /* non-dollar conversion spec */
894 if (argtypes
[fmtpos
] &&
895 argtypes
[fmtpos
] != ATYPE_CHARPTR
)
897 argtypes
[fmtpos
] = ATYPE_CHARPTR
;
898 last_dollar
= Max(last_dollar
, fmtpos
);
901 return false; /* non-dollar conversion spec */
910 if (argtypes
[fmtpos
] &&
911 argtypes
[fmtpos
] != ATYPE_DOUBLE
)
913 argtypes
[fmtpos
] = ATYPE_DOUBLE
;
914 last_dollar
= Max(last_dollar
, fmtpos
);
917 return false; /* non-dollar conversion spec */
923 return false; /* bogus format string */
927 * If we finish the spec with afterstar still set, there's a
928 * non-dollar star in there.
931 return false; /* non-dollar conversion spec */
935 * Format appears valid so far, so collect the arguments in physical
936 * order. (Since we rejected any non-dollar specs that would have
937 * collected arguments, we know that dopr() hasn't collected any yet.)
939 for (i
= 1; i
<= last_dollar
; i
++)
946 argvalues
[i
].i
= va_arg(args
, int);
949 argvalues
[i
].l
= va_arg(args
, long);
952 argvalues
[i
].ll
= va_arg(args
, long long);
955 argvalues
[i
].d
= va_arg(args
, double);
958 argvalues
[i
].cptr
= va_arg(args
, char *);
967 fmtstr(const char *value
, int leftjust
, int minlen
, int maxwidth
,
968 int pointflag
, PrintfTarget
*target
)
971 vallen
; /* amount to pad */
974 * If a maxwidth (precision) is specified, we must not fetch more bytes
978 vallen
= strnlen(value
, maxwidth
);
980 vallen
= strlen(value
);
982 padlen
= compute_padlen(minlen
, vallen
, leftjust
);
986 dopr_outchmulti(' ', padlen
, target
);
990 dostr(value
, vallen
, target
);
992 trailing_pad(padlen
, target
);
996 fmtptr(const void *value
, PrintfTarget
*target
)
1001 /* we rely on regular C library's snprintf to do the basic conversion */
1002 vallen
= snprintf(convert
, sizeof(convert
), "%p", value
);
1004 target
->failed
= true;
1006 dostr(convert
, vallen
, target
);
1010 fmtint(long long value
, char type
, int forcesign
, int leftjust
,
1011 int minlen
, int zpad
, int precision
, int pointflag
,
1012 PrintfTarget
*target
)
1014 unsigned long long uvalue
;
1017 const char *cvt
= "0123456789abcdef";
1021 int padlen
; /* amount to pad */
1022 int zeropad
; /* extra leading zeroes */
1044 cvt
= "0123456789ABCDEF";
1049 return; /* keep compiler quiet */
1052 /* disable MSVC warning about applying unary minus to an unsigned value */
1054 #pragma warning(push)
1055 #pragma warning(disable: 4146)
1058 if (dosign
&& adjust_sign((value
< 0), forcesign
, &signvalue
))
1059 uvalue
= -(unsigned long long) value
;
1061 uvalue
= (unsigned long long) value
;
1063 #pragma warning(pop)
1067 * SUS: the result of converting 0 with an explicit precision of 0 is no
1070 if (value
== 0 && pointflag
&& precision
== 0)
1075 * Convert integer to string. We special-case each of the possible
1076 * base values so as to avoid general-purpose divisions. On most
1077 * machines, division by a fixed constant can be done much more
1078 * cheaply than a general divide.
1084 convert
[sizeof(convert
) - (++vallen
)] = cvt
[uvalue
% 10];
1085 uvalue
= uvalue
/ 10;
1088 else if (base
== 16)
1092 convert
[sizeof(convert
) - (++vallen
)] = cvt
[uvalue
% 16];
1093 uvalue
= uvalue
/ 16;
1096 else /* base == 8 */
1100 convert
[sizeof(convert
) - (++vallen
)] = cvt
[uvalue
% 8];
1101 uvalue
= uvalue
/ 8;
1106 zeropad
= Max(0, precision
- vallen
);
1108 padlen
= compute_padlen(minlen
, vallen
+ zeropad
, leftjust
);
1110 leading_pad(zpad
, signvalue
, &padlen
, target
);
1113 dopr_outchmulti('0', zeropad
, target
);
1115 dostr(convert
+ sizeof(convert
) - vallen
, vallen
, target
);
1117 trailing_pad(padlen
, target
);
1121 fmtchar(int value
, int leftjust
, int minlen
, PrintfTarget
*target
)
1123 int padlen
; /* amount to pad */
1125 padlen
= compute_padlen(minlen
, 1, leftjust
);
1129 dopr_outchmulti(' ', padlen
, target
);
1133 dopr_outch(value
, target
);
1135 trailing_pad(padlen
, target
);
1139 fmtfloat(double value
, char type
, int forcesign
, int leftjust
,
1140 int minlen
, int zpad
, int precision
, int pointflag
,
1141 PrintfTarget
*target
)
1148 int zeropadlen
= 0; /* amount to pad with zeroes */
1149 int padlen
; /* amount to pad with spaces */
1152 * We rely on the regular C library's snprintf to do the basic conversion,
1153 * then handle padding considerations here.
1155 * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
1156 * too wildly more than that with other hardware. In "f" format, snprintf
1157 * could therefore generate at most 308 characters to the left of the
1158 * decimal point; while we need to allow the precision to get as high as
1159 * 308+17 to ensure that we don't truncate significant digits from very
1160 * small values. To handle both these extremes, we use a buffer of 1024
1161 * bytes and limit requested precision to 350 digits; this should prevent
1162 * buffer overrun even with non-IEEE math. If the original precision
1163 * request was more than 350, separately pad with zeroes.
1165 * We handle infinities and NaNs specially to ensure platform-independent
1168 if (precision
< 0) /* cover possible overflow of "accum" */
1170 prec
= Min(precision
, 350);
1174 strcpy(convert
, "NaN");
1176 /* no zero padding, regardless of precision spec */
1181 * Handle sign (NaNs have no sign, so we don't do this in the case
1182 * above). "value < 0.0" will not be true for IEEE minus zero, so we
1183 * detect that by looking for the case where value equals 0.0
1184 * according to == but not according to memcmp.
1186 static const double dzero
= 0.0;
1188 if (adjust_sign((value
< 0.0 ||
1190 memcmp(&value
, &dzero
, sizeof(double)) != 0)),
1191 forcesign
, &signvalue
))
1196 strcpy(convert
, "Infinity");
1198 /* no zero padding, regardless of precision spec */
1202 zeropadlen
= precision
- prec
;
1208 vallen
= snprintf(convert
, sizeof(convert
), fmt
, prec
, value
);
1215 vallen
= snprintf(convert
, sizeof(convert
), fmt
, value
);
1221 * Windows, alone among our supported platforms, likes to emit
1222 * three-digit exponent fields even when two digits would do. Hack
1223 * such results to look like the way everyone else does it.
1227 convert
[vallen
- 5] == 'e' &&
1228 convert
[vallen
- 3] == '0')
1230 convert
[vallen
- 3] = convert
[vallen
- 2];
1231 convert
[vallen
- 2] = convert
[vallen
- 1];
1237 padlen
= compute_padlen(minlen
, vallen
+ zeropadlen
, leftjust
);
1239 leading_pad(zpad
, signvalue
, &padlen
, target
);
1243 /* If 'e' or 'E' format, inject zeroes before the exponent */
1244 char *epos
= strrchr(convert
, 'e');
1247 epos
= strrchr(convert
, 'E');
1250 /* pad before exponent */
1251 dostr(convert
, epos
- convert
, target
);
1252 dopr_outchmulti('0', zeropadlen
, target
);
1253 dostr(epos
, vallen
- (epos
- convert
), target
);
1257 /* no exponent, pad after the digits */
1258 dostr(convert
, vallen
, target
);
1259 dopr_outchmulti('0', zeropadlen
, target
);
1264 /* no zero padding, just emit the number as-is */
1265 dostr(convert
, vallen
, target
);
1268 trailing_pad(padlen
, target
);
1272 target
->failed
= true;
1276 * Nonstandard entry point to print a double value efficiently.
1278 * This is approximately equivalent to strfromd(), but has an API more
1279 * adapted to what float8out() wants. The behavior is like snprintf()
1280 * with a format of "%.ng", where n is the specified precision.
1281 * However, the target buffer must be nonempty (i.e. count > 0), and
1282 * the precision is silently bounded to a sane range.
1285 pg_strfromd(char *str
, size_t count
, int precision
, double value
)
1287 PrintfTarget target
;
1293 /* Set up the target like pg_snprintf, but require nonempty buffer */
1295 target
.bufstart
= target
.bufptr
= str
;
1296 target
.bufend
= str
+ count
- 1;
1297 target
.stream
= NULL
;
1299 target
.failed
= false;
1302 * We bound precision to a reasonable range; the combination of this and
1303 * the knowledge that we're using "g" format without padding allows the
1304 * convert[] buffer to be reasonably small.
1308 else if (precision
> 32)
1312 * The rest is just an inlined version of the fmtfloat() logic above,
1313 * simplified using the knowledge that no padding is wanted.
1317 strcpy(convert
, "NaN");
1322 static const double dzero
= 0.0;
1326 memcmp(&value
, &dzero
, sizeof(double)) != 0))
1334 strcpy(convert
, "Infinity");
1344 vallen
= snprintf(convert
, sizeof(convert
), fmt
, precision
, value
);
1347 target
.failed
= true;
1353 convert
[vallen
- 5] == 'e' &&
1354 convert
[vallen
- 3] == '0')
1356 convert
[vallen
- 3] = convert
[vallen
- 2];
1357 convert
[vallen
- 2] = convert
[vallen
- 1];
1365 dopr_outch(signvalue
, &target
);
1367 dostr(convert
, vallen
, &target
);
1370 *(target
.bufptr
) = '\0';
1371 return target
.failed
? -1 : (target
.bufptr
- target
.bufstart
1377 dostr(const char *str
, int slen
, PrintfTarget
*target
)
1379 /* fast path for common case of slen == 1 */
1382 dopr_outch(*str
, target
);
1390 if (target
->bufend
!= NULL
)
1391 avail
= target
->bufend
- target
->bufptr
;
1396 /* buffer full, can we dump to stream? */
1397 if (target
->stream
== NULL
)
1399 target
->nchars
+= slen
; /* no, lose the data */
1402 flushbuffer(target
);
1405 avail
= Min(avail
, slen
);
1406 memmove(target
->bufptr
, str
, avail
);
1407 target
->bufptr
+= avail
;
1414 dopr_outch(int c
, PrintfTarget
*target
)
1416 if (target
->bufend
!= NULL
&& target
->bufptr
>= target
->bufend
)
1418 /* buffer full, can we dump to stream? */
1419 if (target
->stream
== NULL
)
1421 target
->nchars
++; /* no, lose the data */
1424 flushbuffer(target
);
1426 *(target
->bufptr
++) = c
;
1430 dopr_outchmulti(int c
, int slen
, PrintfTarget
*target
)
1432 /* fast path for common case of slen == 1 */
1435 dopr_outch(c
, target
);
1443 if (target
->bufend
!= NULL
)
1444 avail
= target
->bufend
- target
->bufptr
;
1449 /* buffer full, can we dump to stream? */
1450 if (target
->stream
== NULL
)
1452 target
->nchars
+= slen
; /* no, lose the data */
1455 flushbuffer(target
);
1458 avail
= Min(avail
, slen
);
1459 memset(target
->bufptr
, c
, avail
);
1460 target
->bufptr
+= avail
;
1467 adjust_sign(int is_negative
, int forcesign
, int *signvalue
)
1481 compute_padlen(int minlen
, int vallen
, int leftjust
)
1485 padlen
= minlen
- vallen
;
1495 leading_pad(int zpad
, int signvalue
, int *padlen
, PrintfTarget
*target
)
1499 if (*padlen
> 0 && zpad
)
1503 dopr_outch(signvalue
, target
);
1509 dopr_outchmulti(zpad
, *padlen
, target
);
1513 maxpad
= (signvalue
!= 0);
1514 if (*padlen
> maxpad
)
1516 dopr_outchmulti(' ', *padlen
- maxpad
, target
);
1521 dopr_outch(signvalue
, target
);
1524 else if (*padlen
< 0)
1531 trailing_pad(int padlen
, PrintfTarget
*target
)
1534 dopr_outchmulti(' ', -padlen
, target
);