Age Owner Branch data TLA 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-2025, 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
7585 bruce@momjian.us 174 :CBC 32151910 : pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
175 : : {
176 : : PrintfTarget target;
177 : : 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 : : */
2680 tgl@sss.pgh.pa.us 185 [ + + ]: 32151910 : if (count == 0)
186 : : {
187 : 61146 : str = onebyte;
188 : 61146 : count = 1;
189 : : }
7316 190 : 32151910 : target.bufstart = target.bufptr = str;
191 : 32151910 : target.bufend = str + count - 1;
192 : 32151910 : target.stream = NULL;
2680 193 : 32151910 : target.nchars = 0;
3865 noah@leadboat.com 194 : 32151910 : target.failed = false;
195 : 32151910 : dopr(&target, fmt, args);
7316 tgl@sss.pgh.pa.us 196 : 32151909 : *(target.bufptr) = '\0';
2680 197 [ - + ]: 64303818 : return target.failed ? -1 : (target.bufptr - target.bufstart
198 : 32151909 : + target.nchars);
199 : : }
200 : :
201 : : int
7585 bruce@momjian.us 202 : 15437249 : pg_snprintf(char *str, size_t count, const char *fmt,...)
203 : : {
204 : : int len;
205 : : va_list args;
206 : :
9860 207 : 15437249 : va_start(args, fmt);
7585 208 : 15437249 : len = pg_vsnprintf(str, count, fmt, args);
9860 209 : 15437248 : va_end(args);
9965 scrappy@hub.org 210 : 15437248 : return len;
211 : : }
212 : :
213 : : int
7316 tgl@sss.pgh.pa.us 214 : 5422804 : pg_vsprintf(char *str, const char *fmt, va_list args)
215 : : {
216 : : PrintfTarget target;
217 : :
218 : 5422804 : target.bufstart = target.bufptr = str;
219 : 5422804 : target.bufend = NULL;
220 : 5422804 : target.stream = NULL;
2680 221 : 5422804 : target.nchars = 0; /* not really used in this case */
3865 noah@leadboat.com 222 : 5422804 : target.failed = false;
223 : 5422804 : dopr(&target, fmt, args);
7316 tgl@sss.pgh.pa.us 224 : 5422801 : *(target.bufptr) = '\0';
2680 225 [ - + ]: 10845602 : return target.failed ? -1 : (target.bufptr - target.bufstart
226 : 5422801 : + target.nchars);
227 : : }
228 : :
229 : : int
7580 bruce@momjian.us 230 : 5422804 : pg_sprintf(char *str, const char *fmt,...)
231 : : {
232 : : int len;
233 : : va_list args;
234 : :
235 : 5422804 : va_start(args, fmt);
7316 tgl@sss.pgh.pa.us 236 : 5422804 : len = pg_vsprintf(str, fmt, args);
7580 bruce@momjian.us 237 : 5422800 : va_end(args);
238 : 5422800 : return len;
239 : : }
240 : :
241 : : int
7316 tgl@sss.pgh.pa.us 242 : 2006790 : pg_vfprintf(FILE *stream, const char *fmt, va_list args)
243 : : {
244 : : PrintfTarget target;
245 : : char buffer[1024]; /* size is arbitrary */
246 : :
247 [ - + ]: 2006790 : if (stream == NULL)
248 : : {
7316 tgl@sss.pgh.pa.us 249 :UBC 0 : errno = EINVAL;
250 : 0 : return -1;
251 : : }
7316 tgl@sss.pgh.pa.us 252 :CBC 2006790 : target.bufstart = target.bufptr = buffer;
2680 253 : 2006790 : target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
7316 254 : 2006790 : target.stream = stream;
255 : 2006790 : target.nchars = 0;
3865 noah@leadboat.com 256 : 2006790 : target.failed = false;
257 : 2006790 : dopr(&target, fmt, args);
258 : : /* dump any remaining buffer contents */
7316 tgl@sss.pgh.pa.us 259 : 2006790 : flushbuffer(&target);
3865 noah@leadboat.com 260 [ - + ]: 2006790 : return target.failed ? -1 : target.nchars;
261 : : }
262 : :
263 : : int
7585 bruce@momjian.us 264 : 867100 : pg_fprintf(FILE *stream, const char *fmt,...)
265 : : {
266 : : int len;
267 : : va_list args;
268 : :
269 : 867100 : va_start(args, fmt);
7316 tgl@sss.pgh.pa.us 270 : 867100 : len = pg_vfprintf(stream, fmt, args);
7585 bruce@momjian.us 271 : 867100 : va_end(args);
272 : 867100 : return len;
273 : : }
274 : :
275 : : int
2626 tgl@sss.pgh.pa.us 276 :UBC 0 : pg_vprintf(const char *fmt, va_list args)
277 : : {
278 : 0 : return pg_vfprintf(stdout, fmt, args);
279 : : }
280 : :
281 : : int
7585 bruce@momjian.us 282 :CBC 1131607 : pg_printf(const char *fmt,...)
283 : : {
284 : : int len;
285 : : va_list args;
286 : :
7594 287 : 1131607 : va_start(args, fmt);
7316 tgl@sss.pgh.pa.us 288 : 1131607 : len = pg_vfprintf(stdout, fmt, args);
7594 bruce@momjian.us 289 : 1131607 : va_end(args);
290 : 1131607 : return len;
291 : : }
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
5364 298 : 2007647 : flushbuffer(PrintfTarget *target)
299 : : {
7013 300 : 2007647 : 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 : : */
3865 noah@leadboat.com 306 [ + - + + ]: 2007647 : if (!target->failed && nc > 0)
307 : : {
308 : : size_t written;
309 : :
310 : 1873401 : written = fwrite(target->bufstart, 1, nc, target->stream);
311 : 1873401 : target->nchars += written;
312 [ - + ]: 1873401 : if (written != nc)
3865 noah@leadboat.com 313 :UBC 0 : target->failed = true;
314 : : }
7316 tgl@sss.pgh.pa.us 315 :CBC 2007647 : target->bufptr = target->bufstart;
316 : 2007647 : }
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 : : strchrnul(const char *s, int c)
360 : : {
361 : : while (*s != '\0' && *s != c)
362 : : s++;
363 : : 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
5364 bruce@momjian.us 373 : 39581505 : dopr(PrintfTarget *target, const char *format, va_list args)
374 : : {
2638 tgl@sss.pgh.pa.us 375 : 39581505 : int save_errno = errno;
2631 376 : 39581505 : const char *first_pct = NULL;
377 : : int ch;
378 : : bool have_dollar;
379 : : bool have_star;
380 : : bool afterstar;
381 : : int accum;
382 : : int longlongflag;
383 : : int longflag;
384 : : int pointflag;
385 : : int leftjust;
386 : : int fieldwidth;
387 : : int precision;
388 : : int zpad;
389 : : int forcesign;
390 : : int fmtpos;
391 : : int cvalue;
392 : : long long numvalue;
393 : : double fvalue;
394 : : const char *strvalue;
395 : : 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 : 39581505 : have_dollar = false;
404 : :
405 [ + + ]: 90071636 : while (*format != '\0')
406 : : {
407 : : /* Locate next conversion specifier */
408 [ + + ]: 55742563 : if (*format != '%')
409 : : {
410 : : /* Scan to next '%' or end of string */
411 : 31908919 : const char *next_pct = strchrnul(format + 1, '%');
412 : :
413 : : /* Dump literal data we just scanned over */
414 : 31908919 : dostr(format, next_pct - format, target);
415 [ - + ]: 31908920 : if (target->failed)
7316 tgl@sss.pgh.pa.us 416 :UBC 0 : break;
417 : :
2631 tgl@sss.pgh.pa.us 418 [ + + ]:CBC 31908920 : if (*next_pct == '\0')
7316 419 : 5252430 : break;
2631 420 : 26656490 : format = next_pct;
421 : : }
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 [ + + ]: 50490134 : if (first_pct == NULL)
429 : 39282045 : first_pct = format;
430 : :
431 : : /* Process conversion spec starting at *format */
432 : 50490134 : format++;
433 : :
434 : : /* Fast path for conversion spec that is exactly %s */
435 [ + + ]: 50490134 : if (*format == 's')
436 : : {
437 : 12208656 : format++;
438 : 12208656 : strvalue = va_arg(args, char *);
1606 439 [ + + ]: 12208657 : if (strvalue == NULL)
440 : 4 : strvalue = "(null)";
2631 441 : 12208657 : dostr(strvalue, strlen(strvalue), target);
442 [ - + ]: 12208658 : if (target->failed)
2631 tgl@sss.pgh.pa.us 443 :UBC 0 : break;
2631 tgl@sss.pgh.pa.us 444 :CBC 12208658 : continue;
445 : : }
446 : :
7316 447 : 38281478 : fieldwidth = precision = zpad = leftjust = forcesign = 0;
448 : 38281478 : longflag = longlongflag = pointflag = 0;
449 : 38281478 : fmtpos = accum = 0;
450 : 38281478 : have_star = afterstar = false;
7013 bruce@momjian.us 451 : 14524945 : nextch2:
7316 tgl@sss.pgh.pa.us 452 : 52806423 : ch = *format++;
9932 bruce@momjian.us 453 [ + + + + : 52806423 : switch (ch)
+ + - + -
+ + + + +
+ + + + +
- ]
454 : : {
7316 tgl@sss.pgh.pa.us 455 : 573098 : case '-':
456 : 573098 : leftjust = 1;
457 : 573098 : goto nextch2;
458 : 141 : case '+':
459 : 141 : forcesign = 1;
460 : 141 : goto nextch2;
461 : 4578575 : case '0':
462 : : /* set zero padding if no nonzero digits yet */
463 [ + + + + ]: 4578575 : if (accum == 0 && !pointflag)
464 : 4286387 : 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 : 10297736 : accum = accum * 10 + (ch - '0');
476 : 10297736 : goto nextch2;
477 : 82839 : case '.':
478 [ + + ]: 82839 : if (have_star)
7316 tgl@sss.pgh.pa.us 479 :GBC 148 : have_star = false;
480 : : else
7316 tgl@sss.pgh.pa.us 481 :CBC 82691 : fieldwidth = accum;
482 : 82839 : pointflag = 1;
483 : 82839 : accum = 0;
484 : 82839 : goto nextch2;
485 : 736749 : case '*':
486 [ - + ]: 736749 : 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 : : */
7316 tgl@sss.pgh.pa.us 493 :UBC 0 : afterstar = true;
494 : : }
495 : : else
496 : : {
497 : : /* fetch and process value now */
7013 bruce@momjian.us 498 :CBC 736749 : int starval = va_arg(args, int);
499 : :
7316 tgl@sss.pgh.pa.us 500 [ + + ]: 736749 : if (pointflag)
501 : : {
502 : 30430 : precision = starval;
503 [ - + ]: 30430 : if (precision < 0)
504 : : {
7316 tgl@sss.pgh.pa.us 505 :UBC 0 : precision = 0;
6482 506 : 0 : pointflag = 0;
507 : : }
508 : : }
509 : : else
510 : : {
7316 tgl@sss.pgh.pa.us 511 :CBC 706319 : fieldwidth = starval;
512 [ + + ]: 706319 : if (fieldwidth < 0)
513 : : {
514 : 2823 : leftjust = 1;
515 : 2823 : fieldwidth = -fieldwidth;
516 : : }
517 : : }
518 : : }
519 : 736749 : have_star = true;
520 : 736749 : accum = 0;
521 : 736749 : goto nextch2;
7316 tgl@sss.pgh.pa.us 522 :UBC 0 : case '$':
523 : : /* First dollar sign? */
2631 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 : : }
7316 531 [ # # ]: 0 : if (afterstar)
532 : : {
533 : : /* fetch and process star value */
7013 bruce@momjian.us 534 : 0 : int starval = argvalues[accum].i;
535 : :
7316 tgl@sss.pgh.pa.us 536 [ # # ]: 0 : if (pointflag)
537 : : {
538 : 0 : precision = starval;
539 [ # # ]: 0 : if (precision < 0)
540 : : {
541 : 0 : precision = 0;
6482 542 : 0 : pointflag = 0;
543 : : }
544 : : }
545 : : else
546 : : {
7316 547 : 0 : fieldwidth = starval;
548 [ # # ]: 0 : if (fieldwidth < 0)
549 : : {
550 : 0 : leftjust = 1;
551 : 0 : fieldwidth = -fieldwidth;
552 : : }
553 : : }
554 : 0 : afterstar = false;
555 : : }
556 : : else
557 : 0 : fmtpos = accum;
558 : 0 : accum = 0;
559 : 0 : goto nextch2;
7316 tgl@sss.pgh.pa.us 560 :CBC 2578225 : case 'l':
561 [ + + ]: 2578225 : if (longflag)
562 : 4246 : longlongflag = 1;
563 : : else
564 : 2573979 : longflag = 1;
565 : 2578225 : goto nextch2;
7 tgl@sss.pgh.pa.us 566 :UNC 0 : case 'j':
567 : : #if SIZEOF_INTMAX_T == SIZEOF_LONG
568 : 0 : 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 : 0 : goto nextch2;
4345 tgl@sss.pgh.pa.us 575 :CBC 256135 : case 'z':
576 : : #if SIZEOF_SIZE_T == SIZEOF_LONG
577 : 256135 : 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 : 256135 : goto nextch2;
7316 584 : 22 : case 'h':
585 : : case '\'':
586 : : /* ignore these */
587 : 22 : goto nextch2;
588 : 14984607 : case 'd':
589 : : case 'i':
590 [ + + ]: 14984607 : if (!have_star)
591 : : {
592 [ - + ]: 14964385 : if (pointflag)
7316 tgl@sss.pgh.pa.us 593 :UBC 0 : precision = accum;
594 : : else
7316 tgl@sss.pgh.pa.us 595 :CBC 14964385 : fieldwidth = accum;
596 : : }
597 [ - + ]: 14984607 : if (have_dollar)
598 : : {
7316 tgl@sss.pgh.pa.us 599 [ # # ]:UBC 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 : : }
606 : : else
607 : : {
7316 tgl@sss.pgh.pa.us 608 [ + + ]:CBC 14984607 : if (longlongflag)
2631 609 : 4228 : numvalue = va_arg(args, long long);
7316 610 [ + + ]: 14980379 : else if (longflag)
611 : 2400249 : numvalue = va_arg(args, long);
612 : : else
613 : 12580130 : numvalue = va_arg(args, int);
614 : : }
615 : 14984608 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
616 : : precision, pointflag, target);
9932 bruce@momjian.us 617 : 14984608 : break;
7316 tgl@sss.pgh.pa.us 618 : 21973223 : case 'o':
619 : : case 'u':
620 : : case 'x':
621 : : case 'X':
622 [ + - ]: 21973223 : if (!have_star)
623 : : {
624 [ - + ]: 21973223 : if (pointflag)
7316 tgl@sss.pgh.pa.us 625 :UBC 0 : precision = accum;
626 : : else
7316 tgl@sss.pgh.pa.us 627 :CBC 21973223 : fieldwidth = accum;
628 : : }
629 [ - + ]: 21973223 : if (have_dollar)
630 : : {
7316 tgl@sss.pgh.pa.us 631 [ # # ]:UBC 0 : if (longlongflag)
2631 632 : 0 : numvalue = (unsigned long long) argvalues[fmtpos].ll;
7316 633 [ # # ]: 0 : else if (longflag)
634 : 0 : numvalue = (unsigned long) argvalues[fmtpos].l;
635 : : else
636 : 0 : numvalue = (unsigned int) argvalues[fmtpos].i;
637 : : }
638 : : else
639 : : {
7316 tgl@sss.pgh.pa.us 640 [ + + ]:CBC 21973223 : if (longlongflag)
2631 641 : 18 : numvalue = (unsigned long long) va_arg(args, long long);
7316 642 [ + + ]: 21973205 : else if (longflag)
643 : 425619 : numvalue = (unsigned long) va_arg(args, long);
644 : : else
645 : 21547586 : numvalue = (unsigned int) va_arg(args, int);
646 : : }
647 : 21973223 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
648 : : precision, pointflag, target);
7580 bruce@momjian.us 649 : 21973223 : break;
7316 tgl@sss.pgh.pa.us 650 : 20146 : case 'c':
651 [ + + ]: 20146 : if (!have_star)
652 : : {
653 [ - + ]: 20125 : if (pointflag)
7316 tgl@sss.pgh.pa.us 654 :UBC 0 : precision = accum;
655 : : else
7316 tgl@sss.pgh.pa.us 656 :CBC 20125 : fieldwidth = accum;
657 : : }
658 [ - + ]: 20146 : if (have_dollar)
7316 tgl@sss.pgh.pa.us 659 :UBC 0 : cvalue = (unsigned char) argvalues[fmtpos].i;
660 : : else
7316 tgl@sss.pgh.pa.us 661 :CBC 20146 : cvalue = (unsigned char) va_arg(args, int);
662 : 20146 : fmtchar(cvalue, leftjust, fieldwidth, target);
7580 bruce@momjian.us 663 : 20146 : break;
7316 tgl@sss.pgh.pa.us 664 : 1006543 : case 's':
665 [ + + ]: 1006543 : if (!have_star)
666 : : {
667 [ - + ]: 310324 : if (pointflag)
7316 tgl@sss.pgh.pa.us 668 :UBC 0 : precision = accum;
669 : : else
7316 tgl@sss.pgh.pa.us 670 :CBC 310324 : fieldwidth = accum;
671 : : }
672 [ - + ]: 1006543 : if (have_dollar)
7316 tgl@sss.pgh.pa.us 673 :UBC 0 : strvalue = argvalues[fmtpos].cptr;
674 : : else
7316 tgl@sss.pgh.pa.us 675 :CBC 1006543 : strvalue = va_arg(args, char *);
676 : : /* If string is NULL, silently substitute "(null)" */
1606 677 [ - + ]: 1006543 : if (strvalue == NULL)
1606 tgl@sss.pgh.pa.us 678 :UBC 0 : strvalue = "(null)";
7316 tgl@sss.pgh.pa.us 679 :CBC 1006543 : fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
680 : : target);
7580 bruce@momjian.us 681 : 1006543 : break;
7316 tgl@sss.pgh.pa.us 682 : 279 : case 'p':
683 : : /* fieldwidth/leftjust are ignored ... */
684 [ - + ]: 279 : if (have_dollar)
7316 tgl@sss.pgh.pa.us 685 :UBC 0 : strvalue = argvalues[fmtpos].cptr;
686 : : else
7316 tgl@sss.pgh.pa.us 687 :CBC 279 : strvalue = va_arg(args, char *);
1606 688 : 279 : fmtptr((const void *) strvalue, target);
7580 bruce@momjian.us 689 : 279 : break;
7316 tgl@sss.pgh.pa.us 690 : 294116 : case 'e':
691 : : case 'E':
692 : : case 'f':
693 : : case 'g':
694 : : case 'G':
695 [ + + ]: 294116 : if (!have_star)
696 : : {
697 [ + + ]: 273975 : if (pointflag)
698 : 52409 : precision = accum;
699 : : else
700 : 221566 : fieldwidth = accum;
701 : : }
702 [ - + ]: 294116 : if (have_dollar)
7316 tgl@sss.pgh.pa.us 703 :UBC 0 : fvalue = argvalues[fmtpos].d;
704 : : else
7316 tgl@sss.pgh.pa.us 705 :CBC 294116 : fvalue = va_arg(args, double);
706 : 294116 : fmtfloat(fvalue, ch, forcesign, leftjust,
707 : : fieldwidth, zpad,
708 : : precision, pointflag,
709 : : target);
7580 bruce@momjian.us 710 : 294116 : break;
2638 tgl@sss.pgh.pa.us 711 : 181 : case 'm':
712 : : {
713 : : char errbuf[PG_STRERROR_R_BUFLEN];
714 : 181 : const char *errm = strerror_r(save_errno,
715 : : errbuf, sizeof(errbuf));
716 : :
717 : 181 : dostr(errm, strlen(errm), target);
718 : : }
719 : 176 : break;
7316 720 : 2383 : case '%':
721 : 2383 : dopr_outch('%', target);
7580 bruce@momjian.us 722 : 2383 : break;
2567 tgl@sss.pgh.pa.us 723 :UBC 0 : 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 */
2631 tgl@sss.pgh.pa.us 733 [ - + ]:CBC 38281473 : if (target->failed)
2631 tgl@sss.pgh.pa.us 734 :UBC 0 : break;
735 : : }
736 : :
3865 noah@leadboat.com 737 :CBC 39581503 : return;
738 : :
3865 noah@leadboat.com 739 :UBC 0 : bad_format:
740 : 0 : errno = EINVAL;
741 : 0 : target->failed = true;
742 : : }
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
2631 tgl@sss.pgh.pa.us 751 : 0 : find_arguments(const char *format, va_list args,
752 : : PrintfArgValue *argvalues)
753 : : {
754 : : int ch;
755 : : bool afterstar;
756 : : int accum;
757 : : int longlongflag;
758 : : int longflag;
759 : : int fmtpos;
760 : : int i;
1249 peter@eisentraut.org 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 : : */
2631 tgl@sss.pgh.pa.us 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 : : }
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 : 0 : nextch1:
789 : 0 : ch = *format++;
790 [ # # # # : 0 : switch (ch)
# # # # #
# # # # #
# ]
791 : : {
792 : 0 : case '-':
793 : : case '+':
794 : 0 : goto nextch1;
795 : 0 : 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 : 0 : case '.':
808 : 0 : accum = 0;
809 : 0 : goto nextch1;
810 : 0 : 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 : 0 : 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 : : }
828 : : else
829 : 0 : fmtpos = accum;
830 : 0 : accum = 0;
831 : 0 : goto nextch1;
832 : 0 : case 'l':
833 [ # # ]: 0 : if (longflag)
834 : 0 : longlongflag = 1;
835 : : else
836 : 0 : longflag = 1;
837 : 0 : goto nextch1;
7 tgl@sss.pgh.pa.us 838 :UNC 0 : 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;
2631 tgl@sss.pgh.pa.us 847 :UBC 0 : 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 : 0 : case 'h':
857 : : case '\'':
858 : : /* ignore these */
859 : 0 : goto nextch1;
860 : 0 : case 'd':
861 : : case 'i':
862 : : case 'o':
863 : : case 'u':
864 : : case 'x':
865 : : case 'X':
866 [ # # ]: 0 : if (fmtpos)
867 : : {
868 : : 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 : : }
882 : : else
883 : 0 : return false; /* non-dollar conversion spec */
884 : 0 : break;
885 : 0 : 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 : : }
894 : : else
895 : 0 : return false; /* non-dollar conversion spec */
896 : 0 : break;
897 : 0 : 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 : : }
907 : : else
908 : 0 : return false; /* non-dollar conversion spec */
909 : 0 : break;
910 : 0 : 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 : : }
923 : : else
924 : 0 : return false; /* non-dollar conversion spec */
925 : 0 : break;
926 : 0 : case 'm':
927 : : case '%':
928 : 0 : break;
2567 929 : 0 : 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 : : */
2631 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 : 0 : case ATYPE_NONE:
951 : 0 : return false;
952 : 0 : case ATYPE_INT:
953 : 0 : argvalues[i].i = va_arg(args, int);
954 : 0 : break;
955 : 0 : case ATYPE_LONG:
956 : 0 : argvalues[i].l = va_arg(args, long);
957 : 0 : break;
958 : 0 : case ATYPE_LONGLONG:
959 : 0 : argvalues[i].ll = va_arg(args, long long);
960 : 0 : break;
961 : 0 : case ATYPE_DOUBLE:
962 : 0 : argvalues[i].d = va_arg(args, double);
963 : 0 : break;
964 : 0 : case ATYPE_CHARPTR:
965 : 0 : argvalues[i].cptr = va_arg(args, char *);
966 : 0 : break;
967 : : }
968 : : }
969 : :
970 : 0 : return true;
971 : : }
972 : :
973 : : static void
2631 tgl@sss.pgh.pa.us 974 :CBC 1006543 : fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
975 : : int pointflag, PrintfTarget *target)
976 : : {
977 : : 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 : : */
7316 984 [ + + ]: 1006543 : if (pointflag)
2989 andres@anarazel.de 985 : 10289 : vallen = strnlen(value, maxwidth);
986 : : else
7316 tgl@sss.pgh.pa.us 987 : 996254 : vallen = strlen(value);
988 : :
2631 989 : 1006543 : padlen = compute_padlen(minlen, vallen, leftjust);
990 : :
991 [ + + ]: 1006543 : if (padlen > 0)
992 : : {
993 : 299378 : dopr_outchmulti(' ', padlen, target);
994 : 299378 : padlen = 0;
995 : : }
996 : :
7316 997 : 1006543 : dostr(value, vallen, target);
998 : :
2631 999 : 1006543 : trailing_pad(padlen, target);
9965 scrappy@hub.org 1000 : 1006543 : }
1001 : :
1002 : : static void
1606 tgl@sss.pgh.pa.us 1003 : 279 : fmtptr(const void *value, PrintfTarget *target)
1004 : : {
1005 : : int vallen;
1006 : : char convert[64];
1007 : :
1008 : : /* we rely on regular C library's snprintf to do the basic conversion */
1157 1009 : 279 : vallen = snprintf(convert, sizeof(convert), "%p", value);
3865 noah@leadboat.com 1010 [ - + ]: 279 : if (vallen < 0)
3865 noah@leadboat.com 1011 :UBC 0 : target->failed = true;
1012 : : else
3865 noah@leadboat.com 1013 :CBC 279 : dostr(convert, vallen, target);
7316 tgl@sss.pgh.pa.us 1014 : 279 : }
1015 : :
1016 : : static void
2631 1017 : 36957832 : fmtint(long long value, char type, int forcesign, int leftjust,
1018 : : int minlen, int zpad, int precision, int pointflag,
1019 : : PrintfTarget *target)
1020 : : {
1021 : : unsigned long long uvalue;
1022 : : int base;
1023 : : int dosign;
7316 1024 : 36957832 : const char *cvt = "0123456789abcdef";
9932 bruce@momjian.us 1025 : 36957832 : int signvalue = 0;
1026 : : char convert[64];
7579 1027 : 36957832 : int vallen = 0;
1028 : : int padlen; /* amount to pad */
1029 : : int zeropad; /* extra leading zeroes */
1030 : :
7316 tgl@sss.pgh.pa.us 1031 [ + + + + : 36957832 : switch (type)
+ + ]
1032 : : {
1033 : 14984607 : case 'd':
1034 : : case 'i':
1035 : 14984607 : base = 10;
1036 : 14984607 : dosign = 1;
1037 : 14984607 : break;
1038 : 5454 : case 'o':
1039 : 5454 : base = 8;
1040 : 5454 : dosign = 0;
1041 : 5454 : break;
1042 : 20257001 : case 'u':
1043 : 20257001 : base = 10;
1044 : 20257001 : dosign = 0;
1045 : 20257001 : break;
1046 : 36690 : case 'x':
1047 : 36690 : base = 16;
1048 : 36690 : dosign = 0;
1049 : 36690 : break;
1050 : 1674078 : case 'X':
1051 : 1674078 : cvt = "0123456789ABCDEF";
1052 : 1674078 : base = 16;
1053 : 1674078 : dosign = 0;
1054 : 1674078 : break;
7316 tgl@sss.pgh.pa.us 1055 :GBC 2 : default:
1056 : 2 : 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 +/- */
7579 bruce@momjian.us 1065 [ + + + + ]:CBC 36957830 : if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
2631 andres@anarazel.de 1066 : 1798042 : uvalue = -(unsigned long long) value;
1067 : : else
1068 : 35159786 : 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 : : */
7316 tgl@sss.pgh.pa.us 1077 [ + + - + : 36957828 : if (value == 0 && pointflag && precision == 0)
- - ]
7316 tgl@sss.pgh.pa.us 1078 :UBC 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 : : */
1510 tgl@sss.pgh.pa.us 1087 [ + + ]:CBC 36957828 : if (base == 10)
1088 : : {
1089 : : do
1090 : : {
1091 : 97565106 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10];
1092 : 97565106 : uvalue = uvalue / 10;
1093 [ + + ]: 97565106 : } while (uvalue);
1094 : : }
1095 [ + + ]: 1716222 : else if (base == 16)
1096 : : {
1097 : : do
1098 : : {
1099 : 6066013 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16];
1100 : 6066013 : uvalue = uvalue / 16;
1101 [ + + ]: 6066013 : } while (uvalue);
1102 : : }
1103 : : else /* base == 8 */
1104 : : {
1105 : : do
1106 : : {
1107 : 16362 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8];
1108 : 16362 : uvalue = uvalue / 8;
1109 [ + + ]: 16362 : } while (uvalue);
1110 : : }
1111 : : }
1112 : :
7316 1113 : 36957828 : zeropad = Max(0, precision - vallen);
1114 : :
2631 1115 : 36957828 : padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
1116 : :
1117 : 36957829 : leading_pad(zpad, signvalue, &padlen, target);
1118 : :
1119 [ - + ]: 36957827 : if (zeropad > 0)
2631 tgl@sss.pgh.pa.us 1120 :UBC 0 : dopr_outchmulti('0', zeropad, target);
1121 : :
2631 tgl@sss.pgh.pa.us 1122 :CBC 36957827 : dostr(convert + sizeof(convert) - vallen, vallen, target);
1123 : :
1124 : 36957831 : trailing_pad(padlen, target);
1125 : : }
1126 : :
1127 : : static void
5364 bruce@momjian.us 1128 : 20146 : fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
1129 : : {
1130 : : int padlen; /* amount to pad */
1131 : :
2631 tgl@sss.pgh.pa.us 1132 : 20146 : padlen = compute_padlen(minlen, 1, leftjust);
1133 : :
1134 [ + + ]: 20146 : if (padlen > 0)
1135 : : {
1136 : 21 : dopr_outchmulti(' ', padlen, target);
1137 : 21 : padlen = 0;
1138 : : }
1139 : :
7316 1140 : 20146 : dopr_outch(value, target);
1141 : :
2631 1142 : 20146 : trailing_pad(padlen, target);
9965 scrappy@hub.org 1143 : 20146 : }
1144 : :
1145 : : static void
7579 bruce@momjian.us 1146 : 294116 : fmtfloat(double value, char type, int forcesign, int leftjust,
1147 : : int minlen, int zpad, int precision, int pointflag,
1148 : : PrintfTarget *target)
1149 : : {
7580 1150 : 294116 : int signvalue = 0;
1151 : : int prec;
1152 : : int vallen;
1153 : : char fmt[8];
1154 : : char convert[1024];
3970 1155 : 294116 : int zeropadlen = 0; /* amount to pad with zeroes */
1156 : : 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 [ - + ]: 294116 : if (precision < 0) /* cover possible overflow of "accum" */
3970 bruce@momjian.us 1176 :UBC 0 : precision = 0;
3970 bruce@momjian.us 1177 :CBC 294116 : prec = Min(precision, 350);
1178 : :
2626 tgl@sss.pgh.pa.us 1179 [ + + ]: 294116 : if (isnan(value))
1180 : : {
1181 : 27 : strcpy(convert, "NaN");
1182 : 27 : vallen = 3;
1183 : : /* no zero padding, regardless of precision spec */
1184 : : }
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 [ + + + + ]: 581162 : if (adjust_sign((value < 0.0 ||
1196 [ + + ]: 287073 : (value == 0.0 &&
1197 [ - + ]: 55626 : memcmp(&value, &dzero, sizeof(double)) != 0)),
1198 : : forcesign, &signvalue))
1199 : 7016 : value = -value;
1200 : :
1201 [ + + + + ]: 294089 : if (isinf(value))
1202 : : {
1203 : 57 : strcpy(convert, "Infinity");
1204 : 57 : vallen = 8;
1205 : : /* no zero padding, regardless of precision spec */
1206 : : }
1207 [ + + ]: 294032 : else if (pointflag)
1208 : : {
1209 : 72478 : zeropadlen = precision - prec;
1210 : 72478 : fmt[0] = '%';
1211 : 72478 : fmt[1] = '.';
1212 : 72478 : fmt[2] = '*';
1213 : 72478 : fmt[3] = type;
1214 : 72478 : fmt[4] = '\0';
1157 1215 : 72478 : vallen = snprintf(convert, sizeof(convert), fmt, prec, value);
1216 : : }
1217 : : else
1218 : : {
2626 1219 : 221554 : fmt[0] = '%';
1220 : 221554 : fmt[1] = type;
1221 : 221554 : fmt[2] = '\0';
1157 1222 : 221554 : vallen = snprintf(convert, sizeof(convert), fmt, value);
1223 : : }
2626 1224 [ - + ]: 294089 : if (vallen < 0)
2626 tgl@sss.pgh.pa.us 1225 :UBC 0 : goto fail;
1226 : : }
1227 : :
2631 tgl@sss.pgh.pa.us 1228 :CBC 294116 : padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
1229 : :
1230 : 294116 : leading_pad(zpad, signvalue, &padlen, target);
1231 : :
3970 bruce@momjian.us 1232 [ - + ]: 294116 : if (zeropadlen > 0)
1233 : : {
1234 : : /* If 'e' or 'E' format, inject zeroes before the exponent */
3970 bruce@momjian.us 1235 :UBC 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);
2214 tgl@sss.pgh.pa.us 1243 : 0 : dopr_outchmulti('0', zeropadlen, target);
3970 bruce@momjian.us 1244 : 0 : dostr(epos, vallen - (epos - convert), target);
1245 : : }
1246 : : else
1247 : : {
1248 : : /* no exponent, pad after the digits */
1249 : 0 : dostr(convert, vallen, target);
2214 tgl@sss.pgh.pa.us 1250 : 0 : dopr_outchmulti('0', zeropadlen, target);
1251 : : }
1252 : : }
1253 : : else
1254 : : {
1255 : : /* no zero padding, just emit the number as-is */
3970 bruce@momjian.us 1256 :CBC 294116 : dostr(convert, vallen, target);
1257 : : }
1258 : :
2631 tgl@sss.pgh.pa.us 1259 : 294116 : trailing_pad(padlen, target);
3865 noah@leadboat.com 1260 : 294116 : return;
1261 : :
3865 noah@leadboat.com 1262 :UBC 0 : fail:
1263 : 0 : target->failed = true;
1264 : : }
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
2626 tgl@sss.pgh.pa.us 1276 :CBC 115147 : pg_strfromd(char *str, size_t count, int precision, double value)
1277 : : {
1278 : : PrintfTarget target;
1279 : 115147 : int signvalue = 0;
1280 : : int vallen;
1281 : : char fmt[8];
1282 : : char convert[64];
1283 : :
1284 : : /* Set up the target like pg_snprintf, but require nonempty buffer */
1285 [ - + ]: 115147 : Assert(count > 0);
1286 : 115147 : target.bufstart = target.bufptr = str;
1287 : 115147 : target.bufend = str + count - 1;
1288 : 115147 : target.stream = NULL;
1289 : 115147 : target.nchars = 0;
1290 : 115147 : 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 [ - + ]: 115147 : if (precision < 1)
2626 tgl@sss.pgh.pa.us 1298 :UBC 0 : precision = 1;
2626 tgl@sss.pgh.pa.us 1299 [ - + ]:CBC 115147 : else if (precision > 32)
2626 tgl@sss.pgh.pa.us 1300 :UBC 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 : : */
2626 tgl@sss.pgh.pa.us 1306 [ + + ]:CBC 115147 : if (isnan(value))
1307 : : {
1308 : 6069 : strcpy(convert, "NaN");
1309 : 6069 : vallen = 3;
1310 : : }
1311 : : else
1312 : : {
1313 : : static const double dzero = 0.0;
1314 : :
1315 [ + + ]: 109078 : if (value < 0.0 ||
1316 [ + + ]: 92878 : (value == 0.0 &&
1317 [ + + ]: 13439 : memcmp(&value, &dzero, sizeof(double)) != 0))
1318 : : {
1319 : 16233 : signvalue = '-';
1320 : 16233 : value = -value;
1321 : : }
1322 : :
1323 [ + + + + ]: 109078 : if (isinf(value))
1324 : : {
1325 : 3558 : strcpy(convert, "Infinity");
1326 : 3558 : vallen = 8;
1327 : : }
1328 : : else
1329 : : {
1330 : 105520 : fmt[0] = '%';
1331 : 105520 : fmt[1] = '.';
1332 : 105520 : fmt[2] = '*';
1333 : 105520 : fmt[3] = 'g';
1334 : 105520 : fmt[4] = '\0';
1157 1335 : 105520 : vallen = snprintf(convert, sizeof(convert), fmt, precision, value);
2626 1336 [ - + ]: 105520 : if (vallen < 0)
1337 : : {
2626 tgl@sss.pgh.pa.us 1338 :UBC 0 : target.failed = true;
1339 : 0 : goto fail;
1340 : : }
1341 : : }
1342 : : }
1343 : :
2626 tgl@sss.pgh.pa.us 1344 [ + + ]:CBC 115147 : if (signvalue)
1345 : 16233 : dopr_outch(signvalue, &target);
1346 : :
1347 : 115147 : dostr(convert, vallen, &target);
1348 : :
1349 : 115147 : fail:
1350 : 115147 : *(target.bufptr) = '\0';
1351 [ - + ]: 230294 : return target.failed ? -1 : (target.bufptr - target.bufstart
1352 : 115147 : + target.nchars);
1353 : : }
1354 : :
1355 : :
1356 : : static void
5364 bruce@momjian.us 1357 : 82491681 : dostr(const char *str, int slen, PrintfTarget *target)
1358 : : {
1359 : : /* fast path for common case of slen == 1 */
2631 tgl@sss.pgh.pa.us 1360 [ + + ]: 82491681 : if (slen == 1)
1361 : : {
1362 : 26944575 : dopr_outch(*str, target);
1363 : 26944573 : return;
1364 : : }
1365 : :
7316 1366 [ + + ]: 109551065 : while (slen > 0)
1367 : : {
1368 : : int avail;
1369 : :
1370 [ + + ]: 54460751 : if (target->bufend != NULL)
1371 : 46330359 : avail = target->bufend - target->bufptr;
1372 : : else
1373 : 8130392 : avail = slen;
1374 [ + + ]: 54460751 : if (avail <= 0)
1375 : : {
1376 : : /* buffer full, can we dump to stream? */
1377 [ + + ]: 457592 : if (target->stream == NULL)
1378 : : {
2680 1379 : 456792 : target->nchars += slen; /* no, lose the data */
1380 : 456792 : return;
1381 : : }
7316 1382 : 800 : flushbuffer(target);
1383 : 800 : continue;
1384 : : }
1385 : 54003159 : avail = Min(avail, slen);
1386 : 54003159 : memmove(target->bufptr, str, avail);
1387 : 54003159 : target->bufptr += avail;
1388 : 54003159 : str += avail;
1389 : 54003159 : slen -= avail;
1390 : : }
1391 : : }
1392 : :
1393 : : static void
5364 bruce@momjian.us 1394 : 30179926 : dopr_outch(int c, PrintfTarget *target)
1395 : : {
7316 tgl@sss.pgh.pa.us 1396 [ + + + + ]: 30179926 : if (target->bufend != NULL && target->bufptr >= target->bufend)
1397 : : {
1398 : : /* buffer full, can we dump to stream? */
1399 [ + - ]: 150095 : if (target->stream == NULL)
1400 : : {
2680 1401 : 150095 : target->nchars++; /* no, lose the data */
1402 : 150095 : return;
1403 : : }
7316 tgl@sss.pgh.pa.us 1404 :UBC 0 : flushbuffer(target);
1405 : : }
7316 tgl@sss.pgh.pa.us 1406 :CBC 30029830 : *(target->bufptr++) = c;
1407 : : }
1408 : :
1409 : : static void
2631 1410 : 2927779 : dopr_outchmulti(int c, int slen, PrintfTarget *target)
1411 : : {
1412 : : /* fast path for common case of slen == 1 */
1413 [ + + ]: 2927779 : if (slen == 1)
1414 : : {
1415 : 1391431 : dopr_outch(c, target);
1416 : 1391431 : return;
1417 : : }
1418 : :
1419 [ + + ]: 3072725 : while (slen > 0)
1420 : : {
1421 : : int avail;
1422 : :
1423 [ + + ]: 1536459 : if (target->bufend != NULL)
1424 : 1517849 : avail = target->bufend - target->bufptr;
1425 : : else
1426 : 18610 : avail = slen;
1427 [ + + ]: 1536459 : if (avail <= 0)
1428 : : {
1429 : : /* buffer full, can we dump to stream? */
1430 [ + + ]: 139 : if (target->stream == NULL)
1431 : : {
2631 tgl@sss.pgh.pa.us 1432 :GBC 82 : target->nchars += slen; /* no, lose the data */
1433 : 82 : return;
1434 : : }
2631 tgl@sss.pgh.pa.us 1435 :CBC 57 : flushbuffer(target);
1436 : 57 : continue;
1437 : : }
1438 : 1536320 : avail = Min(avail, slen);
1439 : 1536320 : memset(target->bufptr, c, avail);
1440 : 1536320 : target->bufptr += avail;
1441 : 1536320 : slen -= avail;
1442 : : }
1443 : : }
1444 : :
1445 : :
1446 : : static int
7579 bruce@momjian.us 1447 : 15278700 : adjust_sign(int is_negative, int forcesign, int *signvalue)
1448 : : {
1449 [ + + ]: 15278700 : if (is_negative)
1450 : : {
1451 : 1805058 : *signvalue = '-';
1452 : 1805058 : return true;
1453 : : }
1454 [ + + ]: 13473642 : else if (forcesign)
1455 : 102 : *signvalue = '+';
1456 : 13473642 : return false;
1457 : : }
1458 : :
1459 : :
1460 : : static int
2631 tgl@sss.pgh.pa.us 1461 : 38278636 : compute_padlen(int minlen, int vallen, int leftjust)
1462 : : {
1463 : : int padlen;
1464 : :
1465 : 38278636 : padlen = minlen - vallen;
1466 [ + + ]: 38278636 : if (padlen < 0)
1467 : 32203180 : padlen = 0;
7579 bruce@momjian.us 1468 [ + + ]: 38278636 : if (leftjust)
2631 tgl@sss.pgh.pa.us 1469 : 575921 : padlen = -padlen;
1470 : 38278636 : return padlen;
1471 : : }
1472 : :
1473 : :
1474 : : static void
1475 : 37251944 : leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
1476 : : {
1477 : : int maxpad;
1478 : :
7579 bruce@momjian.us 1479 [ + + + + ]: 37251944 : if (*padlen > 0 && zpad)
1480 : : {
2631 tgl@sss.pgh.pa.us 1481 [ + + ]: 1456430 : if (signvalue)
1482 : : {
1483 : 120 : dopr_outch(signvalue, target);
7316 1484 : 120 : --(*padlen);
2631 1485 : 120 : signvalue = 0;
1486 : : }
1487 [ + + ]: 1456430 : if (*padlen > 0)
1488 : : {
1489 : 1456421 : dopr_outchmulti(zpad, *padlen, target);
1490 : 1456421 : *padlen = 0;
1491 : : }
1492 : : }
1493 : 37251944 : maxpad = (signvalue != 0);
1494 [ + + ]: 37251944 : if (*padlen > maxpad)
1495 : : {
1496 : 793141 : dopr_outchmulti(' ', *padlen - maxpad, target);
1497 : 793141 : *padlen = maxpad;
1498 : : }
1499 [ + + ]: 37251944 : if (signvalue)
1500 : : {
1501 : 1805040 : dopr_outch(signvalue, target);
7579 bruce@momjian.us 1502 [ - + ]: 1805040 : if (*padlen > 0)
7316 tgl@sss.pgh.pa.us 1503 :UBC 0 : --(*padlen);
7316 tgl@sss.pgh.pa.us 1504 [ - + ]:CBC 1805040 : else if (*padlen < 0)
7316 tgl@sss.pgh.pa.us 1505 :UBC 0 : ++(*padlen);
1506 : : }
7579 bruce@momjian.us 1507 :CBC 37251944 : }
1508 : :
1509 : :
1510 : : static void
2631 tgl@sss.pgh.pa.us 1511 : 38278636 : trailing_pad(int padlen, PrintfTarget *target)
1512 : : {
1513 [ + + ]: 38278636 : if (padlen < 0)
1514 : 378818 : dopr_outchmulti(' ', -padlen, target);
7579 bruce@momjian.us 1515 : 38278636 : }
|