/* * printf.c * * Copyright (c) 1991 Symantec Corporation. All rights reserved. * */ #include "stdio.h" #include "stdarg.h" #include "string.h" #include "ansi_private.h" #include #define BUFLEN 512 #define TRUE 1 #define FALSE 0 static struct format { unsigned leftJustify : 1; unsigned forceSign : 1; unsigned altForm : 1; unsigned zeroPad : 1; unsigned havePrecision : 1; unsigned hSize : 1; unsigned lSize : 1; unsigned LSize : 1; char sign; char exponent; int fieldWidth; int precision; } default_format; struct decrec { char sgn; short exp; char sig[SIGDIGLEN]; short pad; /* following fields aren't used by SANE */ short min; short dot; short max; }; static void ftod(int, int, struct decrec *, long double); int vfprintf(FILE *fp, const char *fmt, va_list arg) { register int c, i, j, nwritten = 0; register unsigned long n; long double x; register char *s; char buf[BUFLEN], *digits, *t; struct format F; struct decrec D; for (c = *fmt; c; c = *++fmt) { if (c != '%') goto copy1; F = default_format; /* decode flags */ for (;;) { c = *++fmt; if (c == '-') F.leftJustify = TRUE; else if (c == '+') F.forceSign = TRUE; else if (c == ' ') F.sign = ' '; else if (c == '#') F.altForm = TRUE; else if (c == '0') F.zeroPad = TRUE; else break; } /* decode field width */ if (c == '*') { if ((F.fieldWidth = va_arg(arg, int)) < 0) { F.leftJustify = TRUE; F.fieldWidth = -F.fieldWidth; } c = *++fmt; } else { for (; c >= '0' && c <= '9'; c = *++fmt) F.fieldWidth = (10 * F.fieldWidth) + (c - '0'); } /* decode precision */ if (c == '.') { if ((c = *++fmt) == '*') { F.precision = va_arg(arg, int); c = *++fmt; } else { for (; c >= '0' && c <= '9'; c = *++fmt) F.precision = (10 * F.precision) + (c - '0'); } if (F.precision >= 0) F.havePrecision = TRUE; } /* perform appropriate conversion */ s = &buf[BUFLEN]; if (F.leftJustify) F.zeroPad = FALSE; conv: switch (c) { /* 'h' size modifier */ case 'h': F.hSize = TRUE; c = *++fmt; goto conv; /* 'l' size modifier */ case 'l': F.lSize = TRUE; c = *++fmt; goto conv; /* 'L' size modifier */ case 'L': F.LSize = TRUE; c = *++fmt; goto conv; /* decimal (signed) */ case 'd': case 'i': if (F.lSize) n = va_arg(arg, long); else n = va_arg(arg, int); if (F.hSize) n = (short) n; if ((long) n < 0) { n = -n; F.sign = '-'; } else if (F.forceSign) F.sign = '+'; goto decimal; /* decimal (unsigned) */ case 'u': if (F.lSize) n = va_arg(arg, unsigned long); else n = va_arg(arg, unsigned int); if (F.hSize) n = (unsigned short) n; F.sign = 0; goto decimal; /* decimal (common code) */ decimal: if (!F.havePrecision) { if (F.zeroPad) { F.precision = F.fieldWidth; if (F.sign) --F.precision; } if (F.precision < 1) F.precision = 1; } for (i = 0; n; n /= 10, i++) *--s = n % 10 + '0'; for (; i < F.precision; i++) *--s = '0'; if (F.sign) { *--s = F.sign; i++; } break; /* octal (unsigned) */ case 'o': if (F.lSize) n = va_arg(arg, unsigned long); else n = va_arg(arg, unsigned int); if (F.hSize) n = (unsigned short) n; if (!F.havePrecision) { if (F.zeroPad) F.precision = F.fieldWidth; if (F.precision < 1) F.precision = 1; } for (i = 0; n; n /= 8, i++) *--s = n % 8 + '0'; if (F.altForm && i && *s != '0') { *--s = '0'; i++; } for (; i < F.precision; i++) *--s = '0'; break; /* hexadecimal (unsigned) */ case 'p': F.havePrecision = F.lSize = TRUE; F.precision = 8; /* ... */ case 'X': digits = "0123456789ABCDEF"; goto hexadecimal; case 'x': digits = "0123456789abcdef"; /* ... */ hexadecimal: if (F.lSize) n = va_arg(arg, unsigned long); else n = va_arg(arg, unsigned int); if (F.hSize) n = (unsigned short) n; if (!F.havePrecision) { if (F.zeroPad) { F.precision = F.fieldWidth; if (F.altForm) F.precision -= 2; } if (F.precision < 1) F.precision = 1; } for (i = 0; n; n /= 16, i++) *--s = digits[n % 16]; for (; i < F.precision; i++) *--s = '0'; if (F.altForm) { *--s = c; *--s = '0'; i += 2; } break; #ifndef _NOFLOATING_ /* fixed-point */ case 'f': if (F.LSize) x = va_arg(arg, long double); else x = va_arg(arg, double); if (!F.havePrecision) F.precision = 6; ftod(1, F.precision, &D, x); D.dot = D.exp + D.sig[0]; if ((D.min = D.dot) > 1) D.min = 1; D.max = D.dot + F.precision; if (D.max - D.min > 508) memcpy(D.sig, "\6>>>>>>", 7); goto floating; /* floating-point */ case 'e': case 'E': if (F.LSize) x = va_arg(arg, long double); else x = va_arg(arg, double); if (!F.havePrecision) F.precision = 6; F.exponent = c; ftod(0, D.max = F.precision + 1, &D, x); D.min = D.dot = 1; D.exp += D.sig[0] - 1; goto floating; /* "general" notation */ case 'g': case 'G': if (F.LSize) x = va_arg(arg, long double); else x = va_arg(arg, double); if (!F.havePrecision) F.precision = 6; else if (F.precision == 0) F.precision = 1; F.exponent = c - 2; ftod(0, D.max = F.precision, &D, x); D.min = D.dot = 1; D.exp += D.sig[0] - 1; if (D.exp >= -4 && D.exp < F.precision) { F.exponent = 0; if ((D.dot += D.exp) < 1) D.min = D.dot; } if (!F.altForm && D.max > D.sig[0]) { if ((D.max = D.sig[0]) < D.dot) D.max = D.dot; } goto floating; /* floating (common code) */ floating: if (D.sig[1] > '9') { F.exponent = FALSE; D.dot = 0; D.min = 1; D.max = D.sig[0]; } i = 0; if (F.exponent) { n = D.exp < 0 ? -D.exp : D.exp; for (; n; n /= 10, i++) *--s = n % 10 + '0'; for (; i < 2; i++) *--s = '0'; *--s = D.exp < 0 ? '-' : '+'; *--s = F.exponent; i += 2; } j = D.max; if (j == D.dot && !F.altForm) ++D.dot; do { if (j == D.dot) { *--s = '.'; i++; } *--s = j > 0 && j <= D.sig[0] ? D.sig[j] : '0'; } while (--j >= D.min); i += D.max - j; if (D.sgn) F.sign = '-'; else if (F.forceSign) F.sign = '+'; if (F.zeroPad) { j = F.fieldWidth; if (F.sign) --j; for (; i < j; i++) *--s = '0'; } if (F.sign) { *--s = F.sign; i++; } break; #endif _NOFLOATING_ /* character */ case 'c': *--s = va_arg(arg, int); i = 1; break; /* string */ case 's': s = va_arg(arg, char *); if (F.altForm) { i = (unsigned char) *s++; if (F.havePrecision && i > F.precision) i = F.precision; } else { if (!F.havePrecision) i = strlen(s); else if (t = memchr(s, '\0', F.precision)) i = t - s; else i = F.precision; } break; /* store # bytes written so far */ case 'n': s = va_arg(arg, void *); if (F.hSize) * (short *) s = nwritten; else if (F.lSize) * (long *) s = nwritten; else * (int *) s = nwritten; continue; /* oops - unknown conversion, abort */ default: if (c != '%') goto done; copy1: if (putc(c, fp) < 0) return(EOF); ++nwritten; continue; } /* pad on the left */ if (i < F.fieldWidth && !F.leftJustify) { do { if (putc(' ', fp) < 0) return(EOF); ++nwritten; } while (i < --F.fieldWidth); } /* write the converted result */ if (fwrite(s, 1, i, fp) != i) return(EOF); nwritten += i; /* pad on the right */ for (; i < F.fieldWidth; i++) { if (putc(' ', fp) < 0) return(EOF); ++nwritten; } } /* all done! */ done: return(nwritten); } /* ---------- floating-point conversion ---------- */ #ifndef _NOFLOATING_ static void ftod(int fixed, int precision, struct decrec *d, long double x) { struct { char style; short digits; } form; int i; register short *p = (short *) &x; /* point to SANE extended */ #if __option(mc68881) && __option(native_fp) p[1] = p[0]; #endif #if __option(mc68881) || !__option(native_fp) p++; #endif /* convert to decimal record */ if (precision >= sizeof d->sig) precision = sizeof d->sig - 1; form.style = fixed; form.digits = precision; fp68k(&form, p, d, (short)FX2DEC); /* handle fixed-point overflow */ if (d->sig[1] == '?') { form.style = 0; form.digits = sizeof d->sig - 1; fp68k(&form, p, d, (short)FX2DEC); } /* strip trailing zeroes */ for (i = d->sig[0]; i > 1 && d->sig[i] == '0'; --i) ++d->exp; d->sig[0] = i; /* format 0, INF, NAN */ if (d->sig[1] == '0') { d->sgn = 0; /* delete to allow printing "-0" */ d->exp = 0; } else if (d->sig[1] == 'I') { d->sig[0] = 3; d->sig[2] = 'N'; d->sig[3] = 'F'; } else if (d->sig[1] == 'N') { d->sig[0] = 5; d->sig[2] = 'A'; d->sig[3] = 'N'; } } #endif _NOFLOATING_