/* * Copyright 2005 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 #include #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); return (0); } /* * 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 */ 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(' '); } } } } }