Branch data Line data Source code
1 : : /*
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-2026, PostgreSQL Global Development Group
6 : : *
7 : : * Redistribution and use in source and binary forms, with or without
8 : : * modification, are permitted provided that the following conditions
9 : : * are met:
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.
18 : : *
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
29 : : * SUCH DAMAGE.
30 : : *
31 : : * src/port/snprintf.c
32 : : */
33 : :
34 : : #include "c.h"
35 : :
36 : : #include <math.h>
37 : :
38 : : /*
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().
43 : : */
44 : : #define PG_NL_ARGMAX 31
45 : :
46 : :
47 : : /*
48 : : * SNPRINTF, VSNPRINTF and friends
49 : : *
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:
53 : : *
54 : : * 1. No locale support: the radix character is always '.' and the '
55 : : * (single quote) format flag is ignored.
56 : : *
57 : : * 2. No support for the "%n" format specification.
58 : : *
59 : : * 3. No support for wide characters ("lc" and "ls" formats).
60 : : *
61 : : * 4. No support for "long double" ("Lf" and related formats).
62 : : *
63 : : * 5. Space and '#' flags are not implemented.
64 : : *
65 : : * In addition, we support some extensions over C99:
66 : : *
67 : : * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
68 : : *
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.
72 : : *
73 : : *
74 : : * Historically the result values of sprintf/snprintf varied across platforms.
75 : : * This implementation now follows the C99 standard:
76 : : *
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.
80 : : *
81 : : * 2. For successful writes to streams, the actual number of bytes written
82 : : * to the stream is returned.
83 : : *
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.
89 : : *
90 : : * snprintf buffer overrun can be detected by checking for function result
91 : : * greater than or equal to the supplied count.
92 : : */
93 : :
94 : : /**************************************************************
95 : : * Original:
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 : : **************************************************************/
101 : :
102 : : /* Prevent recursion */
103 : : #undef vsnprintf
104 : : #undef snprintf
105 : : #undef vsprintf
106 : : #undef sprintf
107 : : #undef vfprintf
108 : : #undef fprintf
109 : : #undef vprintf
110 : : #undef printf
111 : :
112 : : /*
113 : : * Info about where the formatted output is going.
114 : : *
115 : : * dopr and subroutines will not write at/past bufend, but snprintf
116 : : * reserves one byte, ensuring it may place the trailing '\0' there.
117 : : *
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.)
124 : : */
125 : : typedef struct
126 : : {
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 */
134 : : } PrintfTarget;
135 : :
136 : : /*
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.
141 : : */
142 : : typedef enum
143 : : {
144 : : ATYPE_NONE = 0,
145 : : ATYPE_INT,
146 : : ATYPE_LONG,
147 : : ATYPE_LONGLONG,
148 : : ATYPE_DOUBLE,
149 : : ATYPE_CHARPTR
150 : : } PrintfArgType;
151 : :
152 : : typedef union
153 : : {
154 : : int i;
155 : : long l;
156 : : long long ll;
157 : : double d;
158 : : char *cptr;
159 : : } PrintfArgValue;
160 : :
161 : :
162 : : static void flushbuffer(PrintfTarget *target);
163 : : static void dopr(PrintfTarget *target, const char *format, va_list args);
164 : :
165 : :
166 : : /*
167 : : * Externally visible entry points.
168 : : *
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().
171 : : */
172 : :
173 : : int
174 : 1923974 : pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
175 : : {
176 : 1923974 : PrintfTarget target;
177 : 1923974 : char onebyte[1];
178 : :
179 : : /*
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
183 : : * depend on count.
184 : : */
185 [ + + ]: 1923974 : if (count == 0)
186 : : {
187 : 9852 : str = onebyte;
188 : 9852 : count = 1;
189 : 9852 : }
190 : 1923974 : target.bufstart = target.bufptr = str;
191 : 1923974 : target.bufend = str + count - 1;
192 : 1923974 : target.stream = NULL;
193 : 1923974 : target.nchars = 0;
194 : 1923974 : target.failed = false;
195 : 1923974 : dopr(&target, fmt, args);
196 : 1923974 : *(target.bufptr) = '\0';
197 [ - + ]: 1923974 : return target.failed ? -1 : (target.bufptr - target.bufstart
198 : 1923974 : + target.nchars);
199 : 1923974 : }
200 : :
201 : : int
202 : 704479 : pg_snprintf(char *str, size_t count, const char *fmt,...)
203 : : {
204 : 704479 : int len;
205 : 704479 : va_list args;
206 : :
207 : 704479 : va_start(args, fmt);
208 : 704479 : len = pg_vsnprintf(str, count, fmt, args);
209 : 704479 : va_end(args);
210 : 1408958 : return len;
211 : 704479 : }
212 : :
213 : : int
214 : 822536 : pg_vsprintf(char *str, const char *fmt, va_list args)
215 : : {
216 : 822536 : PrintfTarget target;
217 : :
218 : 822536 : target.bufstart = target.bufptr = str;
219 : 822536 : target.bufend = NULL;
220 : 822536 : target.stream = NULL;
221 : 822536 : target.nchars = 0; /* not really used in this case */
222 : 822536 : target.failed = false;
223 : 822536 : dopr(&target, fmt, args);
224 : 822536 : *(target.bufptr) = '\0';
225 [ - + ]: 822536 : return target.failed ? -1 : (target.bufptr - target.bufstart
226 : 822536 : + target.nchars);
227 : 822536 : }
228 : :
229 : : int
230 : 822536 : pg_sprintf(char *str, const char *fmt,...)
231 : : {
232 : 822536 : int len;
233 : 822536 : va_list args;
234 : :
235 : 822536 : va_start(args, fmt);
236 : 822536 : len = pg_vsprintf(str, fmt, args);
237 : 822536 : va_end(args);
238 : 1645072 : return len;
239 : 822536 : }
240 : :
241 : : int
242 : 217232 : pg_vfprintf(FILE *stream, const char *fmt, va_list args)
243 : : {
244 : 217232 : PrintfTarget target;
245 : 217232 : char buffer[1024]; /* size is arbitrary */
246 : :
247 [ + - ]: 217232 : if (stream == NULL)
248 : : {
249 : 0 : errno = EINVAL;
250 : 0 : return -1;
251 : : }
252 : 217232 : target.bufstart = target.bufptr = buffer;
253 : 217232 : target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
254 : 217232 : target.stream = stream;
255 : 217232 : target.nchars = 0;
256 : 217232 : target.failed = false;
257 : 217232 : dopr(&target, fmt, args);
258 : : /* dump any remaining buffer contents */
259 : 217232 : flushbuffer(&target);
260 [ - + ]: 217232 : return target.failed ? -1 : target.nchars;
261 : 217232 : }
262 : :
263 : : int
264 : 215435 : pg_fprintf(FILE *stream, const char *fmt,...)
265 : : {
266 : 215435 : int len;
267 : 215435 : va_list args;
268 : :
269 : 215435 : va_start(args, fmt);
270 : 215435 : len = pg_vfprintf(stream, fmt, args);
271 : 215435 : va_end(args);
272 : 430870 : return len;
273 : 215435 : }
274 : :
275 : : int
276 : 0 : pg_vprintf(const char *fmt, va_list args)
277 : : {
278 : 0 : return pg_vfprintf(stdout, fmt, args);
279 : : }
280 : :
281 : : int
282 : 799 : pg_printf(const char *fmt,...)
283 : : {
284 : 799 : int len;
285 : 799 : va_list args;
286 : :
287 : 799 : va_start(args, fmt);
288 : 799 : len = pg_vfprintf(stdout, fmt, args);
289 : 799 : va_end(args);
290 : 1598 : return len;
291 : 799 : }
292 : :
293 : : /*
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.
296 : : */
297 : : static void
298 : 217279 : flushbuffer(PrintfTarget *target)
299 : : {
300 : 217279 : size_t nc = target->bufptr - target->bufstart;
301 : :
302 : : /*
303 : : * Don't write anything if we already failed; this is to ensure we
304 : : * preserve the original failure's errno.
305 : : */
306 [ + - + + ]: 217279 : if (!target->failed && nc > 0)
307 : : {
308 : 179743 : size_t written;
309 : :
310 : 179743 : written = fwrite(target->bufstart, 1, nc, target->stream);
311 : 179743 : target->nchars += written;
312 [ + - ]: 179743 : if (written != nc)
313 : 0 : target->failed = true;
314 : 179743 : }
315 : 217279 : target->bufptr = target->bufstart;
316 : 217279 : }
317 : :
318 : :
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);
339 : :
340 : : /*
341 : : * If strchrnul exists (it's a glibc-ism, but since adopted by some other
342 : : * platforms), it's a good bit faster than the equivalent manual loop.
343 : : * Use it if possible, and if it doesn't exist, use this replacement.
344 : : *
345 : : * Note: glibc declares this as returning "char *", but that would require
346 : : * casting away const internally, so we don't follow that detail.
347 : : *
348 : : * Note: macOS has this too as of Sequoia 15.4, but it's hidden behind
349 : : * a deployment-target check that causes compile errors if the deployment
350 : : * target isn't high enough. So !HAVE_DECL_STRCHRNUL may mean "yes it's
351 : : * declared, but it doesn't compile". To avoid failing in that scenario,
352 : : * use a macro to avoid matching <string.h>'s name.
353 : : */
354 : : #if !HAVE_DECL_STRCHRNUL
355 : :
356 : : #define strchrnul pg_strchrnul
357 : :
358 : : static inline const char *
359 : 2295274 : strchrnul(const char *s, int c)
360 : : {
361 [ + + + + ]: 13761424 : while (*s != '\0' && *s != c)
362 : 11466150 : s++;
363 : 2295274 : return s;
364 : : }
365 : :
366 : : #endif /* !HAVE_DECL_STRCHRNUL */
367 : :
368 : :
369 : : /*
370 : : * dopr(): the guts of *printf for all cases.
371 : : */
372 : : static void
373 : 2963742 : dopr(PrintfTarget *target, const char *format, va_list args)
374 : : {
375 : 2963742 : int save_errno = errno;
376 : 2963742 : const char *first_pct = NULL;
377 : 2963742 : int ch;
378 : 2963742 : bool have_dollar;
379 : 2963742 : bool have_star;
380 : 2963742 : bool afterstar;
381 : 2963742 : int accum;
382 : 2963742 : int longlongflag;
383 : 2963742 : int longflag;
384 : 2963742 : int pointflag;
385 : 2963742 : int leftjust;
386 : 2963742 : int fieldwidth;
387 : 2963742 : int precision;
388 : 2963742 : int zpad;
389 : 2963742 : int forcesign;
390 : 2963742 : int fmtpos;
391 : 2963742 : int cvalue;
392 : 2963742 : long long numvalue;
393 : 2963742 : double fvalue;
394 : 2963742 : const char *strvalue;
395 : 2963742 : PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
396 : :
397 : : /*
398 : : * Initially, we suppose the format string does not use %n$. The first
399 : : * time we come to a conversion spec that has that, we'll call
400 : : * find_arguments() to check for consistent use of %n$ and fill the
401 : : * argvalues array with the argument values in the correct order.
402 : : */
403 : 2963742 : have_dollar = false;
404 : :
405 [ + + ]: 6535291 : while (*format != '\0')
406 : : {
407 : : /* Locate next conversion specifier */
408 [ + + ]: 4101000 : if (*format != '%')
409 : : {
410 : : /* Scan to next '%' or end of string */
411 : 2295274 : const char *next_pct = strchrnul(format + 1, '%');
412 : :
413 : : /* Dump literal data we just scanned over */
414 : 2295274 : dostr(format, next_pct - format, target);
415 [ - + ]: 2295274 : if (target->failed)
416 : 0 : break;
417 : :
418 [ + + ]: 2295274 : if (*next_pct == '\0')
419 : 529451 : break;
420 : 1765823 : format = next_pct;
421 [ + + ]: 2295274 : }
422 : :
423 : : /*
424 : : * Remember start of first conversion spec; if we find %n$, then it's
425 : : * sufficient for find_arguments() to start here, without rescanning
426 : : * earlier literal text.
427 : : */
428 [ + + ]: 3571549 : if (first_pct == NULL)
429 : 2938659 : first_pct = format;
430 : :
431 : : /* Process conversion spec starting at *format */
432 : 3571549 : format++;
433 : :
434 : : /* Fast path for conversion spec that is exactly %s */
435 [ + + ]: 3571549 : if (*format == 's')
436 : : {
437 : 1016288 : format++;
438 : 1016288 : strvalue = va_arg(args, char *);
439 [ + - ]: 1016288 : if (strvalue == NULL)
440 : 0 : strvalue = "(null)";
441 : 1016288 : dostr(strvalue, strlen(strvalue), target);
442 [ + - ]: 1016288 : if (target->failed)
443 : 0 : break;
444 : 1016288 : continue;
445 : : }
446 : :
447 : 2555261 : fieldwidth = precision = zpad = leftjust = forcesign = 0;
448 : 2555261 : longflag = longlongflag = pointflag = 0;
449 : 2555261 : fmtpos = accum = 0;
450 : 2555261 : have_star = afterstar = false;
451 : : nextch2:
452 : 4185519 : ch = *format++;
453 [ + + + + : 4185519 : switch (ch)
+ + - + -
+ + + + +
- + + + -
+ ]
454 : : {
455 : : case '-':
456 : 79887 : leftjust = 1;
457 : 79887 : goto nextch2;
458 : : case '+':
459 : 47 : forcesign = 1;
460 : 47 : goto nextch2;
461 : : case '0':
462 : : /* set zero padding if no nonzero digits yet */
463 [ + - + + ]: 1238218 : if (accum == 0 && !pointflag)
464 : 616968 : zpad = '0';
465 : : /* FALL THRU */
466 : : case '1':
467 : : case '2':
468 : : case '3':
469 : : case '4':
470 : : case '5':
471 : : case '6':
472 : : case '7':
473 : : case '8':
474 : : case '9':
475 : 1248694 : accum = accum * 10 + (ch - '0');
476 : 1248694 : goto nextch2;
477 : : case '.':
478 [ - + ]: 21041 : if (have_star)
479 : 0 : have_star = false;
480 : : else
481 : 21041 : fieldwidth = accum;
482 : 21041 : pointflag = 1;
483 : 21041 : accum = 0;
484 : 21041 : goto nextch2;
485 : : case '*':
486 [ - + ]: 220568 : if (have_dollar)
487 : : {
488 : : /*
489 : : * We'll process value after reading n$. Note it's OK to
490 : : * assume have_dollar is set correctly, because in a valid
491 : : * format string the initial % must have had n$ if * does.
492 : : */
493 : 0 : afterstar = true;
494 : 0 : }
495 : : else
496 : : {
497 : : /* fetch and process value now */
498 : 220568 : int starval = va_arg(args, int);
499 : :
500 [ + + ]: 220568 : if (pointflag)
501 : : {
502 : 9175 : precision = starval;
503 [ + - ]: 9175 : if (precision < 0)
504 : : {
505 : 0 : precision = 0;
506 : 0 : pointflag = 0;
507 : 0 : }
508 : 9175 : }
509 : : else
510 : : {
511 : 211393 : fieldwidth = starval;
512 [ + + ]: 211393 : if (fieldwidth < 0)
513 : : {
514 : 941 : leftjust = 1;
515 : 941 : fieldwidth = -fieldwidth;
516 : 941 : }
517 : : }
518 : 220568 : }
519 : 220568 : have_star = true;
520 : 220568 : accum = 0;
521 : 220568 : goto nextch2;
522 : : case '$':
523 : : /* First dollar sign? */
524 [ # # ]: 0 : if (!have_dollar)
525 : : {
526 : : /* Yup, so examine all conversion specs in format */
527 [ # # ]: 0 : if (!find_arguments(first_pct, args, argvalues))
528 : 0 : goto bad_format;
529 : 0 : have_dollar = true;
530 : 0 : }
531 [ # # ]: 0 : if (afterstar)
532 : : {
533 : : /* fetch and process star value */
534 : 0 : int starval = argvalues[accum].i;
535 : :
536 [ # # ]: 0 : if (pointflag)
537 : : {
538 : 0 : precision = starval;
539 [ # # ]: 0 : if (precision < 0)
540 : : {
541 : 0 : precision = 0;
542 : 0 : pointflag = 0;
543 : 0 : }
544 : 0 : }
545 : : else
546 : : {
547 : 0 : fieldwidth = starval;
548 [ # # ]: 0 : if (fieldwidth < 0)
549 : : {
550 : 0 : leftjust = 1;
551 : 0 : fieldwidth = -fieldwidth;
552 : 0 : }
553 : : }
554 : 0 : afterstar = false;
555 : 0 : }
556 : : else
557 : 0 : fmtpos = accum;
558 : 0 : accum = 0;
559 : 0 : goto nextch2;
560 : : case 'l':
561 [ + + ]: 51660 : if (longflag)
562 : 15083 : longlongflag = 1;
563 : : else
564 : 36577 : longflag = 1;
565 : 51660 : goto nextch2;
566 : : case 'j':
567 : : #if SIZEOF_INTMAX_T == SIZEOF_LONG
568 : 4 : longflag = 1;
569 : : #elif SIZEOF_INTMAX_T == SIZEOF_LONG_LONG
570 : : longlongflag = 1;
571 : : #else
572 : : #error "cannot find integer type of the same size as intmax_t"
573 : : #endif
574 : 4 : goto nextch2;
575 : : case 'z':
576 : : #if SIZEOF_SIZE_T == SIZEOF_LONG
577 : 8357 : longflag = 1;
578 : : #elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
579 : : longlongflag = 1;
580 : : #else
581 : : #error "cannot find integer type of the same size as size_t"
582 : : #endif
583 : 8357 : goto nextch2;
584 : : case 'h':
585 : : case '\'':
586 : : /* ignore these */
587 : 0 : goto nextch2;
588 : : case 'd':
589 : : case 'i':
590 [ + + ]: 1338371 : if (!have_star)
591 : : {
592 [ - + ]: 1331846 : if (pointflag)
593 : 0 : precision = accum;
594 : : else
595 : 1331846 : fieldwidth = accum;
596 : 1331846 : }
597 [ - + ]: 1338371 : if (have_dollar)
598 : : {
599 [ # # ]: 0 : if (longlongflag)
600 : 0 : numvalue = argvalues[fmtpos].ll;
601 [ # # ]: 0 : else if (longflag)
602 : 0 : numvalue = argvalues[fmtpos].l;
603 : : else
604 : 0 : numvalue = argvalues[fmtpos].i;
605 : 0 : }
606 : : else
607 : : {
608 [ + + ]: 1338371 : if (longlongflag)
609 : 12742 : numvalue = va_arg(args, long long);
610 [ + + ]: 1325629 : else if (longflag)
611 : 524 : numvalue = va_arg(args, long);
612 : : else
613 : 1325105 : numvalue = va_arg(args, int);
614 : : }
615 : 2676742 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
616 : 1338371 : precision, pointflag, target);
617 : 1338371 : break;
618 : : case 'o':
619 : : case 'u':
620 : : case 'x':
621 : : case 'X':
622 [ - + ]: 979703 : if (!have_star)
623 : : {
624 [ - + ]: 979703 : if (pointflag)
625 : 0 : precision = accum;
626 : : else
627 : 979703 : fieldwidth = accum;
628 : 979703 : }
629 [ - + ]: 979703 : if (have_dollar)
630 : : {
631 [ # # ]: 0 : if (longlongflag)
632 : 0 : numvalue = (unsigned long long) argvalues[fmtpos].ll;
633 [ # # ]: 0 : else if (longflag)
634 : 0 : numvalue = (unsigned long) argvalues[fmtpos].l;
635 : : else
636 : 0 : numvalue = (unsigned int) argvalues[fmtpos].i;
637 : 0 : }
638 : : else
639 : : {
640 [ + + ]: 979703 : if (longlongflag)
641 : 2341 : numvalue = (unsigned long long) va_arg(args, long long);
642 [ + + ]: 977362 : else if (longflag)
643 : 29331 : numvalue = (unsigned long) va_arg(args, long);
644 : : else
645 : 948031 : numvalue = (unsigned int) va_arg(args, int);
646 : : }
647 : 1959406 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
648 : 979703 : precision, pointflag, target);
649 : 979703 : break;
650 : : case 'c':
651 [ - + ]: 5445 : if (!have_star)
652 : : {
653 [ - + ]: 5445 : if (pointflag)
654 : 0 : precision = accum;
655 : : else
656 : 5445 : fieldwidth = accum;
657 : 5445 : }
658 [ - + ]: 5445 : if (have_dollar)
659 : 0 : cvalue = (unsigned char) argvalues[fmtpos].i;
660 : : else
661 : 5445 : cvalue = (unsigned char) va_arg(args, int);
662 : 5445 : fmtchar(cvalue, leftjust, fieldwidth, target);
663 : 5445 : break;
664 : : case 's':
665 [ + + ]: 212202 : if (!have_star)
666 : : {
667 [ - + ]: 4640 : if (pointflag)
668 : 0 : precision = accum;
669 : : else
670 : 4640 : fieldwidth = accum;
671 : 4640 : }
672 [ - + ]: 212202 : if (have_dollar)
673 : 0 : strvalue = argvalues[fmtpos].cptr;
674 : : else
675 : 212202 : strvalue = va_arg(args, char *);
676 : : /* If string is NULL, silently substitute "(null)" */
677 [ + - ]: 212202 : if (strvalue == NULL)
678 : 0 : strvalue = "(null)";
679 : 424404 : fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
680 : 212202 : target);
681 : 212202 : break;
682 : : case 'p':
683 : : /* fieldwidth/leftjust are ignored ... */
684 [ # # ]: 0 : if (have_dollar)
685 : 0 : strvalue = argvalues[fmtpos].cptr;
686 : : else
687 : 0 : strvalue = va_arg(args, char *);
688 : 0 : fmtptr((const void *) strvalue, target);
689 : 0 : break;
690 : : case 'e':
691 : : case 'E':
692 : : case 'f':
693 : : case 'g':
694 : : case 'G':
695 [ + + ]: 19347 : if (!have_star)
696 : : {
697 [ + + ]: 12866 : if (pointflag)
698 : 11866 : precision = accum;
699 : : else
700 : 1000 : fieldwidth = accum;
701 : 12866 : }
702 [ - + ]: 19347 : if (have_dollar)
703 : 0 : fvalue = argvalues[fmtpos].d;
704 : : else
705 : 19347 : fvalue = va_arg(args, double);
706 : 38694 : fmtfloat(fvalue, ch, forcesign, leftjust,
707 : 19347 : fieldwidth, zpad,
708 : 19347 : precision, pointflag,
709 : 19347 : target);
710 : 19347 : break;
711 : : case 'm':
712 : : {
713 : 5 : char errbuf[PG_STRERROR_R_BUFLEN];
714 : 10 : const char *errm = strerror_r(save_errno,
715 : 5 : errbuf, sizeof(errbuf));
716 : :
717 : 5 : dostr(errm, strlen(errm), target);
718 : 5 : }
719 : 5 : break;
720 : : case '%':
721 : 188 : dopr_outch('%', target);
722 : 188 : break;
723 : : default:
724 : :
725 : : /*
726 : : * Anything else --- in particular, '\0' indicating end of
727 : : * format string --- is bogus.
728 : : */
729 : 0 : goto bad_format;
730 : : }
731 : :
732 : : /* Check for failure after each conversion spec */
733 [ + - ]: 2555261 : if (target->failed)
734 : 0 : break;
735 : : }
736 : :
737 : 2963742 : return;
738 : :
739 : : bad_format:
740 : 0 : errno = EINVAL;
741 : 0 : target->failed = true;
742 : 2963742 : }
743 : :
744 : : /*
745 : : * find_arguments(): sort out the arguments for a format spec with %n$
746 : : *
747 : : * If format is valid, return true and fill argvalues[i] with the value
748 : : * for the conversion spec that has %i$ or *i$. Else return false.
749 : : */
750 : : static bool
751 : 0 : find_arguments(const char *format, va_list args,
752 : : PrintfArgValue *argvalues)
753 : : {
754 : 0 : int ch;
755 : 0 : bool afterstar;
756 : 0 : int accum;
757 : 0 : int longlongflag;
758 : 0 : int longflag;
759 : 0 : int fmtpos;
760 : 0 : int i;
761 : 0 : int last_dollar = 0; /* Init to "no dollar arguments known" */
762 : 0 : PrintfArgType argtypes[PG_NL_ARGMAX + 1] = {0};
763 : :
764 : : /*
765 : : * This loop must accept the same format strings as the one in dopr().
766 : : * However, we don't need to analyze them to the same level of detail.
767 : : *
768 : : * Since we're only called if there's a dollar-type spec somewhere, we can
769 : : * fail immediately if we find a non-dollar spec. Per the C99 standard,
770 : : * all argument references in the format string must be one or the other.
771 : : */
772 [ # # ]: 0 : while (*format != '\0')
773 : : {
774 : : /* Locate next conversion specifier */
775 [ # # ]: 0 : if (*format != '%')
776 : : {
777 : : /* Unlike dopr, we can just quit if there's no more specifiers */
778 : 0 : format = strchr(format + 1, '%');
779 [ # # ]: 0 : if (format == NULL)
780 : 0 : break;
781 : 0 : }
782 : :
783 : : /* Process conversion spec starting at *format */
784 : 0 : format++;
785 : 0 : longflag = longlongflag = 0;
786 : 0 : fmtpos = accum = 0;
787 : 0 : afterstar = false;
788 : : nextch1:
789 : 0 : ch = *format++;
790 [ # # # # : 0 : switch (ch)
# # # # #
# # # # #
# ]
791 : : {
792 : : case '-':
793 : : case '+':
794 : 0 : goto nextch1;
795 : : case '0':
796 : : case '1':
797 : : case '2':
798 : : case '3':
799 : : case '4':
800 : : case '5':
801 : : case '6':
802 : : case '7':
803 : : case '8':
804 : : case '9':
805 : 0 : accum = accum * 10 + (ch - '0');
806 : 0 : goto nextch1;
807 : : case '.':
808 : 0 : accum = 0;
809 : 0 : goto nextch1;
810 : : case '*':
811 [ # # ]: 0 : if (afterstar)
812 : 0 : return false; /* previous star missing dollar */
813 : 0 : afterstar = true;
814 : 0 : accum = 0;
815 : 0 : goto nextch1;
816 : : case '$':
817 [ # # # # ]: 0 : if (accum <= 0 || accum > PG_NL_ARGMAX)
818 : 0 : return false;
819 [ # # ]: 0 : if (afterstar)
820 : : {
821 [ # # # # ]: 0 : if (argtypes[accum] &&
822 : 0 : argtypes[accum] != ATYPE_INT)
823 : 0 : return false;
824 : 0 : argtypes[accum] = ATYPE_INT;
825 [ # # ]: 0 : last_dollar = Max(last_dollar, accum);
826 : 0 : afterstar = false;
827 : 0 : }
828 : : else
829 : 0 : fmtpos = accum;
830 : 0 : accum = 0;
831 : 0 : goto nextch1;
832 : : case 'l':
833 [ # # ]: 0 : if (longflag)
834 : 0 : longlongflag = 1;
835 : : else
836 : 0 : longflag = 1;
837 : 0 : goto nextch1;
838 : : case 'j':
839 : : #if SIZEOF_INTMAX_T == SIZEOF_LONG
840 : 0 : longflag = 1;
841 : : #elif SIZEOF_INTMAX_T == SIZEOF_LONG_LONG
842 : : longlongflag = 1;
843 : : #else
844 : : #error "cannot find integer type of the same size as intmax_t"
845 : : #endif
846 : 0 : goto nextch1;
847 : : case 'z':
848 : : #if SIZEOF_SIZE_T == SIZEOF_LONG
849 : 0 : longflag = 1;
850 : : #elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
851 : : longlongflag = 1;
852 : : #else
853 : : #error "cannot find integer type of the same size as size_t"
854 : : #endif
855 : 0 : goto nextch1;
856 : : case 'h':
857 : : case '\'':
858 : : /* ignore these */
859 : 0 : goto nextch1;
860 : : case 'd':
861 : : case 'i':
862 : : case 'o':
863 : : case 'u':
864 : : case 'x':
865 : : case 'X':
866 [ # # ]: 0 : if (fmtpos)
867 : : {
868 : 0 : PrintfArgType atype;
869 : :
870 [ # # ]: 0 : if (longlongflag)
871 : 0 : atype = ATYPE_LONGLONG;
872 [ # # ]: 0 : else if (longflag)
873 : 0 : atype = ATYPE_LONG;
874 : : else
875 : 0 : atype = ATYPE_INT;
876 [ # # # # ]: 0 : if (argtypes[fmtpos] &&
877 : 0 : argtypes[fmtpos] != atype)
878 : 0 : return false;
879 : 0 : argtypes[fmtpos] = atype;
880 [ # # ]: 0 : last_dollar = Max(last_dollar, fmtpos);
881 [ # # ]: 0 : }
882 : : else
883 : 0 : return false; /* non-dollar conversion spec */
884 : 0 : break;
885 : : case 'c':
886 [ # # ]: 0 : if (fmtpos)
887 : : {
888 [ # # # # ]: 0 : if (argtypes[fmtpos] &&
889 : 0 : argtypes[fmtpos] != ATYPE_INT)
890 : 0 : return false;
891 : 0 : argtypes[fmtpos] = ATYPE_INT;
892 [ # # ]: 0 : last_dollar = Max(last_dollar, fmtpos);
893 : 0 : }
894 : : else
895 : 0 : return false; /* non-dollar conversion spec */
896 : 0 : break;
897 : : case 's':
898 : : case 'p':
899 [ # # ]: 0 : if (fmtpos)
900 : : {
901 [ # # # # ]: 0 : if (argtypes[fmtpos] &&
902 : 0 : argtypes[fmtpos] != ATYPE_CHARPTR)
903 : 0 : return false;
904 : 0 : argtypes[fmtpos] = ATYPE_CHARPTR;
905 [ # # ]: 0 : last_dollar = Max(last_dollar, fmtpos);
906 : 0 : }
907 : : else
908 : 0 : return false; /* non-dollar conversion spec */
909 : 0 : break;
910 : : case 'e':
911 : : case 'E':
912 : : case 'f':
913 : : case 'g':
914 : : case 'G':
915 [ # # ]: 0 : if (fmtpos)
916 : : {
917 [ # # # # ]: 0 : if (argtypes[fmtpos] &&
918 : 0 : argtypes[fmtpos] != ATYPE_DOUBLE)
919 : 0 : return false;
920 : 0 : argtypes[fmtpos] = ATYPE_DOUBLE;
921 [ # # ]: 0 : last_dollar = Max(last_dollar, fmtpos);
922 : 0 : }
923 : : else
924 : 0 : return false; /* non-dollar conversion spec */
925 : 0 : break;
926 : : case 'm':
927 : : case '%':
928 : 0 : break;
929 : : default:
930 : 0 : return false; /* bogus format string */
931 : : }
932 : :
933 : : /*
934 : : * If we finish the spec with afterstar still set, there's a
935 : : * non-dollar star in there.
936 : : */
937 [ # # ]: 0 : if (afterstar)
938 : 0 : return false; /* non-dollar conversion spec */
939 : : }
940 : :
941 : : /*
942 : : * Format appears valid so far, so collect the arguments in physical
943 : : * order. (Since we rejected any non-dollar specs that would have
944 : : * collected arguments, we know that dopr() hasn't collected any yet.)
945 : : */
946 [ # # ]: 0 : for (i = 1; i <= last_dollar; i++)
947 : : {
948 [ # # # # : 0 : switch (argtypes[i])
# # # ]
949 : : {
950 : : case ATYPE_NONE:
951 : 0 : return false;
952 : : case ATYPE_INT:
953 : 0 : argvalues[i].i = va_arg(args, int);
954 : 0 : break;
955 : : case ATYPE_LONG:
956 : 0 : argvalues[i].l = va_arg(args, long);
957 : 0 : break;
958 : : case ATYPE_LONGLONG:
959 : 0 : argvalues[i].ll = va_arg(args, long long);
960 : 0 : break;
961 : : case ATYPE_DOUBLE:
962 : 0 : argvalues[i].d = va_arg(args, double);
963 : 0 : break;
964 : : case ATYPE_CHARPTR:
965 : 0 : argvalues[i].cptr = va_arg(args, char *);
966 : 0 : break;
967 : : }
968 : 0 : }
969 : :
970 : 0 : return true;
971 : 0 : }
972 : :
973 : : static void
974 : 212202 : fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
975 : : int pointflag, PrintfTarget *target)
976 : : {
977 : 212202 : int padlen,
978 : : vallen; /* amount to pad */
979 : :
980 : : /*
981 : : * If a maxwidth (precision) is specified, we must not fetch more bytes
982 : : * than that.
983 : : */
984 [ + + ]: 212202 : if (pointflag)
985 : 2694 : vallen = strnlen(value, maxwidth);
986 : : else
987 : 209508 : vallen = strlen(value);
988 : :
989 : 212202 : padlen = compute_padlen(minlen, vallen, leftjust);
990 : :
991 [ + + ]: 212202 : if (padlen > 0)
992 : : {
993 : 90714 : dopr_outchmulti(' ', padlen, target);
994 : 90714 : padlen = 0;
995 : 90714 : }
996 : :
997 : 212202 : dostr(value, vallen, target);
998 : :
999 : 212202 : trailing_pad(padlen, target);
1000 : 212202 : }
1001 : :
1002 : : static void
1003 : 0 : fmtptr(const void *value, PrintfTarget *target)
1004 : : {
1005 : 0 : int vallen;
1006 : 0 : char convert[64];
1007 : :
1008 : : /* we rely on regular C library's snprintf to do the basic conversion */
1009 : 0 : vallen = snprintf(convert, sizeof(convert), "%p", value);
1010 [ # # ]: 0 : if (vallen < 0)
1011 : 0 : target->failed = true;
1012 : : else
1013 : 0 : dostr(convert, vallen, target);
1014 : 0 : }
1015 : :
1016 : : static void
1017 : 2318074 : fmtint(long long value, char type, int forcesign, int leftjust,
1018 : : int minlen, int zpad, int precision, int pointflag,
1019 : : PrintfTarget *target)
1020 : : {
1021 : 2318074 : unsigned long long uvalue;
1022 : 2318074 : int base;
1023 : 2318074 : int dosign;
1024 : 2318074 : const char *cvt = "0123456789abcdef";
1025 : 2318074 : int signvalue = 0;
1026 : 2318074 : char convert[64];
1027 : 2318074 : int vallen = 0;
1028 : 2318074 : int padlen; /* amount to pad */
1029 : 2318074 : int zeropad; /* extra leading zeroes */
1030 : :
1031 [ + + + - : 2318074 : switch (type)
+ + ]
1032 : : {
1033 : : case 'd':
1034 : : case 'i':
1035 : 1338371 : base = 10;
1036 : 1338371 : dosign = 1;
1037 : 1338371 : break;
1038 : : case 'o':
1039 : 21 : base = 8;
1040 : 21 : dosign = 0;
1041 : 21 : break;
1042 : : case 'u':
1043 : 973839 : base = 10;
1044 : 973839 : dosign = 0;
1045 : 973839 : break;
1046 : : case 'x':
1047 : 4204 : base = 16;
1048 : 4204 : dosign = 0;
1049 : 4204 : break;
1050 : : case 'X':
1051 : 1639 : cvt = "0123456789ABCDEF";
1052 : 1639 : base = 16;
1053 : 1639 : dosign = 0;
1054 : 1639 : break;
1055 : : default:
1056 : 0 : return; /* keep compiler quiet */
1057 : : }
1058 : :
1059 : : /* disable MSVC warning about applying unary minus to an unsigned value */
1060 : : #ifdef _MSC_VER
1061 : : #pragma warning(push)
1062 : : #pragma warning(disable: 4146)
1063 : : #endif
1064 : : /* Handle +/- */
1065 [ + + + + ]: 2318074 : if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
1066 : 84506 : uvalue = -(unsigned long long) value;
1067 : : else
1068 : 2233568 : uvalue = (unsigned long long) value;
1069 : : #ifdef _MSC_VER
1070 : : #pragma warning(pop)
1071 : : #endif
1072 : :
1073 : : /*
1074 : : * SUS: the result of converting 0 with an explicit precision of 0 is no
1075 : : * characters
1076 : : */
1077 [ + + - + : 2318074 : if (value == 0 && pointflag && precision == 0)
# # ]
1078 : 0 : vallen = 0;
1079 : : else
1080 : : {
1081 : : /*
1082 : : * Convert integer to string. We special-case each of the possible
1083 : : * base values so as to avoid general-purpose divisions. On most
1084 : : * machines, division by a fixed constant can be done much more
1085 : : * cheaply than a general divide.
1086 : : */
1087 [ + + ]: 2318074 : if (base == 10)
1088 : : {
1089 : 2312210 : do
1090 : : {
1091 : 4875930 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10];
1092 : 4875930 : uvalue = uvalue / 10;
1093 [ + + ]: 4875930 : } while (uvalue);
1094 : 2312210 : }
1095 [ + + ]: 5864 : else if (base == 16)
1096 : : {
1097 : 5843 : do
1098 : : {
1099 : 10298 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16];
1100 : 10298 : uvalue = uvalue / 16;
1101 [ + + ]: 10298 : } while (uvalue);
1102 : 5843 : }
1103 : : else /* base == 8 */
1104 : : {
1105 : 21 : do
1106 : : {
1107 : 63 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8];
1108 : 63 : uvalue = uvalue / 8;
1109 [ + + ]: 63 : } while (uvalue);
1110 : : }
1111 : : }
1112 : :
1113 [ + - ]: 2318074 : zeropad = Max(0, precision - vallen);
1114 : :
1115 : 2318074 : padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
1116 : :
1117 : 2318074 : leading_pad(zpad, signvalue, &padlen, target);
1118 : :
1119 [ - + ]: 2318074 : if (zeropad > 0)
1120 : 0 : dopr_outchmulti('0', zeropad, target);
1121 : :
1122 : 2318074 : dostr(convert + sizeof(convert) - vallen, vallen, target);
1123 : :
1124 : 2318074 : trailing_pad(padlen, target);
1125 [ - + ]: 2318074 : }
1126 : :
1127 : : static void
1128 : 5445 : fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
1129 : : {
1130 : 5445 : int padlen; /* amount to pad */
1131 : :
1132 : 5445 : padlen = compute_padlen(minlen, 1, leftjust);
1133 : :
1134 [ + - ]: 5445 : if (padlen > 0)
1135 : : {
1136 : 0 : dopr_outchmulti(' ', padlen, target);
1137 : 0 : padlen = 0;
1138 : 0 : }
1139 : :
1140 : 5445 : dopr_outch(value, target);
1141 : :
1142 : 5445 : trailing_pad(padlen, target);
1143 : 5445 : }
1144 : :
1145 : : static void
1146 : 58023 : fmtfloat(double value, char type, int forcesign, int leftjust,
1147 : : int minlen, int zpad, int precision, int pointflag,
1148 : : PrintfTarget *target)
1149 : : {
1150 : 58023 : int signvalue = 0;
1151 : 58023 : int prec;
1152 : 58023 : int vallen;
1153 : 58023 : char fmt[8];
1154 : 58023 : char convert[1024];
1155 : 58023 : int zeropadlen = 0; /* amount to pad with zeroes */
1156 : 58023 : int padlen; /* amount to pad with spaces */
1157 : :
1158 : : /*
1159 : : * We rely on the regular C library's snprintf to do the basic conversion,
1160 : : * then handle padding considerations here.
1161 : : *
1162 : : * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
1163 : : * too wildly more than that with other hardware. In "f" format, snprintf
1164 : : * could therefore generate at most 308 characters to the left of the
1165 : : * decimal point; while we need to allow the precision to get as high as
1166 : : * 308+17 to ensure that we don't truncate significant digits from very
1167 : : * small values. To handle both these extremes, we use a buffer of 1024
1168 : : * bytes and limit requested precision to 350 digits; this should prevent
1169 : : * buffer overrun even with non-IEEE math. If the original precision
1170 : : * request was more than 350, separately pad with zeroes.
1171 : : *
1172 : : * We handle infinities and NaNs specially to ensure platform-independent
1173 : : * output.
1174 : : */
1175 [ + - ]: 58023 : if (precision < 0) /* cover possible overflow of "accum" */
1176 : 0 : precision = 0;
1177 [ + - ]: 58023 : prec = Min(precision, 350);
1178 : :
1179 [ + + + + : 58023 : if (isnan(value))
+ - ]
1180 : : {
1181 : 38685 : strcpy(convert, "NaN");
1182 : 38685 : vallen = 3;
1183 : : /* no zero padding, regardless of precision spec */
1184 : 38685 : }
1185 : : else
1186 : : {
1187 : : /*
1188 : : * Handle sign (NaNs have no sign, so we don't do this in the case
1189 : : * above). "value < 0.0" will not be true for IEEE minus zero, so we
1190 : : * detect that by looking for the case where value equals 0.0
1191 : : * according to == but not according to memcmp.
1192 : : */
1193 : : static const double dzero = 0.0;
1194 : :
1195 [ + + + + : 38601 : if (adjust_sign((value < 0.0 ||
+ + ]
1196 [ + + ]: 19263 : (value == 0.0 &&
1197 : 2023 : memcmp(&value, &dzero, sizeof(double)) != 0)),
1198 : 19338 : forcesign, &signvalue))
1199 : 75 : value = -value;
1200 : :
1201 [ + + ]: 19338 : if (isinf(value))
1202 : : {
1203 : 19 : strcpy(convert, "Infinity");
1204 : 19 : vallen = 8;
1205 : : /* no zero padding, regardless of precision spec */
1206 : 19 : }
1207 [ + + ]: 19319 : else if (pointflag)
1208 : : {
1209 : 18323 : zeropadlen = precision - prec;
1210 : 18323 : fmt[0] = '%';
1211 : 18323 : fmt[1] = '.';
1212 : 18323 : fmt[2] = '*';
1213 : 18323 : fmt[3] = type;
1214 : 18323 : fmt[4] = '\0';
1215 : 18323 : vallen = snprintf(convert, sizeof(convert), fmt, prec, value);
1216 : 18323 : }
1217 : : else
1218 : : {
1219 : 996 : fmt[0] = '%';
1220 : 996 : fmt[1] = type;
1221 : 996 : fmt[2] = '\0';
1222 : 996 : vallen = snprintf(convert, sizeof(convert), fmt, value);
1223 : : }
1224 [ + - ]: 19338 : if (vallen < 0)
1225 : 0 : goto fail;
1226 : : }
1227 : :
1228 : 19347 : padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
1229 : :
1230 : 19347 : leading_pad(zpad, signvalue, &padlen, target);
1231 : :
1232 [ - + ]: 19347 : if (zeropadlen > 0)
1233 : : {
1234 : : /* If 'e' or 'E' format, inject zeroes before the exponent */
1235 : 0 : char *epos = strrchr(convert, 'e');
1236 : :
1237 [ # # ]: 0 : if (!epos)
1238 : 0 : epos = strrchr(convert, 'E');
1239 [ # # ]: 0 : if (epos)
1240 : : {
1241 : : /* pad before exponent */
1242 : 0 : dostr(convert, epos - convert, target);
1243 : 0 : dopr_outchmulti('0', zeropadlen, target);
1244 : 0 : dostr(epos, vallen - (epos - convert), target);
1245 : 0 : }
1246 : : else
1247 : : {
1248 : : /* no exponent, pad after the digits */
1249 : 0 : dostr(convert, vallen, target);
1250 : 0 : dopr_outchmulti('0', zeropadlen, target);
1251 : : }
1252 : 0 : }
1253 : : else
1254 : : {
1255 : : /* no zero padding, just emit the number as-is */
1256 : 19347 : dostr(convert, vallen, target);
1257 : : }
1258 : :
1259 : 19347 : trailing_pad(padlen, target);
1260 : 19347 : return;
1261 : :
1262 : : fail:
1263 : 0 : target->failed = true;
1264 [ - + ]: 19347 : }
1265 : :
1266 : : /*
1267 : : * Nonstandard entry point to print a double value efficiently.
1268 : : *
1269 : : * This is approximately equivalent to strfromd(), but has an API more
1270 : : * adapted to what float8out() wants. The behavior is like snprintf()
1271 : : * with a format of "%.ng", where n is the specified precision.
1272 : : * However, the target buffer must be nonempty (i.e. count > 0), and
1273 : : * the precision is silently bounded to a sane range.
1274 : : */
1275 : : int
1276 : 104779 : pg_strfromd(char *str, size_t count, int precision, double value)
1277 : : {
1278 : 104779 : PrintfTarget target;
1279 : 104779 : int signvalue = 0;
1280 : 104779 : int vallen;
1281 : 104779 : char fmt[8];
1282 : 104779 : char convert[64];
1283 : :
1284 : : /* Set up the target like pg_snprintf, but require nonempty buffer */
1285 [ + - ]: 104779 : Assert(count > 0);
1286 : 104779 : target.bufstart = target.bufptr = str;
1287 : 104779 : target.bufend = str + count - 1;
1288 : 104779 : target.stream = NULL;
1289 : 104779 : target.nchars = 0;
1290 : 104779 : target.failed = false;
1291 : :
1292 : : /*
1293 : : * We bound precision to a reasonable range; the combination of this and
1294 : : * the knowledge that we're using "g" format without padding allows the
1295 : : * convert[] buffer to be reasonably small.
1296 : : */
1297 [ + - ]: 104779 : if (precision < 1)
1298 : 0 : precision = 1;
1299 [ + - ]: 104779 : else if (precision > 32)
1300 : 0 : precision = 32;
1301 : :
1302 : : /*
1303 : : * The rest is just an inlined version of the fmtfloat() logic above,
1304 : : * simplified using the knowledge that no padding is wanted.
1305 : : */
1306 [ + + + + : 104779 : if (isnan(value))
+ - ]
1307 : : {
1308 : 70527 : strcpy(convert, "NaN");
1309 : 70527 : vallen = 3;
1310 : 70527 : }
1311 : : else
1312 : : {
1313 : : static const double dzero = 0.0;
1314 : :
1315 [ + + + + ]: 38729 : if (value < 0.0 ||
1316 [ + + ]: 28852 : (value == 0.0 &&
1317 : 4477 : memcmp(&value, &dzero, sizeof(double)) != 0))
1318 : : {
1319 : 5411 : signvalue = '-';
1320 : 5411 : value = -value;
1321 : 5411 : }
1322 : :
1323 [ + + ]: 34252 : if (isinf(value))
1324 : : {
1325 : 1186 : strcpy(convert, "Infinity");
1326 : 1186 : vallen = 8;
1327 : 1186 : }
1328 : : else
1329 : : {
1330 : 33066 : fmt[0] = '%';
1331 : 33066 : fmt[1] = '.';
1332 : 33066 : fmt[2] = '*';
1333 : 33066 : fmt[3] = 'g';
1334 : 33066 : fmt[4] = '\0';
1335 : 33066 : vallen = snprintf(convert, sizeof(convert), fmt, precision, value);
1336 [ + - ]: 33066 : if (vallen < 0)
1337 : : {
1338 : 0 : target.failed = true;
1339 : 0 : goto fail;
1340 : : }
1341 : : }
1342 : : }
1343 : :
1344 [ + + ]: 36275 : if (signvalue)
1345 : 5411 : dopr_outch(signvalue, &target);
1346 : :
1347 : 36275 : dostr(convert, vallen, &target);
1348 : :
1349 : : fail:
1350 : 36275 : *(target.bufptr) = '\0';
1351 [ - + ]: 36275 : return target.failed ? -1 : (target.bufptr - target.bufstart
1352 : 36275 : + target.nchars);
1353 : 36275 : }
1354 : :
1355 : :
1356 : : static void
1357 : 5897465 : dostr(const char *str, int slen, PrintfTarget *target)
1358 : : {
1359 : : /* fast path for common case of slen == 1 */
1360 [ + + ]: 5897465 : if (slen == 1)
1361 : : {
1362 : 2300117 : dopr_outch(*str, target);
1363 : 2300117 : return;
1364 : : }
1365 : :
1366 [ + + ]: 6983418 : while (slen > 0)
1367 : : {
1368 : 3386070 : int avail;
1369 : :
1370 [ + + ]: 3386070 : if (target->bufend != NULL)
1371 : 2298900 : avail = target->bufend - target->bufptr;
1372 : : else
1373 : 1087170 : avail = slen;
1374 [ + + ]: 3386070 : if (avail <= 0)
1375 : : {
1376 : : /* buffer full, can we dump to stream? */
1377 [ + + ]: 15650 : if (target->stream == NULL)
1378 : : {
1379 : 15622 : target->nchars += slen; /* no, lose the data */
1380 : 15622 : return;
1381 : : }
1382 : 28 : flushbuffer(target);
1383 : 28 : continue;
1384 : : }
1385 [ + + ]: 3370420 : avail = Min(avail, slen);
1386 : 3370420 : memmove(target->bufptr, str, avail);
1387 : 3370420 : target->bufptr += avail;
1388 : 3370420 : str += avail;
1389 : 3370420 : slen -= avail;
1390 [ - + + ]: 3386070 : }
1391 : 5897465 : }
1392 : :
1393 : : static void
1394 : 2537537 : dopr_outch(int c, PrintfTarget *target)
1395 : : {
1396 [ + + + + ]: 2537537 : if (target->bufend != NULL && target->bufptr >= target->bufend)
1397 : : {
1398 : : /* buffer full, can we dump to stream? */
1399 [ - + ]: 1111 : if (target->stream == NULL)
1400 : : {
1401 : 1111 : target->nchars++; /* no, lose the data */
1402 : 1111 : return;
1403 : : }
1404 : 0 : flushbuffer(target);
1405 : 0 : }
1406 : 2536426 : *(target->bufptr++) = c;
1407 : 2537537 : }
1408 : :
1409 : : static void
1410 : 243640 : dopr_outchmulti(int c, int slen, PrintfTarget *target)
1411 : : {
1412 : : /* fast path for common case of slen == 1 */
1413 [ + + ]: 243640 : if (slen == 1)
1414 : : {
1415 : 141761 : dopr_outch(c, target);
1416 : 141761 : return;
1417 : : }
1418 : :
1419 [ + + ]: 203795 : while (slen > 0)
1420 : : {
1421 : 101916 : int avail;
1422 : :
1423 [ + + ]: 101916 : if (target->bufend != NULL)
1424 : 96459 : avail = target->bufend - target->bufptr;
1425 : : else
1426 : 5457 : avail = slen;
1427 [ + + ]: 101916 : if (avail <= 0)
1428 : : {
1429 : : /* buffer full, can we dump to stream? */
1430 [ + - ]: 19 : if (target->stream == NULL)
1431 : : {
1432 : 0 : target->nchars += slen; /* no, lose the data */
1433 : 0 : return;
1434 : : }
1435 : 19 : flushbuffer(target);
1436 : 19 : continue;
1437 : : }
1438 [ + + ]: 101897 : avail = Min(avail, slen);
1439 : 101897 : memset(target->bufptr, c, avail);
1440 : 101897 : target->bufptr += avail;
1441 : 101897 : slen -= avail;
1442 [ - + + ]: 101916 : }
1443 : 243640 : }
1444 : :
1445 : :
1446 : : static int
1447 : 1357709 : adjust_sign(int is_negative, int forcesign, int *signvalue)
1448 : : {
1449 [ + + ]: 1357709 : if (is_negative)
1450 : : {
1451 : 84581 : *signvalue = '-';
1452 : 84581 : return true;
1453 : : }
1454 [ + + ]: 1273128 : else if (forcesign)
1455 : 34 : *signvalue = '+';
1456 : 1273128 : return false;
1457 : 1357709 : }
1458 : :
1459 : :
1460 : : static int
1461 : 2555068 : compute_padlen(int minlen, int vallen, int leftjust)
1462 : : {
1463 : 2555068 : int padlen;
1464 : :
1465 : 2555068 : padlen = minlen - vallen;
1466 [ + + ]: 2555068 : if (padlen < 0)
1467 : 1730381 : padlen = 0;
1468 [ + + ]: 2555068 : if (leftjust)
1469 : 80828 : padlen = -padlen;
1470 : 5110136 : return padlen;
1471 : 2555068 : }
1472 : :
1473 : :
1474 : : static void
1475 : 2337421 : leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
1476 : : {
1477 : 2337421 : int maxpad;
1478 : :
1479 [ + + + + ]: 2337421 : if (*padlen > 0 && zpad)
1480 : : {
1481 [ + + ]: 123458 : if (signvalue)
1482 : : {
1483 : 32 : dopr_outch(signvalue, target);
1484 : 32 : --(*padlen);
1485 : 32 : signvalue = 0;
1486 : 32 : }
1487 [ + + ]: 123458 : if (*padlen > 0)
1488 : : {
1489 : 123455 : dopr_outchmulti(zpad, *padlen, target);
1490 : 123455 : *padlen = 0;
1491 : 123455 : }
1492 : 123458 : }
1493 : 2337421 : maxpad = (signvalue != 0);
1494 [ + + ]: 2337421 : if (*padlen > maxpad)
1495 : : {
1496 : 496 : dopr_outchmulti(' ', *padlen - maxpad, target);
1497 : 496 : *padlen = maxpad;
1498 : 496 : }
1499 [ + + ]: 2337421 : if (signvalue)
1500 : : {
1501 : 84583 : dopr_outch(signvalue, target);
1502 [ - + ]: 84583 : if (*padlen > 0)
1503 : 0 : --(*padlen);
1504 [ + - ]: 84583 : else if (*padlen < 0)
1505 : 0 : ++(*padlen);
1506 : 84583 : }
1507 : 2337421 : }
1508 : :
1509 : :
1510 : : static void
1511 : 2555068 : trailing_pad(int padlen, PrintfTarget *target)
1512 : : {
1513 [ + + ]: 2555068 : if (padlen < 0)
1514 : 28975 : dopr_outchmulti(' ', -padlen, target);
1515 : 2555068 : }
|