1
0
Files
irix-657m-src/eoe/cmd/csh/sh.printf.c
2022-09-29 17:59:04 +03:00

717 lines
16 KiB
C

/* Copyright (c) 1990, 1991 UNIX System Laboratories, Inc. */
/* Copyright (c) 1984, 1986, 1987, 1988, 1989, 1990 AT&T */
/* All Rights Reserved */
/* THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF */
/* UNIX System Laboratories, Inc. */
/* The copyright notice above does not evidence any */
/* actual or intended publication of such source code. */
/*******************************************************************
#ident "$Revision: 2.5 $"
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 wputchar.
* 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 wputchar() is used to send a character
* in wchar_t (== wchar_t + * optional QUOE.)
* DONT USE WITH STDIO!
* This shprintf has been hacked again so that it understands wchar_t
* string when the format specifier %t is used. Also %c has been
* expanded to take a wchar_t 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(wchar_t)<=sizeof(int) so that wchar_t
* is passed as int. Otherwise, %T must be specified instead of %c to
* print a character in wchar_t.
*/
#include <sys/types.h>
#include <stdarg.h>
#include <stdlib.h>
#include <values.h>
#include "sh.h"
static void _print(char *, va_list);
void
shprintf(char *format, ...)
{
va_list args;
va_start(args, format);
_print(format, args);
va_end(args);
}
/*
* 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 long) representation */
#define MAXDIGS 22
/* 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
static void
_print(char *format, va_list args)
{
/* Current position in format */
char *cp;
/* Starting and ending points for value to be printed */
char *bp, *p;
wchar_t *tbp, *tep; /* For "%t". */
wchar_t 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 llength; /* ll */
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 */
__int64_t 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 != '%') {
putbyte (*cp++); /* Ordinary (non-%) character */
} else {
/*
* % has been found.
* First, parse the format specification.
*/
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 == '.') {
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;
llength = 0;
switch(*cp) {
case 'l':
if (*(cp + 1) == 'l') {
llength = 1;
cp++;
}
else
length = 1;
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 (llength)
val = va_arg(args, __int64_t);
else 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) & ~HIBITLL;
*--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
/*
* arg is char
*/
case 'c':
buf[0] = va_arg(args, int);
bp = &buf[0];
p = bp + 1;
break;
/*
* arg is wchar_t
*/
case 'T':
tcbuf[0] = va_arg(args, wchar_t);
tbp = &tcbuf[0];
tep = tbp + 1;
fcode = 't';
break;
/*
* arg is char *
*/
case 's':
bp = va_arg(args, char *);
if( !bp) {
nullstr: bp = "(null)";
p = bp + strlen("(null)");
break;
}
if(prec < 0)
for(n = 0; *bp++; n++);
else
for(n = 0; *bp++ && (n < prec); n++);
p = --bp;
bp -= n;
break;
/*
* arg is wchar_t *
*/
case 't':
tbp = va_arg(args, wchar_t *);
if( !tbp) {
fcode='s';
goto nullstr;
}
if(prec < 0)
for(n = 0; *tbp++; n++);
else
for(n = 0; *tbp++ && (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, tnum;
if (fcode == 't' || fcode == 'T')
tnum = tep - tbp;
else
tnum = p - bp;
nblank = width
#if FLOAT
- (rzero < 0? 0: rzero)
- strlen (suffix)
#endif
- tnum
- (lzero < 0? 0: lzero)
- strlen (prefix);
/* Blanks on left if required */
if (!fminus)
while (--nblank >= 0)
wputchar(' ');
/* Prefix, if any */
while (*prefix != '\0')
wputchar(*prefix++);
/* Zeroes on the left */
while (--lzero >= 0)
wputchar('0');
/* The value itself */
if(fcode == 't') {
while(tbp < tep)
wputchar(*tbp++);
} else {
while(bp < p)
putbyte(*bp++);
}
#if FLOAT
/* Zeroes on the right */
while(--rzero >= 0)
wputchar('0');
/* The suffix */
while(*suffix != '\0')
wputchar(*suffix++);
#endif
/* Blanks on the right if required */
if(fminus)
while(--nblank >= 0)
wputchar(' ');
}
}
}