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
7484 bruce@momjian.us 174 :CBC 31167323 : 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 : : */
2579 tgl@sss.pgh.pa.us 185 [ + + ]: 31167323 : if (count == 0)
186 : : {
187 : 59663 : str = onebyte;
188 : 59663 : count = 1;
189 : : }
7215 190 : 31167323 : target.bufstart = target.bufptr = str;
191 : 31167323 : target.bufend = str + count - 1;
192 : 31167323 : target.stream = NULL;
2579 193 : 31167323 : target.nchars = 0;
3764 noah@leadboat.com 194 : 31167323 : target.failed = false;
195 : 31167323 : dopr(&target, fmt, args);
7215 tgl@sss.pgh.pa.us 196 : 31167324 : *(target.bufptr) = '\0';
2579 197 [ - + ]: 62334648 : return target.failed ? -1 : (target.bufptr - target.bufstart
198 : 31167324 : + target.nchars);
199 : : }
200 : :
201 : : int
7484 bruce@momjian.us 202 : 15073335 : pg_snprintf(char *str, size_t count, const char *fmt,...)
203 : : {
204 : : int len;
205 : : va_list args;
206 : :
9759 207 : 15073335 : va_start(args, fmt);
7484 208 : 15073335 : len = pg_vsnprintf(str, count, fmt, args);
9759 209 : 15073335 : va_end(args);
9864 scrappy@hub.org 210 : 15073335 : return len;
211 : : }
212 : :
213 : : int
7215 tgl@sss.pgh.pa.us 214 : 5301263 : pg_vsprintf(char *str, const char *fmt, va_list args)
215 : : {
216 : : PrintfTarget target;
217 : :
218 : 5301263 : target.bufstart = target.bufptr = str;
219 : 5301263 : target.bufend = NULL;
220 : 5301263 : target.stream = NULL;
2579 221 : 5301263 : target.nchars = 0; /* not really used in this case */
3764 noah@leadboat.com 222 : 5301263 : target.failed = false;
223 : 5301263 : dopr(&target, fmt, args);
7215 tgl@sss.pgh.pa.us 224 : 5301261 : *(target.bufptr) = '\0';
2579 225 [ - + ]: 10602522 : return target.failed ? -1 : (target.bufptr - target.bufstart
226 : 5301261 : + target.nchars);
227 : : }
228 : :
229 : : int
7479 bruce@momjian.us 230 : 5301266 : pg_sprintf(char *str, const char *fmt,...)
231 : : {
232 : : int len;
233 : : va_list args;
234 : :
235 : 5301266 : va_start(args, fmt);
7215 tgl@sss.pgh.pa.us 236 : 5301266 : len = pg_vsprintf(str, fmt, args);
7479 bruce@momjian.us 237 : 5301262 : va_end(args);
238 : 5301262 : return len;
239 : : }
240 : :
241 : : int
7215 tgl@sss.pgh.pa.us 242 : 1981354 : pg_vfprintf(FILE *stream, const char *fmt, va_list args)
243 : : {
244 : : PrintfTarget target;
245 : : char buffer[1024]; /* size is arbitrary */
246 : :
247 [ - + ]: 1981354 : if (stream == NULL)
248 : : {
7215 tgl@sss.pgh.pa.us 249 :UBC 0 : errno = EINVAL;
250 : 0 : return -1;
251 : : }
7215 tgl@sss.pgh.pa.us 252 :CBC 1981354 : target.bufstart = target.bufptr = buffer;
2579 253 : 1981354 : target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
7215 254 : 1981354 : target.stream = stream;
255 : 1981354 : target.nchars = 0;
3764 noah@leadboat.com 256 : 1981354 : target.failed = false;
257 : 1981354 : dopr(&target, fmt, args);
258 : : /* dump any remaining buffer contents */
7215 tgl@sss.pgh.pa.us 259 : 1981354 : flushbuffer(&target);
3764 noah@leadboat.com 260 [ - + ]: 1981354 : return target.failed ? -1 : target.nchars;
261 : : }
262 : :
263 : : int
7484 bruce@momjian.us 264 : 843972 : pg_fprintf(FILE *stream, const char *fmt,...)
265 : : {
266 : : int len;
267 : : va_list args;
268 : :
269 : 843972 : va_start(args, fmt);
7215 tgl@sss.pgh.pa.us 270 : 843972 : len = pg_vfprintf(stream, fmt, args);
7484 bruce@momjian.us 271 : 843972 : va_end(args);
272 : 843972 : return len;
273 : : }
274 : :
275 : : int
2525 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
7484 bruce@momjian.us 282 :CBC 1129439 : pg_printf(const char *fmt,...)
283 : : {
284 : : int len;
285 : : va_list args;
286 : :
7493 287 : 1129439 : va_start(args, fmt);
7215 tgl@sss.pgh.pa.us 288 : 1129439 : len = pg_vfprintf(stdout, fmt, args);
7493 bruce@momjian.us 289 : 1129439 : va_end(args);
290 : 1129439 : 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
5263 298 : 1982208 : flushbuffer(PrintfTarget *target)
299 : : {
6912 300 : 1982208 : 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 : : */
3764 noah@leadboat.com 306 [ + - + + ]: 1982208 : if (!target->failed && nc > 0)
307 : : {
308 : : size_t written;
309 : :
310 : 1848034 : written = fwrite(target->bufstart, 1, nc, target->stream);
311 : 1848034 : target->nchars += written;
312 [ - + ]: 1848034 : if (written != nc)
3764 noah@leadboat.com 313 :UBC 0 : target->failed = true;
314 : : }
7215 tgl@sss.pgh.pa.us 315 :CBC 1982208 : target->bufptr = target->bufstart;
316 : 1982208 : }
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
5263 bruce@momjian.us 373 : 38449941 : dopr(PrintfTarget *target, const char *format, va_list args)
374 : : {
2537 tgl@sss.pgh.pa.us 375 : 38449941 : int save_errno = errno;
2530 376 : 38449941 : 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 : 38449941 : have_dollar = false;
404 : :
405 [ + + ]: 87629175 : while (*format != '\0')
406 : : {
407 : : /* Locate next conversion specifier */
408 [ + + ]: 54352371 : if (*format != '%')
409 : : {
410 : : /* Scan to next '%' or end of string */
411 : 31302710 : const char *next_pct = strchrnul(format + 1, '%');
412 : :
413 : : /* Dump literal data we just scanned over */
414 : 31302710 : dostr(format, next_pct - format, target);
415 [ - + ]: 31302709 : if (target->failed)
7215 tgl@sss.pgh.pa.us 416 :UBC 0 : break;
417 : :
2530 tgl@sss.pgh.pa.us 418 [ + + ]:CBC 31302709 : if (*next_pct == '\0')
7215 419 : 5173136 : break;
2530 420 : 26129573 : 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 [ + + ]: 49179234 : if (first_pct == NULL)
429 : 38148603 : first_pct = format;
430 : :
431 : : /* Process conversion spec starting at *format */
432 : 49179234 : format++;
433 : :
434 : : /* Fast path for conversion spec that is exactly %s */
435 [ + + ]: 49179234 : if (*format == 's')
436 : : {
437 : 11942741 : format++;
438 : 11942741 : strvalue = va_arg(args, char *);
1505 439 [ + + ]: 11942743 : if (strvalue == NULL)
440 : 4 : strvalue = "(null)";
2530 441 : 11942743 : dostr(strvalue, strlen(strvalue), target);
442 [ - + ]: 11942743 : if (target->failed)
2530 tgl@sss.pgh.pa.us 443 :UBC 0 : break;
2530 tgl@sss.pgh.pa.us 444 :CBC 11942743 : continue;
445 : : }
446 : :
7215 447 : 37236493 : fieldwidth = precision = zpad = leftjust = forcesign = 0;
448 : 37236493 : longflag = longlongflag = pointflag = 0;
449 : 37236493 : fmtpos = accum = 0;
450 : 37236493 : have_star = afterstar = false;
6912 bruce@momjian.us 451 : 14390483 : nextch2:
7215 tgl@sss.pgh.pa.us 452 : 51626976 : ch = *format++;
9831 bruce@momjian.us 453 [ + + + + : 51626976 : switch (ch)
+ + - + +
+ + + + +
+ + + +
- ]
454 : : {
7215 tgl@sss.pgh.pa.us 455 : 564268 : case '-':
456 : 564268 : leftjust = 1;
457 : 564268 : goto nextch2;
458 : 141 : case '+':
459 : 141 : forcesign = 1;
460 : 141 : goto nextch2;
461 : 4522623 : case '0':
462 : : /* set zero padding if no nonzero digits yet */
463 [ + + + + ]: 4522623 : if (accum == 0 && !pointflag)
464 : 4230526 : 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 : 10169816 : accum = accum * 10 + (ch - '0');
476 : 10169816 : goto nextch2;
477 : 82204 : case '.':
478 [ + + ]: 82204 : if (have_star)
7215 tgl@sss.pgh.pa.us 479 :GBC 202 : have_star = false;
480 : : else
7215 tgl@sss.pgh.pa.us 481 :CBC 82002 : fieldwidth = accum;
482 : 82204 : pointflag = 1;
483 : 82204 : accum = 0;
484 : 82204 : goto nextch2;
485 : 716277 : case '*':
486 [ - + ]: 716277 : 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 : : */
7215 tgl@sss.pgh.pa.us 493 :UBC 0 : afterstar = true;
494 : : }
495 : : else
496 : : {
497 : : /* fetch and process value now */
6912 bruce@momjian.us 498 :CBC 716277 : int starval = va_arg(args, int);
499 : :
7215 tgl@sss.pgh.pa.us 500 [ + + ]: 716277 : if (pointflag)
501 : : {
502 : 30277 : precision = starval;
503 [ - + ]: 30277 : if (precision < 0)
504 : : {
7215 tgl@sss.pgh.pa.us 505 :UBC 0 : precision = 0;
6381 506 : 0 : pointflag = 0;
507 : : }
508 : : }
509 : : else
510 : : {
7215 tgl@sss.pgh.pa.us 511 :CBC 686000 : fieldwidth = starval;
512 [ + + ]: 686000 : if (fieldwidth < 0)
513 : : {
514 : 2823 : leftjust = 1;
515 : 2823 : fieldwidth = -fieldwidth;
516 : : }
517 : : }
518 : : }
519 : 716277 : have_star = true;
520 : 716277 : accum = 0;
521 : 716277 : goto nextch2;
7215 tgl@sss.pgh.pa.us 522 :UBC 0 : case '$':
523 : : /* First dollar sign? */
2530 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;
2530 tgl@sss.pgh.pa.us 529 :LBC (6) : have_dollar = true;
530 : : }
7215 531 [ # # ]: (6) : if (afterstar)
532 : : {
533 : : /* fetch and process star value */
6912 bruce@momjian.us 534 :UBC 0 : int starval = argvalues[accum].i;
535 : :
7215 tgl@sss.pgh.pa.us 536 [ # # ]: 0 : if (pointflag)
537 : : {
538 : 0 : precision = starval;
539 [ # # ]: 0 : if (precision < 0)
540 : : {
541 : 0 : precision = 0;
6381 542 : 0 : pointflag = 0;
543 : : }
544 : : }
545 : : else
546 : : {
7215 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
7215 tgl@sss.pgh.pa.us 557 :LBC (6) : fmtpos = accum;
558 : (6) : accum = 0;
559 : (6) : goto nextch2;
560 : : #ifdef WIN32
561 : : case 'I':
562 : : /* Windows PRI*{32,64,PTR} size */
563 : : if (format[0] == '3' && format[1] == '2')
564 : : format += 2;
565 : : else if (format[0] == '6' && format[1] == '4')
566 : : {
567 : : format += 2;
568 : : longlongflag = 1;
569 : : }
570 : : else
571 : : {
572 : : #if SIZEOF_VOID_P == SIZEOF_LONG
573 : : longflag = 1;
574 : : #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
575 : : longlongflag = 1;
576 : : #else
577 : : #error "cannot find integer type of the same size as intptr_t"
578 : : #endif
579 : : }
580 : : goto nextch2;
581 : : #endif
7215 tgl@sss.pgh.pa.us 582 :CBC 2690702 : case 'l':
583 [ + + ]: 2690702 : if (longflag)
584 : 4269 : longlongflag = 1;
585 : : else
586 : 2686433 : longflag = 1;
587 : 2690702 : goto nextch2;
4244 588 : 167053 : case 'z':
589 : : #if SIZEOF_SIZE_T == SIZEOF_LONG
590 : 167053 : longflag = 1;
591 : : #elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
592 : : longlongflag = 1;
593 : : #else
594 : : #error "cannot find integer type of the same size as size_t"
595 : : #endif
596 : 167053 : goto nextch2;
7215 597 : 22 : case 'h':
598 : : case '\'':
599 : : /* ignore these */
600 : 22 : goto nextch2;
601 : 14689897 : case 'd':
602 : : case 'i':
603 [ + + ]: 14689897 : if (!have_star)
604 : : {
605 [ - + ]: 14670110 : if (pointflag)
7215 tgl@sss.pgh.pa.us 606 :UBC 0 : precision = accum;
607 : : else
7215 tgl@sss.pgh.pa.us 608 :CBC 14670110 : fieldwidth = accum;
609 : : }
610 [ - + ]: 14689897 : if (have_dollar)
611 : : {
7215 tgl@sss.pgh.pa.us 612 [ # # ]:UBC 0 : if (longlongflag)
613 : 0 : numvalue = argvalues[fmtpos].ll;
614 [ # # ]: 0 : else if (longflag)
615 : 0 : numvalue = argvalues[fmtpos].l;
616 : : else
617 : 0 : numvalue = argvalues[fmtpos].i;
618 : : }
619 : : else
620 : : {
7215 tgl@sss.pgh.pa.us 621 [ + + ]:CBC 14689897 : if (longlongflag)
2530 622 : 4257 : numvalue = va_arg(args, long long);
7215 623 [ + + ]: 14685640 : else if (longflag)
624 : 2401873 : numvalue = va_arg(args, long);
625 : : else
626 : 12283767 : numvalue = va_arg(args, int);
627 : : }
628 : 14689894 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
629 : : precision, pointflag, target);
9831 bruce@momjian.us 630 : 14689896 : break;
7215 tgl@sss.pgh.pa.us 631 : 21252976 : case 'o':
632 : : case 'u':
633 : : case 'x':
634 : : case 'X':
635 [ + - ]: 21252976 : if (!have_star)
636 : : {
637 [ - + ]: 21252976 : if (pointflag)
7215 tgl@sss.pgh.pa.us 638 :UBC 0 : precision = accum;
639 : : else
7215 tgl@sss.pgh.pa.us 640 :CBC 21252976 : fieldwidth = accum;
641 : : }
642 [ - + ]: 21252976 : if (have_dollar)
643 : : {
7215 tgl@sss.pgh.pa.us 644 [ # # ]:UBC 0 : if (longlongflag)
2530 645 : 0 : numvalue = (unsigned long long) argvalues[fmtpos].ll;
7215 646 [ # # ]: 0 : else if (longflag)
647 : 0 : numvalue = (unsigned long) argvalues[fmtpos].l;
648 : : else
649 : 0 : numvalue = (unsigned int) argvalues[fmtpos].i;
650 : : }
651 : : else
652 : : {
7215 tgl@sss.pgh.pa.us 653 [ + + ]:CBC 21252976 : if (longlongflag)
2530 654 : 12 : numvalue = (unsigned long long) va_arg(args, long long);
7215 655 [ + + ]: 21252964 : else if (longflag)
656 : 447344 : numvalue = (unsigned long) va_arg(args, long);
657 : : else
658 : 20805620 : numvalue = (unsigned int) va_arg(args, int);
659 : : }
660 : 21252976 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
661 : : precision, pointflag, target);
7479 bruce@momjian.us 662 : 21252976 : break;
7215 tgl@sss.pgh.pa.us 663 : 20100 : case 'c':
664 [ + + ]: 20100 : if (!have_star)
665 : : {
666 [ - + ]: 20079 : if (pointflag)
7215 tgl@sss.pgh.pa.us 667 :UBC 0 : precision = accum;
668 : : else
7215 tgl@sss.pgh.pa.us 669 :CBC 20079 : fieldwidth = accum;
670 : : }
671 [ - + ]: 20100 : if (have_dollar)
7215 tgl@sss.pgh.pa.us 672 :UBC 0 : cvalue = (unsigned char) argvalues[fmtpos].i;
673 : : else
7215 tgl@sss.pgh.pa.us 674 :CBC 20100 : cvalue = (unsigned char) va_arg(args, int);
675 : 20100 : fmtchar(cvalue, leftjust, fieldwidth, target);
7479 bruce@momjian.us 676 : 20100 : break;
7215 tgl@sss.pgh.pa.us 677 : 986647 : case 's':
678 [ + + ]: 986647 : if (!have_star)
679 : : {
680 [ - + ]: 310412 : if (pointflag)
7215 tgl@sss.pgh.pa.us 681 :UBC 0 : precision = accum;
682 : : else
7215 tgl@sss.pgh.pa.us 683 :CBC 310412 : fieldwidth = accum;
684 : : }
685 [ - + ]: 986647 : if (have_dollar)
7215 tgl@sss.pgh.pa.us 686 :UBC 0 : strvalue = argvalues[fmtpos].cptr;
687 : : else
7215 tgl@sss.pgh.pa.us 688 :CBC 986647 : strvalue = va_arg(args, char *);
689 : : /* If string is NULL, silently substitute "(null)" */
1505 690 [ - + ]: 986647 : if (strvalue == NULL)
1505 tgl@sss.pgh.pa.us 691 :UBC 0 : strvalue = "(null)";
7215 tgl@sss.pgh.pa.us 692 :CBC 986647 : fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
693 : : target);
7479 bruce@momjian.us 694 : 986647 : break;
7215 tgl@sss.pgh.pa.us 695 : 163 : case 'p':
696 : : /* fieldwidth/leftjust are ignored ... */
697 [ - + ]: 163 : if (have_dollar)
7215 tgl@sss.pgh.pa.us 698 :UBC 0 : strvalue = argvalues[fmtpos].cptr;
699 : : else
7215 tgl@sss.pgh.pa.us 700 :CBC 163 : strvalue = va_arg(args, char *);
1505 701 : 163 : fmtptr((const void *) strvalue, target);
7479 bruce@momjian.us 702 : 163 : break;
7215 tgl@sss.pgh.pa.us 703 : 284232 : case 'e':
704 : : case 'E':
705 : : case 'f':
706 : : case 'g':
707 : : case 'G':
708 [ + + ]: 284232 : if (!have_star)
709 : : {
710 [ + + ]: 264199 : if (pointflag)
711 : 51927 : precision = accum;
712 : : else
713 : 212272 : fieldwidth = accum;
714 : : }
715 [ - + ]: 284232 : if (have_dollar)
7215 tgl@sss.pgh.pa.us 716 :UBC 0 : fvalue = argvalues[fmtpos].d;
717 : : else
7215 tgl@sss.pgh.pa.us 718 :CBC 284232 : fvalue = va_arg(args, double);
719 : 284232 : fmtfloat(fvalue, ch, forcesign, leftjust,
720 : : fieldwidth, zpad,
721 : : precision, pointflag,
722 : : target);
7479 bruce@momjian.us 723 : 284232 : break;
2537 tgl@sss.pgh.pa.us 724 : 183 : case 'm':
725 : : {
726 : : char errbuf[PG_STRERROR_R_BUFLEN];
727 : 183 : const char *errm = strerror_r(save_errno,
728 : : errbuf, sizeof(errbuf));
729 : :
730 : 183 : dostr(errm, strlen(errm), target);
731 : : }
732 : 182 : break;
7215 733 : 2295 : case '%':
734 : 2295 : dopr_outch('%', target);
7479 bruce@momjian.us 735 : 2295 : break;
2466 tgl@sss.pgh.pa.us 736 :UBC 0 : default:
737 : :
738 : : /*
739 : : * Anything else --- in particular, '\0' indicating end of
740 : : * format string --- is bogus.
741 : : */
742 : 0 : goto bad_format;
743 : : }
744 : :
745 : : /* Check for failure after each conversion spec */
2530 tgl@sss.pgh.pa.us 746 [ - + ]:CBC 37236491 : if (target->failed)
2530 tgl@sss.pgh.pa.us 747 :UBC 0 : break;
748 : : }
749 : :
3764 noah@leadboat.com 750 :CBC 38449940 : return;
751 : :
3764 noah@leadboat.com 752 :UBC 0 : bad_format:
753 : 0 : errno = EINVAL;
754 : 0 : target->failed = true;
755 : : }
756 : :
757 : : /*
758 : : * find_arguments(): sort out the arguments for a format spec with %n$
759 : : *
760 : : * If format is valid, return true and fill argvalues[i] with the value
761 : : * for the conversion spec that has %i$ or *i$. Else return false.
762 : : */
763 : : static bool
2530 tgl@sss.pgh.pa.us 764 : 0 : find_arguments(const char *format, va_list args,
765 : : PrintfArgValue *argvalues)
766 : : {
767 : : int ch;
768 : : bool afterstar;
769 : : int accum;
770 : : int longlongflag;
771 : : int longflag;
772 : : int fmtpos;
773 : : int i;
1148 peter@eisentraut.org 774 : 0 : int last_dollar = 0; /* Init to "no dollar arguments known" */
775 : 0 : PrintfArgType argtypes[PG_NL_ARGMAX + 1] = {0};
776 : :
777 : : /*
778 : : * This loop must accept the same format strings as the one in dopr().
779 : : * However, we don't need to analyze them to the same level of detail.
780 : : *
781 : : * Since we're only called if there's a dollar-type spec somewhere, we can
782 : : * fail immediately if we find a non-dollar spec. Per the C99 standard,
783 : : * all argument references in the format string must be one or the other.
784 : : */
2530 tgl@sss.pgh.pa.us 785 [ # # ]: 0 : while (*format != '\0')
786 : : {
787 : : /* Locate next conversion specifier */
788 [ # # ]: 0 : if (*format != '%')
789 : : {
790 : : /* Unlike dopr, we can just quit if there's no more specifiers */
791 : 0 : format = strchr(format + 1, '%');
792 [ # # ]: 0 : if (format == NULL)
793 : 0 : break;
794 : : }
795 : :
796 : : /* Process conversion spec starting at *format */
797 : 0 : format++;
798 : 0 : longflag = longlongflag = 0;
799 : 0 : fmtpos = accum = 0;
800 : 0 : afterstar = false;
801 : 0 : nextch1:
802 : 0 : ch = *format++;
803 [ # # # # : 0 : switch (ch)
# # # # #
# # # #
# ]
804 : : {
805 : 0 : case '-':
806 : : case '+':
807 : 0 : goto nextch1;
808 : 0 : case '0':
809 : : case '1':
810 : : case '2':
811 : : case '3':
812 : : case '4':
813 : : case '5':
814 : : case '6':
815 : : case '7':
816 : : case '8':
817 : : case '9':
818 : 0 : accum = accum * 10 + (ch - '0');
819 : 0 : goto nextch1;
820 : 0 : case '.':
821 : 0 : accum = 0;
822 : 0 : goto nextch1;
823 : 0 : case '*':
824 [ # # ]: 0 : if (afterstar)
825 : 0 : return false; /* previous star missing dollar */
826 : 0 : afterstar = true;
827 : 0 : accum = 0;
828 : 0 : goto nextch1;
829 : 0 : case '$':
830 [ # # # # ]: 0 : if (accum <= 0 || accum > PG_NL_ARGMAX)
831 : 0 : return false;
832 [ # # ]: 0 : if (afterstar)
833 : : {
834 [ # # ]: 0 : if (argtypes[accum] &&
835 [ # # ]: 0 : argtypes[accum] != ATYPE_INT)
836 : 0 : return false;
837 : 0 : argtypes[accum] = ATYPE_INT;
838 : 0 : last_dollar = Max(last_dollar, accum);
839 : 0 : afterstar = false;
840 : : }
841 : : else
842 : 0 : fmtpos = accum;
843 : 0 : accum = 0;
844 : 0 : goto nextch1;
845 : : #ifdef WIN32
846 : : case 'I':
847 : : /* Windows PRI*{32,64,PTR} size */
848 : : if (format[0] == '3' && format[1] == '2')
849 : : format += 2;
850 : : else if (format[0] == '6' && format[1] == '4')
851 : : {
852 : : format += 2;
853 : : longlongflag = 1;
854 : : }
855 : : else
856 : : {
857 : : #if SIZEOF_VOID_P == SIZEOF_LONG
858 : : longflag = 1;
859 : : #elif SIZEOF_VOID_P == SIZEOF_LONG_LONG
860 : : longlongflag = 1;
861 : : #else
862 : : #error "cannot find integer type of the same size as intptr_t"
863 : : #endif
864 : : }
865 : : goto nextch1;
866 : : #endif
867 : 0 : case 'l':
868 [ # # ]: 0 : if (longflag)
869 : 0 : longlongflag = 1;
870 : : else
871 : 0 : longflag = 1;
872 : 0 : goto nextch1;
873 : 0 : case 'z':
874 : : #if SIZEOF_SIZE_T == SIZEOF_LONG
875 : 0 : longflag = 1;
876 : : #elif SIZEOF_SIZE_T == SIZEOF_LONG_LONG
877 : : longlongflag = 1;
878 : : #else
879 : : #error "cannot find integer type of the same size as size_t"
880 : : #endif
881 : 0 : goto nextch1;
882 : 0 : case 'h':
883 : : case '\'':
884 : : /* ignore these */
885 : 0 : goto nextch1;
886 : 0 : case 'd':
887 : : case 'i':
888 : : case 'o':
889 : : case 'u':
890 : : case 'x':
891 : : case 'X':
892 [ # # ]: 0 : if (fmtpos)
893 : : {
894 : : PrintfArgType atype;
895 : :
896 [ # # ]: 0 : if (longlongflag)
897 : 0 : atype = ATYPE_LONGLONG;
898 [ # # ]: 0 : else if (longflag)
899 : 0 : atype = ATYPE_LONG;
900 : : else
901 : 0 : atype = ATYPE_INT;
902 [ # # ]: 0 : if (argtypes[fmtpos] &&
903 [ # # ]: 0 : argtypes[fmtpos] != atype)
904 : 0 : return false;
905 : 0 : argtypes[fmtpos] = atype;
906 : 0 : last_dollar = Max(last_dollar, fmtpos);
907 : : }
908 : : else
909 : 0 : return false; /* non-dollar conversion spec */
910 : 0 : break;
911 : 0 : case 'c':
912 [ # # ]: 0 : if (fmtpos)
913 : : {
914 [ # # ]: 0 : if (argtypes[fmtpos] &&
915 [ # # ]: 0 : argtypes[fmtpos] != ATYPE_INT)
916 : 0 : return false;
917 : 0 : argtypes[fmtpos] = ATYPE_INT;
918 : 0 : last_dollar = Max(last_dollar, fmtpos);
919 : : }
920 : : else
921 : 0 : return false; /* non-dollar conversion spec */
922 : 0 : break;
923 : 0 : case 's':
924 : : case 'p':
925 [ # # ]: 0 : if (fmtpos)
926 : : {
927 [ # # ]: 0 : if (argtypes[fmtpos] &&
928 [ # # ]: 0 : argtypes[fmtpos] != ATYPE_CHARPTR)
929 : 0 : return false;
930 : 0 : argtypes[fmtpos] = ATYPE_CHARPTR;
931 : 0 : last_dollar = Max(last_dollar, fmtpos);
932 : : }
933 : : else
934 : 0 : return false; /* non-dollar conversion spec */
935 : 0 : break;
936 : 0 : case 'e':
937 : : case 'E':
938 : : case 'f':
939 : : case 'g':
940 : : case 'G':
941 [ # # ]: 0 : if (fmtpos)
942 : : {
943 [ # # ]: 0 : if (argtypes[fmtpos] &&
944 [ # # ]: 0 : argtypes[fmtpos] != ATYPE_DOUBLE)
945 : 0 : return false;
946 : 0 : argtypes[fmtpos] = ATYPE_DOUBLE;
947 : 0 : last_dollar = Max(last_dollar, fmtpos);
948 : : }
949 : : else
950 : 0 : return false; /* non-dollar conversion spec */
951 : 0 : break;
952 : 0 : case 'm':
953 : : case '%':
954 : 0 : break;
2466 955 : 0 : default:
956 : 0 : return false; /* bogus format string */
957 : : }
958 : :
959 : : /*
960 : : * If we finish the spec with afterstar still set, there's a
961 : : * non-dollar star in there.
962 : : */
2530 963 [ # # ]: 0 : if (afterstar)
964 : 0 : return false; /* non-dollar conversion spec */
965 : : }
966 : :
967 : : /*
968 : : * Format appears valid so far, so collect the arguments in physical
969 : : * order. (Since we rejected any non-dollar specs that would have
970 : : * collected arguments, we know that dopr() hasn't collected any yet.)
971 : : */
972 [ # # ]: 0 : for (i = 1; i <= last_dollar; i++)
973 : : {
974 [ # # # # : 0 : switch (argtypes[i])
# # # ]
975 : : {
976 : 0 : case ATYPE_NONE:
977 : 0 : return false;
978 : 0 : case ATYPE_INT:
979 : 0 : argvalues[i].i = va_arg(args, int);
980 : 0 : break;
981 : 0 : case ATYPE_LONG:
982 : 0 : argvalues[i].l = va_arg(args, long);
983 : 0 : break;
984 : 0 : case ATYPE_LONGLONG:
985 : 0 : argvalues[i].ll = va_arg(args, long long);
986 : 0 : break;
987 : 0 : case ATYPE_DOUBLE:
988 : 0 : argvalues[i].d = va_arg(args, double);
989 : 0 : break;
990 : 0 : case ATYPE_CHARPTR:
991 : 0 : argvalues[i].cptr = va_arg(args, char *);
992 : 0 : break;
993 : : }
994 : : }
995 : :
996 : 0 : return true;
997 : : }
998 : :
999 : : static void
2530 tgl@sss.pgh.pa.us 1000 :CBC 986647 : fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
1001 : : int pointflag, PrintfTarget *target)
1002 : : {
1003 : : int padlen,
1004 : : vallen; /* amount to pad */
1005 : :
1006 : : /*
1007 : : * If a maxwidth (precision) is specified, we must not fetch more bytes
1008 : : * than that.
1009 : : */
7215 1010 [ + + ]: 986647 : if (pointflag)
2888 andres@anarazel.de 1011 : 10244 : vallen = strnlen(value, maxwidth);
1012 : : else
7215 tgl@sss.pgh.pa.us 1013 : 976403 : vallen = strlen(value);
1014 : :
2530 1015 : 986647 : padlen = compute_padlen(minlen, vallen, leftjust);
1016 : :
1017 [ + + ]: 986647 : if (padlen > 0)
1018 : : {
1019 : 291538 : dopr_outchmulti(' ', padlen, target);
1020 : 291538 : padlen = 0;
1021 : : }
1022 : :
7215 1023 : 986647 : dostr(value, vallen, target);
1024 : :
2530 1025 : 986647 : trailing_pad(padlen, target);
9864 scrappy@hub.org 1026 : 986647 : }
1027 : :
1028 : : static void
1505 tgl@sss.pgh.pa.us 1029 : 163 : fmtptr(const void *value, PrintfTarget *target)
1030 : : {
1031 : : int vallen;
1032 : : char convert[64];
1033 : :
1034 : : /* we rely on regular C library's snprintf to do the basic conversion */
1056 1035 : 163 : vallen = snprintf(convert, sizeof(convert), "%p", value);
3764 noah@leadboat.com 1036 [ - + ]: 163 : if (vallen < 0)
3764 noah@leadboat.com 1037 :UBC 0 : target->failed = true;
1038 : : else
3764 noah@leadboat.com 1039 :CBC 163 : dostr(convert, vallen, target);
7215 tgl@sss.pgh.pa.us 1040 : 163 : }
1041 : :
1042 : : static void
2530 1043 : 35942873 : fmtint(long long value, char type, int forcesign, int leftjust,
1044 : : int minlen, int zpad, int precision, int pointflag,
1045 : : PrintfTarget *target)
1046 : : {
1047 : : unsigned long long uvalue;
1048 : : int base;
1049 : : int dosign;
7215 1050 : 35942873 : const char *cvt = "0123456789abcdef";
9831 bruce@momjian.us 1051 : 35942873 : int signvalue = 0;
1052 : : char convert[64];
7478 1053 : 35942873 : int vallen = 0;
1054 : : int padlen; /* amount to pad */
1055 : : int zeropad; /* extra leading zeroes */
1056 : :
7215 tgl@sss.pgh.pa.us 1057 [ + + + + : 35942873 : switch (type)
+ - ]
1058 : : {
1059 : 14689897 : case 'd':
1060 : : case 'i':
1061 : 14689897 : base = 10;
1062 : 14689897 : dosign = 1;
1063 : 14689897 : break;
1064 : 5432 : case 'o':
1065 : 5432 : base = 8;
1066 : 5432 : dosign = 0;
1067 : 5432 : break;
1068 : 19508131 : case 'u':
1069 : 19508131 : base = 10;
1070 : 19508131 : dosign = 0;
1071 : 19508131 : break;
1072 : 36518 : case 'x':
1073 : 36518 : base = 16;
1074 : 36518 : dosign = 0;
1075 : 36518 : break;
1076 : 1702895 : case 'X':
1077 : 1702895 : cvt = "0123456789ABCDEF";
1078 : 1702895 : base = 16;
1079 : 1702895 : dosign = 0;
1080 : 1702895 : break;
7215 tgl@sss.pgh.pa.us 1081 :LBC (2) : default:
1082 : (2) : return; /* keep compiler quiet */
1083 : : }
1084 : :
1085 : : /* disable MSVC warning about applying unary minus to an unsigned value */
1086 : : #ifdef _MSC_VER
1087 : : #pragma warning(push)
1088 : : #pragma warning(disable: 4146)
1089 : : #endif
1090 : : /* Handle +/- */
7478 bruce@momjian.us 1091 [ + + + + ]:CBC 35942873 : if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
2530 andres@anarazel.de 1092 : 1743132 : uvalue = -(unsigned long long) value;
1093 : : else
1094 : 34199738 : uvalue = (unsigned long long) value;
1095 : : #ifdef _MSC_VER
1096 : : #pragma warning(pop)
1097 : : #endif
1098 : :
1099 : : /*
1100 : : * SUS: the result of converting 0 with an explicit precision of 0 is no
1101 : : * characters
1102 : : */
7215 tgl@sss.pgh.pa.us 1103 [ + + - + : 35942870 : if (value == 0 && pointflag && precision == 0)
- - ]
7215 tgl@sss.pgh.pa.us 1104 :UBC 0 : vallen = 0;
1105 : : else
1106 : : {
1107 : : /*
1108 : : * Convert integer to string. We special-case each of the possible
1109 : : * base values so as to avoid general-purpose divisions. On most
1110 : : * machines, division by a fixed constant can be done much more
1111 : : * cheaply than a general divide.
1112 : : */
1409 tgl@sss.pgh.pa.us 1113 [ + + ]:CBC 35942870 : if (base == 10)
1114 : : {
1115 : : do
1116 : : {
1117 : 93928814 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10];
1118 : 93928814 : uvalue = uvalue / 10;
1119 [ + + ]: 93928814 : } while (uvalue);
1120 : : }
1121 [ + + ]: 1744845 : else if (base == 16)
1122 : : {
1123 : : do
1124 : : {
1125 : 6233362 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16];
1126 : 6233362 : uvalue = uvalue / 16;
1127 [ + + ]: 6233362 : } while (uvalue);
1128 : : }
1129 : : else /* base == 8 */
1130 : : {
1131 : : do
1132 : : {
1133 : 16296 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8];
1134 : 16296 : uvalue = uvalue / 8;
1135 [ + + ]: 16296 : } while (uvalue);
1136 : : }
1137 : : }
1138 : :
7215 1139 : 35942870 : zeropad = Max(0, precision - vallen);
1140 : :
2530 1141 : 35942870 : padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
1142 : :
1143 : 35942872 : leading_pad(zpad, signvalue, &padlen, target);
1144 : :
1145 [ - + ]: 35942872 : if (zeropad > 0)
2530 tgl@sss.pgh.pa.us 1146 :UBC 0 : dopr_outchmulti('0', zeropad, target);
1147 : :
2530 tgl@sss.pgh.pa.us 1148 :CBC 35942872 : dostr(convert + sizeof(convert) - vallen, vallen, target);
1149 : :
1150 : 35942872 : trailing_pad(padlen, target);
1151 : : }
1152 : :
1153 : : static void
5263 bruce@momjian.us 1154 : 20100 : fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
1155 : : {
1156 : : int padlen; /* amount to pad */
1157 : :
2530 tgl@sss.pgh.pa.us 1158 : 20100 : padlen = compute_padlen(minlen, 1, leftjust);
1159 : :
1160 [ + + ]: 20100 : if (padlen > 0)
1161 : : {
1162 : 21 : dopr_outchmulti(' ', padlen, target);
1163 : 21 : padlen = 0;
1164 : : }
1165 : :
7215 1166 : 20100 : dopr_outch(value, target);
1167 : :
2530 1168 : 20100 : trailing_pad(padlen, target);
9864 scrappy@hub.org 1169 : 20100 : }
1170 : :
1171 : : static void
7478 bruce@momjian.us 1172 : 284232 : fmtfloat(double value, char type, int forcesign, int leftjust,
1173 : : int minlen, int zpad, int precision, int pointflag,
1174 : : PrintfTarget *target)
1175 : : {
7479 1176 : 284232 : int signvalue = 0;
1177 : : int prec;
1178 : : int vallen;
1179 : : char fmt[8];
1180 : : char convert[1024];
3869 1181 : 284232 : int zeropadlen = 0; /* amount to pad with zeroes */
1182 : : int padlen; /* amount to pad with spaces */
1183 : :
1184 : : /*
1185 : : * We rely on the regular C library's snprintf to do the basic conversion,
1186 : : * then handle padding considerations here.
1187 : : *
1188 : : * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
1189 : : * too wildly more than that with other hardware. In "f" format, snprintf
1190 : : * could therefore generate at most 308 characters to the left of the
1191 : : * decimal point; while we need to allow the precision to get as high as
1192 : : * 308+17 to ensure that we don't truncate significant digits from very
1193 : : * small values. To handle both these extremes, we use a buffer of 1024
1194 : : * bytes and limit requested precision to 350 digits; this should prevent
1195 : : * buffer overrun even with non-IEEE math. If the original precision
1196 : : * request was more than 350, separately pad with zeroes.
1197 : : *
1198 : : * We handle infinities and NaNs specially to ensure platform-independent
1199 : : * output.
1200 : : */
1201 [ - + ]: 284232 : if (precision < 0) /* cover possible overflow of "accum" */
3869 bruce@momjian.us 1202 :UBC 0 : precision = 0;
3869 bruce@momjian.us 1203 :CBC 284232 : prec = Min(precision, 350);
1204 : :
2525 tgl@sss.pgh.pa.us 1205 [ + + ]: 284232 : if (isnan(value))
1206 : : {
1207 : 24 : strcpy(convert, "NaN");
1208 : 24 : vallen = 3;
1209 : : /* no zero padding, regardless of precision spec */
1210 : : }
1211 : : else
1212 : : {
1213 : : /*
1214 : : * Handle sign (NaNs have no sign, so we don't do this in the case
1215 : : * above). "value < 0.0" will not be true for IEEE minus zero, so we
1216 : : * detect that by looking for the case where value equals 0.0
1217 : : * according to == but not according to memcmp.
1218 : : */
1219 : : static const double dzero = 0.0;
1220 : :
1221 [ + + + + ]: 561430 : if (adjust_sign((value < 0.0 ||
1222 [ + + ]: 277222 : (value == 0.0 &&
1223 [ - + ]: 53577 : memcmp(&value, &dzero, sizeof(double)) != 0)),
1224 : : forcesign, &signvalue))
1225 : 6986 : value = -value;
1226 : :
1227 [ + + + + ]: 284208 : if (isinf(value))
1228 : : {
1229 : 48 : strcpy(convert, "Infinity");
1230 : 48 : vallen = 8;
1231 : : /* no zero padding, regardless of precision spec */
1232 : : }
1233 [ + + ]: 284160 : else if (pointflag)
1234 : : {
1235 : 71888 : zeropadlen = precision - prec;
1236 : 71888 : fmt[0] = '%';
1237 : 71888 : fmt[1] = '.';
1238 : 71888 : fmt[2] = '*';
1239 : 71888 : fmt[3] = type;
1240 : 71888 : fmt[4] = '\0';
1056 1241 : 71888 : vallen = snprintf(convert, sizeof(convert), fmt, prec, value);
1242 : : }
1243 : : else
1244 : : {
2525 1245 : 212272 : fmt[0] = '%';
1246 : 212272 : fmt[1] = type;
1247 : 212272 : fmt[2] = '\0';
1056 1248 : 212272 : vallen = snprintf(convert, sizeof(convert), fmt, value);
1249 : : }
2525 1250 [ - + ]: 284208 : if (vallen < 0)
2525 tgl@sss.pgh.pa.us 1251 :UBC 0 : goto fail;
1252 : :
1253 : : /*
1254 : : * Windows, alone among our supported platforms, likes to emit
1255 : : * three-digit exponent fields even when two digits would do. Hack
1256 : : * such results to look like the way everyone else does it.
1257 : : */
1258 : : #ifdef WIN32
1259 : : if (vallen >= 6 &&
1260 : : convert[vallen - 5] == 'e' &&
1261 : : convert[vallen - 3] == '0')
1262 : : {
1263 : : convert[vallen - 3] = convert[vallen - 2];
1264 : : convert[vallen - 2] = convert[vallen - 1];
1265 : : vallen--;
1266 : : }
1267 : : #endif
1268 : : }
1269 : :
2530 tgl@sss.pgh.pa.us 1270 :CBC 284232 : padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
1271 : :
1272 : 284232 : leading_pad(zpad, signvalue, &padlen, target);
1273 : :
3869 bruce@momjian.us 1274 [ - + ]: 284232 : if (zeropadlen > 0)
1275 : : {
1276 : : /* If 'e' or 'E' format, inject zeroes before the exponent */
3869 bruce@momjian.us 1277 :UBC 0 : char *epos = strrchr(convert, 'e');
1278 : :
1279 [ # # ]: 0 : if (!epos)
1280 : 0 : epos = strrchr(convert, 'E');
1281 [ # # ]: 0 : if (epos)
1282 : : {
1283 : : /* pad before exponent */
1284 : 0 : dostr(convert, epos - convert, target);
2113 tgl@sss.pgh.pa.us 1285 : 0 : dopr_outchmulti('0', zeropadlen, target);
3869 bruce@momjian.us 1286 : 0 : dostr(epos, vallen - (epos - convert), target);
1287 : : }
1288 : : else
1289 : : {
1290 : : /* no exponent, pad after the digits */
1291 : 0 : dostr(convert, vallen, target);
2113 tgl@sss.pgh.pa.us 1292 : 0 : dopr_outchmulti('0', zeropadlen, target);
1293 : : }
1294 : : }
1295 : : else
1296 : : {
1297 : : /* no zero padding, just emit the number as-is */
3869 bruce@momjian.us 1298 :CBC 284232 : dostr(convert, vallen, target);
1299 : : }
1300 : :
2530 tgl@sss.pgh.pa.us 1301 : 284232 : trailing_pad(padlen, target);
3764 noah@leadboat.com 1302 : 284232 : return;
1303 : :
3764 noah@leadboat.com 1304 :UBC 0 : fail:
1305 : 0 : target->failed = true;
1306 : : }
1307 : :
1308 : : /*
1309 : : * Nonstandard entry point to print a double value efficiently.
1310 : : *
1311 : : * This is approximately equivalent to strfromd(), but has an API more
1312 : : * adapted to what float8out() wants. The behavior is like snprintf()
1313 : : * with a format of "%.ng", where n is the specified precision.
1314 : : * However, the target buffer must be nonempty (i.e. count > 0), and
1315 : : * the precision is silently bounded to a sane range.
1316 : : */
1317 : : int
2525 tgl@sss.pgh.pa.us 1318 :CBC 115108 : pg_strfromd(char *str, size_t count, int precision, double value)
1319 : : {
1320 : : PrintfTarget target;
1321 : 115108 : int signvalue = 0;
1322 : : int vallen;
1323 : : char fmt[8];
1324 : : char convert[64];
1325 : :
1326 : : /* Set up the target like pg_snprintf, but require nonempty buffer */
1327 [ - + ]: 115108 : Assert(count > 0);
1328 : 115108 : target.bufstart = target.bufptr = str;
1329 : 115108 : target.bufend = str + count - 1;
1330 : 115108 : target.stream = NULL;
1331 : 115108 : target.nchars = 0;
1332 : 115108 : target.failed = false;
1333 : :
1334 : : /*
1335 : : * We bound precision to a reasonable range; the combination of this and
1336 : : * the knowledge that we're using "g" format without padding allows the
1337 : : * convert[] buffer to be reasonably small.
1338 : : */
1339 [ - + ]: 115108 : if (precision < 1)
2525 tgl@sss.pgh.pa.us 1340 :UBC 0 : precision = 1;
2525 tgl@sss.pgh.pa.us 1341 [ - + ]:CBC 115108 : else if (precision > 32)
2525 tgl@sss.pgh.pa.us 1342 :UBC 0 : precision = 32;
1343 : :
1344 : : /*
1345 : : * The rest is just an inlined version of the fmtfloat() logic above,
1346 : : * simplified using the knowledge that no padding is wanted.
1347 : : */
2525 tgl@sss.pgh.pa.us 1348 [ + + ]:CBC 115108 : if (isnan(value))
1349 : : {
1350 : 6051 : strcpy(convert, "NaN");
1351 : 6051 : vallen = 3;
1352 : : }
1353 : : else
1354 : : {
1355 : : static const double dzero = 0.0;
1356 : :
1357 [ + + ]: 109057 : if (value < 0.0 ||
1358 [ + + ]: 92857 : (value == 0.0 &&
1359 [ + + ]: 13439 : memcmp(&value, &dzero, sizeof(double)) != 0))
1360 : : {
1361 : 16233 : signvalue = '-';
1362 : 16233 : value = -value;
1363 : : }
1364 : :
1365 [ + + + + ]: 109057 : if (isinf(value))
1366 : : {
1367 : 3558 : strcpy(convert, "Infinity");
1368 : 3558 : vallen = 8;
1369 : : }
1370 : : else
1371 : : {
1372 : 105499 : fmt[0] = '%';
1373 : 105499 : fmt[1] = '.';
1374 : 105499 : fmt[2] = '*';
1375 : 105499 : fmt[3] = 'g';
1376 : 105499 : fmt[4] = '\0';
1056 1377 : 105499 : vallen = snprintf(convert, sizeof(convert), fmt, precision, value);
2525 1378 [ - + ]: 105499 : if (vallen < 0)
1379 : : {
2525 tgl@sss.pgh.pa.us 1380 :UBC 0 : target.failed = true;
1381 : 0 : goto fail;
1382 : : }
1383 : :
1384 : : #ifdef WIN32
1385 : : if (vallen >= 6 &&
1386 : : convert[vallen - 5] == 'e' &&
1387 : : convert[vallen - 3] == '0')
1388 : : {
1389 : : convert[vallen - 3] = convert[vallen - 2];
1390 : : convert[vallen - 2] = convert[vallen - 1];
1391 : : vallen--;
1392 : : }
1393 : : #endif
1394 : : }
1395 : : }
1396 : :
2525 tgl@sss.pgh.pa.us 1397 [ + + ]:CBC 115108 : if (signvalue)
1398 : 16233 : dopr_outch(signvalue, &target);
1399 : :
1400 : 115108 : dostr(convert, vallen, &target);
1401 : :
1402 : 115108 : fail:
1403 : 115108 : *(target.bufptr) = '\0';
1404 [ - + ]: 230216 : return target.failed ? -1 : (target.bufptr - target.bufstart
1405 : 115108 : + target.nchars);
1406 : : }
1407 : :
1408 : :
1409 : : static void
5263 bruce@momjian.us 1410 : 80574659 : dostr(const char *str, int slen, PrintfTarget *target)
1411 : : {
1412 : : /* fast path for common case of slen == 1 */
2530 tgl@sss.pgh.pa.us 1413 [ + + ]: 80574659 : if (slen == 1)
1414 : : {
1415 : 27078884 : dopr_outch(*str, target);
1416 : 27078880 : return;
1417 : : }
1418 : :
7215 1419 [ + + ]: 105477553 : while (slen > 0)
1420 : : {
1421 : : int avail;
1422 : :
1423 [ + + ]: 52431645 : if (target->bufend != NULL)
1424 : 45275048 : avail = target->bufend - target->bufptr;
1425 : : else
1426 : 7156597 : avail = slen;
1427 [ + + ]: 52431645 : if (avail <= 0)
1428 : : {
1429 : : /* buffer full, can we dump to stream? */
1430 [ + + ]: 450664 : if (target->stream == NULL)
1431 : : {
2579 1432 : 449867 : target->nchars += slen; /* no, lose the data */
1433 : 449867 : return;
1434 : : }
7215 1435 : 797 : flushbuffer(target);
1436 : 797 : continue;
1437 : : }
1438 : 51980981 : avail = Min(avail, slen);
1439 : 51980981 : memmove(target->bufptr, str, avail);
1440 : 51980981 : target->bufptr += avail;
1441 : 51980981 : str += avail;
1442 : 51980981 : slen -= avail;
1443 : : }
1444 : : }
1445 : :
1446 : : static void
5263 bruce@momjian.us 1447 : 30987110 : dopr_outch(int c, PrintfTarget *target)
1448 : : {
7215 tgl@sss.pgh.pa.us 1449 [ + + + + ]: 30987110 : if (target->bufend != NULL && target->bufptr >= target->bufend)
1450 : : {
1451 : : /* buffer full, can we dump to stream? */
1452 [ + - ]: 147535 : if (target->stream == NULL)
1453 : : {
2579 1454 : 147535 : target->nchars++; /* no, lose the data */
1455 : 147535 : return;
1456 : : }
7215 tgl@sss.pgh.pa.us 1457 :UBC 0 : flushbuffer(target);
1458 : : }
7215 tgl@sss.pgh.pa.us 1459 :CBC 30839575 : *(target->bufptr++) = c;
1460 : : }
1461 : :
1462 : : static void
2530 1463 : 3634454 : dopr_outchmulti(int c, int slen, PrintfTarget *target)
1464 : : {
1465 : : /* fast path for common case of slen == 1 */
1466 [ + + ]: 3634454 : if (slen == 1)
1467 : : {
1468 : 2119381 : dopr_outch(c, target);
1469 : 2119381 : return;
1470 : : }
1471 : :
1472 [ + + ]: 3030179 : while (slen > 0)
1473 : : {
1474 : : int avail;
1475 : :
1476 [ + + ]: 1515184 : if (target->bufend != NULL)
1477 : 1497087 : avail = target->bufend - target->bufptr;
1478 : : else
1479 : 18097 : avail = slen;
1480 [ + + ]: 1515184 : if (avail <= 0)
1481 : : {
1482 : : /* buffer full, can we dump to stream? */
1483 [ + + ]: 135 : if (target->stream == NULL)
1484 : : {
2530 tgl@sss.pgh.pa.us 1485 :GBC 78 : target->nchars += slen; /* no, lose the data */
1486 : 78 : return;
1487 : : }
2530 tgl@sss.pgh.pa.us 1488 :CBC 57 : flushbuffer(target);
1489 : 57 : continue;
1490 : : }
1491 : 1515049 : avail = Min(avail, slen);
1492 : 1515049 : memset(target->bufptr, c, avail);
1493 : 1515049 : target->bufptr += avail;
1494 : 1515049 : slen -= avail;
1495 : : }
1496 : : }
1497 : :
1498 : :
1499 : : static int
7478 bruce@momjian.us 1500 : 14974105 : adjust_sign(int is_negative, int forcesign, int *signvalue)
1501 : : {
1502 [ + + ]: 14974105 : if (is_negative)
1503 : : {
1504 : 1750118 : *signvalue = '-';
1505 : 1750118 : return true;
1506 : : }
1507 [ + + ]: 13223987 : else if (forcesign)
1508 : 102 : *signvalue = '+';
1509 : 13223987 : return false;
1510 : : }
1511 : :
1512 : :
1513 : : static int
2530 tgl@sss.pgh.pa.us 1514 : 37233849 : compute_padlen(int minlen, int vallen, int leftjust)
1515 : : {
1516 : : int padlen;
1517 : :
1518 : 37233849 : padlen = minlen - vallen;
1519 [ + + ]: 37233849 : if (padlen < 0)
1520 : 31233978 : padlen = 0;
7478 bruce@momjian.us 1521 [ + + ]: 37233849 : if (leftjust)
2530 tgl@sss.pgh.pa.us 1522 : 567091 : padlen = -padlen;
1523 : 37233849 : return padlen;
1524 : : }
1525 : :
1526 : :
1527 : : static void
1528 : 36227102 : leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
1529 : : {
1530 : : int maxpad;
1531 : :
7478 bruce@momjian.us 1532 [ + + + + ]: 36227102 : if (*padlen > 0 && zpad)
1533 : : {
2530 tgl@sss.pgh.pa.us 1534 [ + + ]: 2174028 : if (signvalue)
1535 : : {
1536 : 120 : dopr_outch(signvalue, target);
7215 1537 : 120 : --(*padlen);
2530 1538 : 120 : signvalue = 0;
1539 : : }
1540 [ + + ]: 2174028 : if (*padlen > 0)
1541 : : {
1542 : 2174019 : dopr_outchmulti(zpad, *padlen, target);
1543 : 2174019 : *padlen = 0;
1544 : : }
1545 : : }
1546 : 36227102 : maxpad = (signvalue != 0);
1547 [ + + ]: 36227102 : if (*padlen > maxpad)
1548 : : {
1549 : 793307 : dopr_outchmulti(' ', *padlen - maxpad, target);
1550 : 793307 : *padlen = maxpad;
1551 : : }
1552 [ + + ]: 36227102 : if (signvalue)
1553 : : {
1554 : 1750100 : dopr_outch(signvalue, target);
7478 bruce@momjian.us 1555 [ - + ]: 1750100 : if (*padlen > 0)
7215 tgl@sss.pgh.pa.us 1556 :UBC 0 : --(*padlen);
7215 tgl@sss.pgh.pa.us 1557 [ - + ]:CBC 1750100 : else if (*padlen < 0)
7215 tgl@sss.pgh.pa.us 1558 :UBC 0 : ++(*padlen);
1559 : : }
7478 bruce@momjian.us 1560 :CBC 36227102 : }
1561 : :
1562 : :
1563 : : static void
2530 tgl@sss.pgh.pa.us 1564 : 37233848 : trailing_pad(int padlen, PrintfTarget *target)
1565 : : {
1566 [ + + ]: 37233848 : if (padlen < 0)
1567 : 375569 : dopr_outchmulti(' ', -padlen, target);
7478 bruce@momjian.us 1568 : 37233848 : }
|