1 /* 2 * Copyright (c) 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that this notice is preserved and that due credit is given 7 * to the University of California at Berkeley. The name of the University 8 * may not be used to endorse or promote products derived from this 9 * software without specific prior written permission. This software 10 * is provided ``as is'' without express or implied warranty. 11 */ 12 13 #if defined(LIBC_SCCS) && !defined(lint) 14 static char sccsid[] = "@(#)vfprintf.c 5.8 (Berkeley) 05/10/88"; 15 #endif /* LIBC_SCCS and not lint */ 16 17 #include <sys/param.h> 18 #include <varargs.h> 19 #include <stdio.h> 20 #include <ctype.h> 21 22 #define MAXBUF 120 23 #define DEFPREC 6 24 25 #define PUTC(ch, fd) {++cnt; putc(ch, fd);} 26 27 #define EFORMAT 0x01 28 #define FFORMAT 0x02 29 #define GFORMAT 0x04 30 31 #define LONGINT 0x01 32 #define LONGDBL 0x02 33 #define SHORTINT 0x04 34 #define GETARG(r) \ 35 r = argsize&LONGINT ? va_arg(argp, long) : \ 36 argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 37 38 static int alternate; 39 static char printsign; 40 41 x_doprnt(fmt, argp, fp) 42 register char *fmt; 43 va_list argp; 44 register FILE *fp; 45 { 46 register u_long reg_ulong; 47 register long reg_long; 48 register int base; 49 register char *digs, *bp, *t, padc; 50 double _double; 51 char argsize, *_cvt(), buf[MAXBUF]; 52 int cnt, n, ladjust, width, prec, size; 53 54 digs = "0123456789abcdef"; 55 for (cnt = 0; *fmt; ++fmt) { 56 if (*fmt != '%') { 57 PUTC(*fmt, fp); 58 continue; 59 } 60 61 alternate = ladjust = width = 0; 62 prec = -1; 63 padc = ' '; 64 argsize = printsign = '\0'; 65 66 flags: switch (*++fmt) { 67 case '#': 68 alternate = 1; 69 goto flags; 70 case '*': 71 /* 72 * ``A negative field width argument is taken as a 73 * - flag followed by a positive field width.'' 74 * -- ANSI X3J11 75 * They don't exclude field widths read from args. 76 */ 77 if ((width = va_arg(argp, int)) >= 0) 78 goto flags; 79 width = -width; 80 /*FALLTHROUGH*/ 81 case '-': 82 ladjust = 1; 83 goto flags; 84 case '+': 85 printsign = '+'; 86 goto flags; 87 case '.': 88 if (*++fmt == '*') 89 prec = va_arg(argp, int); 90 else if (isdigit(*fmt)) { 91 prec = 0; 92 do { 93 prec = 10 * prec + *fmt - '0'; 94 } while isdigit(*++fmt); 95 --fmt; 96 } 97 else { 98 prec = 0; 99 --fmt; 100 goto flags; 101 } 102 if (prec < 0) 103 prec = -1; 104 goto flags; 105 case '0': 106 padc = '0'; 107 /*FALLTHROUGH*/ 108 case '1': case '2': case '3': case '4': 109 case '5': case '6': case '7': case '8': case '9': 110 do { 111 width = 10 * width + *fmt - '0'; 112 } while isdigit(*++fmt); 113 --fmt; 114 case 'L': 115 argsize |= LONGDBL; 116 goto flags; 117 case 'h': 118 argsize |= SHORTINT; 119 goto flags; 120 case 'l': 121 argsize |= LONGINT; 122 goto flags; 123 case '%': /* "%#%" prints as "%" */ 124 PUTC('%', fp); 125 break; 126 case 'c': { 127 char ch; 128 129 ch = va_arg(argp, int); 130 PUTC(ch, fp); 131 break; 132 } 133 case 'd': 134 case 'i': 135 GETARG(reg_long); 136 if (reg_long < 0) { 137 reg_ulong = -reg_long; 138 printsign = '-'; 139 } 140 else { 141 reg_ulong = reg_long; 142 } 143 if (printsign) 144 PUTC(printsign, fp); 145 base = 10; 146 goto num1; 147 case 'E': 148 case 'e': 149 _double = va_arg(argp, double); 150 bp = _cvt(_double, prec, EFORMAT, buf, 151 buf + sizeof(buf), *fmt); 152 goto pbuf; 153 case 'f': 154 _double = va_arg(argp, double); 155 bp = _cvt(_double, prec, FFORMAT, buf, 156 buf + sizeof(buf), 'f'); 157 goto pbuf; 158 case 'G': 159 case 'g': 160 _double = va_arg(argp, double); 161 bp = _cvt(_double, prec, GFORMAT, buf, 162 buf + sizeof(buf), *fmt - 2); 163 pbuf: size = bp - buf; 164 if (size < width && !ladjust) 165 do { 166 PUTC(padc, fp); 167 } while (--width > size); 168 for (t = buf; t < bp; ++t) 169 PUTC(*t, fp); 170 for (; width > size; --width) 171 PUTC(padc, fp); 172 break; 173 case 'n': 174 *(va_arg(argp, int *)) = cnt; 175 break; 176 case 'o': 177 GETARG(reg_ulong); 178 base = 8; 179 if (!reg_ulong || !alternate) 180 goto num1; 181 bp = buf + sizeof(buf) - 1; 182 do { 183 *bp-- = digs[reg_ulong % base]; 184 reg_ulong /= base; 185 } while(reg_ulong); 186 size = &buf[sizeof(buf) - 1] - bp; 187 if (size < --width && !ladjust) 188 do { 189 PUTC(padc, fp); 190 } while (--width > size); 191 PUTC('0', fp); 192 goto num2; 193 case 'p': 194 case 's': 195 if (!(bp = va_arg(argp, char *))) 196 bp = "(null)"; 197 if (width > 0 && !ladjust) { 198 char *savep; 199 200 savep = bp; 201 for (n = 0; *bp && (prec < 0 || n < prec); 202 n++, bp++); 203 bp = savep; 204 while (n++ < width) 205 PUTC(' ', fp); 206 } 207 for (n = 0; *bp; ++bp) { 208 if (++n > prec && prec >= 0) 209 break; 210 PUTC(*bp, fp); 211 } 212 if (n < width && ladjust) 213 do { 214 PUTC(' ', fp); 215 } while (++n < width); 216 break; 217 case 'u': 218 GETARG(reg_ulong); 219 base = 10; 220 goto num1; 221 case 'X': 222 digs = "0123456789ABCDEF"; 223 /*FALLTHROUGH*/ 224 case 'x': 225 GETARG(reg_ulong); 226 if (alternate && reg_ulong) { 227 PUTC('0', fp); 228 PUTC(*fmt, fp); 229 } 230 base = 16; 231 num1: bp = buf + sizeof(buf) - 1; 232 do { 233 *bp-- = digs[reg_ulong % base]; 234 reg_ulong /= base; 235 } while(reg_ulong); 236 size = &buf[sizeof(buf) - 1] - bp; 237 for (; size < prec; *bp-- = '0', ++size); 238 if (size < width && !ladjust) 239 do { 240 PUTC(padc, fp); 241 } while (--width > size); 242 num2: while (++bp != &buf[MAXBUF]) 243 PUTC(*bp, fp); 244 for (; width > size; --width) 245 PUTC(padc, fp); 246 digs = "0123456789abcdef"; 247 break; 248 case '\0': /* "%?" prints ?, unless ? is NULL */ 249 return(ferror(fp) ? -1 : cnt); 250 default: 251 PUTC(*fmt, fp); 252 } 253 } 254 return(ferror(fp) ? -1 : cnt); 255 } 256 257 char * 258 _cvt(number, prec, format, startp, endp, fmtch) 259 double number; 260 int prec, format; 261 char *startp, *endp, fmtch; 262 { 263 register char *p; 264 double fract, integer, tmp, modf(); 265 int decpt, expcnt; 266 char *savep; 267 268 if (prec == -1) /* set default precision */ 269 prec = DEFPREC; 270 271 p = endp - 1; 272 if (number < 0) { /* set sign */ 273 *startp++ = '-'; 274 number = -number; 275 } 276 else if (printsign) 277 *startp++ = '+'; 278 279 /* 280 * if the alternate flag is set, or, at least one digit of precision 281 * was requested, add a decimal point, unless it's the g/G format 282 * in which case we require two digits of precision, since it counts 283 * precision differently. 284 */ 285 decpt = alternate || prec > 1 || !(format&GFORMAT) && prec; 286 287 expcnt = 0; 288 fract = modf(number, &integer); 289 if (integer) { 290 register char *p2; 291 292 /* get integer part of number; count decimal places */ 293 for (; integer; ++expcnt) { 294 tmp = modf(integer / 10, &integer); 295 *p-- = (int)((tmp + .03) * 10) + '0'; 296 } 297 298 /* copy, in reverse order, to start of buffer */ 299 p2 = startp; 300 *p2++ = *++p; 301 302 /* 303 * if the format is g/G, and the resulting exponent will be 304 * greater than the precision, use e/E format. If e/E format, 305 * put in a decimal point as needed, and decrement precision 306 * count for each digit after the decimal point. 307 */ 308 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 309 if (format&GFORMAT) { 310 format |= EFORMAT; 311 312 /* first digit is precision for g/G format */ 313 if (prec) 314 --prec; 315 } 316 if (decpt) 317 *p2++ = '.'; 318 for (; ++p < endp && prec; --prec, *p2++ = *p); 319 320 /* precision ran out; round number */ 321 if (p < endp) { 322 if (*p > '4') { 323 for (savep = p2--;; *p2-- = '0') { 324 if (*p2 == '.') 325 --p2; 326 if (++*p2 <= '9') 327 break; 328 } 329 p2 = savep; 330 } 331 fract = 0; 332 } 333 } 334 /* 335 * g/G in f format; if run out of precision, replace digits 336 * with zeroes, note, have to round first, otherwise lose 337 * rounding point. 338 */ 339 else if (format&GFORMAT) { 340 for (; ++p < endp && prec; --prec, *p2++ = *p); 341 /* precision ran out; round and then add zeroes */ 342 if (p < endp) { 343 if (*p > '4') { 344 for (savep = p2--; ++*p2 > '9'; 345 *p2-- = '0'); 346 p2 = savep; 347 } 348 do { 349 *p2++ = '0'; 350 } while (++p < endp); 351 fract = 0; 352 } 353 if (decpt) 354 *p2++ = '.'; 355 } 356 /* f format */ 357 else { 358 for (; ++p < endp; *p2++ = *p); 359 if (decpt) 360 *p2++ = '.'; 361 } 362 p = p2; 363 } 364 /* 365 * it's unclear from the ANSI X3J11 spec if the g/G format should 366 * just result in an empty string, because it's supposed to remove 367 * trailing zeroes. That seems counter-intuitive, so here it does 368 * what f and e/E do; if no fraction, the number was zero, and if 369 * no precision can't show anything after the decimal point. 370 */ 371 else if (!fract || !prec) { 372 *startp++ = '0'; 373 if (decpt) 374 *startp++ = '.'; 375 *startp++ = '\0'; 376 return(startp); 377 } 378 /* 379 * if the format is g/G, and the resulting exponent will be less than 380 * -4 use e/E format. If e/E format, compute exponent value. 381 */ 382 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 383 format |= EFORMAT; 384 if (fract) 385 for (p = startp; fract;) { 386 fract = modf(fract * 10, &tmp); 387 if (!tmp) { 388 --expcnt; 389 continue; 390 } 391 *p++ = (int)tmp + '0'; 392 break; 393 } 394 else 395 *p++ = '0'; 396 397 /* g/G format, decrement precision for first digit */ 398 if (format&GFORMAT && prec) 399 --prec; 400 401 /* add decimal after first non-zero digit */ 402 if (decpt) 403 *p++ = '.'; 404 } 405 /* 406 * f format or g/G printed as f format; don't worry about decimal 407 * point, if g/G format doesn't need it, will get stripped later. 408 */ 409 else { 410 p = startp; 411 *p++ = '0'; 412 *p++ = '.'; 413 } 414 415 /* finish out requested precision from fractional value */ 416 while (prec--) 417 if (fract) { 418 fract = modf(fract * 10, &tmp); 419 *p++ = (int)tmp + '0'; 420 } 421 else 422 *p++ = '0'; 423 424 /* 425 * if any fractional value left, "round" it back up to the beginning 426 * of the number, fixing the exponent as necessary, and avoiding the 427 * decimal point. 428 */ 429 if (fract) { 430 (void)modf(fract * 10, &tmp); 431 if (tmp > 4) { 432 for (savep = p--;; *p-- = '0') { 433 if (*p == '.') 434 --p; 435 if (p == startp) { 436 *p = '1'; 437 ++expcnt; 438 break; 439 } 440 if (++*p <= '9') 441 break; 442 } 443 p = savep; 444 } 445 } 446 447 /* 448 * if a g/G format and not alternate flag, lose trailing zeroes, 449 * if e/E or g/G format, and last char is decimal point, lose it. 450 */ 451 if (!alternate) { 452 if (format&GFORMAT) 453 for (; p[-1] == '0'; --p); 454 if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 455 --p; 456 } 457 458 /* if an e/E format, add exponent */ 459 if (format&EFORMAT) { 460 *p++ = fmtch; 461 if (--expcnt < 0) { 462 expcnt = -expcnt; 463 *p++ = '-'; 464 } 465 else 466 *p++ = '+'; 467 *p++ = expcnt / 10 + '0'; 468 *p++ = expcnt % 10 + '0'; 469 } 470 return(p); 471 } 472