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*34242Sbostic static char sccsid[] = "@(#)vfprintf.c 5.6 (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 22*34242Sbostic #define MAXBUF 120 23*34242Sbostic #define DEFPREC 6 2434226Sbostic 2534236Sbostic #define PUTC(ch, fd) {++cnt; putc(ch, fd);} 2634236Sbostic 27*34242Sbostic #define EFORMAT 1 28*34242Sbostic #define FFORMAT 2 29*34242Sbostic #define GFORMAT 3 30*34242Sbostic 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; 48*34242Sbostic char argsize, printsign, *_cvt(), buf[MAXBUF]; 49*34242Sbostic int alternate, cnt, n, ladjust, width, prec, size; 5034226Sbostic 5134233Sbostic for (cnt = 0; *fmt; ++fmt) { 5234233Sbostic if (*fmt != '%') { 5334235Sbostic PUTC(*fmt, fp); 5434233Sbostic continue; 5534226Sbostic } 5634226Sbostic 5734235Sbostic alternate = ladjust = width = 0; 5834233Sbostic prec = -1; 5934233Sbostic padc = ' '; 6034235Sbostic argsize = printsign = '\0'; 6134226Sbostic 6234233Sbostic flags: switch (*++fmt) { 6334233Sbostic case '#': 6434233Sbostic alternate = 1; 6534233Sbostic goto flags; 6634233Sbostic case '%': /* "%#%" prints as "%" */ 6734235Sbostic PUTC('%', fp); 6834233Sbostic continue; 6934233Sbostic case '*': 7034235Sbostic /* 7134235Sbostic * ``A negative field width argument is taken as a 7234235Sbostic * - flag followed by a positive field width.'' 7334235Sbostic * -- ANSI X3J11 7434235Sbostic * They don't exclude field widths read from args. 7534235Sbostic */ 7634235Sbostic if ((width = va_arg(argp, int)) >= 0) 7734235Sbostic goto flags; 7834235Sbostic width = -width; 7934235Sbostic /*FALLTHROUGH*/ 8034235Sbostic case '-': 8134235Sbostic ladjust = 1; 8234233Sbostic goto flags; 8334233Sbostic case '+': 8434233Sbostic printsign = '+'; 8534233Sbostic goto flags; 8634233Sbostic case '.': 8734235Sbostic if (*++fmt == '*') 8834235Sbostic prec = va_arg(argp, int); 8934235Sbostic else if (isdigit(*fmt)) { 9034235Sbostic prec = 0; 9134233Sbostic do { 9234236Sbostic prec = 10 * prec + *fmt - '0'; 9334233Sbostic } while isdigit(*++fmt); 9434233Sbostic --fmt; 9534226Sbostic } 9634235Sbostic else { 9734235Sbostic prec = 0; 9834235Sbostic --fmt; 9934235Sbostic goto flags; 10034235Sbostic } 10134235Sbostic if (prec < 0) 10234235Sbostic prec = -1; 10334233Sbostic goto flags; 10434233Sbostic case '0': 10534233Sbostic padc = '0'; 10634235Sbostic /*FALLTHROUGH*/ 10734233Sbostic case '1': case '2': case '3': case '4': 10834233Sbostic case '5': case '6': case '7': case '8': case '9': 10934233Sbostic do { 11034236Sbostic width = 10 * width + *fmt - '0'; 11134233Sbostic } while isdigit(*++fmt); 11234233Sbostic --fmt; 11334235Sbostic case 'L': 11434235Sbostic argsize |= LONGDBL; 11534235Sbostic goto flags; 11634235Sbostic case 'h': 11734235Sbostic argsize |= SHORTINT; 11834235Sbostic goto flags; 11934233Sbostic case 'l': 12034235Sbostic argsize |= LONGINT; 12134233Sbostic goto flags; 12234226Sbostic } 12334226Sbostic 12434233Sbostic digs = "0123456789abcdef"; 12534226Sbostic 12634233Sbostic switch (*fmt) { 12734226Sbostic case 'c': 12834235Sbostic PUTC(va_arg(argp, int), fp); 12934226Sbostic break; 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); 147*34242Sbostic bp = _cvt(_double, prec, buf, EFORMAT, *fmt, 148*34242Sbostic printsign); 14934236Sbostic goto pbuf; 15034235Sbostic case 'f': 15134235Sbostic _double = va_arg(argp, double); 152*34242Sbostic bp = _cvt(_double, prec, buf, FFORMAT, 'f', 153*34242Sbostic printsign); 15434236Sbostic pbuf: size = bp - buf; 15534235Sbostic if (size < width && !ladjust) 15634235Sbostic do { 15734235Sbostic PUTC(padc, fp); 15834235Sbostic } while (--width > size); 15934235Sbostic for (t = buf; t < bp; ++t) 16034235Sbostic PUTC(*t, fp); 16134235Sbostic for (; width > size; --width) 16234235Sbostic PUTC(padc, fp); 16334235Sbostic break; 16434241Sbostic case 'G': 165*34242Sbostic case 'g': 16634241Sbostic _double = va_arg(argp, double); 167*34242Sbostic bp = _cvt(_double, prec, buf, GFORMAT, *fmt - 2, 168*34242Sbostic printsign); 169*34242Sbostic goto pbuf; 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); 24334226Sbostic break; 24434233Sbostic case '\0': /* "%?" prints ?, unless ? is NULL */ 24534235Sbostic return(ferror(fp) ? -1 : cnt); 24634226Sbostic default: 24734235Sbostic PUTC(*fmt, fp); 24834226Sbostic } 24934226Sbostic } 25034235Sbostic return(ferror(fp) ? -1 : cnt); 25134226Sbostic } 252*34242Sbostic 253*34242Sbostic char * 254*34242Sbostic _cvt(number, prec, bp, format, fmtch, printsign) 255*34242Sbostic double number; 256*34242Sbostic int prec, format; 257*34242Sbostic register char *bp; 258*34242Sbostic char fmtch, printsign; 259*34242Sbostic { 260*34242Sbostic int sign, decpt; 261*34242Sbostic register char *t; 262*34242Sbostic register int n; 263*34242Sbostic double fabs(); 264*34242Sbostic char *ecvt(), *fcvt(); 265*34242Sbostic 266*34242Sbostic if (prec == -1) 267*34242Sbostic prec = DEFPREC; 268*34242Sbostic t = fabs(number) < 1 ? ecvt(number, prec + 1, &decpt, &sign) : 269*34242Sbostic fcvt(number, prec + 1, &decpt, &sign); 270*34242Sbostic 271*34242Sbostic if (sign) 272*34242Sbostic *bp++ = '-'; 273*34242Sbostic else if (printsign) 274*34242Sbostic *bp++ = printsign; 275*34242Sbostic 276*34242Sbostic /* E format */ 277*34242Sbostic /* use 'e' format if exponent > precision or less than -4 */ 278*34242Sbostic if (format == EFORMAT || 279*34242Sbostic format == GFORMAT && (decpt > prec || decpt < -3)) { 280*34242Sbostic *bp++ = *t ? *t++ : '0'; 281*34242Sbostic if (format != GFORMAT && prec || prec > 1) { 282*34242Sbostic *bp++ = '.'; 283*34242Sbostic while(prec--) 284*34242Sbostic *bp++ = *t ? *t++ : '0'; 285*34242Sbostic } 286*34242Sbostic if (*t && *t > '4') 287*34242Sbostic ++bp[-1]; 288*34242Sbostic if (format == 2) { 289*34242Sbostic for (; bp[-1] == '0'; --bp); 290*34242Sbostic if (*bp == '.') 291*34242Sbostic --bp; 292*34242Sbostic } 293*34242Sbostic *bp++ = fmtch; 294*34242Sbostic if (--decpt < 0) { 295*34242Sbostic decpt = -decpt; 296*34242Sbostic *bp++ = '-'; 297*34242Sbostic } 298*34242Sbostic else 299*34242Sbostic *bp++ = '+'; 300*34242Sbostic *bp++ = decpt / 10 + '0'; 301*34242Sbostic *bp++ = decpt % 10 + '0'; 302*34242Sbostic } 303*34242Sbostic /* F format */ 304*34242Sbostic else { 305*34242Sbostic if (decpt <= 0) { 306*34242Sbostic *bp++ = '0'; 307*34242Sbostic if (prec) { 308*34242Sbostic *bp++ = '.'; 309*34242Sbostic if (format == FFORMAT) 310*34242Sbostic while (decpt++ < 0 && prec--) 311*34242Sbostic *bp++ = '0'; 312*34242Sbostic else while (decpt++ < 0) 313*34242Sbostic *bp++ = '0'; 314*34242Sbostic } 315*34242Sbostic } 316*34242Sbostic else { 317*34242Sbostic for (n = 1; n <= decpt; n++) 318*34242Sbostic *bp++ = *t++; 319*34242Sbostic if (prec) 320*34242Sbostic *bp++ = '.'; 321*34242Sbostic } 322*34242Sbostic for (n = 1; n <= prec; n++) 323*34242Sbostic *bp++ = *t ? *t++ : '0'; 324*34242Sbostic if (format == GFORMAT) { 325*34242Sbostic for (; bp[-1] == '0'; --bp); 326*34242Sbostic if (bp[-1] == '.') 327*34242Sbostic --bp; 328*34242Sbostic } 329*34242Sbostic } 330*34242Sbostic return(bp); 331*34242Sbostic } 332