134226Sbostic /* 234233Sbostic * Copyright (c) 1988 Regents of the University of California. 334233Sbostic * All rights reserved. 434226Sbostic * 534233Sbostic * Redistribution and use in source and binary forms are permitted 634233Sbostic * provided that this notice is preserved and that due credit is given 734233Sbostic * to the University of California at Berkeley. The name of the University 834233Sbostic * may not be used to endorse or promote products derived from this 934233Sbostic * software without specific prior written permission. This software 1034233Sbostic * is provided ``as is'' without express or implied warranty. 1134226Sbostic */ 1234226Sbostic 1334233Sbostic #if defined(LIBC_SCCS) && !defined(lint) 14*34243Sbostic static char sccsid[] = "@(#)vfprintf.c 5.7 (Berkeley) 05/09/88"; 1534233Sbostic #endif /* LIBC_SCCS and not lint */ 1634233Sbostic 1734233Sbostic #include <sys/param.h> 1834233Sbostic #include <varargs.h> 1934226Sbostic #include <stdio.h> 2034233Sbostic #include <ctype.h> 2134226Sbostic 2234242Sbostic #define MAXBUF 120 2334242Sbostic #define DEFPREC 6 2434226Sbostic 2534236Sbostic #define PUTC(ch, fd) {++cnt; putc(ch, fd);} 2634236Sbostic 2734242Sbostic #define EFORMAT 1 2834242Sbostic #define FFORMAT 2 2934242Sbostic #define GFORMAT 3 3034242Sbostic 3134235Sbostic #define LONGINT 0x01 3234235Sbostic #define LONGDBL 0x02 3334235Sbostic #define SHORTINT 0x04 3434241Sbostic #define GETARG(r) \ 3534241Sbostic r = argsize&LONGINT ? va_arg(argp, long) : \ 3634241Sbostic argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 3734235Sbostic 3834235Sbostic x_doprnt(fmt, argp, fp) 3934233Sbostic register char *fmt; 4034233Sbostic va_list argp; 4134235Sbostic register FILE *fp; 4234226Sbostic { 4334233Sbostic register u_long reg_ulong; 4434233Sbostic register long reg_long; 4534233Sbostic register int base; 4634235Sbostic register char *digs, *bp, *t, padc; 4734235Sbostic double _double; 4834242Sbostic char argsize, printsign, *_cvt(), buf[MAXBUF]; 4934242Sbostic int alternate, cnt, n, ladjust, width, prec, size; 5034226Sbostic 51*34243Sbostic digs = "0123456789abcdef"; 5234233Sbostic for (cnt = 0; *fmt; ++fmt) { 5334233Sbostic if (*fmt != '%') { 5434235Sbostic PUTC(*fmt, fp); 5534233Sbostic continue; 5634226Sbostic } 5734226Sbostic 5834235Sbostic alternate = ladjust = width = 0; 5934233Sbostic prec = -1; 6034233Sbostic padc = ' '; 6134235Sbostic argsize = printsign = '\0'; 6234226Sbostic 6334233Sbostic flags: switch (*++fmt) { 6434233Sbostic case '#': 6534233Sbostic alternate = 1; 6634233Sbostic goto flags; 6734233Sbostic case '*': 6834235Sbostic /* 6934235Sbostic * ``A negative field width argument is taken as a 7034235Sbostic * - flag followed by a positive field width.'' 7134235Sbostic * -- ANSI X3J11 7234235Sbostic * They don't exclude field widths read from args. 7334235Sbostic */ 7434235Sbostic if ((width = va_arg(argp, int)) >= 0) 7534235Sbostic goto flags; 7634235Sbostic width = -width; 7734235Sbostic /*FALLTHROUGH*/ 7834235Sbostic case '-': 7934235Sbostic ladjust = 1; 8034233Sbostic goto flags; 8134233Sbostic case '+': 8234233Sbostic printsign = '+'; 8334233Sbostic goto flags; 8434233Sbostic case '.': 8534235Sbostic if (*++fmt == '*') 8634235Sbostic prec = va_arg(argp, int); 8734235Sbostic else if (isdigit(*fmt)) { 8834235Sbostic prec = 0; 8934233Sbostic do { 9034236Sbostic prec = 10 * prec + *fmt - '0'; 9134233Sbostic } while isdigit(*++fmt); 9234233Sbostic --fmt; 9334226Sbostic } 9434235Sbostic else { 9534235Sbostic prec = 0; 9634235Sbostic --fmt; 9734235Sbostic goto flags; 9834235Sbostic } 9934235Sbostic if (prec < 0) 10034235Sbostic prec = -1; 10134233Sbostic goto flags; 10234233Sbostic case '0': 10334233Sbostic padc = '0'; 10434235Sbostic /*FALLTHROUGH*/ 10534233Sbostic case '1': case '2': case '3': case '4': 10634233Sbostic case '5': case '6': case '7': case '8': case '9': 10734233Sbostic do { 10834236Sbostic width = 10 * width + *fmt - '0'; 10934233Sbostic } while isdigit(*++fmt); 11034233Sbostic --fmt; 11134235Sbostic case 'L': 11234235Sbostic argsize |= LONGDBL; 11334235Sbostic goto flags; 11434235Sbostic case 'h': 11534235Sbostic argsize |= SHORTINT; 11634235Sbostic goto flags; 11734233Sbostic case 'l': 11834235Sbostic argsize |= LONGINT; 11934233Sbostic goto flags; 120*34243Sbostic case '%': /* "%#%" prints as "%" */ 121*34243Sbostic PUTC('%', fp); 122*34243Sbostic break; 123*34243Sbostic case 'c': { 124*34243Sbostic char ch; 12534226Sbostic 126*34243Sbostic ch = va_arg(argp, int); 127*34243Sbostic PUTC(ch, fp); 12834226Sbostic break; 129*34243Sbostic } 13034241Sbostic case 'd': 13134241Sbostic case 'i': 13234241Sbostic GETARG(reg_long); 13334241Sbostic if (reg_long < 0) { 13434241Sbostic reg_ulong = -reg_long; 13534241Sbostic printsign = '-'; 13634241Sbostic } 13734241Sbostic else { 13834241Sbostic reg_ulong = reg_long; 13934241Sbostic } 14034241Sbostic if (printsign) 14134241Sbostic PUTC(printsign, fp); 14234241Sbostic base = 10; 14334241Sbostic goto num1; 14434236Sbostic case 'E': 14534236Sbostic case 'e': 14634236Sbostic _double = va_arg(argp, double); 14734242Sbostic bp = _cvt(_double, prec, buf, EFORMAT, *fmt, 148*34243Sbostic printsign, alternate); 14934236Sbostic goto pbuf; 15034235Sbostic case 'f': 15134235Sbostic _double = va_arg(argp, double); 15234242Sbostic bp = _cvt(_double, prec, buf, FFORMAT, 'f', 153*34243Sbostic printsign, alternate); 154*34243Sbostic goto pbuf; 155*34243Sbostic case 'G': 156*34243Sbostic case 'g': 157*34243Sbostic _double = va_arg(argp, double); 158*34243Sbostic bp = _cvt(_double, prec, buf, GFORMAT, *fmt - 2, 159*34243Sbostic printsign, alternate); 16034236Sbostic pbuf: size = bp - buf; 16134235Sbostic if (size < width && !ladjust) 16234235Sbostic do { 16334235Sbostic PUTC(padc, fp); 16434235Sbostic } while (--width > size); 16534235Sbostic for (t = buf; t < bp; ++t) 16634235Sbostic PUTC(*t, fp); 16734235Sbostic for (; width > size; --width) 16834235Sbostic PUTC(padc, fp); 16934235Sbostic break; 17034235Sbostic case 'n': 17134235Sbostic *(va_arg(argp, int *)) = cnt; 17234235Sbostic break; 17334226Sbostic case 'o': 17434235Sbostic GETARG(reg_ulong); 17534226Sbostic base = 8; 17634235Sbostic if (!reg_ulong || !alternate) 17734235Sbostic goto num1; 17834235Sbostic bp = buf + sizeof(buf) - 1; 17934235Sbostic do { 18034235Sbostic *bp-- = digs[reg_ulong % base]; 18134235Sbostic reg_ulong /= base; 18234235Sbostic } while(reg_ulong); 18334235Sbostic size = &buf[sizeof(buf) - 1] - bp; 18434235Sbostic if (size < --width && !ladjust) 18534235Sbostic do { 18634235Sbostic PUTC(padc, fp); 18734235Sbostic } while (--width > size); 18834235Sbostic PUTC('0', fp); 18934236Sbostic goto num2; 19034235Sbostic case 'p': 19134226Sbostic case 's': 19234235Sbostic if (!(bp = va_arg(argp, char *))) 19334235Sbostic bp = "(null)"; 19434235Sbostic if (width > 0 && !ladjust) { 19534233Sbostic char *savep; 19634226Sbostic 19734235Sbostic savep = bp; 19834235Sbostic for (n = 0; *bp && (prec < 0 || n < prec); 19934235Sbostic n++, bp++); 20034235Sbostic bp = savep; 20134235Sbostic while (n++ < width) 20234235Sbostic PUTC(' ', fp); 20334233Sbostic } 20434235Sbostic for (n = 0; *bp; ++bp) { 20534235Sbostic if (++n > prec && prec >= 0) 20634226Sbostic break; 20734235Sbostic PUTC(*bp, fp); 20834233Sbostic } 20934235Sbostic if (n < width && ladjust) 21034233Sbostic do { 21134235Sbostic PUTC(' ', fp); 21234235Sbostic } while (++n < width); 21334226Sbostic break; 21434226Sbostic case 'u': 21534235Sbostic GETARG(reg_ulong); 21634226Sbostic base = 10; 21734235Sbostic goto num1; 21834226Sbostic case 'X': 21934226Sbostic digs = "0123456789ABCDEF"; 22034233Sbostic /*FALLTHROUGH*/ 22134226Sbostic case 'x': 22234235Sbostic GETARG(reg_ulong); 22334233Sbostic if (alternate && reg_ulong) { 22434235Sbostic PUTC('0', fp); 22534235Sbostic PUTC(*fmt, fp); 22634233Sbostic } 22734226Sbostic base = 16; 22834235Sbostic num1: bp = buf + sizeof(buf) - 1; 22934233Sbostic do { 23034235Sbostic *bp-- = digs[reg_ulong % base]; 23134233Sbostic reg_ulong /= base; 23234233Sbostic } while(reg_ulong); 23334235Sbostic size = &buf[sizeof(buf) - 1] - bp; 23434235Sbostic for (; size < prec; *bp-- = '0', ++size); 23534235Sbostic if (size < width && !ladjust) 23634235Sbostic do { 23734235Sbostic PUTC(padc, fp); 23834235Sbostic } while (--width > size); 23934236Sbostic num2: while (++bp != &buf[MAXBUF]) 24034235Sbostic PUTC(*bp, fp); 24134235Sbostic for (; width > size; --width) 24234235Sbostic PUTC(padc, fp); 243*34243Sbostic digs = "0123456789abcdef"; 24434226Sbostic break; 24534233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 24634235Sbostic return(ferror(fp) ? -1 : cnt); 24734226Sbostic default: 24834235Sbostic PUTC(*fmt, fp); 24934226Sbostic } 25034226Sbostic } 25134235Sbostic return(ferror(fp) ? -1 : cnt); 25234226Sbostic } 25334242Sbostic 25434242Sbostic char * 255*34243Sbostic _cvt(number, prec, bp, format, fmtch, printsign, alternate) 25634242Sbostic double number; 257*34243Sbostic int prec, format, alternate; 25834242Sbostic register char *bp; 25934242Sbostic char fmtch, printsign; 26034242Sbostic { 26134242Sbostic int sign, decpt; 26234242Sbostic register char *t; 26334242Sbostic register int n; 26434242Sbostic double fabs(); 26534242Sbostic char *ecvt(), *fcvt(); 26634242Sbostic 26734242Sbostic if (prec == -1) 26834242Sbostic prec = DEFPREC; 26934242Sbostic t = fabs(number) < 1 ? ecvt(number, prec + 1, &decpt, &sign) : 27034242Sbostic fcvt(number, prec + 1, &decpt, &sign); 27134242Sbostic 27234242Sbostic if (sign) 27334242Sbostic *bp++ = '-'; 27434242Sbostic else if (printsign) 27534242Sbostic *bp++ = printsign; 27634242Sbostic 27734242Sbostic /* E format */ 27834242Sbostic /* use 'e' format if exponent > precision or less than -4 */ 27934242Sbostic if (format == EFORMAT || 28034242Sbostic format == GFORMAT && (decpt > prec || decpt < -3)) { 28134242Sbostic *bp++ = *t ? *t++ : '0'; 28234242Sbostic if (format != GFORMAT && prec || prec > 1) { 28334242Sbostic *bp++ = '.'; 28434242Sbostic while(prec--) 28534242Sbostic *bp++ = *t ? *t++ : '0'; 28634242Sbostic } 287*34243Sbostic else if (alternate) 288*34243Sbostic *bp++ = '.'; 28934242Sbostic if (*t && *t > '4') 29034242Sbostic ++bp[-1]; 291*34243Sbostic if (format == GFORMAT && !alternate) { 29234242Sbostic for (; bp[-1] == '0'; --bp); 293*34243Sbostic if (bp[-1] == '.') 29434242Sbostic --bp; 29534242Sbostic } 29634242Sbostic *bp++ = fmtch; 29734242Sbostic if (--decpt < 0) { 29834242Sbostic decpt = -decpt; 29934242Sbostic *bp++ = '-'; 30034242Sbostic } 30134242Sbostic else 30234242Sbostic *bp++ = '+'; 30334242Sbostic *bp++ = decpt / 10 + '0'; 30434242Sbostic *bp++ = decpt % 10 + '0'; 30534242Sbostic } 30634242Sbostic /* F format */ 30734242Sbostic else { 30834242Sbostic if (decpt <= 0) { 30934242Sbostic *bp++ = '0'; 31034242Sbostic if (prec) { 31134242Sbostic *bp++ = '.'; 31234242Sbostic if (format == FFORMAT) 31334242Sbostic while (decpt++ < 0 && prec--) 31434242Sbostic *bp++ = '0'; 31534242Sbostic else while (decpt++ < 0) 31634242Sbostic *bp++ = '0'; 31734242Sbostic } 318*34243Sbostic else if (alternate) 319*34243Sbostic *bp++ = '.'; 32034242Sbostic } 32134242Sbostic else { 32234242Sbostic for (n = 1; n <= decpt; n++) 32334242Sbostic *bp++ = *t++; 324*34243Sbostic if (prec || alternate) 32534242Sbostic *bp++ = '.'; 32634242Sbostic } 32734242Sbostic for (n = 1; n <= prec; n++) 32834242Sbostic *bp++ = *t ? *t++ : '0'; 329*34243Sbostic if (format == GFORMAT && !alternate) { 33034242Sbostic for (; bp[-1] == '0'; --bp); 33134242Sbostic if (bp[-1] == '.') 33234242Sbostic --bp; 33334242Sbostic } 33434242Sbostic } 33534242Sbostic return(bp); 33634242Sbostic } 337