xref: /onnv-gate/usr/src/cmd/csh/printf.c (revision 356:44e9075e1c86)
10Sstevel@tonic-gate /*
2*356Smuffin  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
30Sstevel@tonic-gate  * Use is subject to license terms.
40Sstevel@tonic-gate  */
50Sstevel@tonic-gate 
60Sstevel@tonic-gate /*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T	*/
70Sstevel@tonic-gate /*	  All Rights Reserved  	*/
80Sstevel@tonic-gate 
90Sstevel@tonic-gate /*
100Sstevel@tonic-gate  * Copyright (c) 1980 Regents of the University of California.
110Sstevel@tonic-gate  * All rights reserved. The Berkeley Software License Agreement
120Sstevel@tonic-gate  * specifies the terms and conditions for redistribution.
130Sstevel@tonic-gate  */
140Sstevel@tonic-gate 
150Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
160Sstevel@tonic-gate 
170Sstevel@tonic-gate /*
180Sstevel@tonic-gate  * Hacked "printf" which prints through putbyte and Putchar.
190Sstevel@tonic-gate  * putbyte() is used to send a pure byte, which might be a part
200Sstevel@tonic-gate  * of a mutlibyte character, mainly for %s.  A control character
210Sstevel@tonic-gate  * for putbyte() may be QUOTE'd meaning not to convert it to ^x
220Sstevel@tonic-gate  * sequence.  In all other cases Putchar() is used to send a character
230Sstevel@tonic-gate  * in tchar (== wchar_t + * optional QUOE.)
240Sstevel@tonic-gate  * DONT USE WITH STDIO!
250Sstevel@tonic-gate  * This printf has been hacked again so that it understands tchar string
260Sstevel@tonic-gate  * when the format specifier %t is used.  Also %c has been expanded
270Sstevel@tonic-gate  * to take a tchar character as well as normal int.
280Sstevel@tonic-gate  * %t is supported in its simplest form; no width or precision will
290Sstevel@tonic-gate  * be understood.
300Sstevel@tonic-gate  * Assumption here is that sizeof(tchar)<=sizeof(int) so that tchar is
310Sstevel@tonic-gate  * passed as int.  Otherwise, %T must be specified instead of %c to
320Sstevel@tonic-gate  * print a character in tchar.
330Sstevel@tonic-gate  */
340Sstevel@tonic-gate 
350Sstevel@tonic-gate #include <stdarg.h>
360Sstevel@tonic-gate #include <values.h>
370Sstevel@tonic-gate #include "sh.h" /* For tchar. */
380Sstevel@tonic-gate 
390Sstevel@tonic-gate #define	HIBITLL		(1ULL << 63)
400Sstevel@tonic-gate 
410Sstevel@tonic-gate void _print(char *format, va_list *args);
420Sstevel@tonic-gate 
430Sstevel@tonic-gate static char *p;
440Sstevel@tonic-gate 
450Sstevel@tonic-gate int
printf(const char * format,...)460Sstevel@tonic-gate printf(const char *format, ...)
470Sstevel@tonic-gate {
480Sstevel@tonic-gate 	va_list stupid;
490Sstevel@tonic-gate 
500Sstevel@tonic-gate 	p = (char *)gettext(format);
510Sstevel@tonic-gate 	va_start(stupid, format);
520Sstevel@tonic-gate 	_print(p, &stupid);
530Sstevel@tonic-gate 	va_end(stupid);
54*356Smuffin 
55*356Smuffin 	return (0);
560Sstevel@tonic-gate }
570Sstevel@tonic-gate 
580Sstevel@tonic-gate /*
590Sstevel@tonic-gate  *	Floating-point code is included or not, depending
600Sstevel@tonic-gate  *	on whether the preprocessor variable FLOAT is 1 or 0.
610Sstevel@tonic-gate  */
620Sstevel@tonic-gate 
630Sstevel@tonic-gate /* Maximum number of digits in any integer (long) representation */
640Sstevel@tonic-gate #define	MAXDIGS	20
650Sstevel@tonic-gate 
660Sstevel@tonic-gate /* Convert a digit character to the corresponding number */
670Sstevel@tonic-gate #define	tonumber(x)	((x) - '0')
680Sstevel@tonic-gate 
690Sstevel@tonic-gate /* Convert a number between 0 and 9 to the corresponding digit */
700Sstevel@tonic-gate #define	todigit(x)	((x) + '0')
710Sstevel@tonic-gate 
720Sstevel@tonic-gate /* Maximum total number of digits in E format */
730Sstevel@tonic-gate #define	MAXECVT	17
740Sstevel@tonic-gate 
750Sstevel@tonic-gate /* Maximum number of digits after decimal point in F format */
760Sstevel@tonic-gate #define	MAXFCVT	60
770Sstevel@tonic-gate 
780Sstevel@tonic-gate /* Maximum significant figures in a floating-point number */
790Sstevel@tonic-gate #define	MAXFSIG	17
800Sstevel@tonic-gate 
810Sstevel@tonic-gate /* Maximum number of characters in an exponent */
820Sstevel@tonic-gate #define	MAXESIZ	4
830Sstevel@tonic-gate 
840Sstevel@tonic-gate /* Maximum (positive) exponent or greater */
850Sstevel@tonic-gate #define	MAXEXP	40
860Sstevel@tonic-gate 
870Sstevel@tonic-gate 
880Sstevel@tonic-gate 
890Sstevel@tonic-gate #define	max(a, b) ((a) > (b) ? (a) : (b))
900Sstevel@tonic-gate #define	min(a, b) ((a) < (b) ? (a) : (b))
910Sstevel@tonic-gate 
920Sstevel@tonic-gate /* If this symbol is nonzero, allow '0' as a flag */
930Sstevel@tonic-gate #define	FZERO 1
940Sstevel@tonic-gate 
950Sstevel@tonic-gate #if FLOAT
960Sstevel@tonic-gate /*
970Sstevel@tonic-gate  *	System-supplied routines for floating conversion
980Sstevel@tonic-gate  */
990Sstevel@tonic-gate char *fcvt();
1000Sstevel@tonic-gate char *ecvt();
1010Sstevel@tonic-gate #endif
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate void
_print(char * format,va_list * args)1040Sstevel@tonic-gate _print(char *format, va_list *args)
1050Sstevel@tonic-gate {
1060Sstevel@tonic-gate 	/* Current position in format */
1070Sstevel@tonic-gate 	char *cp;
1080Sstevel@tonic-gate 
1090Sstevel@tonic-gate 	/* Starting and ending points for value to be printed */
1100Sstevel@tonic-gate 	char *bp, *p;
1110Sstevel@tonic-gate 	tchar *tbp, *tep;	/* For "%t". */
1120Sstevel@tonic-gate 	tchar tcbuf[2];		/* For "%c" or "%T". */
1130Sstevel@tonic-gate 
1140Sstevel@tonic-gate 	/* Field width and precision */
1150Sstevel@tonic-gate 	int width, prec;
1160Sstevel@tonic-gate 
1170Sstevel@tonic-gate 	/* Format code */
1180Sstevel@tonic-gate 	char fcode;
1190Sstevel@tonic-gate 
1200Sstevel@tonic-gate 	/* Number of padding zeroes required on the left */
1210Sstevel@tonic-gate 	int lzero;
1220Sstevel@tonic-gate 
1230Sstevel@tonic-gate 	/* Flags - nonzero if corresponding character appears in format */
1240Sstevel@tonic-gate 	bool length;		/* l */
1250Sstevel@tonic-gate 	bool double_length;	/* ll */
1260Sstevel@tonic-gate 	bool fplus;		/* + */
1270Sstevel@tonic-gate 	bool fminus;		/* - */
1280Sstevel@tonic-gate 	bool fblank;		/* blank */
1290Sstevel@tonic-gate 	bool fsharp;		/* # */
1300Sstevel@tonic-gate #if FZERO
1310Sstevel@tonic-gate 	bool fzero;		/* 0 */
1320Sstevel@tonic-gate #endif
1330Sstevel@tonic-gate 
1340Sstevel@tonic-gate 	/* Pointer to sign, "0x", "0X", or empty */
1350Sstevel@tonic-gate 	char *prefix;
1360Sstevel@tonic-gate #if FLOAT
1370Sstevel@tonic-gate 	/* Exponent or empty */
1380Sstevel@tonic-gate 	char *suffix;
1390Sstevel@tonic-gate 
1400Sstevel@tonic-gate 	/* Buffer to create exponent */
1410Sstevel@tonic-gate 	char expbuf[MAXESIZ + 1];
1420Sstevel@tonic-gate 
1430Sstevel@tonic-gate 	/* Number of padding zeroes required on the right */
1440Sstevel@tonic-gate 	int rzero;
1450Sstevel@tonic-gate 
1460Sstevel@tonic-gate 	/* The value being converted, if real */
1470Sstevel@tonic-gate 	double dval;
1480Sstevel@tonic-gate 
1490Sstevel@tonic-gate 	/* Output values from fcvt and ecvt */
1500Sstevel@tonic-gate 	int decpt, sign;
1510Sstevel@tonic-gate 
1520Sstevel@tonic-gate 	/* Scratch */
1530Sstevel@tonic-gate 	int k;
1540Sstevel@tonic-gate 
1550Sstevel@tonic-gate 	/* Values are developed in this buffer */
1560Sstevel@tonic-gate 	char buf[max(MAXDIGS, max(MAXFCVT + DMAXEXP, MAXECVT) + 1)];
1570Sstevel@tonic-gate #else
1580Sstevel@tonic-gate 	char buf[MAXDIGS];
1590Sstevel@tonic-gate #endif
1600Sstevel@tonic-gate 	/* The value being converted, if integer */
1610Sstevel@tonic-gate 	long long val;
1620Sstevel@tonic-gate 
1630Sstevel@tonic-gate 	/* Set to point to a translate table for digits of whatever radix */
1640Sstevel@tonic-gate 	char *tab;
1650Sstevel@tonic-gate 
1660Sstevel@tonic-gate 	/* Work variables */
1670Sstevel@tonic-gate 	int n, hradix, lowbit;
1680Sstevel@tonic-gate 
1690Sstevel@tonic-gate 	cp = format;
1700Sstevel@tonic-gate 
1710Sstevel@tonic-gate 	/*
1720Sstevel@tonic-gate 	 *	The main loop -- this loop goes through one iteration
1730Sstevel@tonic-gate 	 *	for each ordinary character or format specification.
1740Sstevel@tonic-gate 	 */
1750Sstevel@tonic-gate 	while (*cp)
1760Sstevel@tonic-gate 		if (*cp != '%') {
1770Sstevel@tonic-gate 			/* Ordinary (non-%) character */
1780Sstevel@tonic-gate 			putbyte (*cp++);
1790Sstevel@tonic-gate 		} else {
1800Sstevel@tonic-gate 			/*
1810Sstevel@tonic-gate 			 *	% has been found.
1820Sstevel@tonic-gate 			 *	First, parse the format specification.
1830Sstevel@tonic-gate 			 */
1840Sstevel@tonic-gate 
1850Sstevel@tonic-gate 			/* Scan the <flags> */
1860Sstevel@tonic-gate 			fplus = fminus = fblank = fsharp = 0;
1870Sstevel@tonic-gate #if FZERO
1880Sstevel@tonic-gate 			fzero = 0;
1890Sstevel@tonic-gate #endif
1900Sstevel@tonic-gate scan:
1910Sstevel@tonic-gate 			switch (*++cp) {
1920Sstevel@tonic-gate 			case '+':
1930Sstevel@tonic-gate 				fplus = 1;
1940Sstevel@tonic-gate 				goto scan;
1950Sstevel@tonic-gate 			case '-':
1960Sstevel@tonic-gate 				fminus = 1;
1970Sstevel@tonic-gate 				goto scan;
1980Sstevel@tonic-gate 			case ' ':
1990Sstevel@tonic-gate 				fblank = 1;
2000Sstevel@tonic-gate 				goto scan;
2010Sstevel@tonic-gate 			case '#':
2020Sstevel@tonic-gate 				fsharp = 1;
2030Sstevel@tonic-gate 				goto scan;
2040Sstevel@tonic-gate #if FZERO
2050Sstevel@tonic-gate 			case '0':
2060Sstevel@tonic-gate 				fzero = 1;
2070Sstevel@tonic-gate 				goto scan;
2080Sstevel@tonic-gate #endif
2090Sstevel@tonic-gate 			}
2100Sstevel@tonic-gate 
2110Sstevel@tonic-gate 			/* Scan the field width */
2120Sstevel@tonic-gate 			if (*cp == '*') {
2130Sstevel@tonic-gate 				width = va_arg (*args, int);
2140Sstevel@tonic-gate 				if (width < 0) {
2150Sstevel@tonic-gate 					width = -width;
2160Sstevel@tonic-gate 					fminus = 1;
2170Sstevel@tonic-gate 				}
2180Sstevel@tonic-gate 				cp++;
2190Sstevel@tonic-gate 			} else {
2200Sstevel@tonic-gate 				width = 0;
2210Sstevel@tonic-gate 				while (isdigit(*cp)) {
2220Sstevel@tonic-gate 					n = tonumber(*cp++);
2230Sstevel@tonic-gate 					width = width * 10 + n;
2240Sstevel@tonic-gate 				}
2250Sstevel@tonic-gate 			}
2260Sstevel@tonic-gate 
2270Sstevel@tonic-gate 			/* Scan the precision */
2280Sstevel@tonic-gate 			if (*cp == '.') {
2290Sstevel@tonic-gate 
2300Sstevel@tonic-gate 				/* '*' instead of digits? */
2310Sstevel@tonic-gate 				if (*++cp == '*') {
2320Sstevel@tonic-gate 					prec = va_arg(*args, int);
2330Sstevel@tonic-gate 					cp++;
2340Sstevel@tonic-gate 				} else {
2350Sstevel@tonic-gate 					prec = 0;
2360Sstevel@tonic-gate 					while (isdigit(*cp)) {
2370Sstevel@tonic-gate 						n = tonumber(*cp++);
2380Sstevel@tonic-gate 						prec = prec * 10 + n;
2390Sstevel@tonic-gate 					}
2400Sstevel@tonic-gate 				}
2410Sstevel@tonic-gate 			} else {
2420Sstevel@tonic-gate 				prec = -1;
2430Sstevel@tonic-gate 			}
2440Sstevel@tonic-gate 
2450Sstevel@tonic-gate 			/* Scan the length modifier */
2460Sstevel@tonic-gate 			double_length = length = 0;
2470Sstevel@tonic-gate 			switch (*cp) {
2480Sstevel@tonic-gate 			case 'l':
2490Sstevel@tonic-gate 				if (*(cp + 1) == 'l') {
2500Sstevel@tonic-gate 					cp++;
2510Sstevel@tonic-gate 					double_length = 1;
2520Sstevel@tonic-gate 				} else {
2530Sstevel@tonic-gate 					length = 1;
2540Sstevel@tonic-gate 				}
2550Sstevel@tonic-gate 				/* No break */
2560Sstevel@tonic-gate 			case 'h':
2570Sstevel@tonic-gate 				cp++;
2580Sstevel@tonic-gate 			}
2590Sstevel@tonic-gate 
2600Sstevel@tonic-gate 			/*
2610Sstevel@tonic-gate 			 *	The character addressed by cp must be the
2620Sstevel@tonic-gate 			 *	format letter -- there is nothing left for
2630Sstevel@tonic-gate 			 *	it to be.
2640Sstevel@tonic-gate 			 *
2650Sstevel@tonic-gate 			 *	The status of the +, -, #, blank, and 0
2660Sstevel@tonic-gate 			 *	flags are reflected in the variables
2670Sstevel@tonic-gate 			 *	"fplus", "fminus", "fsharp", "fblank",
2680Sstevel@tonic-gate 			 *	and "fzero", respectively.
2690Sstevel@tonic-gate 			 *	"width" and "prec" contain numbers
2700Sstevel@tonic-gate 			 *	corresponding to the digit strings
2710Sstevel@tonic-gate 			 *	before and after the decimal point,
2720Sstevel@tonic-gate 			 *	respectively. If there was no decimal
2730Sstevel@tonic-gate 			 *	point, "prec" is -1.
2740Sstevel@tonic-gate 			 *
2750Sstevel@tonic-gate 			 *	The following switch sets things up
2760Sstevel@tonic-gate 			 *	for printing.  What ultimately gets
2770Sstevel@tonic-gate 			 *	printed will be padding blanks, a prefix,
2780Sstevel@tonic-gate 			 *	left padding zeroes, a value, right padding
2790Sstevel@tonic-gate 			 *	zeroes, a suffix, and more padding
2800Sstevel@tonic-gate 			 *	blanks.  Padding blanks will not appear
2810Sstevel@tonic-gate 			 *	simultaneously on both the left and the
2820Sstevel@tonic-gate 			 *	right.  Each case in this switch will
2830Sstevel@tonic-gate 			 *	compute the value, and leave in several
2840Sstevel@tonic-gate 			 *	variables the information necessary to
2850Sstevel@tonic-gate 			 *	construct what is to be printed.
2860Sstevel@tonic-gate 			 *
2870Sstevel@tonic-gate 			 *	The prefix is a sign, a blank, "0x", "0X",
2880Sstevel@tonic-gate 			 *	or null, and is addressed by "prefix".
2890Sstevel@tonic-gate 			 *
2900Sstevel@tonic-gate 			 *	The suffix is either null or an exponent,
2910Sstevel@tonic-gate 			 *	and is addressed by "suffix".
2920Sstevel@tonic-gate 			 *
2930Sstevel@tonic-gate 			 *	The value to be printed starts at "bp"
2940Sstevel@tonic-gate 			 *	and continues up to and not including "p".
2950Sstevel@tonic-gate 			 *
2960Sstevel@tonic-gate 			 *	"lzero" and "rzero" will contain the number
2970Sstevel@tonic-gate 			 *	of padding zeroes required on the left
2980Sstevel@tonic-gate 			 *	and right, respectively.  If either of
2990Sstevel@tonic-gate 			 *	these variables is negative, it will be
3000Sstevel@tonic-gate 			 *	treated as if it were zero.
3010Sstevel@tonic-gate 			 *
3020Sstevel@tonic-gate 			 *	The number of padding blanks, and whether
3030Sstevel@tonic-gate 			 *	they go on the left or the right, will be
3040Sstevel@tonic-gate 			 *	computed on exit from the switch.
3050Sstevel@tonic-gate 			 */
3060Sstevel@tonic-gate 
3070Sstevel@tonic-gate 			lzero = 0;
3080Sstevel@tonic-gate 			prefix = "";
3090Sstevel@tonic-gate #if FLOAT
3100Sstevel@tonic-gate 			rzero = lzero;
3110Sstevel@tonic-gate 			suffix = prefix;
3120Sstevel@tonic-gate #endif
3130Sstevel@tonic-gate 			switch (fcode = *cp++) {
3140Sstevel@tonic-gate 
3150Sstevel@tonic-gate 			/*
3160Sstevel@tonic-gate 			 *	fixed point representations
3170Sstevel@tonic-gate 			 *
3180Sstevel@tonic-gate 			 *	"hradix" is half the radix for the conversion.
3190Sstevel@tonic-gate 			 *	Conversion is unsigned unless fcode is 'd'.
3200Sstevel@tonic-gate 			 *	HIBITLL is 1000...000 binary, and is equal to
3210Sstevel@tonic-gate 			 *		the maximum negative number.
3220Sstevel@tonic-gate 			 *	We assume a 2's complement machine
3230Sstevel@tonic-gate 			 */
3240Sstevel@tonic-gate 
3250Sstevel@tonic-gate 			case 'D':
3260Sstevel@tonic-gate 			case 'U':
3270Sstevel@tonic-gate 				length = 1;
3280Sstevel@tonic-gate 			case 'd':
3290Sstevel@tonic-gate 			case 'u':
3300Sstevel@tonic-gate 				hradix = 5;
3310Sstevel@tonic-gate 				goto fixed;
3320Sstevel@tonic-gate 
3330Sstevel@tonic-gate 			case 'O':
3340Sstevel@tonic-gate 				length = 1;
3350Sstevel@tonic-gate 			case 'o':
3360Sstevel@tonic-gate 				hradix = 4;
3370Sstevel@tonic-gate 				goto fixed;
3380Sstevel@tonic-gate 
3390Sstevel@tonic-gate 			case 'X':
3400Sstevel@tonic-gate 			case 'x':
3410Sstevel@tonic-gate 				hradix = 8;
3420Sstevel@tonic-gate 
3430Sstevel@tonic-gate fixed:
3440Sstevel@tonic-gate 				/* Establish default precision */
3450Sstevel@tonic-gate 				if (prec < 0) {
3460Sstevel@tonic-gate 					prec = 1;
3470Sstevel@tonic-gate 				}
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 				/* Fetch the argument to be printed */
3500Sstevel@tonic-gate 				if (double_length) {
3510Sstevel@tonic-gate 					val = va_arg(*args, long long);
3520Sstevel@tonic-gate 				} else if (length) {
3530Sstevel@tonic-gate 					val = va_arg(*args, long);
3540Sstevel@tonic-gate 				} else if (fcode == 'd') {
3550Sstevel@tonic-gate 					val = va_arg(*args, int);
3560Sstevel@tonic-gate 				} else {
3570Sstevel@tonic-gate 					val = va_arg(*args, unsigned);
3580Sstevel@tonic-gate 				}
3590Sstevel@tonic-gate 
3600Sstevel@tonic-gate 				/* If signed conversion, establish sign */
3610Sstevel@tonic-gate 				if (fcode == 'd' || fcode == 'D') {
3620Sstevel@tonic-gate 					if (val < 0) {
3630Sstevel@tonic-gate 						prefix = "-";
3640Sstevel@tonic-gate 						/*
3650Sstevel@tonic-gate 						 *	Negate, checking in
3660Sstevel@tonic-gate 						 *	advance for possible
3670Sstevel@tonic-gate 						 *	overflow.
3680Sstevel@tonic-gate 						 */
3690Sstevel@tonic-gate 						if (val != HIBITLL) {
3700Sstevel@tonic-gate 							val = -val;
3710Sstevel@tonic-gate 						}
3720Sstevel@tonic-gate 					} else if (fplus) {
3730Sstevel@tonic-gate 						prefix = "+";
3740Sstevel@tonic-gate 					} else if (fblank) {
3750Sstevel@tonic-gate 						prefix = " ";
3760Sstevel@tonic-gate 					}
3770Sstevel@tonic-gate 				}
3780Sstevel@tonic-gate #if FZERO
3790Sstevel@tonic-gate 				if (fzero) {
3800Sstevel@tonic-gate 					int n = width - strlen(prefix);
3810Sstevel@tonic-gate 					if (n > prec) {
3820Sstevel@tonic-gate 						prec = n;
3830Sstevel@tonic-gate 					}
3840Sstevel@tonic-gate 				}
3850Sstevel@tonic-gate #endif
3860Sstevel@tonic-gate 				/* Set translate table for digits */
3870Sstevel@tonic-gate 				if (fcode == 'X') {
3880Sstevel@tonic-gate 					tab = "0123456789ABCDEF";
3890Sstevel@tonic-gate 				} else {
3900Sstevel@tonic-gate 					tab = "0123456789abcdef";
3910Sstevel@tonic-gate 				}
3920Sstevel@tonic-gate 
3930Sstevel@tonic-gate 				/* Develop the digits of the value */
3940Sstevel@tonic-gate 				p = bp = buf + MAXDIGS;
3950Sstevel@tonic-gate 				while (val) {
3960Sstevel@tonic-gate 					lowbit = val & 1;
3970Sstevel@tonic-gate 					val = (val >> 1) & ~HIBITLL;
3980Sstevel@tonic-gate 					*--bp = tab[val % hradix * 2 + lowbit];
3990Sstevel@tonic-gate 					val /= hradix;
4000Sstevel@tonic-gate 				}
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 				/* Calculate padding zero requirement */
4030Sstevel@tonic-gate 				lzero = bp - p + prec;
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 				/* Handle the # flag */
4060Sstevel@tonic-gate 				if (fsharp && bp != p) {
4070Sstevel@tonic-gate 					switch (fcode) {
4080Sstevel@tonic-gate 					case 'o':
4090Sstevel@tonic-gate 						if (lzero < 1)
4100Sstevel@tonic-gate 							lzero = 1;
4110Sstevel@tonic-gate 						break;
4120Sstevel@tonic-gate 					case 'x':
4130Sstevel@tonic-gate 						prefix = "0x";
4140Sstevel@tonic-gate 						break;
4150Sstevel@tonic-gate 					case 'X':
4160Sstevel@tonic-gate 						prefix = "0X";
4170Sstevel@tonic-gate 						break;
4180Sstevel@tonic-gate 					}
4190Sstevel@tonic-gate 				}
4200Sstevel@tonic-gate 
4210Sstevel@tonic-gate 				break;
4220Sstevel@tonic-gate #if FLOAT
4230Sstevel@tonic-gate 			case 'E':
4240Sstevel@tonic-gate 			case 'e':
4250Sstevel@tonic-gate 				/*
4260Sstevel@tonic-gate 				 *	E-format.  The general strategy
4270Sstevel@tonic-gate 				 *	here is fairly easy: we take
4280Sstevel@tonic-gate 				 *	what ecvt gives us and re-format it.
4290Sstevel@tonic-gate 				 */
4300Sstevel@tonic-gate 
4310Sstevel@tonic-gate 				/* Establish default precision */
4320Sstevel@tonic-gate 				if (prec < 0) {
4330Sstevel@tonic-gate 					prec = 6;
4340Sstevel@tonic-gate 				}
4350Sstevel@tonic-gate 
4360Sstevel@tonic-gate 				/* Fetch the value */
4370Sstevel@tonic-gate 				dval = va_arg(*args, double);
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 				/* Develop the mantissa */
4400Sstevel@tonic-gate 				bp = ecvt(dval,
4410Sstevel@tonic-gate 				    min(prec + 1, MAXECVT),
4420Sstevel@tonic-gate 				    &decpt,
4430Sstevel@tonic-gate 				    &sign);
4440Sstevel@tonic-gate 
4450Sstevel@tonic-gate 				/* Determine the prefix */
4460Sstevel@tonic-gate e_merge:
4470Sstevel@tonic-gate 				if (sign) {
4480Sstevel@tonic-gate 					prefix = "-";
4490Sstevel@tonic-gate 				} else if (fplus) {
4500Sstevel@tonic-gate 					prefix = "+";
4510Sstevel@tonic-gate 				} else if (fblank) {
4520Sstevel@tonic-gate 					prefix = " ";
4530Sstevel@tonic-gate 				}
4540Sstevel@tonic-gate 
4550Sstevel@tonic-gate 				/* Place the first digit in the buffer */
4560Sstevel@tonic-gate 				p = &buf[0];
4570Sstevel@tonic-gate 				*p++ = *bp != '\0' ? *bp++ : '0';
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 				/* Put in a decimal point if needed */
4600Sstevel@tonic-gate 				if (prec != 0 || fsharp) {
4610Sstevel@tonic-gate 					*p++ = '.';
4620Sstevel@tonic-gate 				}
4630Sstevel@tonic-gate 
4640Sstevel@tonic-gate 				/* Create the rest of the mantissa */
4650Sstevel@tonic-gate 				rzero = prec;
4660Sstevel@tonic-gate 				while (rzero > 0 && *bp != '\0') {
4670Sstevel@tonic-gate 					--rzero;
4680Sstevel@tonic-gate 					*p++ = *bp++;
4690Sstevel@tonic-gate 				}
4700Sstevel@tonic-gate 
4710Sstevel@tonic-gate 				bp = &buf[0];
4720Sstevel@tonic-gate 
4730Sstevel@tonic-gate 				/* Create the exponent */
4740Sstevel@tonic-gate 				suffix = &expbuf[MAXESIZ];
4750Sstevel@tonic-gate 				*suffix = '\0';
4760Sstevel@tonic-gate 				if (dval != 0) {
4770Sstevel@tonic-gate 					n = decpt - 1;
4780Sstevel@tonic-gate 					if (n < 0) {
4790Sstevel@tonic-gate 						n = -n;
4800Sstevel@tonic-gate 					}
4810Sstevel@tonic-gate 					while (n != 0) {
4820Sstevel@tonic-gate 						*--suffix = todigit(n % 10);
4830Sstevel@tonic-gate 						n /= 10;
4840Sstevel@tonic-gate 					}
4850Sstevel@tonic-gate 				}
4860Sstevel@tonic-gate 
4870Sstevel@tonic-gate 				/* Prepend leading zeroes to the exponent */
4880Sstevel@tonic-gate 				while (suffix > &expbuf[MAXESIZ - 2]) {
4890Sstevel@tonic-gate 					*--suffix = '0';
4900Sstevel@tonic-gate 				}
4910Sstevel@tonic-gate 
4920Sstevel@tonic-gate 				/* Put in the exponent sign */
4930Sstevel@tonic-gate 				*--suffix = (decpt > 0 || dval == 0) ?
4940Sstevel@tonic-gate 				    '+' : '-';
4950Sstevel@tonic-gate 
4960Sstevel@tonic-gate 				/* Put in the e */
4970Sstevel@tonic-gate 				*--suffix = isupper(fcode) ? 'E' : 'e';
4980Sstevel@tonic-gate 
4990Sstevel@tonic-gate 				break;
5000Sstevel@tonic-gate 
5010Sstevel@tonic-gate 			case 'f':
5020Sstevel@tonic-gate 				/*
5030Sstevel@tonic-gate 				 *	F-format floating point.  This is
5040Sstevel@tonic-gate 				 *	a good deal less simple than E-format.
5050Sstevel@tonic-gate 				 *	The overall strategy will be to call
5060Sstevel@tonic-gate 				 *	fcvt, reformat its result into buf,
5070Sstevel@tonic-gate 				 *	and calculate how many trailing
5080Sstevel@tonic-gate 				 *	zeroes will be required.  There will
5090Sstevel@tonic-gate 				 *	never be any leading zeroes needed.
5100Sstevel@tonic-gate 				 */
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 				/* Establish default precision */
5130Sstevel@tonic-gate 				if (prec < 0) {
5140Sstevel@tonic-gate 					prec = 6;
5150Sstevel@tonic-gate 				}
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 				/* Fetch the value */
5180Sstevel@tonic-gate 				dval = va_arg(*args, double);
5190Sstevel@tonic-gate 
5200Sstevel@tonic-gate 				/* Do the conversion */
5210Sstevel@tonic-gate 				bp = fcvt(dval,
5220Sstevel@tonic-gate 				    min(prec, MAXFCVT),
5230Sstevel@tonic-gate 				    &decpt,
5240Sstevel@tonic-gate 				    &sign);
5250Sstevel@tonic-gate 
5260Sstevel@tonic-gate 				/* Determine the prefix */
5270Sstevel@tonic-gate f_merge:
5280Sstevel@tonic-gate 				if (sign && decpt > -prec &&
5290Sstevel@tonic-gate 				    *bp != '\0' && *bp != '0') {
5300Sstevel@tonic-gate 					prefix = "-";
5310Sstevel@tonic-gate 				} else if (fplus) {
5320Sstevel@tonic-gate 					prefix = "+";
5330Sstevel@tonic-gate 				} else if (fblank) {
5340Sstevel@tonic-gate 					prefix = " ";
5350Sstevel@tonic-gate 				}
5360Sstevel@tonic-gate 
5370Sstevel@tonic-gate 				/* Initialize buffer pointer */
5380Sstevel@tonic-gate 				p = &buf[0];
5390Sstevel@tonic-gate 
5400Sstevel@tonic-gate 				/* Emit the digits before the decimal point */
5410Sstevel@tonic-gate 				n = decpt;
5420Sstevel@tonic-gate 				k = 0;
5430Sstevel@tonic-gate 				if (n <= 0) {
5440Sstevel@tonic-gate 					*p++ = '0';
5450Sstevel@tonic-gate 				} else {
5460Sstevel@tonic-gate 					do {
5470Sstevel@tonic-gate 						if (*bp == '\0' ||
5480Sstevel@tonic-gate 						    k >= MAXFSIG) {
5490Sstevel@tonic-gate 							*p++ = '0';
5500Sstevel@tonic-gate 						} else {
5510Sstevel@tonic-gate 							*p++ = *bp++;
5520Sstevel@tonic-gate 							++k;
5530Sstevel@tonic-gate 						}
5540Sstevel@tonic-gate 					} while (--n != 0);
5550Sstevel@tonic-gate 				}
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 				/* Decide whether we need a decimal point */
5580Sstevel@tonic-gate 				if (fsharp || prec > 0) {
5590Sstevel@tonic-gate 					*p++ = '.';
5600Sstevel@tonic-gate 				}
5610Sstevel@tonic-gate 
5620Sstevel@tonic-gate 				/* Digits (if any) after the decimal point */
5630Sstevel@tonic-gate 				n = min(prec, MAXFCVT);
5640Sstevel@tonic-gate 				rzero = prec - n;
5650Sstevel@tonic-gate 				while (--n >= 0) {
5660Sstevel@tonic-gate 					if (++decpt <= 0 || *bp == '\0' ||
5670Sstevel@tonic-gate 					    k >= MAXFSIG) {
5680Sstevel@tonic-gate 						*p++ = '0';
5690Sstevel@tonic-gate 					} else {
5700Sstevel@tonic-gate 						*p++ = *bp++;
5710Sstevel@tonic-gate 						++k;
5720Sstevel@tonic-gate 					}
5730Sstevel@tonic-gate 				}
5740Sstevel@tonic-gate 
5750Sstevel@tonic-gate 				bp = &buf[0];
5760Sstevel@tonic-gate 
5770Sstevel@tonic-gate 				break;
5780Sstevel@tonic-gate 
5790Sstevel@tonic-gate 			case 'G':
5800Sstevel@tonic-gate 			case 'g':
5810Sstevel@tonic-gate 				/*
5820Sstevel@tonic-gate 				 *	g-format.  We play around a bit
5830Sstevel@tonic-gate 				 *	and then jump into e or f, as needed.
5840Sstevel@tonic-gate 				 */
5850Sstevel@tonic-gate 
5860Sstevel@tonic-gate 				/* Establish default precision */
5870Sstevel@tonic-gate 				if (prec < 0) {
5880Sstevel@tonic-gate 					prec = 6;
5890Sstevel@tonic-gate 				}
5900Sstevel@tonic-gate 
5910Sstevel@tonic-gate 				/* Fetch the value */
5920Sstevel@tonic-gate 				dval = va_arg(*args, double);
5930Sstevel@tonic-gate 
5940Sstevel@tonic-gate 				/* Do the conversion */
5950Sstevel@tonic-gate 				bp = ecvt(dval,
5960Sstevel@tonic-gate 				    min(prec, MAXECVT),
5970Sstevel@tonic-gate 				    &decpt,
5980Sstevel@tonic-gate 				    &sign);
5990Sstevel@tonic-gate 				if (dval == 0) {
6000Sstevel@tonic-gate 					decpt = 1;
6010Sstevel@tonic-gate 				}
6020Sstevel@tonic-gate 
6030Sstevel@tonic-gate 				k = prec;
6040Sstevel@tonic-gate 				if (!fsharp) {
6050Sstevel@tonic-gate 					n = strlen(bp);
6060Sstevel@tonic-gate 					if (n < k) {
6070Sstevel@tonic-gate 						k = n;
6080Sstevel@tonic-gate 					}
6090Sstevel@tonic-gate 					while (k >= 1 && bp[k-1] == '0') {
6100Sstevel@tonic-gate 						--k;
6110Sstevel@tonic-gate 					}
6120Sstevel@tonic-gate 				}
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 				if (decpt < -3 || decpt > prec) {
6150Sstevel@tonic-gate 					prec = k - 1;
6160Sstevel@tonic-gate 					goto e_merge;
6170Sstevel@tonic-gate 				} else {
6180Sstevel@tonic-gate 					prec = k - decpt;
6190Sstevel@tonic-gate 					goto f_merge;
6200Sstevel@tonic-gate 				}
6210Sstevel@tonic-gate 
6220Sstevel@tonic-gate #endif
6230Sstevel@tonic-gate 			case 'c':
6240Sstevel@tonic-gate #ifdef MBCHAR_1 /* sizeof(int)>=sizeof(tchar) */
6250Sstevel@tonic-gate /*
6260Sstevel@tonic-gate  * A tchar arg is passed as int so we used the normal %c to specify
6270Sstevel@tonic-gate  * such an arugument.
6280Sstevel@tonic-gate  */
6290Sstevel@tonic-gate 				tcbuf[0] = va_arg(*args, int);
6300Sstevel@tonic-gate 				tbp = &tcbuf[0];
6310Sstevel@tonic-gate 				tep = tbp + 1;
6320Sstevel@tonic-gate 				fcode = 't'; /* Fake the rest of code. */
6330Sstevel@tonic-gate 				break;
6340Sstevel@tonic-gate #else
6350Sstevel@tonic-gate /*
6360Sstevel@tonic-gate  * We would have to invent another new format speficier such as "%T" to
6370Sstevel@tonic-gate  * take a tchar arg.  Let's worry about when that time comes.
6380Sstevel@tonic-gate  */
6390Sstevel@tonic-gate 				/*
6400Sstevel@tonic-gate 				 * Following code take care of a char arg
6410Sstevel@tonic-gate 				 * only.
6420Sstevel@tonic-gate 				 */
6430Sstevel@tonic-gate 				buf[0] = va_arg(*args, int);
6440Sstevel@tonic-gate 				bp = &buf[0];
6450Sstevel@tonic-gate 				p = bp + 1;
6460Sstevel@tonic-gate 				break;
6470Sstevel@tonic-gate 			case 'T': /* Corresponding arg is tchar. */
6480Sstevel@tonic-gate 				tcbuf[0] = va_arg(*args, tchar);
6490Sstevel@tonic-gate 				tbp = &tcbuf[0];
6500Sstevel@tonic-gate 				tep = tbp + 1;
6510Sstevel@tonic-gate 				fcode = 't'; /* Fake the rest of code. */
6520Sstevel@tonic-gate 				break;
6530Sstevel@tonic-gate #endif
6540Sstevel@tonic-gate 			case 's':
6550Sstevel@tonic-gate 				bp = va_arg(*args, char *);
6560Sstevel@tonic-gate 				if (bp == 0) {
6570Sstevel@tonic-gate nullstr:				bp = "(null)";
6580Sstevel@tonic-gate 					p = bp + strlen("(null)");
6590Sstevel@tonic-gate 					break;
6600Sstevel@tonic-gate 				}
6610Sstevel@tonic-gate 				if (prec < 0) {
6620Sstevel@tonic-gate 					prec = MAXINT;
6630Sstevel@tonic-gate 				}
6640Sstevel@tonic-gate 				for (n = 0; *bp++ != '\0' && n < prec; n++)
6650Sstevel@tonic-gate 					;
6660Sstevel@tonic-gate 				p = --bp;
6670Sstevel@tonic-gate 				bp -= n;
6680Sstevel@tonic-gate 				break;
6690Sstevel@tonic-gate 
6700Sstevel@tonic-gate 			case 't':
6710Sstevel@tonic-gate 				/*
6720Sstevel@tonic-gate 				 * Special format specifier "%t" tells
6730Sstevel@tonic-gate 				 * printf() to print char strings written
6740Sstevel@tonic-gate 				 * as tchar string.
6750Sstevel@tonic-gate 				 */
6760Sstevel@tonic-gate 				tbp = va_arg(*args, tchar *);
6770Sstevel@tonic-gate 				if (tbp == 0) {
6780Sstevel@tonic-gate 					fcode = 's'; /* Act as if it were %s. */
6790Sstevel@tonic-gate 					goto nullstr;
6800Sstevel@tonic-gate 				}
6810Sstevel@tonic-gate 				if (prec < 0) {
6820Sstevel@tonic-gate 					prec = MAXINT;
6830Sstevel@tonic-gate 				}
6840Sstevel@tonic-gate 				for (n = 0; *tbp++ != 0 && n < prec; n++)
6850Sstevel@tonic-gate 					;
6860Sstevel@tonic-gate 				tep = --tbp;
6870Sstevel@tonic-gate 				tbp -= n;
6880Sstevel@tonic-gate 
6890Sstevel@tonic-gate 				/*
6900Sstevel@tonic-gate 				 * Just to make the following padding
6910Sstevel@tonic-gate 				 * calculation not to go very crazy...
6920Sstevel@tonic-gate 				 */
6930Sstevel@tonic-gate 				bp = NULL;
6940Sstevel@tonic-gate 				p = bp + n;
6950Sstevel@tonic-gate 				break;
6960Sstevel@tonic-gate 
6970Sstevel@tonic-gate 			case '\0':
6980Sstevel@tonic-gate 				cp--;
6990Sstevel@tonic-gate 				break;
7000Sstevel@tonic-gate 
7010Sstevel@tonic-gate 			default:
7020Sstevel@tonic-gate 				p = bp = &fcode;
7030Sstevel@tonic-gate 				p++;
7040Sstevel@tonic-gate 				break;
7050Sstevel@tonic-gate 
7060Sstevel@tonic-gate 			}
7070Sstevel@tonic-gate 			if (fcode != '\0') {
7080Sstevel@tonic-gate 				/* Calculate number of padding blanks */
7090Sstevel@tonic-gate 				int nblank;
7100Sstevel@tonic-gate 				nblank = width
7110Sstevel@tonic-gate #if FLOAT
7120Sstevel@tonic-gate 					- (rzero < 0 ? 0:  rzero)
7130Sstevel@tonic-gate 					- strlen(suffix)
7140Sstevel@tonic-gate #endif
7150Sstevel@tonic-gate 					- (p - bp)
7160Sstevel@tonic-gate 					- (lzero < 0 ? 0 : lzero)
7170Sstevel@tonic-gate 					- strlen(prefix);
7180Sstevel@tonic-gate 
7190Sstevel@tonic-gate 				/* Blanks on left if required */
7200Sstevel@tonic-gate 				if (!fminus) {
7210Sstevel@tonic-gate 					while (--nblank >= 0) {
7220Sstevel@tonic-gate 						Putchar(' ');
7230Sstevel@tonic-gate 					}
7240Sstevel@tonic-gate 				}
7250Sstevel@tonic-gate 
7260Sstevel@tonic-gate 				/* Prefix, if any */
7270Sstevel@tonic-gate 				while (*prefix != '\0') {
7280Sstevel@tonic-gate 					Putchar(*prefix++);
7290Sstevel@tonic-gate 				}
7300Sstevel@tonic-gate 
7310Sstevel@tonic-gate 				/* Zeroes on the left */
7320Sstevel@tonic-gate 				while (--lzero >= 0) {
7330Sstevel@tonic-gate 					Putchar('0');
7340Sstevel@tonic-gate 				}
7350Sstevel@tonic-gate 
7360Sstevel@tonic-gate 				/* The value itself */
7370Sstevel@tonic-gate 				if (fcode == 't') {	/* %t is special. */
7380Sstevel@tonic-gate 					while (tbp < tep) {
7390Sstevel@tonic-gate 					    Putchar(*tbp++);
7400Sstevel@tonic-gate 					}
7410Sstevel@tonic-gate 				} else {	/* For rest of the cases. */
7420Sstevel@tonic-gate 					while (bp < p) {
7430Sstevel@tonic-gate 					    putbyte(*bp++);
7440Sstevel@tonic-gate 					}
7450Sstevel@tonic-gate 				}
7460Sstevel@tonic-gate #if FLOAT
7470Sstevel@tonic-gate 				/* Zeroes on the right */
7480Sstevel@tonic-gate 				while (--rzero >= 0)
7490Sstevel@tonic-gate 					Putchar('0');
7500Sstevel@tonic-gate 
7510Sstevel@tonic-gate 				/* The suffix */
7520Sstevel@tonic-gate 				while (*suffix != '\0') {
7530Sstevel@tonic-gate 					Putchar(*suffix++);
7540Sstevel@tonic-gate 				}
7550Sstevel@tonic-gate #endif
7560Sstevel@tonic-gate 				/* Blanks on the right if required */
7570Sstevel@tonic-gate 				if (fminus) {
7580Sstevel@tonic-gate 					while (--nblank >= 0) {
7590Sstevel@tonic-gate 						Putchar(' ');
7600Sstevel@tonic-gate 					}
7610Sstevel@tonic-gate 				}
7620Sstevel@tonic-gate 			}
7630Sstevel@tonic-gate 		}
7640Sstevel@tonic-gate }
765