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
174 506152540 : 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 : */
185 506152540 : if (count == 0)
186 : {
187 250980 : str = onebyte;
188 250980 : count = 1;
189 : }
190 506152540 : target.bufstart = target.bufptr = str;
191 506152540 : target.bufend = str + count - 1;
192 506152540 : target.stream = NULL;
193 506152540 : target.nchars = 0;
194 506152540 : target.failed = false;
195 506152540 : dopr(&target, fmt, args);
196 506152540 : *(target.bufptr) = '\0';
197 1012305080 : return target.failed ? -1 : (target.bufptr - target.bufstart
198 506152540 : + target.nchars);
199 : }
200 :
201 : int
202 47373348 : pg_snprintf(char *str, size_t count, const char *fmt,...)
203 : {
204 : int len;
205 : va_list args;
206 :
207 47373348 : va_start(args, fmt);
208 47373348 : len = pg_vsnprintf(str, count, fmt, args);
209 47373350 : va_end(args);
210 47373350 : return len;
211 : }
212 :
213 : int
214 15521176 : pg_vsprintf(char *str, const char *fmt, va_list args)
215 : {
216 : PrintfTarget target;
217 :
218 15521176 : target.bufstart = target.bufptr = str;
219 15521176 : target.bufend = NULL;
220 15521176 : target.stream = NULL;
221 15521176 : target.nchars = 0; /* not really used in this case */
222 15521176 : target.failed = false;
223 15521176 : dopr(&target, fmt, args);
224 15521172 : *(target.bufptr) = '\0';
225 31042344 : return target.failed ? -1 : (target.bufptr - target.bufstart
226 15521172 : + target.nchars);
227 : }
228 :
229 : int
230 15521176 : pg_sprintf(char *str, const char *fmt,...)
231 : {
232 : int len;
233 : va_list args;
234 :
235 15521176 : va_start(args, fmt);
236 15521176 : len = pg_vsprintf(str, fmt, args);
237 15521172 : va_end(args);
238 15521172 : return len;
239 : }
240 :
241 : int
242 4586808 : pg_vfprintf(FILE *stream, const char *fmt, va_list args)
243 : {
244 : PrintfTarget target;
245 : char buffer[1024]; /* size is arbitrary */
246 :
247 4586808 : if (stream == NULL)
248 : {
249 0 : errno = EINVAL;
250 0 : return -1;
251 : }
252 4586808 : target.bufstart = target.bufptr = buffer;
253 4586808 : target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
254 4586808 : target.stream = stream;
255 4586808 : target.nchars = 0;
256 4586808 : target.failed = false;
257 4586808 : dopr(&target, fmt, args);
258 : /* dump any remaining buffer contents */
259 4586808 : flushbuffer(&target);
260 4586808 : return target.failed ? -1 : target.nchars;
261 : }
262 :
263 : int
264 2347368 : pg_fprintf(FILE *stream, const char *fmt,...)
265 : {
266 : int len;
267 : va_list args;
268 :
269 2347368 : va_start(args, fmt);
270 2347368 : len = pg_vfprintf(stream, fmt, args);
271 2347368 : va_end(args);
272 2347368 : return len;
273 : }
274 :
275 : int
276 0 : pg_vprintf(const char *fmt, va_list args)
277 : {
278 0 : return pg_vfprintf(stdout, fmt, args);
279 : }
280 :
281 : int
282 2216484 : pg_printf(const char *fmt,...)
283 : {
284 : int len;
285 : va_list args;
286 :
287 2216484 : va_start(args, fmt);
288 2216484 : len = pg_vfprintf(stdout, fmt, args);
289 2216484 : va_end(args);
290 2216484 : 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
298 4587410 : flushbuffer(PrintfTarget *target)
299 : {
300 4587410 : size_t nc = target->bufptr - target->bufstart;
301 :
302 : /*
303 : * Don't write anything if we already failed; this is to ensure we
304 : * preserve the original failure's errno.
305 : */
306 4587410 : if (!target->failed && nc > 0)
307 : {
308 : size_t written;
309 :
310 4320156 : written = fwrite(target->bufstart, 1, nc, target->stream);
311 4320156 : target->nchars += written;
312 4320156 : if (written != nc)
313 0 : target->failed = true;
314 : }
315 4587410 : target->bufptr = target->bufstart;
316 4587410 : }
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
373 526260524 : dopr(PrintfTarget *target, const char *format, va_list args)
374 : {
375 526260524 : int save_errno = errno;
376 526260524 : 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 526260524 : have_dollar = false;
404 :
405 1086621044 : while (*format != '\0')
406 : {
407 : /* Locate next conversion specifier */
408 714304328 : if (*format != '%')
409 : {
410 : /* Scan to next '%' or end of string */
411 509673958 : const char *next_pct = strchrnul(format + 1, '%');
412 :
413 : /* Dump literal data we just scanned over */
414 509673958 : dostr(format, next_pct - format, target);
415 509673956 : if (target->failed)
416 0 : break;
417 :
418 509673956 : if (*next_pct == '\0')
419 153943804 : break;
420 355730152 : 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 560360522 : if (first_pct == NULL)
429 518602624 : first_pct = format;
430 :
431 : /* Process conversion spec starting at *format */
432 560360522 : format++;
433 :
434 : /* Fast path for conversion spec that is exactly %s */
435 560360522 : if (*format == 's')
436 : {
437 93608296 : format++;
438 93608296 : strvalue = va_arg(args, char *);
439 93608294 : if (strvalue == NULL)
440 0 : strvalue = "(null)";
441 93608294 : dostr(strvalue, strlen(strvalue), target);
442 93608296 : if (target->failed)
443 0 : break;
444 93608296 : continue;
445 : }
446 :
447 466752226 : fieldwidth = precision = zpad = leftjust = forcesign = 0;
448 466752226 : longflag = longlongflag = pointflag = 0;
449 466752226 : fmtpos = accum = 0;
450 466752226 : have_star = afterstar = false;
451 538352942 : nextch2:
452 538352942 : ch = *format++;
453 538352942 : switch (ch)
454 : {
455 1083710 : case '-':
456 1083710 : leftjust = 1;
457 1083710 : goto nextch2;
458 282 : case '+':
459 282 : forcesign = 1;
460 282 : goto nextch2;
461 28269784 : case '0':
462 : /* set zero padding if no nonzero digits yet */
463 28269784 : if (accum == 0 && !pointflag)
464 27703248 : 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 59235032 : accum = accum * 10 + (ch - '0');
476 59235032 : goto nextch2;
477 681018 : case '.':
478 681018 : if (have_star)
479 0 : have_star = false;
480 : else
481 681018 : fieldwidth = accum;
482 681018 : pointflag = 1;
483 681018 : accum = 0;
484 681018 : goto nextch2;
485 1425120 : case '*':
486 1425120 : if (have_dollar)
487 : {
488 : /*
489 : * We'll process value after reading n$. Note it's OK to
490 : * assume have_dollar is set correctly, because in a valid
491 : * format string the initial % must have had n$ if * does.
492 : */
493 0 : afterstar = true;
494 : }
495 : else
496 : {
497 : /* fetch and process value now */
498 1425120 : int starval = va_arg(args, int);
499 :
500 1425120 : if (pointflag)
501 : {
502 60766 : precision = starval;
503 60766 : if (precision < 0)
504 : {
505 0 : precision = 0;
506 0 : pointflag = 0;
507 : }
508 : }
509 : else
510 : {
511 1364354 : fieldwidth = starval;
512 1364354 : if (fieldwidth < 0)
513 : {
514 5646 : leftjust = 1;
515 5646 : fieldwidth = -fieldwidth;
516 : }
517 : }
518 : }
519 1425120 : have_star = true;
520 1425120 : accum = 0;
521 1425120 : goto nextch2;
522 0 : case '$':
523 : /* First dollar sign? */
524 0 : if (!have_dollar)
525 : {
526 : /* Yup, so examine all conversion specs in format */
527 0 : if (!find_arguments(first_pct, args, argvalues))
528 2 : goto bad_format;
529 0 : have_dollar = true;
530 : }
531 0 : if (afterstar)
532 : {
533 : /* fetch and process star value */
534 0 : int starval = argvalues[accum].i;
535 :
536 0 : if (pointflag)
537 : {
538 0 : precision = starval;
539 0 : if (precision < 0)
540 : {
541 0 : precision = 0;
542 0 : pointflag = 0;
543 : }
544 : }
545 : else
546 : {
547 0 : fieldwidth = starval;
548 0 : if (fieldwidth < 0)
549 : {
550 0 : leftjust = 1;
551 0 : fieldwidth = -fieldwidth;
552 : }
553 : }
554 0 : afterstar = false;
555 : }
556 : else
557 0 : fmtpos = accum;
558 0 : accum = 0;
559 0 : goto nextch2;
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
582 8837320 : case 'l':
583 8837320 : if (longflag)
584 8336 : longlongflag = 1;
585 : else
586 8828984 : longflag = 1;
587 8837320 : goto nextch2;
588 338148 : case 'z':
589 : #if SIZEOF_SIZE_T == SIZEOF_LONG
590 338148 : 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 338148 : goto nextch2;
597 88 : case 'h':
598 : case '\'':
599 : /* ignore these */
600 88 : goto nextch2;
601 310843568 : case 'd':
602 : case 'i':
603 310843568 : if (!have_star)
604 : {
605 310804400 : if (pointflag)
606 0 : precision = accum;
607 : else
608 310804400 : fieldwidth = accum;
609 : }
610 310843568 : if (have_dollar)
611 : {
612 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 : {
621 310843568 : if (longlongflag)
622 8312 : numvalue = va_arg(args, long long);
623 310835256 : else if (longflag)
624 6007426 : numvalue = va_arg(args, long);
625 : else
626 304827830 : numvalue = va_arg(args, int);
627 : }
628 310843568 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
629 : precision, pointflag, target);
630 310843566 : break;
631 152513656 : case 'o':
632 : case 'u':
633 : case 'x':
634 : case 'X':
635 152513656 : if (!have_star)
636 : {
637 152513656 : if (pointflag)
638 0 : precision = accum;
639 : else
640 152513656 : fieldwidth = accum;
641 : }
642 152513656 : if (have_dollar)
643 : {
644 0 : if (longlongflag)
645 0 : numvalue = (unsigned long long) argvalues[fmtpos].ll;
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 : {
653 152513656 : if (longlongflag)
654 24 : numvalue = (unsigned long long) va_arg(args, long long);
655 152513632 : else if (longflag)
656 3151370 : numvalue = (unsigned long) va_arg(args, long);
657 : else
658 149362262 : numvalue = (unsigned int) va_arg(args, int);
659 : }
660 152513656 : fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
661 : precision, pointflag, target);
662 152513656 : break;
663 40590 : case 'c':
664 40590 : if (!have_star)
665 : {
666 40548 : if (pointflag)
667 0 : precision = accum;
668 : else
669 40548 : fieldwidth = accum;
670 : }
671 40590 : if (have_dollar)
672 0 : cvalue = (unsigned char) argvalues[fmtpos].i;
673 : else
674 40590 : cvalue = (unsigned char) va_arg(args, int);
675 40590 : fmtchar(cvalue, leftjust, fieldwidth, target);
676 40590 : break;
677 1933472 : case 's':
678 1933472 : if (!have_star)
679 : {
680 587738 : if (pointflag)
681 0 : precision = accum;
682 : else
683 587738 : fieldwidth = accum;
684 : }
685 1933472 : if (have_dollar)
686 0 : strvalue = argvalues[fmtpos].cptr;
687 : else
688 1933472 : strvalue = va_arg(args, char *);
689 : /* If string is NULL, silently substitute "(null)" */
690 1933472 : if (strvalue == NULL)
691 0 : strvalue = "(null)";
692 1933472 : fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
693 : target);
694 1933472 : break;
695 82 : case 'p':
696 : /* fieldwidth/leftjust are ignored ... */
697 82 : if (have_dollar)
698 0 : strvalue = argvalues[fmtpos].cptr;
699 : else
700 82 : strvalue = va_arg(args, char *);
701 82 : fmtptr((const void *) strvalue, target);
702 82 : break;
703 1105244 : case 'e':
704 : case 'E':
705 : case 'f':
706 : case 'g':
707 : case 'G':
708 1105244 : if (!have_star)
709 : {
710 1065068 : if (pointflag)
711 620252 : precision = accum;
712 : else
713 444816 : fieldwidth = accum;
714 : }
715 1105244 : if (have_dollar)
716 0 : fvalue = argvalues[fmtpos].d;
717 : else
718 1105244 : fvalue = va_arg(args, double);
719 1105244 : fmtfloat(fvalue, ch, forcesign, leftjust,
720 : fieldwidth, zpad,
721 : precision, pointflag,
722 : target);
723 1105244 : break;
724 416 : case 'm':
725 : {
726 : char errbuf[PG_STRERROR_R_BUFLEN];
727 416 : const char *errm = strerror_r(save_errno,
728 : errbuf, sizeof(errbuf));
729 :
730 416 : dostr(errm, strlen(errm), target);
731 : }
732 418 : break;
733 315196 : case '%':
734 315196 : dopr_outch('%', target);
735 315196 : break;
736 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 */
746 466752224 : if (target->failed)
747 0 : break;
748 : }
749 :
750 526260520 : return;
751 :
752 2 : bad_format:
753 2 : errno = EINVAL;
754 2 : 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
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;
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 : */
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;
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 : */
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 0 : }
995 :
996 0 : return true;
997 : }
998 :
999 : static void
1000 1933472 : 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 : */
1010 1933472 : if (pointflag)
1011 20590 : vallen = strnlen(value, maxwidth);
1012 : else
1013 1912882 : vallen = strlen(value);
1014 :
1015 1933472 : padlen = compute_padlen(minlen, vallen, leftjust);
1016 :
1017 1933472 : if (padlen > 0)
1018 : {
1019 581246 : dopr_outchmulti(' ', padlen, target);
1020 581246 : padlen = 0;
1021 : }
1022 :
1023 1933472 : dostr(value, vallen, target);
1024 :
1025 1933472 : trailing_pad(padlen, target);
1026 1933472 : }
1027 :
1028 : static void
1029 82 : 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 */
1035 82 : vallen = snprintf(convert, sizeof(convert), "%p", value);
1036 82 : if (vallen < 0)
1037 0 : target->failed = true;
1038 : else
1039 82 : dostr(convert, vallen, target);
1040 82 : }
1041 :
1042 : static void
1043 463357222 : 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;
1050 463357222 : const char *cvt = "0123456789abcdef";
1051 463357222 : int signvalue = 0;
1052 : char convert[64];
1053 463357222 : int vallen = 0;
1054 : int padlen; /* amount to pad */
1055 : int zeropad; /* extra leading zeroes */
1056 :
1057 463357222 : switch (type)
1058 : {
1059 310843566 : case 'd':
1060 : case 'i':
1061 310843566 : base = 10;
1062 310843566 : dosign = 1;
1063 310843566 : break;
1064 11414 : case 'o':
1065 11414 : base = 8;
1066 11414 : dosign = 0;
1067 11414 : break;
1068 134322068 : case 'u':
1069 134322068 : base = 10;
1070 134322068 : dosign = 0;
1071 134322068 : break;
1072 82194 : case 'x':
1073 82194 : base = 16;
1074 82194 : dosign = 0;
1075 82194 : break;
1076 18097980 : case 'X':
1077 18097980 : cvt = "0123456789ABCDEF";
1078 18097980 : base = 16;
1079 18097980 : dosign = 0;
1080 18097980 : break;
1081 0 : default:
1082 0 : 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 +/- */
1091 463357222 : if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
1092 33089910 : uvalue = -(unsigned long long) value;
1093 : else
1094 430267310 : 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 : */
1103 463357220 : if (value == 0 && pointflag && precision == 0)
1104 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 : */
1113 463357220 : if (base == 10)
1114 : {
1115 : do
1116 : {
1117 775648900 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10];
1118 775648900 : uvalue = uvalue / 10;
1119 775648900 : } while (uvalue);
1120 : }
1121 18191588 : else if (base == 16)
1122 : {
1123 : do
1124 : {
1125 68613048 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16];
1126 68613048 : uvalue = uvalue / 16;
1127 68613048 : } while (uvalue);
1128 : }
1129 : else /* base == 8 */
1130 : {
1131 : do
1132 : {
1133 34242 : convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8];
1134 34242 : uvalue = uvalue / 8;
1135 34242 : } while (uvalue);
1136 : }
1137 : }
1138 :
1139 463357220 : zeropad = Max(0, precision - vallen);
1140 :
1141 463357220 : padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
1142 :
1143 463357226 : leading_pad(zpad, signvalue, &padlen, target);
1144 :
1145 463357220 : if (zeropad > 0)
1146 0 : dopr_outchmulti('0', zeropad, target);
1147 :
1148 463357220 : dostr(convert + sizeof(convert) - vallen, vallen, target);
1149 :
1150 463357224 : trailing_pad(padlen, target);
1151 : }
1152 :
1153 : static void
1154 40590 : fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
1155 : {
1156 : int padlen; /* amount to pad */
1157 :
1158 40590 : padlen = compute_padlen(minlen, 1, leftjust);
1159 :
1160 40590 : if (padlen > 0)
1161 : {
1162 42 : dopr_outchmulti(' ', padlen, target);
1163 42 : padlen = 0;
1164 : }
1165 :
1166 40590 : dopr_outch(value, target);
1167 :
1168 40590 : trailing_pad(padlen, target);
1169 40590 : }
1170 :
1171 : static void
1172 1105244 : fmtfloat(double value, char type, int forcesign, int leftjust,
1173 : int minlen, int zpad, int precision, int pointflag,
1174 : PrintfTarget *target)
1175 : {
1176 1105244 : int signvalue = 0;
1177 : int prec;
1178 : int vallen;
1179 : char fmt[8];
1180 : char convert[1024];
1181 1105244 : 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 1105244 : if (precision < 0) /* cover possible overflow of "accum" */
1202 0 : precision = 0;
1203 1105244 : prec = Min(precision, 350);
1204 :
1205 1105244 : if (isnan(value))
1206 : {
1207 48 : strcpy(convert, "NaN");
1208 48 : 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 2195772 : if (adjust_sign((value < 0.0 ||
1222 1090576 : (value == 0.0 &&
1223 398576 : memcmp(&value, &dzero, sizeof(double)) != 0)),
1224 : forcesign, &signvalue))
1225 14620 : value = -value;
1226 :
1227 1105196 : if (isinf(value))
1228 : {
1229 96 : strcpy(convert, "Infinity");
1230 96 : vallen = 8;
1231 : /* no zero padding, regardless of precision spec */
1232 : }
1233 1105100 : else if (pointflag)
1234 : {
1235 660284 : zeropadlen = precision - prec;
1236 660284 : fmt[0] = '%';
1237 660284 : fmt[1] = '.';
1238 660284 : fmt[2] = '*';
1239 660284 : fmt[3] = type;
1240 660284 : fmt[4] = '\0';
1241 660284 : vallen = snprintf(convert, sizeof(convert), fmt, prec, value);
1242 : }
1243 : else
1244 : {
1245 444816 : fmt[0] = '%';
1246 444816 : fmt[1] = type;
1247 444816 : fmt[2] = '\0';
1248 444816 : vallen = snprintf(convert, sizeof(convert), fmt, value);
1249 : }
1250 1105196 : if (vallen < 0)
1251 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 :
1270 1105244 : padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
1271 :
1272 1105244 : leading_pad(zpad, signvalue, &padlen, target);
1273 :
1274 1105244 : if (zeropadlen > 0)
1275 : {
1276 : /* If 'e' or 'E' format, inject zeroes before the exponent */
1277 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);
1285 0 : dopr_outchmulti('0', zeropadlen, target);
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);
1292 0 : dopr_outchmulti('0', zeropadlen, target);
1293 : }
1294 : }
1295 : else
1296 : {
1297 : /* no zero padding, just emit the number as-is */
1298 1105244 : dostr(convert, vallen, target);
1299 : }
1300 :
1301 1105244 : trailing_pad(padlen, target);
1302 1105244 : return;
1303 :
1304 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
1318 230216 : pg_strfromd(char *str, size_t count, int precision, double value)
1319 : {
1320 : PrintfTarget target;
1321 230216 : 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 : Assert(count > 0);
1328 230216 : target.bufstart = target.bufptr = str;
1329 230216 : target.bufend = str + count - 1;
1330 230216 : target.stream = NULL;
1331 230216 : target.nchars = 0;
1332 230216 : 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 230216 : if (precision < 1)
1340 0 : precision = 1;
1341 230216 : else if (precision > 32)
1342 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 : */
1348 230216 : if (isnan(value))
1349 : {
1350 12102 : strcpy(convert, "NaN");
1351 12102 : vallen = 3;
1352 : }
1353 : else
1354 : {
1355 : static const double dzero = 0.0;
1356 :
1357 218114 : if (value < 0.0 ||
1358 185714 : (value == 0.0 &&
1359 26878 : memcmp(&value, &dzero, sizeof(double)) != 0))
1360 : {
1361 32466 : signvalue = '-';
1362 32466 : value = -value;
1363 : }
1364 :
1365 218114 : if (isinf(value))
1366 : {
1367 7116 : strcpy(convert, "Infinity");
1368 7116 : vallen = 8;
1369 : }
1370 : else
1371 : {
1372 210998 : fmt[0] = '%';
1373 210998 : fmt[1] = '.';
1374 210998 : fmt[2] = '*';
1375 210998 : fmt[3] = 'g';
1376 210998 : fmt[4] = '\0';
1377 210998 : vallen = snprintf(convert, sizeof(convert), fmt, precision, value);
1378 210998 : if (vallen < 0)
1379 : {
1380 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 :
1397 230216 : if (signvalue)
1398 32466 : dopr_outch(signvalue, &target);
1399 :
1400 230216 : dostr(convert, vallen, &target);
1401 :
1402 230216 : fail:
1403 230216 : *(target.bufptr) = '\0';
1404 460432 : return target.failed ? -1 : (target.bufptr - target.bufstart
1405 230216 : + target.nchars);
1406 : }
1407 :
1408 :
1409 : static void
1410 1069908898 : dostr(const char *str, int slen, PrintfTarget *target)
1411 : {
1412 : /* fast path for common case of slen == 1 */
1413 1069908898 : if (slen == 1)
1414 : {
1415 452474924 : dopr_outch(*str, target);
1416 452474926 : return;
1417 : }
1418 :
1419 1231807086 : while (slen > 0)
1420 : {
1421 : int avail;
1422 :
1423 615206686 : if (target->bufend != NULL)
1424 595841250 : avail = target->bufend - target->bufptr;
1425 : else
1426 19365436 : avail = slen;
1427 615206686 : if (avail <= 0)
1428 : {
1429 : /* buffer full, can we dump to stream? */
1430 834062 : if (target->stream == NULL)
1431 : {
1432 833574 : target->nchars += slen; /* no, lose the data */
1433 833574 : return;
1434 : }
1435 488 : flushbuffer(target);
1436 488 : continue;
1437 : }
1438 614372624 : avail = Min(avail, slen);
1439 614372624 : memmove(target->bufptr, str, avail);
1440 614372624 : target->bufptr += avail;
1441 614372624 : str += avail;
1442 614372624 : slen -= avail;
1443 : }
1444 : }
1445 :
1446 : static void
1447 492841388 : dopr_outch(int c, PrintfTarget *target)
1448 : {
1449 492841388 : if (target->bufend != NULL && target->bufptr >= target->bufend)
1450 : {
1451 : /* buffer full, can we dump to stream? */
1452 256070 : if (target->stream == NULL)
1453 : {
1454 256070 : target->nchars++; /* no, lose the data */
1455 256070 : return;
1456 : }
1457 0 : flushbuffer(target);
1458 : }
1459 492585322 : *(target->bufptr++) = c;
1460 : }
1461 :
1462 : static void
1463 10058202 : dopr_outchmulti(int c, int slen, PrintfTarget *target)
1464 : {
1465 : /* fast path for common case of slen == 1 */
1466 10058202 : if (slen == 1)
1467 : {
1468 6873480 : dopr_outch(c, target);
1469 6873480 : return;
1470 : }
1471 :
1472 6369666 : while (slen > 0)
1473 : {
1474 : int avail;
1475 :
1476 3184944 : if (target->bufend != NULL)
1477 3143804 : avail = target->bufend - target->bufptr;
1478 : else
1479 41140 : avail = slen;
1480 3184944 : if (avail <= 0)
1481 : {
1482 : /* buffer full, can we dump to stream? */
1483 114 : if (target->stream == NULL)
1484 : {
1485 0 : target->nchars += slen; /* no, lose the data */
1486 0 : return;
1487 : }
1488 114 : flushbuffer(target);
1489 114 : continue;
1490 : }
1491 3184830 : avail = Min(avail, slen);
1492 3184830 : memset(target->bufptr, c, avail);
1493 3184830 : target->bufptr += avail;
1494 3184830 : slen -= avail;
1495 : }
1496 : }
1497 :
1498 :
1499 : static int
1500 311948762 : adjust_sign(int is_negative, int forcesign, int *signvalue)
1501 : {
1502 311948762 : if (is_negative)
1503 : {
1504 33104530 : *signvalue = '-';
1505 33104530 : return true;
1506 : }
1507 278844232 : else if (forcesign)
1508 204 : *signvalue = '+';
1509 278844232 : return false;
1510 : }
1511 :
1512 :
1513 : static int
1514 466436528 : compute_padlen(int minlen, int vallen, int leftjust)
1515 : {
1516 : int padlen;
1517 :
1518 466436528 : padlen = minlen - vallen;
1519 466436528 : if (padlen < 0)
1520 435267990 : padlen = 0;
1521 466436528 : if (leftjust)
1522 1089356 : padlen = -padlen;
1523 466436528 : return padlen;
1524 : }
1525 :
1526 :
1527 : static void
1528 464462466 : leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
1529 : {
1530 : int maxpad;
1531 :
1532 464462466 : if (*padlen > 0 && zpad)
1533 : {
1534 7187690 : if (signvalue)
1535 : {
1536 224 : dopr_outch(signvalue, target);
1537 224 : --(*padlen);
1538 224 : signvalue = 0;
1539 : }
1540 7187690 : if (*padlen > 0)
1541 : {
1542 7187672 : dopr_outchmulti(zpad, *padlen, target);
1543 7187672 : *padlen = 0;
1544 : }
1545 : }
1546 464462466 : maxpad = (signvalue != 0);
1547 464462466 : if (*padlen > maxpad)
1548 : {
1549 1554428 : dopr_outchmulti(' ', *padlen - maxpad, target);
1550 1554428 : *padlen = maxpad;
1551 : }
1552 464462466 : if (signvalue)
1553 : {
1554 33104510 : dopr_outch(signvalue, target);
1555 33104510 : if (*padlen > 0)
1556 0 : --(*padlen);
1557 33104510 : else if (*padlen < 0)
1558 0 : ++(*padlen);
1559 : }
1560 464462466 : }
1561 :
1562 :
1563 : static void
1564 466436530 : trailing_pad(int padlen, PrintfTarget *target)
1565 : {
1566 466436530 : if (padlen < 0)
1567 734814 : dopr_outchmulti(' ', -padlen, target);
1568 466436530 : }
|