snprintf.c 16.3 KB
Newer Older
1 2 3
/*
 * Copyright (c) 1983, 1995, 1996 Eric P. Allman
 * Copyright (c) 1988, 1993
4 5 6 7 8 9
 *	The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 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 13
 *	  notice, this list of conditions and the following disclaimer in the
 *	  documentation and/or other materials provided with the distribution.
14
 * 3. All advertising materials mentioning features or use of this software
15
 *	  must display the following acknowledgement:
16 17 18
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
19 20
 *	  may be used to endorse or promote products derived from this software
 *	  without specific prior written permission.
21 22 23 24
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25
 * ARE DISCLAIMED.	IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 27 28 29 30 31 32 33
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
34

35 36 37
#ifndef FRONTEND
#include "postgres.h"
#else
38
#include "postgres_fe.h"
39
#endif
40

41
#ifndef WIN32
42
#include <sys/ioctl.h>
43
#endif
44
#include <sys/param.h>
45

46
/*
47
**	SNPRINTF, VSNPRINT -- counted versions of printf
48 49 50 51 52
**
**	These versions have been grabbed off the net.  They have been
**	cleaned up to compile properly and support for .precision and
**	%lx has been added.
*/
53

54 55 56 57
/**************************************************************
 * Original:
 * Patrick Powell Tue Apr 11 09:48:21 PDT 1995
 * A bombproof version of doprnt (dopr) included.
58
 * Sigh.  This sort of thing is always nasty do deal with.	Note that
59
 * the version here does not include floating point. (now it does ... tgl)
60 61 62 63 64
 *
 * snprintf() is used instead of sprintf() as it does limit checks
 * for string length.  This covers a nasty loophole.
 *
 * The other functions are there to prevent NULL pointers from
65
 * causing nasty effects.
66
 **************************************************************/
67

68
/*static char _id[] = "$PostgreSQL: pgsql/src/port/snprintf.c,v 1.12 2005/03/02 05:22:22 momjian Exp $";*/
69

70
int			snprintf(char *str, size_t count, const char *fmt,...);
71
int			vsnprintf(char *str, size_t count, const char *fmt, va_list args);
72
int			printf(const char *format, ...);
73
static void dopr(char *buffer, const char *format, va_list args, char *end);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
74

Bruce Momjian's avatar
Bruce Momjian committed
75 76 77 78
/*
 *	If vsnprintf() is not before snprintf() in this file, snprintf()
 *	will call the system vsnprintf() on MinGW.
 */
79
int
Bruce Momjian's avatar
Bruce Momjian committed
80
vsnprintf(char *str, size_t count, const char *fmt, va_list args)
81
{
Bruce Momjian's avatar
Bruce Momjian committed
82 83 84 85 86 87 88
	char *end;
	str[0] = '\0';
	end = str + count - 1;
	dopr(str, fmt, args, end);
	if (count > 0)
		end[0] = '\0';
	return strlen(str);
89 90
}

91
int
92
snprintf(char *str, size_t count, const char *fmt,...)
93
{
94
	int			len;
Bruce Momjian's avatar
Bruce Momjian committed
95
	va_list		args;
96

97
	va_start(args, fmt);
Vadim B. Mikheev's avatar
Vadim B. Mikheev committed
98
	len = vsnprintf(str, count, fmt, args);
99
	va_end(args);
100 101 102
	return len;
}

103
int
Bruce Momjian's avatar
Bruce Momjian committed
104
printf(const char *fmt,...)
105
{
Bruce Momjian's avatar
Bruce Momjian committed
106 107 108 109 110 111 112 113 114 115 116 117
	int			len;
	va_list			args;
	char*		buffer[4096];
	char*			p;

	va_start(args, fmt);
	len = vsnprintf((char*)buffer, (size_t)4096, fmt, args);
	va_end(args);
	p = (char*)buffer;
	for(;*p;p++)
		putchar(*p);
	return len;
118
}
119

120 121 122 123
/*
 * dopr(): poor man's version of doprintf
 */

124 125 126 127 128 129 130 131
static void fmtstr(char *value, int ljust, int len, int zpad, int maxwidth,
				   char *end, char **output);
static void fmtnum(int64 value, int base, int dosign, int ljust, int len,
				   int zpad, char *end, char **output);
static void fmtfloat(double value, char type, int ljust, int len,
					 int precision, int pointflag, char *end, char **output);
static void dostr(char *str, int cut, char *end, char **output);
static void dopr_outch(int c, char *end, char **output);
132

133 134 135 136
#define	FMTSTR		1
#define	FMTNUM		2
#define	FMTFLOAT	3
#define	FMTCHAR		4
137 138

static void
139
dopr(char *buffer, const char *format, va_list args, char *end)
140
{
141
	int			ch;
142
	int64		value;
143
	double		fvalue;
144
	int			longlongflag = 0;
145 146 147 148 149 150 151
	int			longflag = 0;
	int			pointflag = 0;
	int			maxwidth = 0;
	char	   *strvalue;
	int			ljust;
	int			len;
	int			zpad;
152 153 154 155 156 157
	int			i;
	const char*		format_save;
	const char*		fmtbegin;
	int			fmtpos = 1;
	int			realpos = 0;
	int			position;
158
	char		*output;
159 160 161
	int			percents = 1;
	const char *p;
	struct fmtpar {
162 163 164
		const char*	fmtbegin;
		const char*	fmtend;
		void*	value;
165
		int64	numvalue;
166 167 168 169 170 171 172 173 174 175 176 177 178
		double	fvalue;
		int	charvalue;
		int	ljust;
		int	len;
		int	zpad;
		int	maxwidth;
		int	base;
		int	dosign;
		char	type;
		int	precision;
		int	pointflag;
		char	func;
		int	realpos;
179
	} *fmtpar, **fmtparptr;
180

181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
	/* Create enough structures to hold all arguments */
	for (p = format; *p != '\0'; p++)
		if (*p == '%')	/* counts %% as two, so overcounts */
			percents++;
#ifndef FRONTEND
	fmtpar = pgport_palloc(sizeof(struct fmtpar) * percents);
	fmtparptr = pgport_palloc(sizeof(struct fmtpar *) * percents);
#else
	if ((fmtpar = malloc(sizeof(struct fmtpar) * percents)) == NULL)
	{
		fprintf(stderr, _("out of memory\n"));
		exit(1);
	}
	if ((fmtparptr = malloc(sizeof(struct fmtpar *) * percents)) == NULL)
	{
		fprintf(stderr, _("out of memory\n"));
		exit(1);
	}
#endif
			
201
	format_save = format;
202

203 204 205 206 207 208 209
	output = buffer;
	while ((ch = *format++))
	{
		switch (ch)
		{
			case '%':
				ljust = len = zpad = maxwidth = 0;
210
				longflag = longlongflag = pointflag = 0;
211 212 213
				fmtbegin = format - 1;
				realpos = 0;
				position = 0;
214 215 216 217 218
		nextch:
				ch = *format++;
				switch (ch)
				{
					case 0:
219
						goto performpr;
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
					case '-':
						ljust = 1;
						goto nextch;
					case '0':	/* set zero padding if len not set */
						if (len == 0 && !pointflag)
							zpad = '0';
					case '1':
					case '2':
					case '3':
					case '4':
					case '5':
					case '6':
					case '7':
					case '8':
					case '9':
						if (pointflag)
							maxwidth = maxwidth * 10 + ch - '0';
						else
238
						{ 
239
							len = len * 10 + ch - '0';
240 241 242 243 244 245
							position = position * 10 + ch - '0';
						}
						goto nextch;
					case '$':
						realpos = position;
						len = 0;
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
						goto nextch;
					case '*':
						if (pointflag)
							maxwidth = va_arg(args, int);
						else
							len = va_arg(args, int);
						goto nextch;
					case '.':
						pointflag = 1;
						goto nextch;
					case 'l':
						if (longflag)
							longlongflag = 1;
						else
							longflag = 1;
261
						goto nextch;
262 263
					case 'u':
					case 'U':
264
						/* fmtnum(value,base,dosign,ljust,len,zpad,&output) */
265
						if (longflag)
266 267
						{
							if (longlongflag)
268
								value = va_arg(args, uint64);
269
							else
270
								value = va_arg(args, unsigned long);
271
						}
272
						else
273
							value = va_arg(args, unsigned int);
274 275 276 277 278 279 280 281 282 283 284
						fmtpar[fmtpos].fmtbegin = fmtbegin;
						fmtpar[fmtpos].fmtend = format;
						fmtpar[fmtpos].numvalue = value;
						fmtpar[fmtpos].base = 10;
						fmtpar[fmtpos].dosign = 0;
						fmtpar[fmtpos].ljust = ljust;
						fmtpar[fmtpos].len = len;
						fmtpar[fmtpos].zpad = zpad;
						fmtpar[fmtpos].func = FMTNUM;
						fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
						fmtpos++;
285 286 287
						break;
					case 'o':
					case 'O':
288
						/* fmtnum(value,base,dosign,ljust,len,zpad,&output) */
289
						if (longflag)
290 291
						{
							if (longlongflag)
292
								value = va_arg(args, uint64);
293
							else
294
								value = va_arg(args, unsigned long);
295
						}
296
						else
297
							value = va_arg(args, unsigned int);
298 299 300 301 302 303 304 305 306 307 308
						fmtpar[fmtpos].fmtbegin = fmtbegin;
						fmtpar[fmtpos].fmtend = format;
						fmtpar[fmtpos].numvalue = value;
						fmtpar[fmtpos].base = 8;
						fmtpar[fmtpos].dosign = 0;
						fmtpar[fmtpos].ljust = ljust;
						fmtpar[fmtpos].len = len;
						fmtpar[fmtpos].zpad = zpad;
						fmtpar[fmtpos].func = FMTNUM;
						fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
						fmtpos++;
309 310 311 312 313 314
						break;
					case 'd':
					case 'D':
						if (longflag)
						{
							if (longlongflag)
315
							{
316
								value = va_arg(args, int64);
317
							}
318 319 320 321 322
							else
								value = va_arg(args, long);
						}
						else
							value = va_arg(args, int);
323 324 325 326 327 328 329 330 331 332 333
						fmtpar[fmtpos].fmtbegin = fmtbegin;
						fmtpar[fmtpos].fmtend = format;
						fmtpar[fmtpos].numvalue = value;
						fmtpar[fmtpos].base = 10;
						fmtpar[fmtpos].dosign = 1;
						fmtpar[fmtpos].ljust = ljust;
						fmtpar[fmtpos].len = len;
						fmtpar[fmtpos].zpad = zpad;
						fmtpar[fmtpos].func = FMTNUM;
						fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
						fmtpos++;
334 335 336
						break;
					case 'x':
						if (longflag)
337 338
						{
							if (longlongflag)
339
								value = va_arg(args, uint64);
340
							else
341
								value = va_arg(args, unsigned long);
342
						}
343
						else
344
							value = va_arg(args, unsigned int);
345 346 347 348 349 350 351 352 353 354 355
						fmtpar[fmtpos].fmtbegin = fmtbegin;
						fmtpar[fmtpos].fmtend = format;
						fmtpar[fmtpos].numvalue = value;
						fmtpar[fmtpos].base = 16;
						fmtpar[fmtpos].dosign = 0;
						fmtpar[fmtpos].ljust = ljust;
						fmtpar[fmtpos].len = len;
						fmtpar[fmtpos].zpad = zpad;
						fmtpar[fmtpos].func = FMTNUM;
						fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
						fmtpos++;
356 357 358
						break;
					case 'X':
						if (longflag)
359 360
						{
							if (longlongflag)
361
								value = va_arg(args, uint64);
362
							else
363
								value = va_arg(args, unsigned long);
364
						}
365
						else
366
							value = va_arg(args, unsigned int);
367 368 369 370 371 372 373 374 375 376 377
						fmtpar[fmtpos].fmtbegin = fmtbegin;
						fmtpar[fmtpos].fmtend = format;
						fmtpar[fmtpos].numvalue = value;
						fmtpar[fmtpos].base = -16;
						fmtpar[fmtpos].dosign = 1;
						fmtpar[fmtpos].ljust = ljust;
						fmtpar[fmtpos].len = len;
						fmtpar[fmtpos].zpad = zpad;
						fmtpar[fmtpos].func = FMTNUM;
						fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
						fmtpos++;
378 379 380 381 382 383 384
						break;
					case 's':
						strvalue = va_arg(args, char *);
						if (maxwidth > 0 || !pointflag)
						{
							if (pointflag && len > maxwidth)
								len = maxwidth; /* Adjust padding */
385 386 387 388 389 390 391 392 393 394
							fmtpar[fmtpos].fmtbegin = fmtbegin;
							fmtpar[fmtpos].fmtend = format;
							fmtpar[fmtpos].value = strvalue;
							fmtpar[fmtpos].ljust = ljust;
							fmtpar[fmtpos].len = len;
							fmtpar[fmtpos].zpad = zpad;
							fmtpar[fmtpos].maxwidth = maxwidth;
							fmtpar[fmtpos].func = FMTSTR;
							fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
							fmtpos++;
395 396 397 398
						}
						break;
					case 'c':
						ch = va_arg(args, int);
399 400 401 402 403 404
						fmtpar[fmtpos].fmtbegin = fmtbegin;
						fmtpar[fmtpos].fmtend = format;
						fmtpar[fmtpos].charvalue = ch;
						fmtpar[fmtpos].func = FMTCHAR;
						fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
						fmtpos++;
405
						break;
406 407 408 409 410 411
					case 'e':
					case 'E':
					case 'f':
					case 'g':
					case 'G':
						fvalue = va_arg(args, double);
412 413 414 415 416 417 418 419 420 421 422
						fmtpar[fmtpos].fmtbegin = fmtbegin;
						fmtpar[fmtpos].fmtend = format;
						fmtpar[fmtpos].fvalue = fvalue;
						fmtpar[fmtpos].type = ch;
						fmtpar[fmtpos].ljust = ljust;
						fmtpar[fmtpos].len = len;
						fmtpar[fmtpos].maxwidth = maxwidth;
						fmtpar[fmtpos].pointflag = pointflag;
						fmtpar[fmtpos].func = FMTFLOAT;
						fmtpar[fmtpos].realpos = realpos?realpos:fmtpos;
						fmtpos++;
423
						break;
424
					case '%':
425
						break;
426
					default:
427
						dostr("???????", 0, end, &output);
428 429 430
				}
				break;
			default:
431
				dopr_outch(ch, end, &output);
432 433 434
				break;
		}
	}
435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455
performpr:
	/* shuffle pointers */
	for(i = 1; i < fmtpos; i++)
		fmtparptr[i] = &fmtpar[fmtpar[i].realpos];
	output = buffer;
	format = format_save;
	while ((ch = *format++))
	{
		for(i = 1; i < fmtpos; i++)
		{
			if(ch == '%' && *format == '%')
			{
				format++;
				continue;
			}
			if(fmtpar[i].fmtbegin == format - 1)
			{
				switch(fmtparptr[i]->func){
				case FMTSTR:
					fmtstr(fmtparptr[i]->value, fmtparptr[i]->ljust,
						fmtparptr[i]->len, fmtparptr[i]->zpad,
456
						fmtparptr[i]->maxwidth, end, &output);
457 458 459 460
					break;
				case FMTNUM:
					fmtnum(fmtparptr[i]->numvalue, fmtparptr[i]->base,
						fmtparptr[i]->dosign, fmtparptr[i]->ljust,
461
						fmtparptr[i]->len, fmtparptr[i]->zpad, end, &output);
462 463 464 465
					break;
				case FMTFLOAT:
					fmtfloat(fmtparptr[i]->fvalue, fmtparptr[i]->type,
						fmtparptr[i]->ljust, fmtparptr[i]->len,
466
						fmtparptr[i]->precision, fmtparptr[i]->pointflag,
467
						end, &output);
468 469
					break;
				case FMTCHAR:
470
					dopr_outch(fmtparptr[i]->charvalue, end, &output);
471 472 473 474 475 476
					break;
				}
				format = fmtpar[i].fmtend;
				goto nochar;
			}
		}
477
		dopr_outch(ch, end, &output);
478 479
nochar:
	/* nothing */
480
	; /* semicolon required because a goto has to be attached to a statement */
481
	}
482
	*output = '\0';
483 484 485 486 487 488 489 490

#ifndef FRONTEND
	pgport_pfree(fmtpar);
	pgport_pfree(fmtparptr);
#else
	free(fmtpar);
	free(fmtparptr);
#endif
491 492 493
}

static void
494 495
fmtstr(char *value, int ljust, int len, int zpad, int maxwidth, char *end,
	   char **output)
496
{
497 498
	int			padlen,
				strlen;			/* amount to pad */
499

500 501 502 503 504 505 506 507 508 509 510 511
	if (value == 0)
		value = "<NULL>";
	for (strlen = 0; value[strlen]; ++strlen);	/* strlen */
	if (strlen > maxwidth && maxwidth)
		strlen = maxwidth;
	padlen = len - strlen;
	if (padlen < 0)
		padlen = 0;
	if (ljust)
		padlen = -padlen;
	while (padlen > 0)
	{
512
		dopr_outch(' ', end, output);
513 514
		--padlen;
	}
515
	dostr(value, maxwidth, end, output);
516 517
	while (padlen < 0)
	{
518
		dopr_outch(' ', end, output);
519 520
		++padlen;
	}
521 522 523
}

static void
524 525
fmtnum(int64 value, int base, int dosign, int ljust, int len, int zpad,
	   char *end, char **output)
526
{
527
	int			signvalue = 0;
528
	uint64		uvalue;
529
	char		convert[64];
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
	int			place = 0;
	int			padlen = 0;		/* amount to pad */
	int			caps = 0;

	/*
	 * DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad
	 * %d\n", value, base, dosign, ljust, len, zpad ));
	 */
	uvalue = value;
	if (dosign)
	{
		if (value < 0)
		{
			signvalue = '-';
			uvalue = -value;
		}
	}
	if (base < 0)
	{
		caps = 1;
		base = -base;
	}
	do
	{
554
		convert[place++] = (caps ? "0123456789ABCDEF" : "0123456789abcdef")
555 556 557 558
			[uvalue % (unsigned) base];
		uvalue = (uvalue / (unsigned) base);
	} while (uvalue);
	convert[place] = 0;
559 560 561 562 563 564 565

	if (len < 0)
	{
		/* this could happen with a "*" width spec */
		ljust = 1;
		len = -len;
	}
566 567 568 569 570
	padlen = len - place;
	if (padlen < 0)
		padlen = 0;
	if (ljust)
		padlen = -padlen;
571

572 573 574 575 576 577 578 579
	/*
	 * DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n",
	 * convert,place,signvalue,padlen));
	 */
	if (zpad && padlen > 0)
	{
		if (signvalue)
		{
580
			dopr_outch(signvalue, end, output);
581 582 583 584 585
			--padlen;
			signvalue = 0;
		}
		while (padlen > 0)
		{
586
			dopr_outch(zpad, end, output);
587 588 589 590 591
			--padlen;
		}
	}
	while (padlen > 0)
	{
592
		dopr_outch(' ', end, output);
593 594 595
		--padlen;
	}
	if (signvalue)
596
		dopr_outch(signvalue, end, output);
597
	while (place > 0)
598
		dopr_outch(convert[--place], end, output);
599 600
	while (padlen < 0)
	{
601
		dopr_outch(' ', end, output);
602 603
		++padlen;
	}
604 605 606
}

static void
607 608
fmtfloat(double value, char type, int ljust, int len, int precision,
		 int pointflag, char *end, char **output)
609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
{
	char		fmt[32];
	char		convert[512];
	int			padlen = 0;		/* amount to pad */

	/* we rely on regular C library's sprintf to do the basic conversion */
	if (pointflag)
		sprintf(fmt, "%%.%d%c", precision, type);
	else
		sprintf(fmt, "%%%c", type);
	sprintf(convert, fmt, value);

	if (len < 0)
	{
		/* this could happen with a "*" width spec */
		ljust = 1;
		len = -len;
	}
	padlen = len - strlen(convert);
	if (padlen < 0)
		padlen = 0;
	if (ljust)
		padlen = -padlen;

	while (padlen > 0)
	{
635
		dopr_outch(' ', end, output);
636 637
		--padlen;
	}
638
	dostr(convert, 0, end, output);
639 640
	while (padlen < 0)
	{
641
		dopr_outch(' ', end, output);
642 643 644 645 646
		++padlen;
	}
}

static void
647
dostr(char *str, int cut, char *end, char **output)
648
{
649 650 651
	if (cut)
	{
		while (*str && cut-- > 0)
652
			dopr_outch(*str++, end, output);
653 654 655 656
	}
	else
	{
		while (*str)
657
			dopr_outch(*str++, end, output);
658
	}
659 660 661
}

static void
662
dopr_outch(int c, char *end, char **output)
663
{
664
#ifdef NOT_USED
665
	if (iscntrl((unsigned char) c) && c != '\n' && c != '\t')
666 667
	{
		c = '@' + (c & 0x1F);
668 669
		if (end == 0 || *output < end)
			*(*output)++ = '^';
670
	}
671
#endif
672 673
	if (end == 0 || *output < end)
		*(*output)++ = c;
674
}