]> granicus.if.org Git - postgresql/blob - src/port/snprintf.c
Update copyright for 2015
[postgresql] / src / port / snprintf.c
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  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *        notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *        notice, this list of conditions and the following disclaimer in the
13  *        documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of the University nor the names of its contributors
15  *        may be used to endorse or promote products derived from this software
16  *        without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  *
30  * src/port/snprintf.c
31  */
32
33 #include "c.h"
34
35 #include <limits.h>
36 #ifndef WIN32
37 #include <sys/ioctl.h>
38 #endif
39 #include <sys/param.h>
40
41 #ifndef NL_ARGMAX
42 #define NL_ARGMAX 16
43 #endif
44
45
46 /*
47  *      SNPRINTF, VSNPRINTF and friends
48  *
49  * These versions have been grabbed off the net.  They have been
50  * cleaned up to compile properly and support for most of the Single Unix
51  * Specification has been added.  Remaining unimplemented features are:
52  *
53  * 1. No locale support: the radix character is always '.' and the '
54  * (single quote) format flag is ignored.
55  *
56  * 2. No support for the "%n" format specification.
57  *
58  * 3. No support for wide characters ("lc" and "ls" formats).
59  *
60  * 4. No support for "long double" ("Lf" and related formats).
61  *
62  * 5. Space and '#' flags are not implemented.
63  *
64  *
65  * The result values of these functions are not the same across different
66  * platforms.  This implementation is compatible with the Single Unix Spec:
67  *
68  * 1. -1 is returned only if processing is abandoned due to an invalid
69  * parameter, such as incorrect format string.  (Although not required by
70  * the spec, this happens only when no characters have yet been transmitted
71  * to the destination.)
72  *
73  * 2. For snprintf and sprintf, 0 is returned if str == NULL or count == 0;
74  * no data has been stored.
75  *
76  * 3. Otherwise, the number of bytes actually transmitted to the destination
77  * is returned (excluding the trailing '\0' for snprintf and sprintf).
78  *
79  * For snprintf with nonzero count, the result cannot be more than count-1
80  * (a trailing '\0' is always stored); it is not possible to distinguish
81  * buffer overrun from exact fit.  This is unlike some implementations that
82  * return the number of bytes that would have been needed for the complete
83  * result string.
84  */
85
86 /**************************************************************
87  * Original:
88  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
89  * A bombproof version of doprnt (dopr) included.
90  * Sigh.  This sort of thing is always nasty do deal with.  Note that
91  * the version here does not include floating point. (now it does ... tgl)
92  **************************************************************/
93
94 /* Prevent recursion */
95 #undef  vsnprintf
96 #undef  snprintf
97 #undef  sprintf
98 #undef  vfprintf
99 #undef  fprintf
100 #undef  printf
101
102 /* Info about where the formatted output is going */
103 typedef struct
104 {
105         char       *bufptr;                     /* next buffer output position */
106         char       *bufstart;           /* first buffer element */
107         char       *bufend;                     /* last buffer element, or NULL */
108         /* bufend == NULL is for sprintf, where we assume buf is big enough */
109         FILE       *stream;                     /* eventual output destination, or NULL */
110         int                     nchars;                 /* # chars already sent to stream */
111 } PrintfTarget;
112
113 /*
114  * Info about the type and value of a formatting parameter.  Note that we
115  * don't currently support "long double", "wint_t", or "wchar_t *" data,
116  * nor the '%n' formatting code; else we'd need more types.  Also, at this
117  * level we need not worry about signed vs unsigned values.
118  */
119 typedef enum
120 {
121         ATYPE_NONE = 0,
122         ATYPE_INT,
123         ATYPE_LONG,
124         ATYPE_LONGLONG,
125         ATYPE_DOUBLE,
126         ATYPE_CHARPTR
127 } PrintfArgType;
128
129 typedef union
130 {
131         int                     i;
132         long            l;
133         int64           ll;
134         double          d;
135         char       *cptr;
136 } PrintfArgValue;
137
138
139 static void flushbuffer(PrintfTarget *target);
140 static int      dopr(PrintfTarget *target, const char *format, va_list args);
141
142
143 int
144 pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
145 {
146         PrintfTarget target;
147
148         if (str == NULL || count == 0)
149                 return 0;
150         target.bufstart = target.bufptr = str;
151         target.bufend = str + count - 1;
152         target.stream = NULL;
153         /* target.nchars is unused in this case */
154         if (dopr(&target, fmt, args))
155         {
156                 *(target.bufptr) = '\0';
157                 errno = EINVAL;                 /* bad format */
158                 return -1;
159         }
160         *(target.bufptr) = '\0';
161         return target.bufptr - target.bufstart;
162 }
163
164 int
165 pg_snprintf(char *str, size_t count, const char *fmt,...)
166 {
167         int                     len;
168         va_list         args;
169
170         va_start(args, fmt);
171         len = pg_vsnprintf(str, count, fmt, args);
172         va_end(args);
173         return len;
174 }
175
176 static int
177 pg_vsprintf(char *str, const char *fmt, va_list args)
178 {
179         PrintfTarget target;
180
181         if (str == NULL)
182                 return 0;
183         target.bufstart = target.bufptr = str;
184         target.bufend = NULL;
185         target.stream = NULL;
186         /* target.nchars is unused in this case */
187         if (dopr(&target, fmt, args))
188         {
189                 *(target.bufptr) = '\0';
190                 errno = EINVAL;                 /* bad format */
191                 return -1;
192         }
193         *(target.bufptr) = '\0';
194         return target.bufptr - target.bufstart;
195 }
196
197 int
198 pg_sprintf(char *str, const char *fmt,...)
199 {
200         int                     len;
201         va_list         args;
202
203         va_start(args, fmt);
204         len = pg_vsprintf(str, fmt, args);
205         va_end(args);
206         return len;
207 }
208
209 int
210 pg_vfprintf(FILE *stream, const char *fmt, va_list args)
211 {
212         PrintfTarget target;
213         char            buffer[1024];   /* size is arbitrary */
214
215         if (stream == NULL)
216         {
217                 errno = EINVAL;
218                 return -1;
219         }
220         target.bufstart = target.bufptr = buffer;
221         target.bufend = buffer + sizeof(buffer) - 1;
222         target.stream = stream;
223         target.nchars = 0;
224         if (dopr(&target, fmt, args))
225         {
226                 errno = EINVAL;                 /* bad format */
227                 return -1;
228         }
229         /* dump any remaining buffer contents */
230         flushbuffer(&target);
231         return target.nchars;
232 }
233
234 int
235 pg_fprintf(FILE *stream, const char *fmt,...)
236 {
237         int                     len;
238         va_list         args;
239
240         va_start(args, fmt);
241         len = pg_vfprintf(stream, fmt, args);
242         va_end(args);
243         return len;
244 }
245
246 int
247 pg_printf(const char *fmt,...)
248 {
249         int                     len;
250         va_list         args;
251
252         va_start(args, fmt);
253         len = pg_vfprintf(stdout, fmt, args);
254         va_end(args);
255         return len;
256 }
257
258 /* call this only when stream is defined */
259 static void
260 flushbuffer(PrintfTarget *target)
261 {
262         size_t          nc = target->bufptr - target->bufstart;
263
264         if (nc > 0)
265                 target->nchars += fwrite(target->bufstart, 1, nc, target->stream);
266         target->bufptr = target->bufstart;
267 }
268
269
270 static void fmtstr(char *value, int leftjust, int minlen, int maxwidth,
271            int pointflag, PrintfTarget *target);
272 static void fmtptr(void *value, PrintfTarget *target);
273 static void fmtint(int64 value, char type, int forcesign,
274            int leftjust, int minlen, int zpad, int precision, int pointflag,
275            PrintfTarget *target);
276 static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target);
277 static void fmtfloat(double value, char type, int forcesign,
278                  int leftjust, int minlen, int zpad, int precision, int pointflag,
279                  PrintfTarget *target);
280 static void dostr(const char *str, int slen, PrintfTarget *target);
281 static void dopr_outch(int c, PrintfTarget *target);
282 static int      adjust_sign(int is_negative, int forcesign, int *signvalue);
283 static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen);
284 static void leading_pad(int zpad, int *signvalue, int *padlen,
285                         PrintfTarget *target);
286 static void trailing_pad(int *padlen, PrintfTarget *target);
287
288
289 /*
290  * dopr(): poor man's version of doprintf
291  */
292 static int
293 dopr(PrintfTarget *target, const char *format, va_list args)
294 {
295         const char *format_start = format;
296         int                     ch;
297         bool            have_dollar;
298         bool            have_non_dollar;
299         bool            have_star;
300         bool            afterstar;
301         int                     accum;
302         int                     longlongflag;
303         int                     longflag;
304         int                     pointflag;
305         int                     leftjust;
306         int                     fieldwidth;
307         int                     precision;
308         int                     zpad;
309         int                     forcesign;
310         int                     last_dollar;
311         int                     fmtpos;
312         int                     cvalue;
313         int64           numvalue;
314         double          fvalue;
315         char       *strvalue;
316         int                     i;
317         PrintfArgType argtypes[NL_ARGMAX + 1];
318         PrintfArgValue argvalues[NL_ARGMAX + 1];
319
320         /*
321          * Parse the format string to determine whether there are %n$ format
322          * specs, and identify the types and order of the format parameters.
323          */
324         have_dollar = have_non_dollar = false;
325         last_dollar = 0;
326         MemSet(argtypes, 0, sizeof(argtypes));
327
328         while ((ch = *format++) != '\0')
329         {
330                 if (ch != '%')
331                         continue;
332                 longflag = longlongflag = pointflag = 0;
333                 fmtpos = accum = 0;
334                 afterstar = false;
335 nextch1:
336                 ch = *format++;
337                 if (ch == '\0')
338                         break;                          /* illegal, but we don't complain */
339                 switch (ch)
340                 {
341                         case '-':
342                         case '+':
343                                 goto nextch1;
344                         case '0':
345                         case '1':
346                         case '2':
347                         case '3':
348                         case '4':
349                         case '5':
350                         case '6':
351                         case '7':
352                         case '8':
353                         case '9':
354                                 accum = accum * 10 + (ch - '0');
355                                 goto nextch1;
356                         case '.':
357                                 pointflag = 1;
358                                 accum = 0;
359                                 goto nextch1;
360                         case '*':
361                                 if (afterstar)
362                                         have_non_dollar = true;         /* multiple stars */
363                                 afterstar = true;
364                                 accum = 0;
365                                 goto nextch1;
366                         case '$':
367                                 have_dollar = true;
368                                 if (accum <= 0 || accum > NL_ARGMAX)
369                                         return -1;
370                                 if (afterstar)
371                                 {
372                                         if (argtypes[accum] &&
373                                                 argtypes[accum] != ATYPE_INT)
374                                                 return -1;
375                                         argtypes[accum] = ATYPE_INT;
376                                         last_dollar = Max(last_dollar, accum);
377                                         afterstar = false;
378                                 }
379                                 else
380                                         fmtpos = accum;
381                                 accum = 0;
382                                 goto nextch1;
383                         case 'l':
384                                 if (longflag)
385                                         longlongflag = 1;
386                                 else
387                                         longflag = 1;
388                                 goto nextch1;
389                         case 'z':
390 #if SIZEOF_SIZE_T == 8
391 #ifdef HAVE_LONG_INT_64
392                                 longflag = 1;
393 #elif defined(HAVE_LONG_LONG_INT_64)
394                                 longlongflag = 1;
395 #else
396 #error "Don't know how to print 64bit integers"
397 #endif
398 #else
399                                 /* assume size_t is same size as int */
400 #endif
401                                 goto nextch1;
402                         case 'h':
403                         case '\'':
404                                 /* ignore these */
405                                 goto nextch1;
406                         case 'd':
407                         case 'i':
408                         case 'o':
409                         case 'u':
410                         case 'x':
411                         case 'X':
412                                 if (fmtpos)
413                                 {
414                                         PrintfArgType atype;
415
416                                         if (longlongflag)
417                                                 atype = ATYPE_LONGLONG;
418                                         else if (longflag)
419                                                 atype = ATYPE_LONG;
420                                         else
421                                                 atype = ATYPE_INT;
422                                         if (argtypes[fmtpos] &&
423                                                 argtypes[fmtpos] != atype)
424                                                 return -1;
425                                         argtypes[fmtpos] = atype;
426                                         last_dollar = Max(last_dollar, fmtpos);
427                                 }
428                                 else
429                                         have_non_dollar = true;
430                                 break;
431                         case 'c':
432                                 if (fmtpos)
433                                 {
434                                         if (argtypes[fmtpos] &&
435                                                 argtypes[fmtpos] != ATYPE_INT)
436                                                 return -1;
437                                         argtypes[fmtpos] = ATYPE_INT;
438                                         last_dollar = Max(last_dollar, fmtpos);
439                                 }
440                                 else
441                                         have_non_dollar = true;
442                                 break;
443                         case 's':
444                         case 'p':
445                                 if (fmtpos)
446                                 {
447                                         if (argtypes[fmtpos] &&
448                                                 argtypes[fmtpos] != ATYPE_CHARPTR)
449                                                 return -1;
450                                         argtypes[fmtpos] = ATYPE_CHARPTR;
451                                         last_dollar = Max(last_dollar, fmtpos);
452                                 }
453                                 else
454                                         have_non_dollar = true;
455                                 break;
456                         case 'e':
457                         case 'E':
458                         case 'f':
459                         case 'g':
460                         case 'G':
461                                 if (fmtpos)
462                                 {
463                                         if (argtypes[fmtpos] &&
464                                                 argtypes[fmtpos] != ATYPE_DOUBLE)
465                                                 return -1;
466                                         argtypes[fmtpos] = ATYPE_DOUBLE;
467                                         last_dollar = Max(last_dollar, fmtpos);
468                                 }
469                                 else
470                                         have_non_dollar = true;
471                                 break;
472                         case '%':
473                                 break;
474                 }
475
476                 /*
477                  * If we finish the spec with afterstar still set, there's a
478                  * non-dollar star in there.
479                  */
480                 if (afterstar)
481                         have_non_dollar = true;
482         }
483
484         /* Per spec, you use either all dollar or all not. */
485         if (have_dollar && have_non_dollar)
486                 return -1;
487
488         /*
489          * In dollar mode, collect the arguments in physical order.
490          */
491         for (i = 1; i <= last_dollar; i++)
492         {
493                 switch (argtypes[i])
494                 {
495                         case ATYPE_NONE:
496                                 return -1;              /* invalid format */
497                         case ATYPE_INT:
498                                 argvalues[i].i = va_arg(args, int);
499                                 break;
500                         case ATYPE_LONG:
501                                 argvalues[i].l = va_arg(args, long);
502                                 break;
503                         case ATYPE_LONGLONG:
504                                 argvalues[i].ll = va_arg(args, int64);
505                                 break;
506                         case ATYPE_DOUBLE:
507                                 argvalues[i].d = va_arg(args, double);
508                                 break;
509                         case ATYPE_CHARPTR:
510                                 argvalues[i].cptr = va_arg(args, char *);
511                                 break;
512                 }
513         }
514
515         /*
516          * At last we can parse the format for real.
517          */
518         format = format_start;
519         while ((ch = *format++) != '\0')
520         {
521                 if (ch != '%')
522                 {
523                         dopr_outch(ch, target);
524                         continue;
525                 }
526                 fieldwidth = precision = zpad = leftjust = forcesign = 0;
527                 longflag = longlongflag = pointflag = 0;
528                 fmtpos = accum = 0;
529                 have_star = afterstar = false;
530 nextch2:
531                 ch = *format++;
532                 if (ch == '\0')
533                         break;                          /* illegal, but we don't complain */
534                 switch (ch)
535                 {
536                         case '-':
537                                 leftjust = 1;
538                                 goto nextch2;
539                         case '+':
540                                 forcesign = 1;
541                                 goto nextch2;
542                         case '0':
543                                 /* set zero padding if no nonzero digits yet */
544                                 if (accum == 0 && !pointflag)
545                                         zpad = '0';
546                                 /* FALL THRU */
547                         case '1':
548                         case '2':
549                         case '3':
550                         case '4':
551                         case '5':
552                         case '6':
553                         case '7':
554                         case '8':
555                         case '9':
556                                 accum = accum * 10 + (ch - '0');
557                                 goto nextch2;
558                         case '.':
559                                 if (have_star)
560                                         have_star = false;
561                                 else
562                                         fieldwidth = accum;
563                                 pointflag = 1;
564                                 accum = 0;
565                                 goto nextch2;
566                         case '*':
567                                 if (have_dollar)
568                                 {
569                                         /* process value after reading n$ */
570                                         afterstar = true;
571                                 }
572                                 else
573                                 {
574                                         /* fetch and process value now */
575                                         int                     starval = va_arg(args, int);
576
577                                         if (pointflag)
578                                         {
579                                                 precision = starval;
580                                                 if (precision < 0)
581                                                 {
582                                                         precision = 0;
583                                                         pointflag = 0;
584                                                 }
585                                         }
586                                         else
587                                         {
588                                                 fieldwidth = starval;
589                                                 if (fieldwidth < 0)
590                                                 {
591                                                         leftjust = 1;
592                                                         fieldwidth = -fieldwidth;
593                                                 }
594                                         }
595                                 }
596                                 have_star = true;
597                                 accum = 0;
598                                 goto nextch2;
599                         case '$':
600                                 if (afterstar)
601                                 {
602                                         /* fetch and process star value */
603                                         int                     starval = argvalues[accum].i;
604
605                                         if (pointflag)
606                                         {
607                                                 precision = starval;
608                                                 if (precision < 0)
609                                                 {
610                                                         precision = 0;
611                                                         pointflag = 0;
612                                                 }
613                                         }
614                                         else
615                                         {
616                                                 fieldwidth = starval;
617                                                 if (fieldwidth < 0)
618                                                 {
619                                                         leftjust = 1;
620                                                         fieldwidth = -fieldwidth;
621                                                 }
622                                         }
623                                         afterstar = false;
624                                 }
625                                 else
626                                         fmtpos = accum;
627                                 accum = 0;
628                                 goto nextch2;
629                         case 'l':
630                                 if (longflag)
631                                         longlongflag = 1;
632                                 else
633                                         longflag = 1;
634                                 goto nextch2;
635                         case 'z':
636 #if SIZEOF_SIZE_T == 8
637 #ifdef HAVE_LONG_INT_64
638                                 longflag = 1;
639 #elif defined(HAVE_LONG_LONG_INT_64)
640                                 longlongflag = 1;
641 #else
642 #error "Don't know how to print 64bit integers"
643 #endif
644 #else
645                                 /* assume size_t is same size as int */
646 #endif
647                                 goto nextch2;
648                         case 'h':
649                         case '\'':
650                                 /* ignore these */
651                                 goto nextch2;
652                         case 'd':
653                         case 'i':
654                                 if (!have_star)
655                                 {
656                                         if (pointflag)
657                                                 precision = accum;
658                                         else
659                                                 fieldwidth = accum;
660                                 }
661                                 if (have_dollar)
662                                 {
663                                         if (longlongflag)
664                                                 numvalue = argvalues[fmtpos].ll;
665                                         else if (longflag)
666                                                 numvalue = argvalues[fmtpos].l;
667                                         else
668                                                 numvalue = argvalues[fmtpos].i;
669                                 }
670                                 else
671                                 {
672                                         if (longlongflag)
673                                                 numvalue = va_arg(args, int64);
674                                         else if (longflag)
675                                                 numvalue = va_arg(args, long);
676                                         else
677                                                 numvalue = va_arg(args, int);
678                                 }
679                                 fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
680                                            precision, pointflag, target);
681                                 break;
682                         case 'o':
683                         case 'u':
684                         case 'x':
685                         case 'X':
686                                 if (!have_star)
687                                 {
688                                         if (pointflag)
689                                                 precision = accum;
690                                         else
691                                                 fieldwidth = accum;
692                                 }
693                                 if (have_dollar)
694                                 {
695                                         if (longlongflag)
696                                                 numvalue = (uint64) argvalues[fmtpos].ll;
697                                         else if (longflag)
698                                                 numvalue = (unsigned long) argvalues[fmtpos].l;
699                                         else
700                                                 numvalue = (unsigned int) argvalues[fmtpos].i;
701                                 }
702                                 else
703                                 {
704                                         if (longlongflag)
705                                                 numvalue = (uint64) va_arg(args, int64);
706                                         else if (longflag)
707                                                 numvalue = (unsigned long) va_arg(args, long);
708                                         else
709                                                 numvalue = (unsigned int) va_arg(args, int);
710                                 }
711                                 fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
712                                            precision, pointflag, target);
713                                 break;
714                         case 'c':
715                                 if (!have_star)
716                                 {
717                                         if (pointflag)
718                                                 precision = accum;
719                                         else
720                                                 fieldwidth = accum;
721                                 }
722                                 if (have_dollar)
723                                         cvalue = (unsigned char) argvalues[fmtpos].i;
724                                 else
725                                         cvalue = (unsigned char) va_arg(args, int);
726                                 fmtchar(cvalue, leftjust, fieldwidth, target);
727                                 break;
728                         case 's':
729                                 if (!have_star)
730                                 {
731                                         if (pointflag)
732                                                 precision = accum;
733                                         else
734                                                 fieldwidth = accum;
735                                 }
736                                 if (have_dollar)
737                                         strvalue = argvalues[fmtpos].cptr;
738                                 else
739                                         strvalue = va_arg(args, char *);
740                                 fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
741                                            target);
742                                 break;
743                         case 'p':
744                                 /* fieldwidth/leftjust are ignored ... */
745                                 if (have_dollar)
746                                         strvalue = argvalues[fmtpos].cptr;
747                                 else
748                                         strvalue = va_arg(args, char *);
749                                 fmtptr((void *) strvalue, target);
750                                 break;
751                         case 'e':
752                         case 'E':
753                         case 'f':
754                         case 'g':
755                         case 'G':
756                                 if (!have_star)
757                                 {
758                                         if (pointflag)
759                                                 precision = accum;
760                                         else
761                                                 fieldwidth = accum;
762                                 }
763                                 if (have_dollar)
764                                         fvalue = argvalues[fmtpos].d;
765                                 else
766                                         fvalue = va_arg(args, double);
767                                 fmtfloat(fvalue, ch, forcesign, leftjust,
768                                                  fieldwidth, zpad,
769                                                  precision, pointflag,
770                                                  target);
771                                 break;
772                         case '%':
773                                 dopr_outch('%', target);
774                                 break;
775                 }
776         }
777
778         return 0;
779 }
780
781 static size_t
782 pg_strnlen(const char *str, size_t maxlen)
783 {
784         const char *p = str;
785
786         while (maxlen-- > 0 && *p)
787                 p++;
788         return p - str;
789 }
790
791 static void
792 fmtstr(char *value, int leftjust, int minlen, int maxwidth,
793            int pointflag, PrintfTarget *target)
794 {
795         int                     padlen,
796                                 vallen;                 /* amount to pad */
797
798         /*
799          * If a maxwidth (precision) is specified, we must not fetch more bytes
800          * than that.
801          */
802         if (pointflag)
803                 vallen = pg_strnlen(value, maxwidth);
804         else
805                 vallen = strlen(value);
806
807         adjust_padlen(minlen, vallen, leftjust, &padlen);
808
809         while (padlen > 0)
810         {
811                 dopr_outch(' ', target);
812                 --padlen;
813         }
814
815         dostr(value, vallen, target);
816
817         trailing_pad(&padlen, target);
818 }
819
820 static void
821 fmtptr(void *value, PrintfTarget *target)
822 {
823         int                     vallen;
824         char            convert[64];
825
826         /* we rely on regular C library's sprintf to do the basic conversion */
827         vallen = sprintf(convert, "%p", value);
828
829         dostr(convert, vallen, target);
830 }
831
832 static void
833 fmtint(int64 value, char type, int forcesign, int leftjust,
834            int minlen, int zpad, int precision, int pointflag,
835            PrintfTarget *target)
836 {
837         uint64          base;
838         int                     dosign;
839         const char *cvt = "0123456789abcdef";
840         int                     signvalue = 0;
841         char            convert[64];
842         int                     vallen = 0;
843         int                     padlen = 0;             /* amount to pad */
844         int                     zeropad;                /* extra leading zeroes */
845
846         switch (type)
847         {
848                 case 'd':
849                 case 'i':
850                         base = 10;
851                         dosign = 1;
852                         break;
853                 case 'o':
854                         base = 8;
855                         dosign = 0;
856                         break;
857                 case 'u':
858                         base = 10;
859                         dosign = 0;
860                         break;
861                 case 'x':
862                         base = 16;
863                         dosign = 0;
864                         break;
865                 case 'X':
866                         cvt = "0123456789ABCDEF";
867                         base = 16;
868                         dosign = 0;
869                         break;
870                 default:
871                         return;                         /* keep compiler quiet */
872         }
873
874         /* Handle +/- */
875         if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
876                 value = -value;
877
878         /*
879          * SUS: the result of converting 0 with an explicit precision of 0 is no
880          * characters
881          */
882         if (value == 0 && pointflag && precision == 0)
883                 vallen = 0;
884         else
885         {
886                 /* make integer string */
887                 uint64          uvalue = (uint64) value;
888
889                 do
890                 {
891                         convert[vallen++] = cvt[uvalue % base];
892                         uvalue = uvalue / base;
893                 } while (uvalue);
894         }
895
896         zeropad = Max(0, precision - vallen);
897
898         adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen);
899
900         leading_pad(zpad, &signvalue, &padlen, target);
901
902         while (zeropad-- > 0)
903                 dopr_outch('0', target);
904
905         while (vallen > 0)
906                 dopr_outch(convert[--vallen], target);
907
908         trailing_pad(&padlen, target);
909 }
910
911 static void
912 fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
913 {
914         int                     padlen = 0;             /* amount to pad */
915
916         adjust_padlen(minlen, 1, leftjust, &padlen);
917
918         while (padlen > 0)
919         {
920                 dopr_outch(' ', target);
921                 --padlen;
922         }
923
924         dopr_outch(value, target);
925
926         trailing_pad(&padlen, target);
927 }
928
929 static void
930 fmtfloat(double value, char type, int forcesign, int leftjust,
931                  int minlen, int zpad, int precision, int pointflag,
932                  PrintfTarget *target)
933 {
934         int                     signvalue = 0;
935         int                     vallen;
936         char            fmt[32];
937         char            convert[512];
938         int                     padlen = 0;             /* amount to pad */
939
940         /* we rely on regular C library's sprintf to do the basic conversion */
941         if (pointflag)
942                 sprintf(fmt, "%%.%d%c", precision, type);
943         else
944                 sprintf(fmt, "%%%c", type);
945
946         if (adjust_sign((value < 0), forcesign, &signvalue))
947                 value = -value;
948
949         vallen = sprintf(convert, fmt, value);
950
951         adjust_padlen(minlen, vallen, leftjust, &padlen);
952
953         leading_pad(zpad, &signvalue, &padlen, target);
954
955         dostr(convert, vallen, target);
956
957         trailing_pad(&padlen, target);
958 }
959
960 static void
961 dostr(const char *str, int slen, PrintfTarget *target)
962 {
963         while (slen > 0)
964         {
965                 int                     avail;
966
967                 if (target->bufend != NULL)
968                         avail = target->bufend - target->bufptr;
969                 else
970                         avail = slen;
971                 if (avail <= 0)
972                 {
973                         /* buffer full, can we dump to stream? */
974                         if (target->stream == NULL)
975                                 return;                 /* no, lose the data */
976                         flushbuffer(target);
977                         continue;
978                 }
979                 avail = Min(avail, slen);
980                 memmove(target->bufptr, str, avail);
981                 target->bufptr += avail;
982                 str += avail;
983                 slen -= avail;
984         }
985 }
986
987 static void
988 dopr_outch(int c, PrintfTarget *target)
989 {
990         if (target->bufend != NULL && target->bufptr >= target->bufend)
991         {
992                 /* buffer full, can we dump to stream? */
993                 if (target->stream == NULL)
994                         return;                         /* no, lose the data */
995                 flushbuffer(target);
996         }
997         *(target->bufptr++) = c;
998 }
999
1000
1001 static int
1002 adjust_sign(int is_negative, int forcesign, int *signvalue)
1003 {
1004         if (is_negative)
1005         {
1006                 *signvalue = '-';
1007                 return true;
1008         }
1009         else if (forcesign)
1010                 *signvalue = '+';
1011         return false;
1012 }
1013
1014
1015 static void
1016 adjust_padlen(int minlen, int vallen, int leftjust, int *padlen)
1017 {
1018         *padlen = minlen - vallen;
1019         if (*padlen < 0)
1020                 *padlen = 0;
1021         if (leftjust)
1022                 *padlen = -(*padlen);
1023 }
1024
1025
1026 static void
1027 leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target)
1028 {
1029         if (*padlen > 0 && zpad)
1030         {
1031                 if (*signvalue)
1032                 {
1033                         dopr_outch(*signvalue, target);
1034                         --(*padlen);
1035                         *signvalue = 0;
1036                 }
1037                 while (*padlen > 0)
1038                 {
1039                         dopr_outch(zpad, target);
1040                         --(*padlen);
1041                 }
1042         }
1043         while (*padlen > (*signvalue != 0))
1044         {
1045                 dopr_outch(' ', target);
1046                 --(*padlen);
1047         }
1048         if (*signvalue)
1049         {
1050                 dopr_outch(*signvalue, target);
1051                 if (*padlen > 0)
1052                         --(*padlen);
1053                 else if (*padlen < 0)
1054                         ++(*padlen);
1055         }
1056 }
1057
1058
1059 static void
1060 trailing_pad(int *padlen, PrintfTarget *target)
1061 {
1062         while (*padlen < 0)
1063         {
1064                 dopr_outch(' ', target);
1065                 ++(*padlen);
1066         }
1067 }