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-2024, 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
6974 bruce@momjian.us 174 :CBC 35847442 : 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 : : */
2069 tgl@sss.pgh.pa.us 185 [ + + ]: 35847442 : if (count == 0)
186 : : {
187 : 119286 : str = onebyte;
188 : 119286 : count = 1;
189 : : }
6705 190 : 35847442 : target.bufstart = target.bufptr = str;
191 : 35847442 : target.bufend = str + count - 1;
192 : 35847442 : target.stream = NULL;
2069 193 : 35847442 : target.nchars = 0;
3254 noah@leadboat.com 194 : 35847442 : target.failed = false;
195 : 35847442 : dopr(&target, fmt, args);
6705 tgl@sss.pgh.pa.us 196 : 35847442 : *(target.bufptr) = '\0';
2069 197 [ - + ]: 71694884 : return target.failed ? -1 : (target.bufptr - target.bufstart
198 : 35847442 : + target.nchars);
199 : : }
200 : :
201 : : int
6974 bruce@momjian.us 202 : 19532303 : pg_snprintf(char *str, size_t count, const char *fmt,...)
203 : : {
204 : : int len;
205 : : va_list args;
206 : :
9249 207 : 19532303 : va_start(args, fmt);
6974 208 : 19532303 : len = pg_vsnprintf(str, count, fmt, args);
9249 209 : 19532303 : va_end(args);
9354 scrappy@hub.org 210 : 19532303 : return len;
211 : : }
212 : :
213 : : int
6705 tgl@sss.pgh.pa.us 214 : 6592610 : pg_vsprintf(char *str, const char *fmt, va_list args)
215 : : {
216 : : PrintfTarget target;
217 : :
218 : 6592610 : target.bufstart = target.bufptr = str;
219 : 6592610 : target.bufend = NULL;
220 : 6592610 : target.stream = NULL;
2069 221 : 6592610 : target.nchars = 0; /* not really used in this case */
3254 noah@leadboat.com 222 : 6592610 : target.failed = false;
223 : 6592610 : dopr(&target, fmt, args);
6705 tgl@sss.pgh.pa.us 224 : 6592610 : *(target.bufptr) = '\0';
2069 225 [ - + ]: 13185220 : return target.failed ? -1 : (target.bufptr - target.bufstart
226 : 6592610 : + target.nchars);
227 : : }
228 : :
229 : : int
6969 bruce@momjian.us 230 : 6592610 : pg_sprintf(char *str, const char *fmt,...)
231 : : {
232 : : int len;
233 : : va_list args;
234 : :
235 : 6592610 : va_start(args, fmt);
6705 tgl@sss.pgh.pa.us 236 : 6592610 : len = pg_vsprintf(str, fmt, args);
6969 bruce@momjian.us 237 : 6592610 : va_end(args);
238 : 6592610 : return len;
239 : : }
240 : :
241 : : int
6705 tgl@sss.pgh.pa.us 242 : 2230032 : pg_vfprintf(FILE *stream, const char *fmt, va_list args)
243 : : {
244 : : PrintfTarget target;
245 : : char buffer[1024]; /* size is arbitrary */
246 : :
247 [ - + ]: 2230032 : if (stream == NULL)
248 : : {
6705 tgl@sss.pgh.pa.us 249 :UBC 0 : errno = EINVAL;
250 : 0 : return -1;
251 : : }
6705 tgl@sss.pgh.pa.us 252 :CBC 2230032 : target.bufstart = target.bufptr = buffer;
2069 253 : 2230032 : target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
6705 254 : 2230032 : target.stream = stream;
255 : 2230032 : target.nchars = 0;
3254 noah@leadboat.com 256 : 2230032 : target.failed = false;
257 : 2230032 : dopr(&target, fmt, args);
258 : : /* dump any remaining buffer contents */
6705 tgl@sss.pgh.pa.us 259 : 2230032 : flushbuffer(&target);
3254 noah@leadboat.com 260 [ - + ]: 2230032 : return target.failed ? -1 : target.nchars;
261 : : }
262 : :
263 : : int
6974 bruce@momjian.us 264 : 1116989 : pg_fprintf(FILE *stream, const char *fmt,...)
265 : : {
266 : : int len;
267 : : va_list args;
268 : :
269 : 1116989 : va_start(args, fmt);
6705 tgl@sss.pgh.pa.us 270 : 1116989 : len = pg_vfprintf(stream, fmt, args);
6974 bruce@momjian.us 271 : 1116989 : va_end(args);
272 : 1116989 : return len;
273 : : }
274 : :
275 : : int
2015 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
6974 bruce@momjian.us 282 :CBC 1105278 : pg_printf(const char *fmt,...)
283 : : {
284 : : int len;
285 : : va_list args;
286 : :
6983 287 : 1105278 : va_start(args, fmt);
6705 tgl@sss.pgh.pa.us 288 : 1105278 : len = pg_vfprintf(stdout, fmt, args);
6983 bruce@momjian.us 289 : 1105278 : va_end(args);
290 : 1105278 : 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
4753 298 : 2230356 : flushbuffer(PrintfTarget *target)
299 : : {
6402 300 : 2230356 : 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 : : */
3254 noah@leadboat.com 306 [ + - + + ]: 2230356 : if (!target->failed && nc > 0)
307 : : {
308 : : size_t written;
309 : :
310 : 2103605 : written = fwrite(target->bufstart, 1, nc, target->stream);
311 : 2103605 : target->nchars += written;
312 [ - + ]: 2103605 : if (written != nc)
3254 noah@leadboat.com 313 :UBC 0 : target->failed = true;
314 : : }
6705 tgl@sss.pgh.pa.us 315 :CBC 2230356 : target->bufptr = target->bufstart;
316 : 2230356 : }
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), it's a good bit faster than the
342 : : * equivalent manual loop. If it doesn't exist, provide a replacement.
343 : : *
344 : : * Note: glibc declares this as returning "char *", but that would require
345 : : * casting away const internally, so we don't follow that detail.
346 : : */
347 : : #ifndef HAVE_STRCHRNUL
348 : :
349 : : static inline const char *
350 : : strchrnul(const char *s, int c)
351 : : {
352 : : while (*s != '\0' && *s != c)
353 : : s++;
354 : : return s;
355 : : }
356 : :
357 : : #else
358 : :
359 : : /*
360 : : * glibc's <string.h> declares strchrnul only if _GNU_SOURCE is defined.
361 : : * While we typically use that on glibc platforms, configure will set
362 : : * HAVE_STRCHRNUL whether it's used or not. Fill in the missing declaration
363 : : * so that this file will compile cleanly with or without _GNU_SOURCE.
364 : : */
365 : : #ifndef _GNU_SOURCE
366 : : extern char *strchrnul(const char *s, int c);
367 : : #endif
368 : :
369 : : #endif /* HAVE_STRCHRNUL */
370 : :
371 : :
372 : : /*
373 : : * dopr(): the guts of *printf for all cases.
374 : : */
375 : : static void
4753 bruce@momjian.us 376 : 44670084 : dopr(PrintfTarget *target, const char *format, va_list args)
377 : : {
2027 tgl@sss.pgh.pa.us 378 : 44670084 : int save_errno = errno;
2020 379 : 44670084 : const char *first_pct = NULL;
380 : : int ch;
381 : : bool have_dollar;
382 : : bool have_star;
383 : : bool afterstar;
384 : : int accum;
385 : : int longlongflag;
386 : : int longflag;
387 : : int pointflag;
388 : : int leftjust;
389 : : int fieldwidth;
390 : : int precision;
391 : : int zpad;
392 : : int forcesign;
393 : : int fmtpos;
394 : : int cvalue;
395 : : long long numvalue;
396 : : double fvalue;
397 : : const char *strvalue;
398 : : PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
399 : :
400 : : /*
401 : : * Initially, we suppose the format string does not use %n$. The first
402 : : * time we come to a conversion spec that has that, we'll call
403 : : * find_arguments() to check for consistent use of %n$ and fill the
404 : : * argvalues array with the argument values in the correct order.
405 : : */
406 : 44670084 : have_dollar = false;
407 : :
408 [ + + ]: 108788727 : while (*format != '\0')
409 : : {
410 : : /* Locate next conversion specifier */
411 [ + + ]: 70098668 : if (*format != '%')
412 : : {
413 : : /* Scan to next '%' or end of string */
414 : 39475349 : const char *next_pct = strchrnul(format + 1, '%');
415 : :
416 : : /* Dump literal data we just scanned over */
417 : 39475349 : dostr(format, next_pct - format, target);
418 [ - + ]: 39475349 : if (target->failed)
6705 tgl@sss.pgh.pa.us 419 :UBC 0 : break;
420 : :
2020 tgl@sss.pgh.pa.us 421 [ + + ]:CBC 39475349 : if (*next_pct == '\0')
6705 422 : 5980025 : break;
2020 423 : 33495324 : format = next_pct;
424 : : }
425 : :
426 : : /*
427 : : * Remember start of first conversion spec; if we find %n$, then it's
428 : : * sufficient for find_arguments() to start here, without rescanning
429 : : * earlier literal text.
430 : : */
431 [ + + ]: 64118643 : if (first_pct == NULL)
432 : 44156523 : first_pct = format;
433 : :
434 : : /* Process conversion spec starting at *format */
435 : 64118643 : format++;
436 : :
437 : : /* Fast path for conversion spec that is exactly %s */
438 [ + + ]: 64118643 : if (*format == 's')
439 : : {
440 : 19847133 : format++;
441 : 19847133 : strvalue = va_arg(args, char *);
995 442 [ - + ]: 19847133 : if (strvalue == NULL)
995 tgl@sss.pgh.pa.us 443 :UBC 0 : strvalue = "(null)";
2020 tgl@sss.pgh.pa.us 444 :CBC 19847133 : dostr(strvalue, strlen(strvalue), target);
445 [ - + ]: 19847133 : if (target->failed)
2020 tgl@sss.pgh.pa.us 446 :UBC 0 : break;
2020 tgl@sss.pgh.pa.us 447 :CBC 19847133 : continue;
448 : : }
449 : :
6705 450 : 44271510 : fieldwidth = precision = zpad = leftjust = forcesign = 0;
451 : 44271510 : longflag = longlongflag = pointflag = 0;
452 : 44271510 : fmtpos = accum = 0;
453 : 44271510 : have_star = afterstar = false;
6402 bruce@momjian.us 454 : 37556846 : nextch2:
6705 tgl@sss.pgh.pa.us 455 : 81828356 : ch = *format++;
9321 bruce@momjian.us 456 [ + + + + : 81828356 : switch (ch)
+ + - + +
+ + + + +
+ + + +
- ]
457 : : {
6705 tgl@sss.pgh.pa.us 458 : 497141 : case '-':
459 : 497141 : leftjust = 1;
460 : 497141 : goto nextch2;
461 : 132 : case '+':
462 : 132 : forcesign = 1;
463 : 132 : goto nextch2;
464 : 15008532 : case '0':
465 : : /* set zero padding if no nonzero digits yet */
466 [ + + + + ]: 15008532 : if (accum == 0 && !pointflag)
467 : 14727124 : zpad = '0';
468 : : /* FALL THRU */
469 : : case '1':
470 : : case '2':
471 : : case '3':
472 : : case '4':
473 : : case '5':
474 : : case '6':
475 : : case '7':
476 : : case '8':
477 : : case '9':
478 : 31426405 : accum = accum * 10 + (ch - '0');
479 : 31426405 : goto nextch2;
480 : 449438 : case '.':
481 [ - + ]: 449438 : if (have_star)
6705 tgl@sss.pgh.pa.us 482 :UBC 0 : have_star = false;
483 : : else
6705 tgl@sss.pgh.pa.us 484 :CBC 449438 : fieldwidth = accum;
485 : 449438 : pointflag = 1;
486 : 449438 : accum = 0;
487 : 449438 : goto nextch2;
488 : 679578 : case '*':
489 [ - + ]: 679578 : if (have_dollar)
490 : : {
491 : : /*
492 : : * We'll process value after reading n$. Note it's OK to
493 : : * assume have_dollar is set correctly, because in a valid
494 : : * format string the initial % must have had n$ if * does.
495 : : */
6705 tgl@sss.pgh.pa.us 496 :UBC 0 : afterstar = true;
497 : : }
498 : : else
499 : : {
500 : : /* fetch and process value now */
6402 bruce@momjian.us 501 :CBC 679578 : int starval = va_arg(args, int);
502 : :
6705 tgl@sss.pgh.pa.us 503 [ + + ]: 679578 : if (pointflag)
504 : : {
505 : 29357 : precision = starval;
506 [ - + ]: 29357 : if (precision < 0)
507 : : {
6705 tgl@sss.pgh.pa.us 508 :UBC 0 : precision = 0;
5871 509 : 0 : pointflag = 0;
510 : : }
511 : : }
512 : : else
513 : : {
6705 tgl@sss.pgh.pa.us 514 :CBC 650221 : fieldwidth = starval;
515 [ + + ]: 650221 : if (fieldwidth < 0)
516 : : {
517 : 2823 : leftjust = 1;
518 : 2823 : fieldwidth = -fieldwidth;
519 : : }
520 : : }
521 : : }
522 : 679578 : have_star = true;
523 : 679578 : accum = 0;
524 : 679578 : goto nextch2;
6705 tgl@sss.pgh.pa.us 525 :UBC 0 : case '$':
526 : : /* First dollar sign? */
2020 527 [ # # ]: 0 : if (!have_dollar)
528 : : {
529 : : /* Yup, so examine all conversion specs in format */
530 [ # # ]: 0 : if (!find_arguments(first_pct, args, argvalues))
531 : 0 : goto bad_format;
532 : 0 : have_dollar = true;
533 : : }
6705 534 [ # # ]: 0 : if (afterstar)
535 : : {
536 : : /* fetch and process star value */
6402 bruce@momjian.us 537 : 0 : int starval = argvalues[accum].i;
538 : :
6705 tgl@sss.pgh.pa.us 539 [ # # ]: 0 : if (pointflag)
540 : : {
541 : 0 : precision = starval;
542 [ # # ]: 0 : if (precision < 0)
543 : : {
544 : 0 : precision = 0;
5871 545 : 0 : pointflag = 0;
546 : : }
547 : : }
548 : : else
549 : : {
6705 550 : 0 : fieldwidth = starval;
551 [ # # ]: 0 : if (fieldwidth < 0)
552 : : {
553 : 0 : leftjust = 1;
554 : 0 : fieldwidth = -fieldwidth;
555 : : }
556 : : }
557 : 0 : afterstar = false;
558 : : }
559 : : else
560 : 0 : fmtpos = accum;
561 : 0 : accum = 0;
562 : 0 : goto nextch2;
6705 tgl@sss.pgh.pa.us 563 :CBC 4343959 : case 'l':
564 [ + + ]: 4343959 : if (longflag)
565 : 841355 : longlongflag = 1;
566 : : else
567 : 3502604 : longflag = 1;
568 : 4343959 : goto nextch2;
3734 569 : 160171 : case 'z':
570 : : #if SIZEOF_SIZE_T == 8
571 : : #ifdef HAVE_LONG_INT_64
572 : 160171 : longflag = 1;
573 : : #elif defined(HAVE_LONG_LONG_INT_64)
574 : : longlongflag = 1;
575 : : #else
576 : : #error "Don't know how to print 64bit integers"
577 : : #endif
578 : : #else
579 : : /* assume size_t is same size as int */
580 : : #endif
581 : 160171 : goto nextch2;
6705 582 : 22 : case 'h':
583 : : case '\'':
584 : : /* ignore these */
585 : 22 : goto nextch2;
586 : 16830781 : case 'd':
587 : : case 'i':
588 [ + + ]: 16830781 : if (!have_star)
589 : : {
590 [ - + ]: 16811197 : if (pointflag)
6705 tgl@sss.pgh.pa.us 591 :UBC 0 : precision = accum;
592 : : else
6705 tgl@sss.pgh.pa.us 593 :CBC 16811197 : fieldwidth = accum;
594 : : }
595 [ - + ]: 16830781 : if (have_dollar)
596 : : {
6705 tgl@sss.pgh.pa.us 597 [ # # ]:UBC 0 : if (longlongflag)
598 : 0 : numvalue = argvalues[fmtpos].ll;
599 [ # # ]: 0 : else if (longflag)
600 : 0 : numvalue = argvalues[fmtpos].l;
601 : : else
602 : 0 : numvalue = argvalues[fmtpos].i;
603 : : }
604 : : else
605 : : {
6705 tgl@sss.pgh.pa.us 606 [ + + ]:CBC 16830781 : if (longlongflag)
2020 607 : 764503 : numvalue = va_arg(args, long long);
6705 608 [ + + ]: 16066278 : else if (longflag)
609 : 2368679 : numvalue = va_arg(args, long);
610 : : else
611 : 13697599 : numvalue = va_arg(args, int);
612 : : }
613 : 16830781 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
614 : : precision, pointflag, target);
9321 bruce@momjian.us 615 : 16830781 : break;
6705 tgl@sss.pgh.pa.us 616 : 25626545 : case 'o':
617 : : case 'u':
618 : : case 'x':
619 : : case 'X':
620 [ + - ]: 25626545 : if (!have_star)
621 : : {
622 [ - + ]: 25626545 : if (pointflag)
6705 tgl@sss.pgh.pa.us 623 :UBC 0 : precision = accum;
624 : : else
6705 tgl@sss.pgh.pa.us 625 :CBC 25626545 : fieldwidth = accum;
626 : : }
627 [ - + ]: 25626545 : if (have_dollar)
628 : : {
6705 tgl@sss.pgh.pa.us 629 [ # # ]:UBC 0 : if (longlongflag)
2020 630 : 0 : numvalue = (unsigned long long) argvalues[fmtpos].ll;
6705 631 [ # # ]: 0 : else if (longflag)
632 : 0 : numvalue = (unsigned long) argvalues[fmtpos].l;
633 : : else
634 : 0 : numvalue = (unsigned int) argvalues[fmtpos].i;
635 : : }
636 : : else
637 : : {
6705 tgl@sss.pgh.pa.us 638 [ + + ]:CBC 25626545 : if (longlongflag)
2020 639 : 76852 : numvalue = (unsigned long long) va_arg(args, long long);
6705 640 [ + + ]: 25549693 : else if (longflag)
641 : 452741 : numvalue = (unsigned long) va_arg(args, long);
642 : : else
643 : 25096952 : numvalue = (unsigned int) va_arg(args, int);
644 : : }
645 : 25626545 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
646 : : precision, pointflag, target);
6969 bruce@momjian.us 647 : 25626545 : break;
6705 tgl@sss.pgh.pa.us 648 : 19342 : case 'c':
649 [ + + ]: 19342 : if (!have_star)
650 : : {
651 [ - + ]: 19321 : if (pointflag)
6705 tgl@sss.pgh.pa.us 652 :UBC 0 : precision = accum;
653 : : else
6705 tgl@sss.pgh.pa.us 654 :CBC 19321 : fieldwidth = accum;
655 : : }
656 [ - + ]: 19342 : if (have_dollar)
6705 tgl@sss.pgh.pa.us 657 :UBC 0 : cvalue = (unsigned char) argvalues[fmtpos].i;
658 : : else
6705 tgl@sss.pgh.pa.us 659 :CBC 19342 : cvalue = (unsigned char) va_arg(args, int);
660 : 19342 : fmtchar(cvalue, leftjust, fieldwidth, target);
6969 bruce@momjian.us 661 : 19342 : break;
6705 tgl@sss.pgh.pa.us 662 : 903307 : case 's':
663 [ + + ]: 903307 : if (!have_star)
664 : : {
665 [ - + ]: 263028 : if (pointflag)
6705 tgl@sss.pgh.pa.us 666 :UBC 0 : precision = accum;
667 : : else
6705 tgl@sss.pgh.pa.us 668 :CBC 263028 : fieldwidth = accum;
669 : : }
670 [ - + ]: 903307 : if (have_dollar)
6705 tgl@sss.pgh.pa.us 671 :UBC 0 : strvalue = argvalues[fmtpos].cptr;
672 : : else
6705 tgl@sss.pgh.pa.us 673 :CBC 903307 : strvalue = va_arg(args, char *);
674 : : /* If string is NULL, silently substitute "(null)" */
995 675 [ - + ]: 903307 : if (strvalue == NULL)
995 tgl@sss.pgh.pa.us 676 :UBC 0 : strvalue = "(null)";
6705 tgl@sss.pgh.pa.us 677 :CBC 903307 : fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
678 : : target);
6969 bruce@momjian.us 679 : 903307 : break;
6705 tgl@sss.pgh.pa.us 680 : 41 : case 'p':
681 : : /* fieldwidth/leftjust are ignored ... */
682 [ - + ]: 41 : if (have_dollar)
6705 tgl@sss.pgh.pa.us 683 :UBC 0 : strvalue = argvalues[fmtpos].cptr;
684 : : else
6705 tgl@sss.pgh.pa.us 685 :CBC 41 : strvalue = va_arg(args, char *);
995 686 : 41 : fmtptr((const void *) strvalue, target);
6969 bruce@momjian.us 687 : 41 : break;
6705 tgl@sss.pgh.pa.us 688 : 667186 : case 'e':
689 : : case 'E':
690 : : case 'f':
691 : : case 'g':
692 : : case 'G':
693 [ + + ]: 667186 : if (!have_star)
694 : : {
695 [ + + ]: 647492 : if (pointflag)
696 : 420081 : precision = accum;
697 : : else
698 : 227411 : fieldwidth = accum;
699 : : }
700 [ - + ]: 667186 : if (have_dollar)
6705 tgl@sss.pgh.pa.us 701 :UBC 0 : fvalue = argvalues[fmtpos].d;
702 : : else
6705 tgl@sss.pgh.pa.us 703 :CBC 667186 : fvalue = va_arg(args, double);
704 : 667186 : fmtfloat(fvalue, ch, forcesign, leftjust,
705 : : fieldwidth, zpad,
706 : : precision, pointflag,
707 : : target);
6969 bruce@momjian.us 708 : 667186 : break;
2027 tgl@sss.pgh.pa.us 709 : 133 : case 'm':
710 : : {
711 : : char errbuf[PG_STRERROR_R_BUFLEN];
712 : 133 : const char *errm = strerror_r(save_errno,
713 : : errbuf, sizeof(errbuf));
714 : :
715 : 133 : dostr(errm, strlen(errm), target);
716 : : }
717 : 133 : break;
6705 718 : 224175 : case '%':
719 : 224175 : dopr_outch('%', target);
6969 bruce@momjian.us 720 : 224175 : break;
1956 tgl@sss.pgh.pa.us 721 :UBC 0 : default:
722 : :
723 : : /*
724 : : * Anything else --- in particular, '\0' indicating end of
725 : : * format string --- is bogus.
726 : : */
727 : 0 : goto bad_format;
728 : : }
729 : :
730 : : /* Check for failure after each conversion spec */
2020 tgl@sss.pgh.pa.us 731 [ - + ]:CBC 44271510 : if (target->failed)
2020 tgl@sss.pgh.pa.us 732 :UBC 0 : break;
733 : : }
734 : :
3254 noah@leadboat.com 735 :CBC 44670084 : return;
736 : :
3254 noah@leadboat.com 737 :UBC 0 : bad_format:
738 : 0 : errno = EINVAL;
739 : 0 : target->failed = true;
740 : : }
741 : :
742 : : /*
743 : : * find_arguments(): sort out the arguments for a format spec with %n$
744 : : *
745 : : * If format is valid, return true and fill argvalues[i] with the value
746 : : * for the conversion spec that has %i$ or *i$. Else return false.
747 : : */
748 : : static bool
2020 tgl@sss.pgh.pa.us 749 : 0 : find_arguments(const char *format, va_list args,
750 : : PrintfArgValue *argvalues)
751 : : {
752 : : int ch;
753 : : bool afterstar;
754 : : int accum;
755 : : int longlongflag;
756 : : int longflag;
757 : : int fmtpos;
758 : : int i;
638 peter@eisentraut.org 759 : 0 : int last_dollar = 0; /* Init to "no dollar arguments known" */
760 : 0 : PrintfArgType argtypes[PG_NL_ARGMAX + 1] = {0};
761 : :
762 : : /*
763 : : * This loop must accept the same format strings as the one in dopr().
764 : : * However, we don't need to analyze them to the same level of detail.
765 : : *
766 : : * Since we're only called if there's a dollar-type spec somewhere, we can
767 : : * fail immediately if we find a non-dollar spec. Per the C99 standard,
768 : : * all argument references in the format string must be one or the other.
769 : : */
2020 tgl@sss.pgh.pa.us 770 [ # # ]: 0 : while (*format != '\0')
771 : : {
772 : : /* Locate next conversion specifier */
773 [ # # ]: 0 : if (*format != '%')
774 : : {
775 : : /* Unlike dopr, we can just quit if there's no more specifiers */
776 : 0 : format = strchr(format + 1, '%');
777 [ # # ]: 0 : if (format == NULL)
778 : 0 : break;
779 : : }
780 : :
781 : : /* Process conversion spec starting at *format */
782 : 0 : format++;
783 : 0 : longflag = longlongflag = 0;
784 : 0 : fmtpos = accum = 0;
785 : 0 : afterstar = false;
786 : 0 : nextch1:
787 : 0 : ch = *format++;
788 [ # # # # : 0 : switch (ch)
# # # # #
# # # #
# ]
789 : : {
790 : 0 : case '-':
791 : : case '+':
792 : 0 : goto nextch1;
793 : 0 : case '0':
794 : : case '1':
795 : : case '2':
796 : : case '3':
797 : : case '4':
798 : : case '5':
799 : : case '6':
800 : : case '7':
801 : : case '8':
802 : : case '9':
803 : 0 : accum = accum * 10 + (ch - '0');
804 : 0 : goto nextch1;
805 : 0 : case '.':
806 : 0 : accum = 0;
807 : 0 : goto nextch1;
808 : 0 : case '*':
809 [ # # ]: 0 : if (afterstar)
810 : 0 : return false; /* previous star missing dollar */
811 : 0 : afterstar = true;
812 : 0 : accum = 0;
813 : 0 : goto nextch1;
814 : 0 : case '$':
815 [ # # # # ]: 0 : if (accum <= 0 || accum > PG_NL_ARGMAX)
816 : 0 : return false;
817 [ # # ]: 0 : if (afterstar)
818 : : {
819 [ # # ]: 0 : if (argtypes[accum] &&
820 [ # # ]: 0 : argtypes[accum] != ATYPE_INT)
821 : 0 : return false;
822 : 0 : argtypes[accum] = ATYPE_INT;
823 : 0 : last_dollar = Max(last_dollar, accum);
824 : 0 : afterstar = false;
825 : : }
826 : : else
827 : 0 : fmtpos = accum;
828 : 0 : accum = 0;
829 : 0 : goto nextch1;
830 : 0 : case 'l':
831 [ # # ]: 0 : if (longflag)
832 : 0 : longlongflag = 1;
833 : : else
834 : 0 : longflag = 1;
835 : 0 : goto nextch1;
836 : 0 : case 'z':
837 : : #if SIZEOF_SIZE_T == 8
838 : : #ifdef HAVE_LONG_INT_64
839 : 0 : longflag = 1;
840 : : #elif defined(HAVE_LONG_LONG_INT_64)
841 : : longlongflag = 1;
842 : : #else
843 : : #error "Don't know how to print 64bit integers"
844 : : #endif
845 : : #else
846 : : /* assume size_t is same size as int */
847 : : #endif
848 : 0 : goto nextch1;
849 : 0 : case 'h':
850 : : case '\'':
851 : : /* ignore these */
852 : 0 : goto nextch1;
853 : 0 : case 'd':
854 : : case 'i':
855 : : case 'o':
856 : : case 'u':
857 : : case 'x':
858 : : case 'X':
859 [ # # ]: 0 : if (fmtpos)
860 : : {
861 : : PrintfArgType atype;
862 : :
863 [ # # ]: 0 : if (longlongflag)
864 : 0 : atype = ATYPE_LONGLONG;
865 [ # # ]: 0 : else if (longflag)
866 : 0 : atype = ATYPE_LONG;
867 : : else
868 : 0 : atype = ATYPE_INT;
869 [ # # ]: 0 : if (argtypes[fmtpos] &&
870 [ # # ]: 0 : argtypes[fmtpos] != atype)
871 : 0 : return false;
872 : 0 : argtypes[fmtpos] = atype;
873 : 0 : last_dollar = Max(last_dollar, fmtpos);
874 : : }
875 : : else
876 : 0 : return false; /* non-dollar conversion spec */
877 : 0 : break;
878 : 0 : case 'c':
879 [ # # ]: 0 : if (fmtpos)
880 : : {
881 [ # # ]: 0 : if (argtypes[fmtpos] &&
882 [ # # ]: 0 : argtypes[fmtpos] != ATYPE_INT)
883 : 0 : return false;
884 : 0 : argtypes[fmtpos] = ATYPE_INT;
885 : 0 : last_dollar = Max(last_dollar, fmtpos);
886 : : }
887 : : else
888 : 0 : return false; /* non-dollar conversion spec */
889 : 0 : break;
890 : 0 : case 's':
891 : : case 'p':
892 [ # # ]: 0 : if (fmtpos)
893 : : {
894 [ # # ]: 0 : if (argtypes[fmtpos] &&
895 [ # # ]: 0 : argtypes[fmtpos] != ATYPE_CHARPTR)
896 : 0 : return false;
897 : 0 : argtypes[fmtpos] = ATYPE_CHARPTR;
898 : 0 : last_dollar = Max(last_dollar, fmtpos);
899 : : }
900 : : else
901 : 0 : return false; /* non-dollar conversion spec */
902 : 0 : break;
903 : 0 : case 'e':
904 : : case 'E':
905 : : case 'f':
906 : : case 'g':
907 : : case 'G':
908 [ # # ]: 0 : if (fmtpos)
909 : : {
910 [ # # ]: 0 : if (argtypes[fmtpos] &&
911 [ # # ]: 0 : argtypes[fmtpos] != ATYPE_DOUBLE)
912 : 0 : return false;
913 : 0 : argtypes[fmtpos] = ATYPE_DOUBLE;
914 : 0 : last_dollar = Max(last_dollar, fmtpos);
915 : : }
916 : : else
917 : 0 : return false; /* non-dollar conversion spec */
918 : 0 : break;
919 : 0 : case 'm':
920 : : case '%':
921 : 0 : break;
1956 922 : 0 : default:
923 : 0 : return false; /* bogus format string */
924 : : }
925 : :
926 : : /*
927 : : * If we finish the spec with afterstar still set, there's a
928 : : * non-dollar star in there.
929 : : */
2020 930 [ # # ]: 0 : if (afterstar)
931 : 0 : return false; /* non-dollar conversion spec */
932 : : }
933 : :
934 : : /*
935 : : * Format appears valid so far, so collect the arguments in physical
936 : : * order. (Since we rejected any non-dollar specs that would have
937 : : * collected arguments, we know that dopr() hasn't collected any yet.)
938 : : */
939 [ # # ]: 0 : for (i = 1; i <= last_dollar; i++)
940 : : {
941 [ # # # # : 0 : switch (argtypes[i])
# # # ]
942 : : {
943 : 0 : case ATYPE_NONE:
944 : 0 : return false;
945 : 0 : case ATYPE_INT:
946 : 0 : argvalues[i].i = va_arg(args, int);
947 : 0 : break;
948 : 0 : case ATYPE_LONG:
949 : 0 : argvalues[i].l = va_arg(args, long);
950 : 0 : break;
951 : 0 : case ATYPE_LONGLONG:
952 : 0 : argvalues[i].ll = va_arg(args, long long);
953 : 0 : break;
954 : 0 : case ATYPE_DOUBLE:
955 : 0 : argvalues[i].d = va_arg(args, double);
956 : 0 : break;
957 : 0 : case ATYPE_CHARPTR:
958 : 0 : argvalues[i].cptr = va_arg(args, char *);
959 : 0 : break;
960 : : }
961 : : }
962 : :
963 : 0 : return true;
964 : : }
965 : :
966 : : static void
2020 tgl@sss.pgh.pa.us 967 :CBC 903307 : fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
968 : : int pointflag, PrintfTarget *target)
969 : : {
970 : : int padlen,
971 : : vallen; /* amount to pad */
972 : :
973 : : /*
974 : : * If a maxwidth (precision) is specified, we must not fetch more bytes
975 : : * than that.
976 : : */
6705 977 [ + + ]: 903307 : if (pointflag)
2378 andres@anarazel.de 978 : 9663 : vallen = strnlen(value, maxwidth);
979 : : else
6705 tgl@sss.pgh.pa.us 980 : 893644 : vallen = strlen(value);
981 : :
2020 982 : 903307 : padlen = compute_padlen(minlen, vallen, leftjust);
983 : :
984 [ + + ]: 903307 : if (padlen > 0)
985 : : {
986 : 271459 : dopr_outchmulti(' ', padlen, target);
987 : 271459 : padlen = 0;
988 : : }
989 : :
6705 990 : 903307 : dostr(value, vallen, target);
991 : :
2020 992 : 903307 : trailing_pad(padlen, target);
9354 scrappy@hub.org 993 : 903307 : }
994 : :
995 : : static void
995 tgl@sss.pgh.pa.us 996 : 41 : fmtptr(const void *value, PrintfTarget *target)
997 : : {
998 : : int vallen;
999 : : char convert[64];
1000 : :
1001 : : /* we rely on regular C library's snprintf to do the basic conversion */
546 1002 : 41 : vallen = snprintf(convert, sizeof(convert), "%p", value);
3254 noah@leadboat.com 1003 [ - + ]: 41 : if (vallen < 0)
3254 noah@leadboat.com 1004 :UBC 0 : target->failed = true;
1005 : : else
3254 noah@leadboat.com 1006 :CBC 41 : dostr(convert, vallen, target);
6705 tgl@sss.pgh.pa.us 1007 : 41 : }
1008 : :
1009 : : static void
2020 1010 : 42457326 : fmtint(long long value, char type, int forcesign, int leftjust,
1011 : : int minlen, int zpad, int precision, int pointflag,
1012 : : PrintfTarget *target)
1013 : : {
1014 : : unsigned long long uvalue;
1015 : : int base;
1016 : : int dosign;
6705 1017 : 42457326 : const char *cvt = "0123456789abcdef";
9321 bruce@momjian.us 1018 : 42457326 : int signvalue = 0;
1019 : : char convert[64];
6968 1020 : 42457326 : int vallen = 0;
1021 : : int padlen; /* amount to pad */
1022 : : int zeropad; /* extra leading zeroes */
1023 : :
6705 tgl@sss.pgh.pa.us 1024 [ + + + + : 42457326 : switch (type)
+ - ]
1025 : : {
1026 : 16830781 : case 'd':
1027 : : case 'i':
1028 : 16830781 : base = 10;
1029 : 16830781 : dosign = 1;
1030 : 16830781 : break;
1031 : 6029 : case 'o':
1032 : 6029 : base = 8;
1033 : 6029 : dosign = 0;
1034 : 6029 : break;
1035 : 16694732 : case 'u':
1036 : 16694732 : base = 10;
1037 : 16694732 : dosign = 0;
1038 : 16694732 : break;
1039 : 28283 : case 'x':
1040 : 28283 : base = 16;
1041 : 28283 : dosign = 0;
1042 : 28283 : break;
1043 : 8897501 : case 'X':
1044 : 8897501 : cvt = "0123456789ABCDEF";
1045 : 8897501 : base = 16;
1046 : 8897501 : dosign = 0;
1047 : 8897501 : break;
6705 tgl@sss.pgh.pa.us 1048 :UBC 0 : default:
1049 : 0 : return; /* keep compiler quiet */
1050 : : }
1051 : :
1052 : : /* disable MSVC warning about applying unary minus to an unsigned value */
1053 : : #ifdef _MSC_VER
1054 : : #pragma warning(push)
1055 : : #pragma warning(disable: 4146)
1056 : : #endif
1057 : : /* Handle +/- */
6968 bruce@momjian.us 1058 [ + + + + ]:CBC 42457326 : if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
2020 andres@anarazel.de 1059 : 1336302 : uvalue = -(unsigned long long) value;
1060 : : else
1061 : 41121024 : uvalue = (unsigned long long) value;
1062 : : #ifdef _MSC_VER
1063 : : #pragma warning(pop)
1064 : : #endif
1065 : :
1066 : : /*
1067 : : * SUS: the result of converting 0 with an explicit precision of 0 is no
1068 : : * characters
1069 : : */
6705 tgl@sss.pgh.pa.us 1070 [ + + - + : 42457326 : if (value == 0 && pointflag && precision == 0)
- - ]
6705 tgl@sss.pgh.pa.us 1071 :UBC 0 : vallen = 0;
1072 : : else
1073 : : {
1074 : : /*
1075 : : * Convert integer to string. We special-case each of the possible
1076 : : * base values so as to avoid general-purpose divisions. On most
1077 : : * machines, division by a fixed constant can be done much more
1078 : : * cheaply than a general divide.
1079 : : */
899 tgl@sss.pgh.pa.us 1080 [ + + ]:CBC 42457326 : if (base == 10)
1081 : : {
1082 : : do
1083 : : {
1084 : 93018174 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10];
1085 : 93018174 : uvalue = uvalue / 10;
1086 [ + + ]: 93018174 : } while (uvalue);
1087 : : }
1088 [ + + ]: 8931813 : else if (base == 16)
1089 : : {
1090 : : do
1091 : : {
1092 : 33707602 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16];
1093 : 33707602 : uvalue = uvalue / 16;
1094 [ + + ]: 33707602 : } while (uvalue);
1095 : : }
1096 : : else /* base == 8 */
1097 : : {
1098 : : do
1099 : : {
1100 : 18087 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8];
1101 : 18087 : uvalue = uvalue / 8;
1102 [ + + ]: 18087 : } while (uvalue);
1103 : : }
1104 : : }
1105 : :
6705 1106 : 42457326 : zeropad = Max(0, precision - vallen);
1107 : :
2020 1108 : 42457326 : padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
1109 : :
1110 : 42457326 : leading_pad(zpad, signvalue, &padlen, target);
1111 : :
1112 [ - + ]: 42457326 : if (zeropad > 0)
2020 tgl@sss.pgh.pa.us 1113 :UBC 0 : dopr_outchmulti('0', zeropad, target);
1114 : :
2020 tgl@sss.pgh.pa.us 1115 :CBC 42457326 : dostr(convert + sizeof(convert) - vallen, vallen, target);
1116 : :
1117 : 42457326 : trailing_pad(padlen, target);
1118 : : }
1119 : :
1120 : : static void
4753 bruce@momjian.us 1121 : 19342 : fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
1122 : : {
1123 : : int padlen; /* amount to pad */
1124 : :
2020 tgl@sss.pgh.pa.us 1125 : 19342 : padlen = compute_padlen(minlen, 1, leftjust);
1126 : :
1127 [ + + ]: 19342 : if (padlen > 0)
1128 : : {
1129 : 21 : dopr_outchmulti(' ', padlen, target);
1130 : 21 : padlen = 0;
1131 : : }
1132 : :
6705 1133 : 19342 : dopr_outch(value, target);
1134 : :
2020 1135 : 19342 : trailing_pad(padlen, target);
9354 scrappy@hub.org 1136 : 19342 : }
1137 : :
1138 : : static void
6968 bruce@momjian.us 1139 : 667186 : fmtfloat(double value, char type, int forcesign, int leftjust,
1140 : : int minlen, int zpad, int precision, int pointflag,
1141 : : PrintfTarget *target)
1142 : : {
6969 1143 : 667186 : int signvalue = 0;
1144 : : int prec;
1145 : : int vallen;
1146 : : char fmt[8];
1147 : : char convert[1024];
3359 1148 : 667186 : int zeropadlen = 0; /* amount to pad with zeroes */
1149 : : int padlen; /* amount to pad with spaces */
1150 : :
1151 : : /*
1152 : : * We rely on the regular C library's snprintf to do the basic conversion,
1153 : : * then handle padding considerations here.
1154 : : *
1155 : : * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
1156 : : * too wildly more than that with other hardware. In "f" format, snprintf
1157 : : * could therefore generate at most 308 characters to the left of the
1158 : : * decimal point; while we need to allow the precision to get as high as
1159 : : * 308+17 to ensure that we don't truncate significant digits from very
1160 : : * small values. To handle both these extremes, we use a buffer of 1024
1161 : : * bytes and limit requested precision to 350 digits; this should prevent
1162 : : * buffer overrun even with non-IEEE math. If the original precision
1163 : : * request was more than 350, separately pad with zeroes.
1164 : : *
1165 : : * We handle infinities and NaNs specially to ensure platform-independent
1166 : : * output.
1167 : : */
1168 [ - + ]: 667186 : if (precision < 0) /* cover possible overflow of "accum" */
3359 bruce@momjian.us 1169 :UBC 0 : precision = 0;
3359 bruce@momjian.us 1170 :CBC 667186 : prec = Min(precision, 350);
1171 : :
2015 tgl@sss.pgh.pa.us 1172 [ + + ]: 667186 : if (isnan(value))
1173 : : {
1174 : 70 : strcpy(convert, "NaN");
1175 : 70 : vallen = 3;
1176 : : /* no zero padding, regardless of precision spec */
1177 : : }
1178 : : else
1179 : : {
1180 : : /*
1181 : : * Handle sign (NaNs have no sign, so we don't do this in the case
1182 : : * above). "value < 0.0" will not be true for IEEE minus zero, so we
1183 : : * detect that by looking for the case where value equals 0.0
1184 : : * according to == but not according to memcmp.
1185 : : */
1186 : : static const double dzero = 0.0;
1187 : :
1188 [ + + + + ]: 1326458 : if (adjust_sign((value < 0.0 ||
1189 [ + + ]: 659342 : (value == 0.0 &&
1190 [ - + ]: 262197 : memcmp(&value, &dzero, sizeof(double)) != 0)),
1191 : : forcesign, &signvalue))
1192 : 7774 : value = -value;
1193 : :
1194 [ + + + + ]: 667116 : if (isinf(value))
1195 : : {
1196 : 48 : strcpy(convert, "Infinity");
1197 : 48 : vallen = 8;
1198 : : /* no zero padding, regardless of precision spec */
1199 : : }
1200 [ + + ]: 667068 : else if (pointflag)
1201 : : {
1202 : 439657 : zeropadlen = precision - prec;
1203 : 439657 : fmt[0] = '%';
1204 : 439657 : fmt[1] = '.';
1205 : 439657 : fmt[2] = '*';
1206 : 439657 : fmt[3] = type;
1207 : 439657 : fmt[4] = '\0';
546 1208 : 439657 : vallen = snprintf(convert, sizeof(convert), fmt, prec, value);
1209 : : }
1210 : : else
1211 : : {
2015 1212 : 227411 : fmt[0] = '%';
1213 : 227411 : fmt[1] = type;
1214 : 227411 : fmt[2] = '\0';
546 1215 : 227411 : vallen = snprintf(convert, sizeof(convert), fmt, value);
1216 : : }
2015 1217 [ - + ]: 667116 : if (vallen < 0)
2015 tgl@sss.pgh.pa.us 1218 :UBC 0 : goto fail;
1219 : :
1220 : : /*
1221 : : * Windows, alone among our supported platforms, likes to emit
1222 : : * three-digit exponent fields even when two digits would do. Hack
1223 : : * such results to look like the way everyone else does it.
1224 : : */
1225 : : #ifdef WIN32
1226 : : if (vallen >= 6 &&
1227 : : convert[vallen - 5] == 'e' &&
1228 : : convert[vallen - 3] == '0')
1229 : : {
1230 : : convert[vallen - 3] = convert[vallen - 2];
1231 : : convert[vallen - 2] = convert[vallen - 1];
1232 : : vallen--;
1233 : : }
1234 : : #endif
1235 : : }
1236 : :
2020 tgl@sss.pgh.pa.us 1237 :CBC 667186 : padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
1238 : :
1239 : 667186 : leading_pad(zpad, signvalue, &padlen, target);
1240 : :
3359 bruce@momjian.us 1241 [ - + ]: 667186 : if (zeropadlen > 0)
1242 : : {
1243 : : /* If 'e' or 'E' format, inject zeroes before the exponent */
3359 bruce@momjian.us 1244 :UBC 0 : char *epos = strrchr(convert, 'e');
1245 : :
1246 [ # # ]: 0 : if (!epos)
1247 : 0 : epos = strrchr(convert, 'E');
1248 [ # # ]: 0 : if (epos)
1249 : : {
1250 : : /* pad before exponent */
1251 : 0 : dostr(convert, epos - convert, target);
1603 tgl@sss.pgh.pa.us 1252 : 0 : dopr_outchmulti('0', zeropadlen, target);
3359 bruce@momjian.us 1253 : 0 : dostr(epos, vallen - (epos - convert), target);
1254 : : }
1255 : : else
1256 : : {
1257 : : /* no exponent, pad after the digits */
1258 : 0 : dostr(convert, vallen, target);
1603 tgl@sss.pgh.pa.us 1259 : 0 : dopr_outchmulti('0', zeropadlen, target);
1260 : : }
1261 : : }
1262 : : else
1263 : : {
1264 : : /* no zero padding, just emit the number as-is */
3359 bruce@momjian.us 1265 :CBC 667186 : dostr(convert, vallen, target);
1266 : : }
1267 : :
2020 tgl@sss.pgh.pa.us 1268 : 667186 : trailing_pad(padlen, target);
3254 noah@leadboat.com 1269 : 667186 : return;
1270 : :
3254 noah@leadboat.com 1271 :UBC 0 : fail:
1272 : 0 : target->failed = true;
1273 : : }
1274 : :
1275 : : /*
1276 : : * Nonstandard entry point to print a double value efficiently.
1277 : : *
1278 : : * This is approximately equivalent to strfromd(), but has an API more
1279 : : * adapted to what float8out() wants. The behavior is like snprintf()
1280 : : * with a format of "%.ng", where n is the specified precision.
1281 : : * However, the target buffer must be nonempty (i.e. count > 0), and
1282 : : * the precision is silently bounded to a sane range.
1283 : : */
1284 : : int
2015 tgl@sss.pgh.pa.us 1285 :CBC 115015 : pg_strfromd(char *str, size_t count, int precision, double value)
1286 : : {
1287 : : PrintfTarget target;
1288 : 115015 : int signvalue = 0;
1289 : : int vallen;
1290 : : char fmt[8];
1291 : : char convert[64];
1292 : :
1293 : : /* Set up the target like pg_snprintf, but require nonempty buffer */
1294 [ - + ]: 115015 : Assert(count > 0);
1295 : 115015 : target.bufstart = target.bufptr = str;
1296 : 115015 : target.bufend = str + count - 1;
1297 : 115015 : target.stream = NULL;
1298 : 115015 : target.nchars = 0;
1299 : 115015 : target.failed = false;
1300 : :
1301 : : /*
1302 : : * We bound precision to a reasonable range; the combination of this and
1303 : : * the knowledge that we're using "g" format without padding allows the
1304 : : * convert[] buffer to be reasonably small.
1305 : : */
1306 [ - + ]: 115015 : if (precision < 1)
2015 tgl@sss.pgh.pa.us 1307 :UBC 0 : precision = 1;
2015 tgl@sss.pgh.pa.us 1308 [ - + ]:CBC 115015 : else if (precision > 32)
2015 tgl@sss.pgh.pa.us 1309 :UBC 0 : precision = 32;
1310 : :
1311 : : /*
1312 : : * The rest is just an inlined version of the fmtfloat() logic above,
1313 : : * simplified using the knowledge that no padding is wanted.
1314 : : */
2015 tgl@sss.pgh.pa.us 1315 [ + + ]:CBC 115015 : if (isnan(value))
1316 : : {
1317 : 6042 : strcpy(convert, "NaN");
1318 : 6042 : vallen = 3;
1319 : : }
1320 : : else
1321 : : {
1322 : : static const double dzero = 0.0;
1323 : :
1324 [ + + ]: 108973 : if (value < 0.0 ||
1325 [ + + ]: 92776 : (value == 0.0 &&
1326 [ + + ]: 13433 : memcmp(&value, &dzero, sizeof(double)) != 0))
1327 : : {
1328 : 16230 : signvalue = '-';
1329 : 16230 : value = -value;
1330 : : }
1331 : :
1332 [ + + + + ]: 108973 : if (isinf(value))
1333 : : {
1334 : 3546 : strcpy(convert, "Infinity");
1335 : 3546 : vallen = 8;
1336 : : }
1337 : : else
1338 : : {
1339 : 105427 : fmt[0] = '%';
1340 : 105427 : fmt[1] = '.';
1341 : 105427 : fmt[2] = '*';
1342 : 105427 : fmt[3] = 'g';
1343 : 105427 : fmt[4] = '\0';
546 1344 : 105427 : vallen = snprintf(convert, sizeof(convert), fmt, precision, value);
2015 1345 [ - + ]: 105427 : if (vallen < 0)
1346 : : {
2015 tgl@sss.pgh.pa.us 1347 :UBC 0 : target.failed = true;
1348 : 0 : goto fail;
1349 : : }
1350 : :
1351 : : #ifdef WIN32
1352 : : if (vallen >= 6 &&
1353 : : convert[vallen - 5] == 'e' &&
1354 : : convert[vallen - 3] == '0')
1355 : : {
1356 : : convert[vallen - 3] = convert[vallen - 2];
1357 : : convert[vallen - 2] = convert[vallen - 1];
1358 : : vallen--;
1359 : : }
1360 : : #endif
1361 : : }
1362 : : }
1363 : :
2015 tgl@sss.pgh.pa.us 1364 [ + + ]:CBC 115015 : if (signvalue)
1365 : 16230 : dopr_outch(signvalue, &target);
1366 : :
1367 : 115015 : dostr(convert, vallen, &target);
1368 : :
1369 : 115015 : fail:
1370 : 115015 : *(target.bufptr) = '\0';
1371 [ - + ]: 230030 : return target.failed ? -1 : (target.bufptr - target.bufstart
1372 : 115015 : + target.nchars);
1373 : : }
1374 : :
1375 : :
1376 : : static void
4753 bruce@momjian.us 1377 : 103465490 : dostr(const char *str, int slen, PrintfTarget *target)
1378 : : {
1379 : : /* fast path for common case of slen == 1 */
2020 tgl@sss.pgh.pa.us 1380 [ + + ]: 103465490 : if (slen == 1)
1381 : : {
1382 : 33675488 : dopr_outch(*str, target);
1383 : 33675488 : return;
1384 : : }
1385 : :
6705 1386 [ + + ]: 138079928 : while (slen > 0)
1387 : : {
1388 : : int avail;
1389 : :
1390 [ + + ]: 68833764 : if (target->bufend != NULL)
1391 : 64037612 : avail = target->bufend - target->bufptr;
1392 : : else
1393 : 4796152 : avail = slen;
1394 [ + + ]: 68833764 : if (avail <= 0)
1395 : : {
1396 : : /* buffer full, can we dump to stream? */
1397 [ + + ]: 544105 : if (target->stream == NULL)
1398 : : {
2069 1399 : 543838 : target->nchars += slen; /* no, lose the data */
1400 : 543838 : return;
1401 : : }
6705 1402 : 267 : flushbuffer(target);
1403 : 267 : continue;
1404 : : }
1405 : 68289659 : avail = Min(avail, slen);
1406 : 68289659 : memmove(target->bufptr, str, avail);
1407 : 68289659 : target->bufptr += avail;
1408 : 68289659 : str += avail;
1409 : 68289659 : slen -= avail;
1410 : : }
1411 : : }
1412 : :
1413 : : static void
4753 bruce@momjian.us 1414 : 39077261 : dopr_outch(int c, PrintfTarget *target)
1415 : : {
6705 tgl@sss.pgh.pa.us 1416 [ + + + + ]: 39077261 : if (target->bufend != NULL && target->bufptr >= target->bufend)
1417 : : {
1418 : : /* buffer full, can we dump to stream? */
1419 [ + - ]: 212168 : if (target->stream == NULL)
1420 : : {
2069 1421 : 212168 : target->nchars++; /* no, lose the data */
1422 : 212168 : return;
1423 : : }
6705 tgl@sss.pgh.pa.us 1424 :UBC 0 : flushbuffer(target);
1425 : : }
6705 tgl@sss.pgh.pa.us 1426 :CBC 38865093 : *(target->bufptr++) = c;
1427 : : }
1428 : :
1429 : : static void
2020 1430 : 5321672 : dopr_outchmulti(int c, int slen, PrintfTarget *target)
1431 : : {
1432 : : /* fast path for common case of slen == 1 */
1433 [ + + ]: 5321672 : if (slen == 1)
1434 : : {
1435 : 3797857 : dopr_outch(c, target);
1436 : 3797857 : return;
1437 : : }
1438 : :
1439 [ + + ]: 3047741 : while (slen > 0)
1440 : : {
1441 : : int avail;
1442 : :
1443 [ + + ]: 1523926 : if (target->bufend != NULL)
1444 : 1515458 : avail = target->bufend - target->bufptr;
1445 : : else
1446 : 8468 : avail = slen;
1447 [ + + ]: 1523926 : if (avail <= 0)
1448 : : {
1449 : : /* buffer full, can we dump to stream? */
1450 [ - + ]: 57 : if (target->stream == NULL)
1451 : : {
2020 tgl@sss.pgh.pa.us 1452 :UBC 0 : target->nchars += slen; /* no, lose the data */
1453 : 0 : return;
1454 : : }
2020 tgl@sss.pgh.pa.us 1455 :CBC 57 : flushbuffer(target);
1456 : 57 : continue;
1457 : : }
1458 : 1523869 : avail = Min(avail, slen);
1459 : 1523869 : memset(target->bufptr, c, avail);
1460 : 1523869 : target->bufptr += avail;
1461 : 1523869 : slen -= avail;
1462 : : }
1463 : : }
1464 : :
1465 : :
1466 : : static int
6968 bruce@momjian.us 1467 : 17497897 : adjust_sign(int is_negative, int forcesign, int *signvalue)
1468 : : {
1469 [ + + ]: 17497897 : if (is_negative)
1470 : : {
1471 : 1344076 : *signvalue = '-';
1472 : 1344076 : return true;
1473 : : }
1474 [ + + ]: 16153821 : else if (forcesign)
1475 : 93 : *signvalue = '+';
1476 : 16153821 : return false;
1477 : : }
1478 : :
1479 : :
1480 : : static int
2020 tgl@sss.pgh.pa.us 1481 : 44047161 : compute_padlen(int minlen, int vallen, int leftjust)
1482 : : {
1483 : : int padlen;
1484 : :
1485 : 44047161 : padlen = minlen - vallen;
1486 [ + + ]: 44047161 : if (padlen < 0)
1487 : 27647556 : padlen = 0;
6968 bruce@momjian.us 1488 [ + + ]: 44047161 : if (leftjust)
2020 tgl@sss.pgh.pa.us 1489 : 499964 : padlen = -padlen;
1490 : 44047161 : return padlen;
1491 : : }
1492 : :
1493 : :
1494 : : static void
1495 : 43124512 : leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
1496 : : {
1497 : : int maxpad;
1498 : :
6968 bruce@momjian.us 1499 [ + + + + ]: 43124512 : if (*padlen > 0 && zpad)
1500 : : {
2020 tgl@sss.pgh.pa.us 1501 [ + + ]: 3920431 : if (signvalue)
1502 : : {
1503 : 90 : dopr_outch(signvalue, target);
6705 1504 : 90 : --(*padlen);
2020 1505 : 90 : signvalue = 0;
1506 : : }
1507 [ + + ]: 3920431 : if (*padlen > 0)
1508 : : {
1509 : 3920422 : dopr_outchmulti(zpad, *padlen, target);
1510 : 3920422 : *padlen = 0;
1511 : : }
1512 : : }
1513 : 43124512 : maxpad = (signvalue != 0);
1514 [ + + ]: 43124512 : if (*padlen > maxpad)
1515 : : {
1516 : 784434 : dopr_outchmulti(' ', *padlen - maxpad, target);
1517 : 784434 : *padlen = maxpad;
1518 : : }
1519 [ + + ]: 43124512 : if (signvalue)
1520 : : {
1521 : 1344079 : dopr_outch(signvalue, target);
6968 bruce@momjian.us 1522 [ - + ]: 1344079 : if (*padlen > 0)
6705 tgl@sss.pgh.pa.us 1523 :UBC 0 : --(*padlen);
6705 tgl@sss.pgh.pa.us 1524 [ - + ]:CBC 1344079 : else if (*padlen < 0)
6705 tgl@sss.pgh.pa.us 1525 :UBC 0 : ++(*padlen);
1526 : : }
6968 bruce@momjian.us 1527 :CBC 43124512 : }
1528 : :
1529 : :
1530 : : static void
2020 tgl@sss.pgh.pa.us 1531 : 44047161 : trailing_pad(int padlen, PrintfTarget *target)
1532 : : {
1533 [ + + ]: 44047161 : if (padlen < 0)
1534 : 345336 : dopr_outchmulti(' ', -padlen, target);
6968 bruce@momjian.us 1535 : 44047161 : }
|