diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/csh/printf.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/csh/printf.c')
-rw-r--r-- | usr/src/cmd/csh/printf.c | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/usr/src/cmd/csh/printf.c b/usr/src/cmd/csh/printf.c new file mode 100644 index 0000000000..68272cb5a4 --- /dev/null +++ b/usr/src/cmd/csh/printf.c @@ -0,0 +1,762 @@ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +/* Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */ +/* All Rights Reserved */ + +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley Software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * 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 <stdarg.h> +#include <values.h> +#include "sh.h" /* For tchar. */ + +#define HIBITLL (1ULL << 63) + +void _print(char *format, va_list *args); + +static char *p; + +int +printf(const char *format, ...) +{ + va_list stupid; + + p = (char *)gettext(format); + va_start(stupid, format); + _print(p, &stupid); + va_end(stupid); +} + +/* + * Floating-point code is included or not, depending + * on whether the preprocessor variable FLOAT is 1 or 0. + */ + +/* Maximum number of digits in any integer (long) representation */ +#define MAXDIGS 20 + +/* 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 + +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; + 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 double_length; /* ll */ + 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 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 */ + double_length = length = 0; + switch (*cp) { + case 'l': + if (*(cp + 1) == 'l') { + cp++; + double_length = 1; + } else { + 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'. + * HIBITLL 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 (double_length) { + val = va_arg(*args, long long); + } 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 != HIBITLL) { + 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 + 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; + + 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(' '); + } + } + } + } +} |