726 lines
16 KiB
C
Executable File
726 lines
16 KiB
C
Executable File
/* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
|
|
/* All Rights Reserved */
|
|
|
|
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T */
|
|
/* The copyright notice above does not evidence any */
|
|
/* actual or intended publication of such source code. */
|
|
|
|
#ident "@(#)printf.c 1.6 92/07/14 SMI" /* SVr4.0 1.3 */
|
|
|
|
/*******************************************************************
|
|
|
|
PROPRIETARY NOTICE (Combined)
|
|
|
|
This source code is unpublished proprietary information
|
|
constituting, or derived under license from AT&T's UNIX(r) System V.
|
|
In addition, portions of such source code were derived from Berkeley
|
|
4.3 BSD under license from the Regents of the University of
|
|
California.
|
|
|
|
|
|
|
|
Copyright Notice
|
|
|
|
Notice of copyright on this source code product does not indicate
|
|
publication.
|
|
|
|
(c) 1986,1987,1988,1989 Sun Microsystems, Inc
|
|
(c) 1983,1984,1985,1986,1987,1988,1989 AT&T.
|
|
All rights reserved.
|
|
********************************************************************/
|
|
|
|
/*
|
|
* Hacked "printf" which prints through putbyte and putchar.
|
|
* putbyte() is used to send a pure byte, which might be a part
|
|
* of a mutlibyte character, mainly for %s. A control character
|
|
* for putbyte() may be QUOTE'd meaning not to convert it to ^x
|
|
* sequence. In all other cases putchar() is used to send a character
|
|
* in tchar (== wchar_t + * optional QUOE.)
|
|
* DONT USE WITH STDIO!
|
|
* This printf has been hacked again so that it understands tchar string
|
|
* when the format specifier %t is used. Also %c has been expanded
|
|
* to take a tchar character as well as normal int.
|
|
* %t is supported in its simplest form; no width or precision will
|
|
* be understood.
|
|
* Assumption here is that sizeof(tchar)<=sizeof(int) so that tchar is
|
|
* passed as int. Otherwise, %T must be specified instead of %c to
|
|
* print a character in tchar.
|
|
*/
|
|
|
|
#include <varargs.h>
|
|
#include <values.h>
|
|
#include "sh.h" /* For tchar. */
|
|
|
|
|
|
static char *p;
|
|
printf (format, va_alist)
|
|
char *format;
|
|
va_dcl
|
|
{
|
|
va_list stupid;
|
|
|
|
|
|
p = (char *)gettext (format);
|
|
va_start( stupid );
|
|
_print (p, &stupid);
|
|
}
|
|
|
|
/*
|
|
* Floating-point code is included or not, depending
|
|
* on whether the preprocessor variable FLOAT is 1 or 0.
|
|
*/
|
|
|
|
/*#include <ctype.h> "sh.h" defines its own version of isxxxx(). */
|
|
/*#include "param.h"... well, here is the contents of this .h file. */
|
|
/* Maximum number of digits in any integer (long) representation */
|
|
#define MAXDIGS 11
|
|
|
|
/* Convert a digit character to the corresponding number */
|
|
#define tonumber(x) ((x)-'0')
|
|
|
|
/* Convert a number between 0 and 9 to the corresponding digit */
|
|
#define todigit(x) ((x)+'0')
|
|
|
|
/* Maximum total number of digits in E format */
|
|
#define MAXECVT 17
|
|
|
|
/* Maximum number of digits after decimal point in F format */
|
|
#define MAXFCVT 60
|
|
|
|
/* Maximum significant figures in a floating-point number */
|
|
#define MAXFSIG 17
|
|
|
|
/* Maximum number of characters in an exponent */
|
|
#define MAXESIZ 4
|
|
|
|
/* Maximum (positive) exponent or greater */
|
|
#define MAXEXP 40
|
|
|
|
|
|
|
|
#define max(a,b) ((a) > (b)? (a): (b))
|
|
#define min(a,b) ((a) < (b)? (a): (b))
|
|
|
|
/* If this symbol is nonzero, allow '0' as a flag */
|
|
#define FZERO 1
|
|
|
|
#if FLOAT
|
|
/*
|
|
* System-supplied routines for floating conversion
|
|
*/
|
|
char *fcvt();
|
|
char *ecvt();
|
|
#endif
|
|
|
|
_print (format, args)
|
|
char *format;
|
|
va_list *args;
|
|
{
|
|
/* Current position in format */
|
|
char *cp;
|
|
|
|
/* Starting and ending points for value to be printed */
|
|
char *bp, *p;
|
|
tchar *tbp, *tep; /* For "%t". */
|
|
tchar tcbuf[2];/* For "%c" or "%T". */
|
|
|
|
/* Field width and precision */
|
|
int width, prec;
|
|
|
|
/* Format code */
|
|
char fcode;
|
|
|
|
/* Number of padding zeroes required on the left */
|
|
int lzero;
|
|
|
|
/* Flags - nonzero if corresponding character appears in format */
|
|
bool length; /* l */
|
|
bool fplus; /* + */
|
|
bool fminus; /* - */
|
|
bool fblank; /* blank */
|
|
bool fsharp; /* # */
|
|
#if FZERO
|
|
bool fzero; /* 0 */
|
|
#endif
|
|
|
|
/* Pointer to sign, "0x", "0X", or empty */
|
|
char *prefix;
|
|
#if FLOAT
|
|
/* Exponent or empty */
|
|
char *suffix;
|
|
|
|
/* Buffer to create exponent */
|
|
char expbuf[MAXESIZ + 1];
|
|
|
|
/* Number of padding zeroes required on the right */
|
|
int rzero;
|
|
|
|
/* The value being converted, if real */
|
|
double dval;
|
|
|
|
/* Output values from fcvt and ecvt */
|
|
int decpt, sign;
|
|
|
|
/* Scratch */
|
|
int k;
|
|
|
|
/* Values are developed in this buffer */
|
|
char buf[max (MAXDIGS, max (MAXFCVT + DMAXEXP, MAXECVT) + 1)];
|
|
#else
|
|
char buf[MAXDIGS];
|
|
#endif
|
|
/* The value being converted, if integer */
|
|
long val;
|
|
|
|
/* Set to point to a translate table for digits of whatever radix */
|
|
char *tab;
|
|
|
|
/* Work variables */
|
|
int n, hradix, lowbit;
|
|
|
|
cp = format;
|
|
|
|
/*
|
|
* The main loop -- this loop goes through one iteration
|
|
* for each ordinary character or format specification.
|
|
*/
|
|
while (*cp)
|
|
if (*cp != '%') {
|
|
/* Ordinary (non-%) character */
|
|
putbyte (*cp++);
|
|
} else {
|
|
/*
|
|
* % has been found.
|
|
* First, parse the format specification.
|
|
*/
|
|
|
|
/* Scan the <flags> */
|
|
fplus = fminus = fblank = fsharp = 0;
|
|
#if FZERO
|
|
fzero = 0;
|
|
#endif
|
|
scan: switch (*++cp) {
|
|
case '+':
|
|
fplus = 1;
|
|
goto scan;
|
|
case '-':
|
|
fminus = 1;
|
|
goto scan;
|
|
case ' ':
|
|
fblank = 1;
|
|
goto scan;
|
|
case '#':
|
|
fsharp = 1;
|
|
goto scan;
|
|
#if FZERO
|
|
case '0':
|
|
fzero = 1;
|
|
goto scan;
|
|
#endif
|
|
}
|
|
|
|
/* Scan the field width */
|
|
if (*cp == '*') {
|
|
width = va_arg (*args, int);
|
|
if (width < 0) {
|
|
width = -width;
|
|
fminus = 1;
|
|
}
|
|
cp++;
|
|
} else {
|
|
width = 0;
|
|
while (isdigit (*cp)) {
|
|
n = tonumber (*cp++);
|
|
width = width * 10 + n;
|
|
}
|
|
}
|
|
|
|
/* Scan the precision */
|
|
if (*cp == '.') {
|
|
|
|
/* '*' instead of digits? */
|
|
if (*++cp == '*') {
|
|
prec = va_arg (*args, int);
|
|
cp++;
|
|
} else {
|
|
prec = 0;
|
|
while (isdigit (*cp)) {
|
|
n = tonumber (*cp++);
|
|
prec = prec * 10 + n;
|
|
}
|
|
}
|
|
} else
|
|
prec = -1;
|
|
|
|
/* Scan the length modifier */
|
|
length = 0;
|
|
switch (*cp) {
|
|
case 'l':
|
|
length = 1;
|
|
/* No break */
|
|
case 'h':
|
|
cp++;
|
|
}
|
|
|
|
/*
|
|
* The character addressed by cp must be the
|
|
* format letter -- there is nothing left for
|
|
* it to be.
|
|
*
|
|
* The status of the +, -, #, blank, and 0
|
|
* flags are reflected in the variables
|
|
* "fplus", "fminus", "fsharp", "fblank",
|
|
* and "fzero", respectively.
|
|
* "width" and "prec" contain numbers
|
|
* corresponding to the digit strings
|
|
* before and after the decimal point,
|
|
* respectively. If there was no decimal
|
|
* point, "prec" is -1.
|
|
*
|
|
* The following switch sets things up
|
|
* for printing. What ultimately gets
|
|
* printed will be padding blanks, a prefix,
|
|
* left padding zeroes, a value, right padding
|
|
* zeroes, a suffix, and more padding
|
|
* blanks. Padding blanks will not appear
|
|
* simultaneously on both the left and the
|
|
* right. Each case in this switch will
|
|
* compute the value, and leave in several
|
|
* variables the information necessary to
|
|
* construct what is to be printed.
|
|
*
|
|
* The prefix is a sign, a blank, "0x", "0X",
|
|
* or null, and is addressed by "prefix".
|
|
*
|
|
* The suffix is either null or an exponent,
|
|
* and is addressed by "suffix".
|
|
*
|
|
* The value to be printed starts at "bp"
|
|
* and continues up to and not including "p".
|
|
*
|
|
* "lzero" and "rzero" will contain the number
|
|
* of padding zeroes required on the left
|
|
* and right, respectively. If either of
|
|
* these variables is negative, it will be
|
|
* treated as if it were zero.
|
|
*
|
|
* The number of padding blanks, and whether
|
|
* they go on the left or the right, will be
|
|
* computed on exit from the switch.
|
|
*/
|
|
|
|
lzero = 0;
|
|
prefix = "";
|
|
#if FLOAT
|
|
rzero = lzero;
|
|
suffix = prefix;
|
|
#endif
|
|
switch (fcode = *cp++) {
|
|
|
|
/*
|
|
* fixed point representations
|
|
*
|
|
* "hradix" is half the radix for the conversion.
|
|
* Conversion is unsigned unless fcode is 'd'.
|
|
* HIBITL is 1000...000 binary, and is equal to
|
|
* the maximum negative number.
|
|
* We assume a 2's complement machine
|
|
*/
|
|
|
|
case 'D':
|
|
case 'U':
|
|
length = 1;
|
|
case 'd':
|
|
case 'u':
|
|
hradix = 5;
|
|
goto fixed;
|
|
|
|
case 'O':
|
|
length = 1;
|
|
case 'o':
|
|
hradix = 4;
|
|
goto fixed;
|
|
|
|
case 'X':
|
|
case 'x':
|
|
hradix = 8;
|
|
|
|
fixed:
|
|
/* Establish default precision */
|
|
if (prec < 0)
|
|
prec = 1;
|
|
|
|
/* Fetch the argument to be printed */
|
|
if (length)
|
|
val = va_arg (*args, long);
|
|
else if (fcode == 'd')
|
|
val = va_arg (*args, int);
|
|
else
|
|
val = va_arg (*args, unsigned);
|
|
|
|
/* If signed conversion, establish sign */
|
|
if (fcode == 'd' || fcode == 'D') {
|
|
if (val < 0) {
|
|
prefix = "-";
|
|
/*
|
|
* Negate, checking in
|
|
* advance for possible
|
|
* overflow.
|
|
*/
|
|
if (val != HIBITL)
|
|
val = -val;
|
|
} else if (fplus)
|
|
prefix = "+";
|
|
else if (fblank)
|
|
prefix = " ";
|
|
}
|
|
#if FZERO
|
|
if (fzero) {
|
|
int n = width - strlen (prefix);
|
|
if (n > prec)
|
|
prec = n;
|
|
}
|
|
#endif
|
|
/* Set translate table for digits */
|
|
if (fcode == 'X')
|
|
tab = "0123456789ABCDEF";
|
|
else
|
|
tab = "0123456789abcdef";
|
|
|
|
/* Develop the digits of the value */
|
|
p = bp = buf + MAXDIGS;
|
|
while (val) {
|
|
lowbit = val & 1;
|
|
val = (val >> 1) & ~HIBITL;
|
|
*--bp = tab[val % hradix * 2 + lowbit];
|
|
val /= hradix;
|
|
}
|
|
|
|
/* Calculate padding zero requirement */
|
|
lzero = bp - p + prec;
|
|
|
|
/* Handle the # flag */
|
|
if (fsharp && bp != p)
|
|
switch (fcode) {
|
|
case 'o':
|
|
if (lzero < 1)
|
|
lzero = 1;
|
|
break;
|
|
case 'x':
|
|
prefix = "0x";
|
|
break;
|
|
case 'X':
|
|
prefix = "0X";
|
|
break;
|
|
}
|
|
|
|
break;
|
|
#if FLOAT
|
|
case 'E':
|
|
case 'e':
|
|
/*
|
|
* E-format. The general strategy
|
|
* here is fairly easy: we take
|
|
* what ecvt gives us and re-format it.
|
|
*/
|
|
|
|
/* Establish default precision */
|
|
if (prec < 0)
|
|
prec = 6;
|
|
|
|
/* Fetch the value */
|
|
dval = va_arg (*args, double);
|
|
|
|
/* Develop the mantissa */
|
|
bp = ecvt (dval,
|
|
min (prec + 1, MAXECVT),
|
|
&decpt,
|
|
&sign);
|
|
|
|
/* Determine the prefix */
|
|
e_merge:
|
|
if (sign)
|
|
prefix = "-";
|
|
else if (fplus)
|
|
prefix = "+";
|
|
else if (fblank)
|
|
prefix = " ";
|
|
|
|
/* Place the first digit in the buffer */
|
|
p = &buf[0];
|
|
*p++ = *bp != '\0'? *bp++: '0';
|
|
|
|
/* Put in a decimal point if needed */
|
|
if (prec != 0 || fsharp)
|
|
*p++ = '.';
|
|
|
|
/* Create the rest of the mantissa */
|
|
rzero = prec;
|
|
while (rzero > 0 && *bp!= '\0') {
|
|
--rzero;
|
|
*p++ = *bp++;
|
|
}
|
|
|
|
bp = &buf[0];
|
|
|
|
/* Create the exponent */
|
|
suffix = &expbuf[MAXESIZ];
|
|
*suffix = '\0';
|
|
if (dval != 0) {
|
|
n = decpt - 1;
|
|
if (n < 0)
|
|
n = -n;
|
|
while (n != 0) {
|
|
*--suffix = todigit (n % 10);
|
|
n /= 10;
|
|
}
|
|
}
|
|
|
|
/* Prepend leading zeroes to the exponent */
|
|
while (suffix > &expbuf[MAXESIZ - 2])
|
|
*--suffix = '0';
|
|
|
|
/* Put in the exponent sign */
|
|
*--suffix = (decpt > 0 || dval == 0)? '+': '-';
|
|
|
|
/* Put in the e */
|
|
*--suffix = isupper(fcode)? 'E' : 'e';
|
|
|
|
break;
|
|
|
|
case 'f':
|
|
/*
|
|
* F-format floating point. This is
|
|
* a good deal less simple than E-format.
|
|
* The overall strategy will be to call
|
|
* fcvt, reformat its result into buf,
|
|
* and calculate how many trailing
|
|
* zeroes will be required. There will
|
|
* never be any leading zeroes needed.
|
|
*/
|
|
|
|
/* Establish default precision */
|
|
if (prec < 0)
|
|
prec = 6;
|
|
|
|
/* Fetch the value */
|
|
dval = va_arg (*args, double);
|
|
|
|
/* Do the conversion */
|
|
bp = fcvt (dval,
|
|
min (prec, MAXFCVT),
|
|
&decpt,
|
|
&sign);
|
|
|
|
/* Determine the prefix */
|
|
f_merge:
|
|
if (sign && decpt > -prec &&
|
|
*bp != '\0' && *bp != '0')
|
|
prefix = "-";
|
|
else if (fplus)
|
|
prefix = "+";
|
|
else if (fblank)
|
|
prefix = " ";
|
|
|
|
/* Initialize buffer pointer */
|
|
p = &buf[0];
|
|
|
|
/* Emit the digits before the decimal point */
|
|
n = decpt;
|
|
k = 0;
|
|
if (n <= 0)
|
|
*p++ = '0';
|
|
else
|
|
do if (*bp == '\0' || k >= MAXFSIG)
|
|
*p++ = '0';
|
|
else {
|
|
*p++ = *bp++;
|
|
++k;
|
|
}
|
|
while (--n != 0);
|
|
|
|
/* Decide whether we need a decimal point */
|
|
if (fsharp || prec > 0)
|
|
*p++ = '.';
|
|
|
|
/* Digits (if any) after the decimal point */
|
|
n = min (prec, MAXFCVT);
|
|
rzero = prec - n;
|
|
while (--n >= 0)
|
|
if (++decpt <= 0
|
|
|| *bp == '\0'
|
|
|| k >= MAXFSIG)
|
|
*p++ = '0';
|
|
else {
|
|
*p++ = *bp++;
|
|
++k;
|
|
}
|
|
|
|
bp = &buf[0];
|
|
|
|
break;
|
|
|
|
case 'G':
|
|
case 'g':
|
|
/*
|
|
* g-format. We play around a bit
|
|
* and then jump into e or f, as needed.
|
|
*/
|
|
|
|
/* Establish default precision */
|
|
if (prec < 0)
|
|
prec = 6;
|
|
|
|
/* Fetch the value */
|
|
dval = va_arg (*args, double);
|
|
|
|
/* Do the conversion */
|
|
bp = ecvt (dval,
|
|
min (prec, MAXECVT),
|
|
&decpt,
|
|
&sign);
|
|
if (dval == 0)
|
|
decpt = 1;
|
|
|
|
k = prec;
|
|
if (!fsharp) {
|
|
n = strlen (bp);
|
|
if (n < k)
|
|
k = n;
|
|
while (k >= 1 && bp[k-1] == '0')
|
|
--k;
|
|
}
|
|
|
|
if (decpt < -3 || decpt > prec) {
|
|
prec = k - 1;
|
|
goto e_merge;
|
|
} else {
|
|
prec = k - decpt;
|
|
goto f_merge;
|
|
}
|
|
|
|
#endif
|
|
case 'c':
|
|
#ifdef MBCHAR_1 /* sizeof(int)>=sizeof(tchar) */
|
|
/* A tchar arg is passed as int so we used the normal %c to specify
|
|
* such an arugument.
|
|
*/ tcbuf[0] = va_arg (*args, int);
|
|
tbp = &tcbuf[0];
|
|
tep = tbp + 1;
|
|
fcode='t'; /* Fake the rest of code. */
|
|
break;
|
|
#else
|
|
/* We would have to invent another new format speficier such as "%T" to
|
|
* take a tchar arg. Let's worry about when that time comes.
|
|
*/ /* Following code take care of a char arg
|
|
* only.
|
|
*/
|
|
buf[0] = va_arg (*args, int);
|
|
bp = &buf[0];
|
|
p = bp + 1;
|
|
break;
|
|
case 'T': /* Corresponding arg is tchar. */
|
|
tcbuf[0] = va_arg (*args, tchar);
|
|
tbp = &tcbuf[0];
|
|
tep = tbp + 1;
|
|
fcode='t'; /* Fake the rest of code. */
|
|
break;
|
|
#endif
|
|
case 's':
|
|
bp = va_arg (*args, char *);
|
|
if (bp == 0) {
|
|
nullstr: bp = "(null)";
|
|
p = bp + strlen("(null)");
|
|
break;
|
|
}
|
|
if (prec < 0)
|
|
prec = MAXINT;
|
|
for (n=0; *bp++ != '\0' && n < prec; n++);
|
|
p = --bp;
|
|
bp -= n;
|
|
break;
|
|
|
|
case 't': /* Special format specifier "%t" tells
|
|
* printf() to print char strings written
|
|
* as tchar string.
|
|
*/
|
|
tbp = va_arg (*args, tchar *);
|
|
if (tbp == 0) {
|
|
fcode='s';/* Act as if it were %s. */
|
|
goto nullstr;
|
|
}
|
|
if (prec < 0)
|
|
prec = MAXINT;
|
|
for (n=0; *tbp++ != 0 && n < prec; n++);
|
|
tep = --tbp;
|
|
tbp -= n;
|
|
|
|
/* Just to make the following padding
|
|
* calculation not to go very crazy...
|
|
*/
|
|
bp = NULL;
|
|
p = bp+n;
|
|
break;
|
|
|
|
case '\0':
|
|
cp--;
|
|
break;
|
|
|
|
/* case '%': */
|
|
default:
|
|
p = bp = &fcode;
|
|
p++;
|
|
break;
|
|
|
|
}
|
|
if (fcode != '\0') {
|
|
/* Calculate number of padding blanks */
|
|
int nblank;
|
|
nblank = width
|
|
#if FLOAT
|
|
- (rzero < 0? 0: rzero)
|
|
- strlen (suffix)
|
|
#endif
|
|
- (p - bp)
|
|
- (lzero < 0? 0: lzero)
|
|
- strlen (prefix);
|
|
|
|
/* Blanks on left if required */
|
|
if (!fminus)
|
|
while (--nblank >= 0)
|
|
putchar (' ');
|
|
|
|
/* Prefix, if any */
|
|
while (*prefix != '\0')
|
|
putchar (*prefix++);
|
|
|
|
/* Zeroes on the left */
|
|
while (--lzero >= 0)
|
|
putchar ('0');
|
|
|
|
/* The value itself */
|
|
if(fcode == 't'){/* %t is special. */
|
|
while (tbp < tep)
|
|
putchar (*tbp++);
|
|
}else{/* For rest of the cases. */
|
|
while (bp < p)
|
|
putbyte (*bp++);
|
|
}
|
|
#if FLOAT
|
|
/* Zeroes on the right */
|
|
while (--rzero >= 0)
|
|
putchar ('0');
|
|
|
|
/* The suffix */
|
|
while (*suffix != '\0')
|
|
putchar (*suffix++);
|
|
#endif
|
|
/* Blanks on the right if required */
|
|
if (fminus)
|
|
while (--nblank >= 0)
|
|
putchar (' ');
|
|
}
|
|
}
|
|
}
|
|
|