1 /*
2  * Copyright (c) 2003-2019, PgPool Global Development Group
3  * Copyright (c) 1983, 1995, 1996 Eric P. Allman
4  * Copyright (c) 1988, 1993
5  *	The Regents of the University of California.  All rights reserved.
6  * Portions Copyright (c) 1996-2020, PostgreSQL Global Development Group
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *	  notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *	  notice, this list of conditions and the following disclaimer in the
15  *	  documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *	  may be used to endorse or promote products derived from this software
18  *	  without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  *
32  * src/port/snprintf.c
33  */
34 
35 #if 0
36 #include "c.h"
37 #endif
38 
39 #include <stdarg.h>
40 #include <stdio.h>
41 #include <stdint.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <errno.h>
46 #include "pool_parser.h"
47 #include "stringinfo.h"
48 #include "utils/palloc.h"
49 
50 /*
51  * We used to use the platform's NL_ARGMAX here, but that's a bad idea,
52  * first because the point of this module is to remove platform dependencies
53  * not perpetuate them, and second because some platforms use ridiculously
54  * large values, leading to excessive stack consumption in dopr().
55  */
56 #define PG_NL_ARGMAX 31
57 
58 /* --------------------------------------------------------------------- */
59 /* c.h                                                                   */
60 /* --------------------------------------------------------------------- */
61 
62 /* Get a bit mask of the bits set in non-long aligned addresses */
63 #define LONG_ALIGN_MASK (sizeof(long) - 1)
64 
65 #define MEMSET_LOOP_LIMIT 1024
66 
67 /*
68  * MemSet
69  *  Exactly the same as standard library function memset(), but considerably
70  *  faster for zeroing small word-aligned structures (such as parsetree nodes).
71  *  This has to be a macro because the main point is to avoid function-call
72  *  overhead.   However, we have also found that the loop is faster than
73  *  native libc memset() on some platforms, even those with assembler
74  *  memset() functions.  More research needs to be done, perhaps with
75  *  MEMSET_LOOP_LIMIT tests in configure.
76  */
77 #define MemSet(start, val, len) \
78     do \
79     { \
80         /* must be void* because we don't know if it is integer aligned yet */ \
81         void   *_vstart = (void *) (start); \
82         int     _val = (val); \
83         Size    _len = (len); \
84 \
85         if ((((intptr_t) _vstart) & LONG_ALIGN_MASK) == 0 && \
86             (_len & LONG_ALIGN_MASK) == 0 && \
87             _val == 0 && \
88             _len <= MEMSET_LOOP_LIMIT && \
89             /* \
90              *  If MEMSET_LOOP_LIMIT == 0, optimizer should find \
91              *  the whole "if" false at compile time. \
92              */ \
93             MEMSET_LOOP_LIMIT != 0) \
94         { \
95             long *_start = (long *) _vstart; \
96             long *_stop = (long *) ((char *) _start + _len); \
97             while (_start < _stop) \
98                 *_start++ = 0; \
99         } \
100         else \
101             memset(_vstart, _val, _len); \
102     } while (0)
103 
104 /*
105  *	SNPRINTF, VSNPRINTF and friends
106  *
107  * These versions have been grabbed off the net.  They have been
108  * cleaned up to compile properly and support for most of the C99
109  * specification has been added.  Remaining unimplemented features are:
110  *
111  * 1. No locale support: the radix character is always '.' and the '
112  * (single quote) format flag is ignored.
113  *
114  * 2. No support for the "%n" format specification.
115  *
116  * 3. No support for wide characters ("lc" and "ls" formats).
117  *
118  * 4. No support for "long double" ("Lf" and related formats).
119  *
120  * 5. Space and '#' flags are not implemented.
121  *
122  * In addition, we support some extensions over C99:
123  *
124  * 1. Argument order control through "%n$" and "*n$", as required by POSIX.
125  *
126  * 2. "%m" expands to the value of strerror(errno), where errno is the
127  * value that variable had at the start of the call.  This is a glibc
128  * extension, but a very useful one.
129  *
130  *
131  * Historically the result values of sprintf/snprintf varied across platforms.
132  * This implementation now follows the C99 standard:
133  *
134  * 1. -1 is returned if an error is detected in the format string, or if
135  * a write to the target stream fails (as reported by fwrite).  Note that
136  * overrunning snprintf's target buffer is *not* an error.
137  *
138  * 2. For successful writes to streams, the actual number of bytes written
139  * to the stream is returned.
140  *
141  * 3. For successful sprintf/snprintf, the number of bytes that would have
142  * been written to an infinite-size buffer (excluding the trailing '\0')
143  * is returned.  snprintf will truncate its output to fit in the buffer
144  * (ensuring a trailing '\0' unless count == 0), but this is not reflected
145  * in the function result.
146  *
147  * snprintf buffer overrun can be detected by checking for function result
148  * greater than or equal to the supplied count.
149  */
150 
151 /**************************************************************
152  * Original:
153  * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
154  * A bombproof version of doprnt (dopr) included.
155  * Sigh.  This sort of thing is always nasty do deal with.  Note that
156  * the version here does not include floating point. (now it does ... tgl)
157  **************************************************************/
158 
159 /* Prevent recursion */
160 #undef	vsnprintf
161 #undef	snprintf
162 #undef	vsprintf
163 #undef	sprintf
164 #undef	vfprintf
165 #undef	fprintf
166 #undef	vprintf
167 #undef	printf
168 
169 /*
170  * Info about where the formatted output is going.
171  *
172  * dopr and subroutines will not write at/past bufend, but snprintf
173  * reserves one byte, ensuring it may place the trailing '\0' there.
174  *
175  * In snprintf, we use nchars to count the number of bytes dropped on the
176  * floor due to buffer overrun.  The correct result of snprintf is thus
177  * (bufptr - bufstart) + nchars.  (This isn't as inconsistent as it might
178  * seem: nchars is the number of emitted bytes that are not in the buffer now,
179  * either because we sent them to the stream or because we couldn't fit them
180  * into the buffer to begin with.)
181  */
182 typedef struct
183 {
184 	char	   *bufptr;			/* next buffer output position */
185 	char	   *bufstart;		/* first buffer element */
186 	char	   *bufend;			/* last+1 buffer element, or NULL */
187 	/* bufend == NULL is for sprintf, where we assume buf is big enough */
188 	FILE	   *stream;			/* eventual output destination, or NULL */
189 	int			nchars;			/* # chars sent to stream, or dropped */
190 	bool		failed;			/* call is a failure; errno is set */
191 } PrintfTarget;
192 
193 /*
194  * Info about the type and value of a formatting parameter.  Note that we
195  * don't currently support "long double", "wint_t", or "wchar_t *" data,
196  * nor the '%n' formatting code; else we'd need more types.  Also, at this
197  * level we need not worry about signed vs unsigned values.
198  */
199 typedef enum
200 {
201 	ATYPE_NONE = 0,
202 	ATYPE_INT,
203 	ATYPE_LONG,
204 	ATYPE_LONGLONG,
205 	ATYPE_DOUBLE,
206 	ATYPE_CHARPTR
207 } PrintfArgType;
208 
209 typedef union
210 {
211 	int			i;
212 	long		l;
213 	long long	ll;
214 	double		d;
215 	char	   *cptr;
216 } PrintfArgValue;
217 
218 
219 static void flushbuffer(PrintfTarget *target);
220 static void dopr(PrintfTarget *target, const char *format, va_list args);
221 
222 
223 /*
224  * Externally visible entry points.
225  *
226  * All of these are just wrappers around dopr().  Note it's essential that
227  * they not change the value of "errno" before reaching dopr().
228  */
229 
230 int
pg_vsnprintf(char * str,size_t count,const char * fmt,va_list args)231 pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
232 {
233 	PrintfTarget target;
234 	char		onebyte[1];
235 
236 	/*
237 	 * C99 allows the case str == NULL when count == 0.  Rather than
238 	 * special-casing this situation further down, we substitute a one-byte
239 	 * local buffer.  Callers cannot tell, since the function result doesn't
240 	 * depend on count.
241 	 */
242 	if (count == 0)
243 	{
244 		str = onebyte;
245 		count = 1;
246 	}
247 	target.bufstart = target.bufptr = str;
248 	target.bufend = str + count - 1;
249 	target.stream = NULL;
250 	target.nchars = 0;
251 	target.failed = false;
252 	dopr(&target, fmt, args);
253 	*(target.bufptr) = '\0';
254 	return target.failed ? -1 : (target.bufptr - target.bufstart
255 								 + target.nchars);
256 }
257 
258 #if 0
259 int
260 pg_snprintf(char *str, size_t count, const char *fmt,...)
261 {
262 	int			len;
263 	va_list		args;
264 
265 	va_start(args, fmt);
266 	len = pg_vsnprintf(str, count, fmt, args);
267 	va_end(args);
268 	return len;
269 }
270 
271 int
272 pg_vsprintf(char *str, const char *fmt, va_list args)
273 {
274 	PrintfTarget target;
275 
276 	target.bufstart = target.bufptr = str;
277 	target.bufend = NULL;
278 	target.stream = NULL;
279 	target.nchars = 0;			/* not really used in this case */
280 	target.failed = false;
281 	dopr(&target, fmt, args);
282 	*(target.bufptr) = '\0';
283 	return target.failed ? -1 : (target.bufptr - target.bufstart
284 								 + target.nchars);
285 }
286 
287 int
288 pg_sprintf(char *str, const char *fmt,...)
289 {
290 	int			len;
291 	va_list		args;
292 
293 	va_start(args, fmt);
294 	len = pg_vsprintf(str, fmt, args);
295 	va_end(args);
296 	return len;
297 }
298 
299 int
300 pg_vfprintf(FILE *stream, const char *fmt, va_list args)
301 {
302 	PrintfTarget target;
303 	char		buffer[1024];	/* size is arbitrary */
304 
305 	if (stream == NULL)
306 	{
307 		errno = EINVAL;
308 		return -1;
309 	}
310 	target.bufstart = target.bufptr = buffer;
311 	target.bufend = buffer + sizeof(buffer);	/* use the whole buffer */
312 	target.stream = stream;
313 	target.nchars = 0;
314 	target.failed = false;
315 	dopr(&target, fmt, args);
316 	/* dump any remaining buffer contents */
317 	flushbuffer(&target);
318 	return target.failed ? -1 : target.nchars;
319 }
320 
321 int
322 pg_fprintf(FILE *stream, const char *fmt,...)
323 {
324 	int			len;
325 	va_list		args;
326 
327 	va_start(args, fmt);
328 	len = pg_vfprintf(stream, fmt, args);
329 	va_end(args);
330 	return len;
331 }
332 
333 int
334 pg_vprintf(const char *fmt, va_list args)
335 {
336 	return pg_vfprintf(stdout, fmt, args);
337 }
338 
339 int
340 pg_printf(const char *fmt,...)
341 {
342 	int			len;
343 	va_list		args;
344 
345 	va_start(args, fmt);
346 	len = pg_vfprintf(stdout, fmt, args);
347 	va_end(args);
348 	return len;
349 }
350 #endif
351 
352 /*
353  * Attempt to write the entire buffer to target->stream; discard the entire
354  * buffer in any case.  Call this only when target->stream is defined.
355  */
356 static void
flushbuffer(PrintfTarget * target)357 flushbuffer(PrintfTarget *target)
358 {
359 	size_t		nc = target->bufptr - target->bufstart;
360 
361 	/*
362 	 * Don't write anything if we already failed; this is to ensure we
363 	 * preserve the original failure's errno.
364 	 */
365 	if (!target->failed && nc > 0)
366 	{
367 		size_t		written;
368 
369 		written = fwrite(target->bufstart, 1, nc, target->stream);
370 		target->nchars += written;
371 		if (written != nc)
372 			target->failed = true;
373 	}
374 	target->bufptr = target->bufstart;
375 }
376 
377 
378 static bool find_arguments(const char *format, va_list args,
379 						   PrintfArgValue *argvalues);
380 static void fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
381 				   int pointflag, PrintfTarget *target);
382 static void fmtptr(void *value, PrintfTarget *target);
383 static void fmtint(long long value, char type, int forcesign,
384 				   int leftjust, int minlen, int zpad, int precision, int pointflag,
385 				   PrintfTarget *target);
386 static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target);
387 static void fmtfloat(double value, char type, int forcesign,
388 					 int leftjust, int minlen, int zpad, int precision, int pointflag,
389 					 PrintfTarget *target);
390 static void dostr(const char *str, int slen, PrintfTarget *target);
391 static void dopr_outch(int c, PrintfTarget *target);
392 static void dopr_outchmulti(int c, int slen, PrintfTarget *target);
393 static int	adjust_sign(int is_negative, int forcesign, int *signvalue);
394 static int	compute_padlen(int minlen, int vallen, int leftjust);
395 static void leading_pad(int zpad, int signvalue, int *padlen,
396 						PrintfTarget *target);
397 static void trailing_pad(int padlen, PrintfTarget *target);
398 
399 /*
400  * If strchrnul exists (it's a glibc-ism), it's a good bit faster than the
401  * equivalent manual loop.  If it doesn't exist, provide a replacement.
402  *
403  * Note: glibc declares this as returning "char *", but that would require
404  * casting away const internally, so we don't follow that detail.
405  */
406 #ifndef HAVE_STRCHRNUL
407 
408 static inline const char *
strchrnul(const char * s,int c)409 strchrnul(const char *s, int c)
410 {
411 	while (*s != '\0' && *s != c)
412 		s++;
413 	return s;
414 }
415 
416 #else
417 
418 /*
419  * glibc's <string.h> declares strchrnul only if _GNU_SOURCE is defined.
420  * While we typically use that on glibc platforms, configure will set
421  * HAVE_STRCHRNUL whether it's used or not.  Fill in the missing declaration
422  * so that this file will compile cleanly with or without _GNU_SOURCE.
423  */
424 #ifndef _GNU_SOURCE
425 extern char *strchrnul(const char *s, int c);
426 #endif
427 
428 #endif							/* HAVE_STRCHRNUL */
429 
430 
431 /*
432  * dopr(): the guts of *printf for all cases.
433  */
434 static void
dopr(PrintfTarget * target,const char * format,va_list args)435 dopr(PrintfTarget *target, const char *format, va_list args)
436 {
437 	int			save_errno = errno;
438 	const char *first_pct = NULL;
439 	int			ch;
440 	bool		have_dollar;
441 	bool		have_star;
442 	bool		afterstar;
443 	int			accum;
444 	int			longlongflag;
445 	int			longflag;
446 	int			pointflag;
447 	int			leftjust;
448 	int			fieldwidth;
449 	int			precision;
450 	int			zpad;
451 	int			forcesign;
452 	int			fmtpos;
453 	int			cvalue;
454 	long long	numvalue;
455 	double		fvalue;
456 	char	   *strvalue;
457 	PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
458 
459 	/*
460 	 * Initially, we suppose the format string does not use %n$.  The first
461 	 * time we come to a conversion spec that has that, we'll call
462 	 * find_arguments() to check for consistent use of %n$ and fill the
463 	 * argvalues array with the argument values in the correct order.
464 	 */
465 	have_dollar = false;
466 
467 	while (*format != '\0')
468 	{
469 		/* Locate next conversion specifier */
470 		if (*format != '%')
471 		{
472 			/* Scan to next '%' or end of string */
473 			const char *next_pct = strchrnul(format + 1, '%');
474 
475 			/* Dump literal data we just scanned over */
476 			dostr(format, next_pct - format, target);
477 			if (target->failed)
478 				break;
479 
480 			if (*next_pct == '\0')
481 				break;
482 			format = next_pct;
483 		}
484 
485 		/*
486 		 * Remember start of first conversion spec; if we find %n$, then it's
487 		 * sufficient for find_arguments() to start here, without rescanning
488 		 * earlier literal text.
489 		 */
490 		if (first_pct == NULL)
491 			first_pct = format;
492 
493 		/* Process conversion spec starting at *format */
494 		format++;
495 
496 		/* Fast path for conversion spec that is exactly %s */
497 		if (*format == 's')
498 		{
499 			format++;
500 			strvalue = va_arg(args, char *);
501 			Assert(strvalue != NULL);
502 			dostr(strvalue, strlen(strvalue), target);
503 			if (target->failed)
504 				break;
505 			continue;
506 		}
507 
508 		fieldwidth = precision = zpad = leftjust = forcesign = 0;
509 		longflag = longlongflag = pointflag = 0;
510 		fmtpos = accum = 0;
511 		have_star = afterstar = false;
512 nextch2:
513 		ch = *format++;
514 		switch (ch)
515 		{
516 			case '-':
517 				leftjust = 1;
518 				goto nextch2;
519 			case '+':
520 				forcesign = 1;
521 				goto nextch2;
522 			case '0':
523 				/* set zero padding if no nonzero digits yet */
524 				if (accum == 0 && !pointflag)
525 					zpad = '0';
526 				/* FALL THRU */
527 			case '1':
528 			case '2':
529 			case '3':
530 			case '4':
531 			case '5':
532 			case '6':
533 			case '7':
534 			case '8':
535 			case '9':
536 				accum = accum * 10 + (ch - '0');
537 				goto nextch2;
538 			case '.':
539 				if (have_star)
540 					have_star = false;
541 				else
542 					fieldwidth = accum;
543 				pointflag = 1;
544 				accum = 0;
545 				goto nextch2;
546 			case '*':
547 				if (have_dollar)
548 				{
549 					/*
550 					 * We'll process value after reading n$.  Note it's OK to
551 					 * assume have_dollar is set correctly, because in a valid
552 					 * format string the initial % must have had n$ if * does.
553 					 */
554 					afterstar = true;
555 				}
556 				else
557 				{
558 					/* fetch and process value now */
559 					int			starval = va_arg(args, int);
560 
561 					if (pointflag)
562 					{
563 						precision = starval;
564 						if (precision < 0)
565 						{
566 							precision = 0;
567 							pointflag = 0;
568 						}
569 					}
570 					else
571 					{
572 						fieldwidth = starval;
573 						if (fieldwidth < 0)
574 						{
575 							leftjust = 1;
576 							fieldwidth = -fieldwidth;
577 						}
578 					}
579 				}
580 				have_star = true;
581 				accum = 0;
582 				goto nextch2;
583 			case '$':
584 				/* First dollar sign? */
585 				if (!have_dollar)
586 				{
587 					/* Yup, so examine all conversion specs in format */
588 					if (!find_arguments(first_pct, args, argvalues))
589 						goto bad_format;
590 					have_dollar = true;
591 				}
592 				if (afterstar)
593 				{
594 					/* fetch and process star value */
595 					int			starval = argvalues[accum].i;
596 
597 					if (pointflag)
598 					{
599 						precision = starval;
600 						if (precision < 0)
601 						{
602 							precision = 0;
603 							pointflag = 0;
604 						}
605 					}
606 					else
607 					{
608 						fieldwidth = starval;
609 						if (fieldwidth < 0)
610 						{
611 							leftjust = 1;
612 							fieldwidth = -fieldwidth;
613 						}
614 					}
615 					afterstar = false;
616 				}
617 				else
618 					fmtpos = accum;
619 				accum = 0;
620 				goto nextch2;
621 			case 'l':
622 				if (longflag)
623 					longlongflag = 1;
624 				else
625 					longflag = 1;
626 				goto nextch2;
627 			case 'z':
628 #if SIZEOF_SIZE_T == 8
629 #ifdef HAVE_LONG_INT_64
630 				longflag = 1;
631 #elif defined(HAVE_LONG_LONG_INT_64)
632 				longlongflag = 1;
633 #else
634 #error "Don't know how to print 64bit integers"
635 #endif
636 #else
637 				/* assume size_t is same size as int */
638 #endif
639 				goto nextch2;
640 			case 'h':
641 			case '\'':
642 				/* ignore these */
643 				goto nextch2;
644 			case 'd':
645 			case 'i':
646 				if (!have_star)
647 				{
648 					if (pointflag)
649 						precision = accum;
650 					else
651 						fieldwidth = accum;
652 				}
653 				if (have_dollar)
654 				{
655 					if (longlongflag)
656 						numvalue = argvalues[fmtpos].ll;
657 					else if (longflag)
658 						numvalue = argvalues[fmtpos].l;
659 					else
660 						numvalue = argvalues[fmtpos].i;
661 				}
662 				else
663 				{
664 					if (longlongflag)
665 						numvalue = va_arg(args, long long);
666 					else if (longflag)
667 						numvalue = va_arg(args, long);
668 					else
669 						numvalue = va_arg(args, int);
670 				}
671 				fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
672 					   precision, pointflag, target);
673 				break;
674 			case 'o':
675 			case 'u':
676 			case 'x':
677 			case 'X':
678 				if (!have_star)
679 				{
680 					if (pointflag)
681 						precision = accum;
682 					else
683 						fieldwidth = accum;
684 				}
685 				if (have_dollar)
686 				{
687 					if (longlongflag)
688 						numvalue = (unsigned long long) argvalues[fmtpos].ll;
689 					else if (longflag)
690 						numvalue = (unsigned long) argvalues[fmtpos].l;
691 					else
692 						numvalue = (unsigned int) argvalues[fmtpos].i;
693 				}
694 				else
695 				{
696 					if (longlongflag)
697 						numvalue = (unsigned long long) va_arg(args, long long);
698 					else if (longflag)
699 						numvalue = (unsigned long) va_arg(args, long);
700 					else
701 						numvalue = (unsigned int) va_arg(args, int);
702 				}
703 				fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
704 					   precision, pointflag, target);
705 				break;
706 			case 'c':
707 				if (!have_star)
708 				{
709 					if (pointflag)
710 						precision = accum;
711 					else
712 						fieldwidth = accum;
713 				}
714 				if (have_dollar)
715 					cvalue = (unsigned char) argvalues[fmtpos].i;
716 				else
717 					cvalue = (unsigned char) va_arg(args, int);
718 				fmtchar(cvalue, leftjust, fieldwidth, target);
719 				break;
720 			case 's':
721 				if (!have_star)
722 				{
723 					if (pointflag)
724 						precision = accum;
725 					else
726 						fieldwidth = accum;
727 				}
728 				if (have_dollar)
729 					strvalue = argvalues[fmtpos].cptr;
730 				else
731 					strvalue = va_arg(args, char *);
732 				/* Whine if someone tries to print a NULL string */
733 				Assert(strvalue != NULL);
734 				fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
735 					   target);
736 				break;
737 			case 'p':
738 				/* fieldwidth/leftjust are ignored ... */
739 				if (have_dollar)
740 					strvalue = argvalues[fmtpos].cptr;
741 				else
742 					strvalue = va_arg(args, char *);
743 				fmtptr((void *) strvalue, target);
744 				break;
745 			case 'e':
746 			case 'E':
747 			case 'f':
748 			case 'g':
749 			case 'G':
750 				if (!have_star)
751 				{
752 					if (pointflag)
753 						precision = accum;
754 					else
755 						fieldwidth = accum;
756 				}
757 				if (have_dollar)
758 					fvalue = argvalues[fmtpos].d;
759 				else
760 					fvalue = va_arg(args, double);
761 				fmtfloat(fvalue, ch, forcesign, leftjust,
762 						 fieldwidth, zpad,
763 						 precision, pointflag,
764 						 target);
765 				break;
766 			case 'm':
767 				{
768 					char		errbuf[PG_STRERROR_R_BUFLEN];
769 					const char *errm = strerror_r(save_errno,
770 												  errbuf, sizeof(errbuf));
771 
772 					dostr(errm, strlen(errm), target);
773 				}
774 				break;
775 			case '%':
776 				dopr_outch('%', target);
777 				break;
778 			default:
779 
780 				/*
781 				 * Anything else --- in particular, '\0' indicating end of
782 				 * format string --- is bogus.
783 				 */
784 				goto bad_format;
785 		}
786 
787 		/* Check for failure after each conversion spec */
788 		if (target->failed)
789 			break;
790 	}
791 
792 	return;
793 
794 bad_format:
795 	errno = EINVAL;
796 	target->failed = true;
797 }
798 
799 /*
800  * find_arguments(): sort out the arguments for a format spec with %n$
801  *
802  * If format is valid, return true and fill argvalues[i] with the value
803  * for the conversion spec that has %i$ or *i$.  Else return false.
804  */
805 static bool
find_arguments(const char * format,va_list args,PrintfArgValue * argvalues)806 find_arguments(const char *format, va_list args,
807 			   PrintfArgValue *argvalues)
808 {
809 	int			ch;
810 	bool		afterstar;
811 	int			accum;
812 	int			longlongflag;
813 	int			longflag;
814 	int			fmtpos;
815 	int			i;
816 	int			last_dollar;
817 	PrintfArgType argtypes[PG_NL_ARGMAX + 1];
818 
819 	/* Initialize to "no dollar arguments known" */
820 	last_dollar = 0;
821 	MemSet(argtypes, 0, sizeof(argtypes));
822 
823 	/*
824 	 * This loop must accept the same format strings as the one in dopr().
825 	 * However, we don't need to analyze them to the same level of detail.
826 	 *
827 	 * Since we're only called if there's a dollar-type spec somewhere, we can
828 	 * fail immediately if we find a non-dollar spec.  Per the C99 standard,
829 	 * all argument references in the format string must be one or the other.
830 	 */
831 	while (*format != '\0')
832 	{
833 		/* Locate next conversion specifier */
834 		if (*format != '%')
835 		{
836 			/* Unlike dopr, we can just quit if there's no more specifiers */
837 			format = strchr(format + 1, '%');
838 			if (format == NULL)
839 				break;
840 		}
841 
842 		/* Process conversion spec starting at *format */
843 		format++;
844 		longflag = longlongflag = 0;
845 		fmtpos = accum = 0;
846 		afterstar = false;
847 nextch1:
848 		ch = *format++;
849 		switch (ch)
850 		{
851 			case '-':
852 			case '+':
853 				goto nextch1;
854 			case '0':
855 			case '1':
856 			case '2':
857 			case '3':
858 			case '4':
859 			case '5':
860 			case '6':
861 			case '7':
862 			case '8':
863 			case '9':
864 				accum = accum * 10 + (ch - '0');
865 				goto nextch1;
866 			case '.':
867 				accum = 0;
868 				goto nextch1;
869 			case '*':
870 				if (afterstar)
871 					return false;	/* previous star missing dollar */
872 				afterstar = true;
873 				accum = 0;
874 				goto nextch1;
875 			case '$':
876 				if (accum <= 0 || accum > PG_NL_ARGMAX)
877 					return false;
878 				if (afterstar)
879 				{
880 					if (argtypes[accum] &&
881 						argtypes[accum] != ATYPE_INT)
882 						return false;
883 					argtypes[accum] = ATYPE_INT;
884 					last_dollar = Max(last_dollar, accum);
885 					afterstar = false;
886 				}
887 				else
888 					fmtpos = accum;
889 				accum = 0;
890 				goto nextch1;
891 			case 'l':
892 				if (longflag)
893 					longlongflag = 1;
894 				else
895 					longflag = 1;
896 				goto nextch1;
897 			case 'z':
898 #if SIZEOF_SIZE_T == 8
899 #ifdef HAVE_LONG_INT_64
900 				longflag = 1;
901 #elif defined(HAVE_LONG_LONG_INT_64)
902 				longlongflag = 1;
903 #else
904 #error "Don't know how to print 64bit integers"
905 #endif
906 #else
907 				/* assume size_t is same size as int */
908 #endif
909 				goto nextch1;
910 			case 'h':
911 			case '\'':
912 				/* ignore these */
913 				goto nextch1;
914 			case 'd':
915 			case 'i':
916 			case 'o':
917 			case 'u':
918 			case 'x':
919 			case 'X':
920 				if (fmtpos)
921 				{
922 					PrintfArgType atype;
923 
924 					if (longlongflag)
925 						atype = ATYPE_LONGLONG;
926 					else if (longflag)
927 						atype = ATYPE_LONG;
928 					else
929 						atype = ATYPE_INT;
930 					if (argtypes[fmtpos] &&
931 						argtypes[fmtpos] != atype)
932 						return false;
933 					argtypes[fmtpos] = atype;
934 					last_dollar = Max(last_dollar, fmtpos);
935 				}
936 				else
937 					return false;	/* non-dollar conversion spec */
938 				break;
939 			case 'c':
940 				if (fmtpos)
941 				{
942 					if (argtypes[fmtpos] &&
943 						argtypes[fmtpos] != ATYPE_INT)
944 						return false;
945 					argtypes[fmtpos] = ATYPE_INT;
946 					last_dollar = Max(last_dollar, fmtpos);
947 				}
948 				else
949 					return false;	/* non-dollar conversion spec */
950 				break;
951 			case 's':
952 			case 'p':
953 				if (fmtpos)
954 				{
955 					if (argtypes[fmtpos] &&
956 						argtypes[fmtpos] != ATYPE_CHARPTR)
957 						return false;
958 					argtypes[fmtpos] = ATYPE_CHARPTR;
959 					last_dollar = Max(last_dollar, fmtpos);
960 				}
961 				else
962 					return false;	/* non-dollar conversion spec */
963 				break;
964 			case 'e':
965 			case 'E':
966 			case 'f':
967 			case 'g':
968 			case 'G':
969 				if (fmtpos)
970 				{
971 					if (argtypes[fmtpos] &&
972 						argtypes[fmtpos] != ATYPE_DOUBLE)
973 						return false;
974 					argtypes[fmtpos] = ATYPE_DOUBLE;
975 					last_dollar = Max(last_dollar, fmtpos);
976 				}
977 				else
978 					return false;	/* non-dollar conversion spec */
979 				break;
980 			case 'm':
981 			case '%':
982 				break;
983 			default:
984 				return false;	/* bogus format string */
985 		}
986 
987 		/*
988 		 * If we finish the spec with afterstar still set, there's a
989 		 * non-dollar star in there.
990 		 */
991 		if (afterstar)
992 			return false;		/* non-dollar conversion spec */
993 	}
994 
995 	/*
996 	 * Format appears valid so far, so collect the arguments in physical
997 	 * order.  (Since we rejected any non-dollar specs that would have
998 	 * collected arguments, we know that dopr() hasn't collected any yet.)
999 	 */
1000 	for (i = 1; i <= last_dollar; i++)
1001 	{
1002 		switch (argtypes[i])
1003 		{
1004 			case ATYPE_NONE:
1005 				return false;
1006 			case ATYPE_INT:
1007 				argvalues[i].i = va_arg(args, int);
1008 				break;
1009 			case ATYPE_LONG:
1010 				argvalues[i].l = va_arg(args, long);
1011 				break;
1012 			case ATYPE_LONGLONG:
1013 				argvalues[i].ll = va_arg(args, long long);
1014 				break;
1015 			case ATYPE_DOUBLE:
1016 				argvalues[i].d = va_arg(args, double);
1017 				break;
1018 			case ATYPE_CHARPTR:
1019 				argvalues[i].cptr = va_arg(args, char *);
1020 				break;
1021 		}
1022 	}
1023 
1024 	return true;
1025 }
1026 
1027 static void
fmtstr(const char * value,int leftjust,int minlen,int maxwidth,int pointflag,PrintfTarget * target)1028 fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
1029 	   int pointflag, PrintfTarget *target)
1030 {
1031 	int			padlen,
1032 				vallen;			/* amount to pad */
1033 
1034 	/*
1035 	 * If a maxwidth (precision) is specified, we must not fetch more bytes
1036 	 * than that.
1037 	 */
1038 	if (pointflag)
1039 		vallen = strnlen(value, maxwidth);
1040 	else
1041 		vallen = strlen(value);
1042 
1043 	padlen = compute_padlen(minlen, vallen, leftjust);
1044 
1045 	if (padlen > 0)
1046 	{
1047 		dopr_outchmulti(' ', padlen, target);
1048 		padlen = 0;
1049 	}
1050 
1051 	dostr(value, vallen, target);
1052 
1053 	trailing_pad(padlen, target);
1054 }
1055 
1056 static void
fmtptr(void * value,PrintfTarget * target)1057 fmtptr(void *value, PrintfTarget *target)
1058 {
1059 	int			vallen;
1060 	char		convert[64];
1061 
1062 	/* we rely on regular C library's sprintf to do the basic conversion */
1063 	vallen = sprintf(convert, "%p", value);
1064 	if (vallen < 0)
1065 		target->failed = true;
1066 	else
1067 		dostr(convert, vallen, target);
1068 }
1069 
1070 static void
fmtint(long long value,char type,int forcesign,int leftjust,int minlen,int zpad,int precision,int pointflag,PrintfTarget * target)1071 fmtint(long long value, char type, int forcesign, int leftjust,
1072 	   int minlen, int zpad, int precision, int pointflag,
1073 	   PrintfTarget *target)
1074 {
1075 	unsigned long long base;
1076 	unsigned long long uvalue;
1077 	int			dosign;
1078 	const char *cvt = "0123456789abcdef";
1079 	int			signvalue = 0;
1080 	char		convert[64];
1081 	int			vallen = 0;
1082 	int			padlen;			/* amount to pad */
1083 	int			zeropad;		/* extra leading zeroes */
1084 
1085 	switch (type)
1086 	{
1087 		case 'd':
1088 		case 'i':
1089 			base = 10;
1090 			dosign = 1;
1091 			break;
1092 		case 'o':
1093 			base = 8;
1094 			dosign = 0;
1095 			break;
1096 		case 'u':
1097 			base = 10;
1098 			dosign = 0;
1099 			break;
1100 		case 'x':
1101 			base = 16;
1102 			dosign = 0;
1103 			break;
1104 		case 'X':
1105 			cvt = "0123456789ABCDEF";
1106 			base = 16;
1107 			dosign = 0;
1108 			break;
1109 		default:
1110 			return;				/* keep compiler quiet */
1111 	}
1112 
1113 	/* disable MSVC warning about applying unary minus to an unsigned value */
1114 #ifdef _MSC_VER
1115 #pragma warning(push)
1116 #pragma warning(disable: 4146)
1117 #endif
1118 	/* Handle +/- */
1119 	if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
1120 		uvalue = -(unsigned long long) value;
1121 	else
1122 		uvalue = (unsigned long long) value;
1123 #ifdef _MSC_VER
1124 #pragma warning(pop)
1125 #endif
1126 
1127 	/*
1128 	 * SUS: the result of converting 0 with an explicit precision of 0 is no
1129 	 * characters
1130 	 */
1131 	if (value == 0 && pointflag && precision == 0)
1132 		vallen = 0;
1133 	else
1134 	{
1135 		/* make integer string */
1136 		do
1137 		{
1138 			convert[sizeof(convert) - (++vallen)] = cvt[uvalue % base];
1139 			uvalue = uvalue / base;
1140 		} while (uvalue);
1141 	}
1142 
1143 	zeropad = Max(0, precision - vallen);
1144 
1145 	padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
1146 
1147 	leading_pad(zpad, signvalue, &padlen, target);
1148 
1149 	if (zeropad > 0)
1150 		dopr_outchmulti('0', zeropad, target);
1151 
1152 	dostr(convert + sizeof(convert) - vallen, vallen, target);
1153 
1154 	trailing_pad(padlen, target);
1155 }
1156 
1157 static void
fmtchar(int value,int leftjust,int minlen,PrintfTarget * target)1158 fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
1159 {
1160 	int			padlen;			/* amount to pad */
1161 
1162 	padlen = compute_padlen(minlen, 1, leftjust);
1163 
1164 	if (padlen > 0)
1165 	{
1166 		dopr_outchmulti(' ', padlen, target);
1167 		padlen = 0;
1168 	}
1169 
1170 	dopr_outch(value, target);
1171 
1172 	trailing_pad(padlen, target);
1173 }
1174 
1175 static void
fmtfloat(double value,char type,int forcesign,int leftjust,int minlen,int zpad,int precision,int pointflag,PrintfTarget * target)1176 fmtfloat(double value, char type, int forcesign, int leftjust,
1177 		 int minlen, int zpad, int precision, int pointflag,
1178 		 PrintfTarget *target)
1179 {
1180 	int			signvalue = 0;
1181 	int			prec;
1182 	int			vallen;
1183 	char		fmt[8];
1184 	char		convert[1024];
1185 	int			zeropadlen = 0; /* amount to pad with zeroes */
1186 	int			padlen;			/* amount to pad with spaces */
1187 
1188 	/*
1189 	 * We rely on the regular C library's sprintf to do the basic conversion,
1190 	 * then handle padding considerations here.
1191 	 *
1192 	 * The dynamic range of "double" is about 1E+-308 for IEEE math, and not
1193 	 * too wildly more than that with other hardware.  In "f" format, sprintf
1194 	 * could therefore generate at most 308 characters to the left of the
1195 	 * decimal point; while we need to allow the precision to get as high as
1196 	 * 308+17 to ensure that we don't truncate significant digits from very
1197 	 * small values.  To handle both these extremes, we use a buffer of 1024
1198 	 * bytes and limit requested precision to 350 digits; this should prevent
1199 	 * buffer overrun even with non-IEEE math.  If the original precision
1200 	 * request was more than 350, separately pad with zeroes.
1201 	 *
1202 	 * We handle infinities and NaNs specially to ensure platform-independent
1203 	 * output.
1204 	 */
1205 	if (precision < 0)			/* cover possible overflow of "accum" */
1206 		precision = 0;
1207 	prec = Min(precision, 350);
1208 
1209 	if (isnan(value))
1210 	{
1211 		strcpy(convert, "NaN");
1212 		vallen = 3;
1213 		/* no zero padding, regardless of precision spec */
1214 	}
1215 	else
1216 	{
1217 		/*
1218 		 * Handle sign (NaNs have no sign, so we don't do this in the case
1219 		 * above).  "value < 0.0" will not be true for IEEE minus zero, so we
1220 		 * detect that by looking for the case where value equals 0.0
1221 		 * according to == but not according to memcmp.
1222 		 */
1223 		static const double dzero = 0.0;
1224 
1225 		if (adjust_sign((value < 0.0 ||
1226 						 (value == 0.0 &&
1227 						  memcmp(&value, &dzero, sizeof(double)) != 0)),
1228 						forcesign, &signvalue))
1229 			value = -value;
1230 
1231 		if (isinf(value))
1232 		{
1233 			strcpy(convert, "Infinity");
1234 			vallen = 8;
1235 			/* no zero padding, regardless of precision spec */
1236 		}
1237 		else if (pointflag)
1238 		{
1239 			zeropadlen = precision - prec;
1240 			fmt[0] = '%';
1241 			fmt[1] = '.';
1242 			fmt[2] = '*';
1243 			fmt[3] = type;
1244 			fmt[4] = '\0';
1245 			vallen = sprintf(convert, fmt, prec, value);
1246 		}
1247 		else
1248 		{
1249 			fmt[0] = '%';
1250 			fmt[1] = type;
1251 			fmt[2] = '\0';
1252 			vallen = sprintf(convert, fmt, value);
1253 		}
1254 		if (vallen < 0)
1255 			goto fail;
1256 
1257 		/*
1258 		 * Windows, alone among our supported platforms, likes to emit
1259 		 * three-digit exponent fields even when two digits would do.  Hack
1260 		 * such results to look like the way everyone else does it.
1261 		 */
1262 #ifdef WIN32
1263 		if (vallen >= 6 &&
1264 			convert[vallen - 5] == 'e' &&
1265 			convert[vallen - 3] == '0')
1266 		{
1267 			convert[vallen - 3] = convert[vallen - 2];
1268 			convert[vallen - 2] = convert[vallen - 1];
1269 			vallen--;
1270 		}
1271 #endif
1272 	}
1273 
1274 	padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
1275 
1276 	leading_pad(zpad, signvalue, &padlen, target);
1277 
1278 	if (zeropadlen > 0)
1279 	{
1280 		/* If 'e' or 'E' format, inject zeroes before the exponent */
1281 		char	   *epos = strrchr(convert, 'e');
1282 
1283 		if (!epos)
1284 			epos = strrchr(convert, 'E');
1285 		if (epos)
1286 		{
1287 			/* pad before exponent */
1288 			dostr(convert, epos - convert, target);
1289 			dopr_outchmulti('0', zeropadlen, target);
1290 			dostr(epos, vallen - (epos - convert), target);
1291 		}
1292 		else
1293 		{
1294 			/* no exponent, pad after the digits */
1295 			dostr(convert, vallen, target);
1296 			dopr_outchmulti('0', zeropadlen, target);
1297 		}
1298 	}
1299 	else
1300 	{
1301 		/* no zero padding, just emit the number as-is */
1302 		dostr(convert, vallen, target);
1303 	}
1304 
1305 	trailing_pad(padlen, target);
1306 	return;
1307 
1308 fail:
1309 	target->failed = true;
1310 }
1311 
1312 /*
1313  * Nonstandard entry point to print a double value efficiently.
1314  *
1315  * This is approximately equivalent to strfromd(), but has an API more
1316  * adapted to what float8out() wants.  The behavior is like snprintf()
1317  * with a format of "%.ng", where n is the specified precision.
1318  * However, the target buffer must be nonempty (i.e. count > 0), and
1319  * the precision is silently bounded to a sane range.
1320  */
1321 int
pg_strfromd(char * str,size_t count,int precision,double value)1322 pg_strfromd(char *str, size_t count, int precision, double value)
1323 {
1324 	PrintfTarget target;
1325 	int			signvalue = 0;
1326 	int			vallen;
1327 	char		fmt[8];
1328 	char		convert[64];
1329 
1330 	/* Set up the target like pg_snprintf, but require nonempty buffer */
1331 	Assert(count > 0);
1332 	target.bufstart = target.bufptr = str;
1333 	target.bufend = str + count - 1;
1334 	target.stream = NULL;
1335 	target.nchars = 0;
1336 	target.failed = false;
1337 
1338 	/*
1339 	 * We bound precision to a reasonable range; the combination of this and
1340 	 * the knowledge that we're using "g" format without padding allows the
1341 	 * convert[] buffer to be reasonably small.
1342 	 */
1343 	if (precision < 1)
1344 		precision = 1;
1345 	else if (precision > 32)
1346 		precision = 32;
1347 
1348 	/*
1349 	 * The rest is just an inlined version of the fmtfloat() logic above,
1350 	 * simplified using the knowledge that no padding is wanted.
1351 	 */
1352 	if (isnan(value))
1353 	{
1354 		strcpy(convert, "NaN");
1355 		vallen = 3;
1356 	}
1357 	else
1358 	{
1359 		static const double dzero = 0.0;
1360 
1361 		if (value < 0.0 ||
1362 			(value == 0.0 &&
1363 			 memcmp(&value, &dzero, sizeof(double)) != 0))
1364 		{
1365 			signvalue = '-';
1366 			value = -value;
1367 		}
1368 
1369 		if (isinf(value))
1370 		{
1371 			strcpy(convert, "Infinity");
1372 			vallen = 8;
1373 		}
1374 		else
1375 		{
1376 			fmt[0] = '%';
1377 			fmt[1] = '.';
1378 			fmt[2] = '*';
1379 			fmt[3] = 'g';
1380 			fmt[4] = '\0';
1381 			vallen = sprintf(convert, fmt, precision, value);
1382 			if (vallen < 0)
1383 			{
1384 				target.failed = true;
1385 				goto fail;
1386 			}
1387 
1388 #ifdef WIN32
1389 			if (vallen >= 6 &&
1390 				convert[vallen - 5] == 'e' &&
1391 				convert[vallen - 3] == '0')
1392 			{
1393 				convert[vallen - 3] = convert[vallen - 2];
1394 				convert[vallen - 2] = convert[vallen - 1];
1395 				vallen--;
1396 			}
1397 #endif
1398 		}
1399 	}
1400 
1401 	if (signvalue)
1402 		dopr_outch(signvalue, &target);
1403 
1404 	dostr(convert, vallen, &target);
1405 
1406 fail:
1407 	*(target.bufptr) = '\0';
1408 	return target.failed ? -1 : (target.bufptr - target.bufstart
1409 								 + target.nchars);
1410 }
1411 
1412 
1413 static void
dostr(const char * str,int slen,PrintfTarget * target)1414 dostr(const char *str, int slen, PrintfTarget *target)
1415 {
1416 	/* fast path for common case of slen == 1 */
1417 	if (slen == 1)
1418 	{
1419 		dopr_outch(*str, target);
1420 		return;
1421 	}
1422 
1423 	while (slen > 0)
1424 	{
1425 		int			avail;
1426 
1427 		if (target->bufend != NULL)
1428 			avail = target->bufend - target->bufptr;
1429 		else
1430 			avail = slen;
1431 		if (avail <= 0)
1432 		{
1433 			/* buffer full, can we dump to stream? */
1434 			if (target->stream == NULL)
1435 			{
1436 				target->nchars += slen; /* no, lose the data */
1437 				return;
1438 			}
1439 			flushbuffer(target);
1440 			continue;
1441 		}
1442 		avail = Min(avail, slen);
1443 		memmove(target->bufptr, str, avail);
1444 		target->bufptr += avail;
1445 		str += avail;
1446 		slen -= avail;
1447 	}
1448 }
1449 
1450 static void
dopr_outch(int c,PrintfTarget * target)1451 dopr_outch(int c, PrintfTarget *target)
1452 {
1453 	if (target->bufend != NULL && target->bufptr >= target->bufend)
1454 	{
1455 		/* buffer full, can we dump to stream? */
1456 		if (target->stream == NULL)
1457 		{
1458 			target->nchars++;	/* no, lose the data */
1459 			return;
1460 		}
1461 		flushbuffer(target);
1462 	}
1463 	*(target->bufptr++) = c;
1464 }
1465 
1466 static void
dopr_outchmulti(int c,int slen,PrintfTarget * target)1467 dopr_outchmulti(int c, int slen, PrintfTarget *target)
1468 {
1469 	/* fast path for common case of slen == 1 */
1470 	if (slen == 1)
1471 	{
1472 		dopr_outch(c, target);
1473 		return;
1474 	}
1475 
1476 	while (slen > 0)
1477 	{
1478 		int			avail;
1479 
1480 		if (target->bufend != NULL)
1481 			avail = target->bufend - target->bufptr;
1482 		else
1483 			avail = slen;
1484 		if (avail <= 0)
1485 		{
1486 			/* buffer full, can we dump to stream? */
1487 			if (target->stream == NULL)
1488 			{
1489 				target->nchars += slen; /* no, lose the data */
1490 				return;
1491 			}
1492 			flushbuffer(target);
1493 			continue;
1494 		}
1495 		avail = Min(avail, slen);
1496 		memset(target->bufptr, c, avail);
1497 		target->bufptr += avail;
1498 		slen -= avail;
1499 	}
1500 }
1501 
1502 
1503 static int
adjust_sign(int is_negative,int forcesign,int * signvalue)1504 adjust_sign(int is_negative, int forcesign, int *signvalue)
1505 {
1506 	if (is_negative)
1507 	{
1508 		*signvalue = '-';
1509 		return true;
1510 	}
1511 	else if (forcesign)
1512 		*signvalue = '+';
1513 	return false;
1514 }
1515 
1516 
1517 static int
compute_padlen(int minlen,int vallen,int leftjust)1518 compute_padlen(int minlen, int vallen, int leftjust)
1519 {
1520 	int			padlen;
1521 
1522 	padlen = minlen - vallen;
1523 	if (padlen < 0)
1524 		padlen = 0;
1525 	if (leftjust)
1526 		padlen = -padlen;
1527 	return padlen;
1528 }
1529 
1530 
1531 static void
leading_pad(int zpad,int signvalue,int * padlen,PrintfTarget * target)1532 leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
1533 {
1534 	int			maxpad;
1535 
1536 	if (*padlen > 0 && zpad)
1537 	{
1538 		if (signvalue)
1539 		{
1540 			dopr_outch(signvalue, target);
1541 			--(*padlen);
1542 			signvalue = 0;
1543 		}
1544 		if (*padlen > 0)
1545 		{
1546 			dopr_outchmulti(zpad, *padlen, target);
1547 			*padlen = 0;
1548 		}
1549 	}
1550 	maxpad = (signvalue != 0);
1551 	if (*padlen > maxpad)
1552 	{
1553 		dopr_outchmulti(' ', *padlen - maxpad, target);
1554 		*padlen = maxpad;
1555 	}
1556 	if (signvalue)
1557 	{
1558 		dopr_outch(signvalue, target);
1559 		if (*padlen > 0)
1560 			--(*padlen);
1561 		else if (*padlen < 0)
1562 			++(*padlen);
1563 	}
1564 }
1565 
1566 
1567 static void
trailing_pad(int padlen,PrintfTarget * target)1568 trailing_pad(int padlen, PrintfTarget *target)
1569 {
1570 	if (padlen < 0)
1571 		dopr_outchmulti(' ', -padlen, target);
1572 }
1573