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-2018, 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 <ctype.h>
37 #ifdef _MSC_VER
38 #include <float.h> /* for _isnan */
39 #endif
40 #include <limits.h>
41 #include <math.h>
42 #ifndef WIN32
43 #include <sys/ioctl.h>
44 #endif
45 #include <sys/param.h>
46
47 /*
48 * We used to use the platform's NL_ARGMAX here, but that's a bad idea,
49 * first because the point of this module is to remove platform dependencies
50 * not perpetuate them, and second because some platforms use ridiculously
51 * large values, leading to excessive stack consumption in dopr().
52 */
53 #define PG_NL_ARGMAX 31
54
55
56 /*
57 * SNPRINTF, VSNPRINTF and friends
58 *
59 * These versions have been grabbed off the net. They have been
60 * cleaned up to compile properly and support for most of the C99
61 * specification has been added. Remaining unimplemented features are:
62 *
63 * 1. No locale support: the radix character is always '.' and the '
64 * (single quote) format flag is ignored.
65 *
66 * 2. No support for the "%n" format specification.
67 *
68 * 3. No support for wide characters ("lc" and "ls" formats).
69 *
70 * 4. No support for "long double" ("Lf" and related formats).
71 *
72 * 5. Space and '#' flags are not implemented.
73 *
74 *
75 * Historically the result values of sprintf/snprintf varied across platforms.
76 * This implementation now follows the C99 standard:
77 *
78 * 1. -1 is returned if an error is detected in the format string, or if
79 * a write to the target stream fails (as reported by fwrite). Note that
80 * overrunning snprintf's target buffer is *not* an error.
81 *
82 * 2. For successful writes to streams, the actual number of bytes written
83 * to the stream is returned.
84 *
85 * 3. For successful sprintf/snprintf, the number of bytes that would have
86 * been written to an infinite-size buffer (excluding the trailing '\0')
87 * is returned. snprintf will truncate its output to fit in the buffer
88 * (ensuring a trailing '\0' unless count == 0), but this is not reflected
89 * in the function result.
90 *
91 * snprintf buffer overrun can be detected by checking for function result
92 * greater than or equal to the supplied count.
93 */
94
95 /**************************************************************
96 * Original:
97 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
98 * A bombproof version of doprnt (dopr) included.
99 * Sigh. This sort of thing is always nasty do deal with. Note that
100 * the version here does not include floating point. (now it does ... tgl)
101 **************************************************************/
102
103 /* Prevent recursion */
104 #undef vsnprintf
105 #undef snprintf
106 #undef sprintf
107 #undef vfprintf
108 #undef fprintf
109 #undef printf
110
111 /*
112 * Info about where the formatted output is going.
113 *
114 * dopr and subroutines will not write at/past bufend, but snprintf
115 * reserves one byte, ensuring it may place the trailing '\0' there.
116 *
117 * In snprintf, we use nchars to count the number of bytes dropped on the
118 * floor due to buffer overrun. The correct result of snprintf is thus
119 * (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
120 * seem: nchars is the number of emitted bytes that are not in the buffer now,
121 * either because we sent them to the stream or because we couldn't fit them
122 * into the buffer to begin with.)
123 */
124 typedef struct
125 {
126 char *bufptr; /* next buffer output position */
127 char *bufstart; /* first buffer element */
128 char *bufend; /* last+1 buffer element, or NULL */
129 /* bufend == NULL is for sprintf, where we assume buf is big enough */
130 FILE *stream; /* eventual output destination, or NULL */
131 int nchars; /* # chars sent to stream, or dropped */
132 bool failed; /* call is a failure; errno is set */
133 } PrintfTarget;
134
135 /*
136 * Info about the type and value of a formatting parameter. Note that we
137 * don't currently support "long double", "wint_t", or "wchar_t *" data,
138 * nor the '%n' formatting code; else we'd need more types. Also, at this
139 * level we need not worry about signed vs unsigned values.
140 */
141 typedef enum
142 {
143 ATYPE_NONE = 0,
144 ATYPE_INT,
145 ATYPE_LONG,
146 ATYPE_LONGLONG,
147 ATYPE_DOUBLE,
148 ATYPE_CHARPTR
149 } PrintfArgType;
150
151 typedef union
152 {
153 int i;
154 long l;
155 int64 ll;
156 double d;
157 char *cptr;
158 } PrintfArgValue;
159
160
161 static void flushbuffer(PrintfTarget *target);
162 static void dopr(PrintfTarget *target, const char *format, va_list args);
163
164
165 int
pg_vsnprintf(char * str,size_t count,const char * fmt,va_list args)166 pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
167 {
168 PrintfTarget target;
169 char onebyte[1];
170
171 /*
172 * C99 allows the case str == NULL when count == 0. Rather than
173 * special-casing this situation further down, we substitute a one-byte
174 * local buffer. Callers cannot tell, since the function result doesn't
175 * depend on count.
176 */
177 if (count == 0)
178 {
179 str = onebyte;
180 count = 1;
181 }
182 target.bufstart = target.bufptr = str;
183 target.bufend = str + count - 1;
184 target.stream = NULL;
185 target.nchars = 0;
186 target.failed = false;
187 dopr(&target, fmt, args);
188 *(target.bufptr) = '\0';
189 return target.failed ? -1 : (target.bufptr - target.bufstart
190 + target.nchars);
191 }
192
193 int
pg_snprintf(char * str,size_t count,const char * fmt,...)194 pg_snprintf(char *str, size_t count, const char *fmt,...)
195 {
196 int len;
197 va_list args;
198
199 va_start(args, fmt);
200 len = pg_vsnprintf(str, count, fmt, args);
201 va_end(args);
202 return len;
203 }
204
205 static int
pg_vsprintf(char * str,const char * fmt,va_list args)206 pg_vsprintf(char *str, const char *fmt, va_list args)
207 {
208 PrintfTarget target;
209
210 target.bufstart = target.bufptr = str;
211 target.bufend = NULL;
212 target.stream = NULL;
213 target.nchars = 0; /* not really used in this case */
214 target.failed = false;
215 dopr(&target, fmt, args);
216 *(target.bufptr) = '\0';
217 return target.failed ? -1 : (target.bufptr - target.bufstart
218 + target.nchars);
219 }
220
221 int
pg_sprintf(char * str,const char * fmt,...)222 pg_sprintf(char *str, const char *fmt,...)
223 {
224 int len;
225 va_list args;
226
227 va_start(args, fmt);
228 len = pg_vsprintf(str, fmt, args);
229 va_end(args);
230 return len;
231 }
232
233 int
pg_vfprintf(FILE * stream,const char * fmt,va_list args)234 pg_vfprintf(FILE *stream, const char *fmt, va_list args)
235 {
236 PrintfTarget target;
237 char buffer[1024]; /* size is arbitrary */
238
239 if (stream == NULL)
240 {
241 errno = EINVAL;
242 return -1;
243 }
244 target.bufstart = target.bufptr = buffer;
245 target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
246 target.stream = stream;
247 target.nchars = 0;
248 target.failed = false;
249 dopr(&target, fmt, args);
250 /* dump any remaining buffer contents */
251 flushbuffer(&target);
252 return target.failed ? -1 : target.nchars;
253 }
254
255 int
pg_fprintf(FILE * stream,const char * fmt,...)256 pg_fprintf(FILE *stream, const char *fmt,...)
257 {
258 int len;
259 va_list args;
260
261 va_start(args, fmt);
262 len = pg_vfprintf(stream, fmt, args);
263 va_end(args);
264 return len;
265 }
266
267 int
pg_printf(const char * fmt,...)268 pg_printf(const char *fmt,...)
269 {
270 int len;
271 va_list args;
272
273 va_start(args, fmt);
274 len = pg_vfprintf(stdout, fmt, args);
275 va_end(args);
276 return len;
277 }
278
279 /*
280 * Attempt to write the entire buffer to target->stream; discard the entire
281 * buffer in any case. Call this only when target->stream is defined.
282 */
283 static void
flushbuffer(PrintfTarget * target)284 flushbuffer(PrintfTarget *target)
285 {
286 size_t nc = target->bufptr - target->bufstart;
287
288 /*
289 * Don't write anything if we already failed; this is to ensure we
290 * preserve the original failure's errno.
291 */
292 if (!target->failed && nc > 0)
293 {
294 size_t written;
295
296 written = fwrite(target->bufstart, 1, nc, target->stream);
297 target->nchars += written;
298 if (written != nc)
299 target->failed = true;
300 }
301 target->bufptr = target->bufstart;
302 }
303
304
305 static void fmtstr(char *value, int leftjust, int minlen, int maxwidth,
306 int pointflag, PrintfTarget *target);
307 static void fmtptr(void *value, PrintfTarget *target);
308 static void fmtint(int64 value, char type, int forcesign,
309 int leftjust, int minlen, int zpad, int precision, int pointflag,
310 PrintfTarget *target);
311 static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target);
312 static void fmtfloat(double value, char type, int forcesign,
313 int leftjust, int minlen, int zpad, int precision, int pointflag,
314 PrintfTarget *target);
315 static void dostr(const char *str, int slen, PrintfTarget *target);
316 static void dopr_outch(int c, PrintfTarget *target);
317 static int adjust_sign(int is_negative, int forcesign, int *signvalue);
318 static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen);
319 static void leading_pad(int zpad, int *signvalue, int *padlen,
320 PrintfTarget *target);
321 static void trailing_pad(int *padlen, PrintfTarget *target);
322
323
324 /*
325 * dopr(): poor man's version of doprintf
326 */
327 static void
dopr(PrintfTarget * target,const char * format,va_list args)328 dopr(PrintfTarget *target, const char *format, va_list args)
329 {
330 const char *format_start = format;
331 int ch;
332 bool have_dollar;
333 bool have_non_dollar;
334 bool have_star;
335 bool afterstar;
336 int accum;
337 int longlongflag;
338 int longflag;
339 int pointflag;
340 int leftjust;
341 int fieldwidth;
342 int precision;
343 int zpad;
344 int forcesign;
345 int last_dollar;
346 int fmtpos;
347 int cvalue;
348 int64 numvalue;
349 double fvalue;
350 char *strvalue;
351 int i;
352 PrintfArgType argtypes[PG_NL_ARGMAX + 1];
353 PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
354
355 /*
356 * Parse the format string to determine whether there are %n$ format
357 * specs, and identify the types and order of the format parameters.
358 */
359 have_dollar = have_non_dollar = false;
360 last_dollar = 0;
361 MemSet(argtypes, 0, sizeof(argtypes));
362
363 while ((ch = *format++) != '\0')
364 {
365 if (ch != '%')
366 continue;
367 longflag = longlongflag = pointflag = 0;
368 fmtpos = accum = 0;
369 afterstar = false;
370 nextch1:
371 ch = *format++;
372 if (ch == '\0')
373 break; /* illegal, but we don't complain */
374 switch (ch)
375 {
376 case '-':
377 case '+':
378 goto nextch1;
379 case '0':
380 case '1':
381 case '2':
382 case '3':
383 case '4':
384 case '5':
385 case '6':
386 case '7':
387 case '8':
388 case '9':
389 accum = accum * 10 + (ch - '0');
390 goto nextch1;
391 case '.':
392 pointflag = 1;
393 accum = 0;
394 goto nextch1;
395 case '*':
396 if (afterstar)
397 have_non_dollar = true; /* multiple stars */
398 afterstar = true;
399 accum = 0;
400 goto nextch1;
401 case '$':
402 have_dollar = true;
403 if (accum <= 0 || accum > PG_NL_ARGMAX)
404 goto bad_format;
405 if (afterstar)
406 {
407 if (argtypes[accum] &&
408 argtypes[accum] != ATYPE_INT)
409 goto bad_format;
410 argtypes[accum] = ATYPE_INT;
411 last_dollar = Max(last_dollar, accum);
412 afterstar = false;
413 }
414 else
415 fmtpos = accum;
416 accum = 0;
417 goto nextch1;
418 case 'l':
419 if (longflag)
420 longlongflag = 1;
421 else
422 longflag = 1;
423 goto nextch1;
424 case 'z':
425 #if SIZEOF_SIZE_T == 8
426 #ifdef HAVE_LONG_INT_64
427 longflag = 1;
428 #elif defined(HAVE_LONG_LONG_INT_64)
429 longlongflag = 1;
430 #else
431 #error "Don't know how to print 64bit integers"
432 #endif
433 #else
434 /* assume size_t is same size as int */
435 #endif
436 goto nextch1;
437 case 'h':
438 case '\'':
439 /* ignore these */
440 goto nextch1;
441 case 'd':
442 case 'i':
443 case 'o':
444 case 'u':
445 case 'x':
446 case 'X':
447 if (fmtpos)
448 {
449 PrintfArgType atype;
450
451 if (longlongflag)
452 atype = ATYPE_LONGLONG;
453 else if (longflag)
454 atype = ATYPE_LONG;
455 else
456 atype = ATYPE_INT;
457 if (argtypes[fmtpos] &&
458 argtypes[fmtpos] != atype)
459 goto bad_format;
460 argtypes[fmtpos] = atype;
461 last_dollar = Max(last_dollar, fmtpos);
462 }
463 else
464 have_non_dollar = true;
465 break;
466 case 'c':
467 if (fmtpos)
468 {
469 if (argtypes[fmtpos] &&
470 argtypes[fmtpos] != ATYPE_INT)
471 goto bad_format;
472 argtypes[fmtpos] = ATYPE_INT;
473 last_dollar = Max(last_dollar, fmtpos);
474 }
475 else
476 have_non_dollar = true;
477 break;
478 case 's':
479 case 'p':
480 if (fmtpos)
481 {
482 if (argtypes[fmtpos] &&
483 argtypes[fmtpos] != ATYPE_CHARPTR)
484 goto bad_format;
485 argtypes[fmtpos] = ATYPE_CHARPTR;
486 last_dollar = Max(last_dollar, fmtpos);
487 }
488 else
489 have_non_dollar = true;
490 break;
491 case 'e':
492 case 'E':
493 case 'f':
494 case 'g':
495 case 'G':
496 if (fmtpos)
497 {
498 if (argtypes[fmtpos] &&
499 argtypes[fmtpos] != ATYPE_DOUBLE)
500 goto bad_format;
501 argtypes[fmtpos] = ATYPE_DOUBLE;
502 last_dollar = Max(last_dollar, fmtpos);
503 }
504 else
505 have_non_dollar = true;
506 break;
507 case '%':
508 break;
509 }
510
511 /*
512 * If we finish the spec with afterstar still set, there's a
513 * non-dollar star in there.
514 */
515 if (afterstar)
516 have_non_dollar = true;
517 }
518
519 /* Per spec, you use either all dollar or all not. */
520 if (have_dollar && have_non_dollar)
521 goto bad_format;
522
523 /*
524 * In dollar mode, collect the arguments in physical order.
525 */
526 for (i = 1; i <= last_dollar; i++)
527 {
528 switch (argtypes[i])
529 {
530 case ATYPE_NONE:
531 goto bad_format;
532 case ATYPE_INT:
533 argvalues[i].i = va_arg(args, int);
534 break;
535 case ATYPE_LONG:
536 argvalues[i].l = va_arg(args, long);
537 break;
538 case ATYPE_LONGLONG:
539 argvalues[i].ll = va_arg(args, int64);
540 break;
541 case ATYPE_DOUBLE:
542 argvalues[i].d = va_arg(args, double);
543 break;
544 case ATYPE_CHARPTR:
545 argvalues[i].cptr = va_arg(args, char *);
546 break;
547 }
548 }
549
550 /*
551 * At last we can parse the format for real.
552 */
553 format = format_start;
554 while ((ch = *format++) != '\0')
555 {
556 if (target->failed)
557 break;
558
559 if (ch != '%')
560 {
561 dopr_outch(ch, target);
562 continue;
563 }
564 fieldwidth = precision = zpad = leftjust = forcesign = 0;
565 longflag = longlongflag = pointflag = 0;
566 fmtpos = accum = 0;
567 have_star = afterstar = false;
568 nextch2:
569 ch = *format++;
570 if (ch == '\0')
571 break; /* illegal, but we don't complain */
572 switch (ch)
573 {
574 case '-':
575 leftjust = 1;
576 goto nextch2;
577 case '+':
578 forcesign = 1;
579 goto nextch2;
580 case '0':
581 /* set zero padding if no nonzero digits yet */
582 if (accum == 0 && !pointflag)
583 zpad = '0';
584 /* FALL THRU */
585 case '1':
586 case '2':
587 case '3':
588 case '4':
589 case '5':
590 case '6':
591 case '7':
592 case '8':
593 case '9':
594 accum = accum * 10 + (ch - '0');
595 goto nextch2;
596 case '.':
597 if (have_star)
598 have_star = false;
599 else
600 fieldwidth = accum;
601 pointflag = 1;
602 accum = 0;
603 goto nextch2;
604 case '*':
605 if (have_dollar)
606 {
607 /* process value after reading n$ */
608 afterstar = true;
609 }
610 else
611 {
612 /* fetch and process value now */
613 int starval = va_arg(args, int);
614
615 if (pointflag)
616 {
617 precision = starval;
618 if (precision < 0)
619 {
620 precision = 0;
621 pointflag = 0;
622 }
623 }
624 else
625 {
626 fieldwidth = starval;
627 if (fieldwidth < 0)
628 {
629 leftjust = 1;
630 fieldwidth = -fieldwidth;
631 }
632 }
633 }
634 have_star = true;
635 accum = 0;
636 goto nextch2;
637 case '$':
638 if (afterstar)
639 {
640 /* fetch and process star value */
641 int starval = argvalues[accum].i;
642
643 if (pointflag)
644 {
645 precision = starval;
646 if (precision < 0)
647 {
648 precision = 0;
649 pointflag = 0;
650 }
651 }
652 else
653 {
654 fieldwidth = starval;
655 if (fieldwidth < 0)
656 {
657 leftjust = 1;
658 fieldwidth = -fieldwidth;
659 }
660 }
661 afterstar = false;
662 }
663 else
664 fmtpos = accum;
665 accum = 0;
666 goto nextch2;
667 case 'l':
668 if (longflag)
669 longlongflag = 1;
670 else
671 longflag = 1;
672 goto nextch2;
673 case 'z':
674 #if SIZEOF_SIZE_T == 8
675 #ifdef HAVE_LONG_INT_64
676 longflag = 1;
677 #elif defined(HAVE_LONG_LONG_INT_64)
678 longlongflag = 1;
679 #else
680 #error "Don't know how to print 64bit integers"
681 #endif
682 #else
683 /* assume size_t is same size as int */
684 #endif
685 goto nextch2;
686 case 'h':
687 case '\'':
688 /* ignore these */
689 goto nextch2;
690 case 'd':
691 case 'i':
692 if (!have_star)
693 {
694 if (pointflag)
695 precision = accum;
696 else
697 fieldwidth = accum;
698 }
699 if (have_dollar)
700 {
701 if (longlongflag)
702 numvalue = argvalues[fmtpos].ll;
703 else if (longflag)
704 numvalue = argvalues[fmtpos].l;
705 else
706 numvalue = argvalues[fmtpos].i;
707 }
708 else
709 {
710 if (longlongflag)
711 numvalue = va_arg(args, int64);
712 else if (longflag)
713 numvalue = va_arg(args, long);
714 else
715 numvalue = va_arg(args, int);
716 }
717 fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
718 precision, pointflag, target);
719 break;
720 case 'o':
721 case 'u':
722 case 'x':
723 case 'X':
724 if (!have_star)
725 {
726 if (pointflag)
727 precision = accum;
728 else
729 fieldwidth = accum;
730 }
731 if (have_dollar)
732 {
733 if (longlongflag)
734 numvalue = (uint64) argvalues[fmtpos].ll;
735 else if (longflag)
736 numvalue = (unsigned long) argvalues[fmtpos].l;
737 else
738 numvalue = (unsigned int) argvalues[fmtpos].i;
739 }
740 else
741 {
742 if (longlongflag)
743 numvalue = (uint64) va_arg(args, int64);
744 else if (longflag)
745 numvalue = (unsigned long) va_arg(args, long);
746 else
747 numvalue = (unsigned int) va_arg(args, int);
748 }
749 fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
750 precision, pointflag, target);
751 break;
752 case 'c':
753 if (!have_star)
754 {
755 if (pointflag)
756 precision = accum;
757 else
758 fieldwidth = accum;
759 }
760 if (have_dollar)
761 cvalue = (unsigned char) argvalues[fmtpos].i;
762 else
763 cvalue = (unsigned char) va_arg(args, int);
764 fmtchar(cvalue, leftjust, fieldwidth, target);
765 break;
766 case 's':
767 if (!have_star)
768 {
769 if (pointflag)
770 precision = accum;
771 else
772 fieldwidth = accum;
773 }
774 if (have_dollar)
775 strvalue = argvalues[fmtpos].cptr;
776 else
777 strvalue = va_arg(args, char *);
778 /* If string is NULL, silently substitute "(null)" */
779 if (strvalue == NULL)
780 strvalue = "(null)";
781 fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
782 target);
783 break;
784 case 'p':
785 /* fieldwidth/leftjust are ignored ... */
786 if (have_dollar)
787 strvalue = argvalues[fmtpos].cptr;
788 else
789 strvalue = va_arg(args, char *);
790 fmtptr((void *) strvalue, target);
791 break;
792 case 'e':
793 case 'E':
794 case 'f':
795 case 'g':
796 case 'G':
797 if (!have_star)
798 {
799 if (pointflag)
800 precision = accum;
801 else
802 fieldwidth = accum;
803 }
804 if (have_dollar)
805 fvalue = argvalues[fmtpos].d;
806 else
807 fvalue = va_arg(args, double);
808 fmtfloat(fvalue, ch, forcesign, leftjust,
809 fieldwidth, zpad,
810 precision, pointflag,
811 target);
812 break;
813 case '%':
814 dopr_outch('%', target);
815 break;
816 }
817 }
818
819 return;
820
821 bad_format:
822 errno = EINVAL;
823 target->failed = true;
824 }
825
826 static size_t
pg_strnlen(const char * str,size_t maxlen)827 pg_strnlen(const char *str, size_t maxlen)
828 {
829 const char *p = str;
830
831 while (maxlen-- > 0 && *p)
832 p++;
833 return p - str;
834 }
835
836 static void
fmtstr(char * value,int leftjust,int minlen,int maxwidth,int pointflag,PrintfTarget * target)837 fmtstr(char *value, int leftjust, int minlen, int maxwidth,
838 int pointflag, PrintfTarget *target)
839 {
840 int padlen,
841 vallen; /* amount to pad */
842
843 /*
844 * If a maxwidth (precision) is specified, we must not fetch more bytes
845 * than that.
846 */
847 if (pointflag)
848 vallen = pg_strnlen(value, maxwidth);
849 else
850 vallen = strlen(value);
851
852 adjust_padlen(minlen, vallen, leftjust, &padlen);
853
854 while (padlen > 0)
855 {
856 dopr_outch(' ', target);
857 --padlen;
858 }
859
860 dostr(value, vallen, target);
861
862 trailing_pad(&padlen, target);
863 }
864
865 static void
fmtptr(void * value,PrintfTarget * target)866 fmtptr(void *value, PrintfTarget *target)
867 {
868 int vallen;
869 char convert[64];
870
871 /* we rely on regular C library's sprintf to do the basic conversion */
872 vallen = sprintf(convert, "%p", value);
873 if (vallen < 0)
874 target->failed = true;
875 else
876 dostr(convert, vallen, target);
877 }
878
879 static void
fmtint(int64 value,char type,int forcesign,int leftjust,int minlen,int zpad,int precision,int pointflag,PrintfTarget * target)880 fmtint(int64 value, char type, int forcesign, int leftjust,
881 int minlen, int zpad, int precision, int pointflag,
882 PrintfTarget *target)
883 {
884 uint64 base;
885 int dosign;
886 const char *cvt = "0123456789abcdef";
887 int signvalue = 0;
888 char convert[64];
889 int vallen = 0;
890 int padlen = 0; /* amount to pad */
891 int zeropad; /* extra leading zeroes */
892
893 switch (type)
894 {
895 case 'd':
896 case 'i':
897 base = 10;
898 dosign = 1;
899 break;
900 case 'o':
901 base = 8;
902 dosign = 0;
903 break;
904 case 'u':
905 base = 10;
906 dosign = 0;
907 break;
908 case 'x':
909 base = 16;
910 dosign = 0;
911 break;
912 case 'X':
913 cvt = "0123456789ABCDEF";
914 base = 16;
915 dosign = 0;
916 break;
917 default:
918 return; /* keep compiler quiet */
919 }
920
921 /* Handle +/- */
922 if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
923 value = -value;
924
925 /*
926 * SUS: the result of converting 0 with an explicit precision of 0 is no
927 * characters
928 */
929 if (value == 0 && pointflag && precision == 0)
930 vallen = 0;
931 else
932 {
933 /* make integer string */
934 uint64 uvalue = (uint64) value;
935
936 do
937 {
938 convert[vallen++] = cvt[uvalue % base];
939 uvalue = uvalue / base;
940 } while (uvalue);
941 }
942
943 zeropad = Max(0, precision - vallen);
944
945 adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen);
946
947 leading_pad(zpad, &signvalue, &padlen, target);
948
949 while (zeropad-- > 0)
950 dopr_outch('0', target);
951
952 while (vallen > 0)
953 dopr_outch(convert[--vallen], target);
954
955 trailing_pad(&padlen, target);
956 }
957
958 static void
fmtchar(int value,int leftjust,int minlen,PrintfTarget * target)959 fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
960 {
961 int padlen = 0; /* amount to pad */
962
963 adjust_padlen(minlen, 1, leftjust, &padlen);
964
965 while (padlen > 0)
966 {
967 dopr_outch(' ', target);
968 --padlen;
969 }
970
971 dopr_outch(value, target);
972
973 trailing_pad(&padlen, target);
974 }
975
976 static void
fmtfloat(double value,char type,int forcesign,int leftjust,int minlen,int zpad,int precision,int pointflag,PrintfTarget * target)977 fmtfloat(double value, char type, int forcesign, int leftjust,
978 int minlen, int zpad, int precision, int pointflag,
979 PrintfTarget *target)
980 {
981 int signvalue = 0;
982 int prec;
983 int vallen;
984 char fmt[32];
985 char convert[1024];
986 int zeropadlen = 0; /* amount to pad with zeroes */
987 int padlen = 0; /* amount to pad with spaces */
988
989 /*
990 * We rely on the regular C library's sprintf to do the basic conversion,
991 * then handle padding considerations here.
992 *
993 * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
994 * too wildly more than that with other hardware. In "f" format, sprintf
995 * could therefore generate at most 308 characters to the left of the
996 * decimal point; while we need to allow the precision to get as high as
997 * 308+17 to ensure that we don't truncate significant digits from very
998 * small values. To handle both these extremes, we use a buffer of 1024
999 * bytes and limit requested precision to 350 digits; this should prevent
1000 * buffer overrun even with non-IEEE math. If the original precision
1001 * request was more than 350, separately pad with zeroes.
1002 */
1003 if (precision < 0) /* cover possible overflow of "accum" */
1004 precision = 0;
1005 prec = Min(precision, 350);
1006
1007 if (pointflag)
1008 {
1009 if (sprintf(fmt, "%%.%d%c", prec, type) < 0)
1010 goto fail;
1011 zeropadlen = precision - prec;
1012 }
1013 else if (sprintf(fmt, "%%%c", type) < 0)
1014 goto fail;
1015
1016 if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue))
1017 value = -value;
1018
1019 vallen = sprintf(convert, fmt, value);
1020 if (vallen < 0)
1021 goto fail;
1022
1023 /* If it's infinity or NaN, forget about doing any zero-padding */
1024 if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
1025 zeropadlen = 0;
1026
1027 adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen);
1028
1029 leading_pad(zpad, &signvalue, &padlen, target);
1030
1031 if (zeropadlen > 0)
1032 {
1033 /* If 'e' or 'E' format, inject zeroes before the exponent */
1034 char *epos = strrchr(convert, 'e');
1035
1036 if (!epos)
1037 epos = strrchr(convert, 'E');
1038 if (epos)
1039 {
1040 /* pad after exponent */
1041 dostr(convert, epos - convert, target);
1042 while (zeropadlen-- > 0)
1043 dopr_outch('0', target);
1044 dostr(epos, vallen - (epos - convert), target);
1045 }
1046 else
1047 {
1048 /* no exponent, pad after the digits */
1049 dostr(convert, vallen, target);
1050 while (zeropadlen-- > 0)
1051 dopr_outch('0', target);
1052 }
1053 }
1054 else
1055 {
1056 /* no zero padding, just emit the number as-is */
1057 dostr(convert, vallen, target);
1058 }
1059
1060 trailing_pad(&padlen, target);
1061 return;
1062
1063 fail:
1064 target->failed = true;
1065 }
1066
1067 static void
dostr(const char * str,int slen,PrintfTarget * target)1068 dostr(const char *str, int slen, PrintfTarget *target)
1069 {
1070 while (slen > 0)
1071 {
1072 int avail;
1073
1074 if (target->bufend != NULL)
1075 avail = target->bufend - target->bufptr;
1076 else
1077 avail = slen;
1078 if (avail <= 0)
1079 {
1080 /* buffer full, can we dump to stream? */
1081 if (target->stream == NULL)
1082 {
1083 target->nchars += slen; /* no, lose the data */
1084 return;
1085 }
1086 flushbuffer(target);
1087 continue;
1088 }
1089 avail = Min(avail, slen);
1090 memmove(target->bufptr, str, avail);
1091 target->bufptr += avail;
1092 str += avail;
1093 slen -= avail;
1094 }
1095 }
1096
1097 static void
dopr_outch(int c,PrintfTarget * target)1098 dopr_outch(int c, PrintfTarget *target)
1099 {
1100 if (target->bufend != NULL && target->bufptr >= target->bufend)
1101 {
1102 /* buffer full, can we dump to stream? */
1103 if (target->stream == NULL)
1104 {
1105 target->nchars++; /* no, lose the data */
1106 return;
1107 }
1108 flushbuffer(target);
1109 }
1110 *(target->bufptr++) = c;
1111 }
1112
1113
1114 static int
adjust_sign(int is_negative,int forcesign,int * signvalue)1115 adjust_sign(int is_negative, int forcesign, int *signvalue)
1116 {
1117 if (is_negative)
1118 {
1119 *signvalue = '-';
1120 return true;
1121 }
1122 else if (forcesign)
1123 *signvalue = '+';
1124 return false;
1125 }
1126
1127
1128 static void
adjust_padlen(int minlen,int vallen,int leftjust,int * padlen)1129 adjust_padlen(int minlen, int vallen, int leftjust, int *padlen)
1130 {
1131 *padlen = minlen - vallen;
1132 if (*padlen < 0)
1133 *padlen = 0;
1134 if (leftjust)
1135 *padlen = -(*padlen);
1136 }
1137
1138
1139 static void
leading_pad(int zpad,int * signvalue,int * padlen,PrintfTarget * target)1140 leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target)
1141 {
1142 if (*padlen > 0 && zpad)
1143 {
1144 if (*signvalue)
1145 {
1146 dopr_outch(*signvalue, target);
1147 --(*padlen);
1148 *signvalue = 0;
1149 }
1150 while (*padlen > 0)
1151 {
1152 dopr_outch(zpad, target);
1153 --(*padlen);
1154 }
1155 }
1156 while (*padlen > (*signvalue != 0))
1157 {
1158 dopr_outch(' ', target);
1159 --(*padlen);
1160 }
1161 if (*signvalue)
1162 {
1163 dopr_outch(*signvalue, target);
1164 if (*padlen > 0)
1165 --(*padlen);
1166 else if (*padlen < 0)
1167 ++(*padlen);
1168 }
1169 }
1170
1171
1172 static void
trailing_pad(int * padlen,PrintfTarget * target)1173 trailing_pad(int *padlen, PrintfTarget *target)
1174 {
1175 while (*padlen < 0)
1176 {
1177 dopr_outch(' ', target);
1178 ++(*padlen);
1179 }
1180 }
1181