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.23 (Berkeley) 05/18/88"; 15 #endif /* LIBC_SCCS and not lint */ 16 17 #include <sys/types.h> 18 #include <varargs.h> 19 #include <stdio.h> 20 #include <ctype.h> 21 22 #define MAXBUF 512 23 24 #define PUTC(ch) {++cnt; putc((char)ch, fp);} 25 26 #define ARG() \ 27 _ulong = flags&LONGINT ? va_arg(argp, long) : \ 28 flags&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 29 30 /* have to deal with the negative buffer count kludge */ 31 #define NEGATIVE_COUNT_KLUDGE 32 33 #define LONGINT 0x01 /* long integer */ 34 #define LONGDBL 0x02 /* long double; unimplemented */ 35 #define SHORTINT 0x04 /* short integer */ 36 #define ALT 0x08 /* alternate form */ 37 #define LADJUST 0x10 /* left adjustment */ 38 39 _doprnt(fmt0, argp, fp) 40 u_char *fmt0; 41 va_list argp; 42 register FILE *fp; 43 { 44 register u_char *fmt; 45 register int ch, cnt, n; 46 register char *t; 47 double _double; 48 u_long _ulong; 49 int base, flags, prec, size, width; 50 char padc, sign, *digs, buf[MAXBUF], *_cvt(); 51 52 fmt = fmt0; 53 digs = "0123456789abcdef"; 54 for (cnt = 0;; ++fmt) { 55 n = fp->_cnt; 56 for (t = fp->_ptr; (ch = *fmt) && ch != '%'; ++cnt, ++fmt) 57 if (--n < 0 58 #ifdef NEGATIVE_COUNT_KLUDGE 59 && (!(fp->_flag & _IOLBF) || -n >= fp->_bufsiz) 60 #endif 61 || ch == '\n' && fp->_flag&_IOLBF) { 62 fp->_cnt = n; 63 fp->_ptr = t; 64 (void)_flsbuf(ch, fp); 65 n = fp->_cnt; 66 t = fp->_ptr; 67 } 68 else 69 *t++ = ch; 70 fp->_cnt = n; 71 fp->_ptr = t; 72 if (!ch) 73 return(cnt); 74 75 flags = width = 0; 76 prec = -1; 77 padc = ' '; 78 sign = '\0'; 79 80 rflag: switch (*++fmt) { 81 case ' ': 82 sign = ' '; 83 goto rflag; 84 case '#': 85 flags |= ALT; 86 goto rflag; 87 case '*': 88 /* 89 * ``A negative field width argument is taken as a 90 * - flag followed by a positive field width.'' 91 * -- ANSI X3J11 92 * They don't exclude field widths read from args. 93 */ 94 if ((width = va_arg(argp, int)) >= 0) 95 goto rflag; 96 width = -width; 97 /*FALLTHROUGH*/ 98 case '-': 99 flags |= LADJUST; 100 goto rflag; 101 case '+': 102 sign = '+'; 103 goto rflag; 104 case '.': 105 if (*++fmt == '*') 106 n = va_arg(argp, int); 107 else if (isascii(*fmt) && isdigit(*fmt)) { 108 n = 0; 109 do { 110 n = 10 * n + *fmt - '0'; 111 } while (isascii(*++fmt) && isdigit(*fmt)); 112 --fmt; 113 } 114 else { 115 --fmt; 116 prec = 0; 117 goto rflag; 118 } 119 prec = n < 0 ? -1 : n; 120 goto rflag; 121 case '0': 122 padc = '0'; 123 /*FALLTHROUGH*/ 124 case '1': case '2': case '3': case '4': 125 case '5': case '6': case '7': case '8': case '9': 126 n = 0; 127 do { 128 n = 10 * n + *fmt - '0'; 129 } while (isascii(*++fmt) && isdigit(*fmt)); 130 width = n; 131 --fmt; 132 goto rflag; 133 case 'L': 134 /* 135 * C doesn't have a long double; use long for now. 136 * flags |= LONGDBL; 137 */ 138 flags |= LONGINT; 139 goto rflag; 140 case 'h': 141 flags |= SHORTINT; 142 goto rflag; 143 case 'l': 144 flags |= LONGINT; 145 goto rflag; 146 case 'c': 147 buf[0] = va_arg(argp, int); 148 size = 1; 149 t = buf; 150 goto pforw; 151 case 'd': 152 case 'i': 153 ARG(); 154 if ((long)_ulong < 0) { 155 _ulong = -_ulong; 156 sign = '-'; 157 } 158 base = 10; 159 goto number; 160 case 'e': 161 case 'E': 162 case 'f': 163 case 'g': 164 case 'G': 165 _double = va_arg(argp, double); 166 size = _cvt(_double, prec, flags, *fmt, padc, &sign, 167 buf, buf + sizeof(buf)) - buf; 168 t = buf; 169 /* 170 * zero-padded sign put out here; blank padded sign 171 * placed in number in _cvt(). 172 */ 173 if (sign && padc == '0') { 174 PUTC(sign); 175 --width; 176 } 177 goto pforw; 178 case 'n': 179 if (flags&LONGDBL || flags&LONGINT) 180 *va_arg(argp, long *) = cnt; 181 else if (flags&SHORTINT) 182 *va_arg(argp, short *) = cnt; 183 else 184 *va_arg(argp, int *) = cnt; 185 break; 186 case 'o': 187 ARG(); 188 base = 8; 189 goto nosign; 190 case 'p': 191 /* 192 * ``The argument shall be a pointer to void. The 193 * value of the pointer is converted to a sequence 194 * of printable characters, in an implementation- 195 * defined manner.'' 196 * -- ANSI X3J11 197 */ 198 /*NOSTRICT*/ 199 _ulong = (u_long)va_arg(argp, void *); 200 base = 16; 201 goto nosign; 202 case 's': 203 if (!(t = va_arg(argp, char *))) 204 t = "(null)"; 205 if (prec >= 0) { 206 /* 207 * can't use strlen; can only look for the 208 * NUL in the first `prec' characters, and 209 * strlen() will go further. 210 */ 211 char *p, *memchr(); 212 213 if (p = memchr(t, 0, prec)) { 214 size = p - t; 215 if (size > prec) 216 size = prec; 217 } 218 else 219 size = prec; 220 } 221 else 222 size = strlen(t); 223 pforw: if (!(flags&LADJUST) && width) 224 for (n = size; n++ < width;) 225 PUTC(padc); 226 if (fp->_cnt - (n = size) >= 0) { 227 cnt += n; 228 fp->_cnt -= n; 229 bcopy(t, fp->_ptr, n); 230 fp->_ptr += n; 231 } 232 else for (; n--; ++t) 233 PUTC(*t); 234 if (flags&LADJUST) 235 while (width-- > size) 236 PUTC(' '); 237 break; 238 case 'u': 239 ARG(); 240 base = 10; 241 goto nosign; 242 case 'X': 243 digs = "0123456789ABCDEF"; 244 /*FALLTHROUGH*/ 245 case 'x': 246 ARG(); 247 base = 16; 248 /* leading 0x/X only if non-zero */ 249 if (!_ulong) 250 flags &= ~ALT; 251 252 /* unsigned conversions */ 253 nosign: sign = NULL; 254 /* 255 * ``The result of converting a zero value with an 256 * explicit precision of zero is no characters.'' 257 * -- ANSI X3J11 258 */ 259 number: if (!_ulong && !prec) 260 break; 261 262 t = buf + MAXBUF - 1; 263 do { 264 *t-- = digs[_ulong % base]; 265 _ulong /= base; 266 } while(_ulong); 267 for (size = buf + MAXBUF - 1 - t; size < prec; ++size) 268 *t-- = '0'; 269 270 /* alternate mode for hex and octal numbers */ 271 if (flags&ALT) 272 switch (base) { 273 case 16: 274 /* avoid "00000x35" */ 275 if (padc == ' ') { 276 *t-- = *fmt; 277 *t-- = '0'; 278 } 279 else { 280 PUTC('0'); 281 PUTC(*fmt); 282 } 283 width -= 2; 284 break; 285 case 8: 286 if (t[1] != '0') { 287 *t-- = '0'; 288 --width; 289 } 290 break; 291 } 292 293 if (sign) { 294 /* avoid "0000-3" */ 295 if (padc == ' ') 296 *t-- = sign; 297 else 298 PUTC(sign); 299 --width; 300 } 301 302 if (!(flags&LADJUST)) 303 while (size++ < width) 304 PUTC(padc); 305 while (++t < buf + MAXBUF) 306 PUTC(*t); 307 while (width-- > size) 308 PUTC(' '); 309 digs = "0123456789abcdef"; 310 break; 311 case '\0': /* "%?" prints ?, unless ? is NULL */ 312 return(cnt); 313 default: 314 PUTC(*fmt); 315 } 316 } 317 /*NOTREACHED*/ 318 } 319 320 #define EFORMAT 0x01 321 #define FFORMAT 0x02 322 #define GFORMAT 0x04 323 #define DEFPREC 6 324 325 static char * 326 _cvt(number, prec, flags, fmtch, padc, sign, startp, endp) 327 double number; 328 register int prec; 329 int flags; 330 u_char fmtch; 331 char padc, *sign, *startp, *endp; 332 { 333 register char *p; 334 register int expcnt, format; 335 double fract, integer, tmp, modf(); 336 int decpt; 337 char *savep; 338 339 if (prec == -1) 340 prec = DEFPREC; 341 342 if (number < 0) { 343 *sign = '-'; 344 number = -number; 345 } 346 347 /* if blank padded, add sign in as part of the number */ 348 if (*sign && padc == ' ') 349 *startp++ = *sign; 350 351 switch(fmtch) { 352 case 'e': 353 case 'E': 354 format = EFORMAT; 355 break; 356 case 'f': 357 format = FFORMAT; 358 break; 359 case 'g': 360 case 'G': 361 format = GFORMAT; 362 fmtch -= 2; 363 } 364 365 /* 366 * if the alternate flag is set, or, at least one digit of precision 367 * was requested, add a decimal point, unless it's the g/G format 368 * in which case we require two digits of precision, as it counts 369 * precision differently. 370 */ 371 decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 372 373 expcnt = 0; 374 p = endp - 1; 375 fract = modf(number, &integer); 376 if (integer) { 377 register char *p2; 378 379 /* get integer part of number; count decimal places */ 380 for (; integer; ++expcnt) { 381 tmp = modf(integer / 10, &integer); 382 *p-- = (int)((tmp + .03) * 10) + '0'; 383 } 384 385 /* copy, in reverse order, to start of buffer */ 386 p2 = startp; 387 *p2++ = *++p; 388 389 /* 390 * if the format is g/G, and the resulting exponent will be 391 * greater than the precision, use e/E format. If e/E format, 392 * put in a decimal point as needed, and decrement precision 393 * count for each digit after the decimal point. 394 */ 395 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 396 if (format&GFORMAT) { 397 format |= EFORMAT; 398 399 /* first digit is precision for g/G format */ 400 if (prec) 401 --prec; 402 } 403 if (decpt) 404 *p2++ = '.'; 405 for (; ++p < endp && prec; --prec, *p2++ = *p); 406 407 /* precision ran out, round */ 408 if (p < endp) { 409 if (*p > '4') { 410 for (savep = p2--;; *p2-- = '0') { 411 if (*p2 == '.') 412 --p2; 413 if (++*p2 <= '9') 414 break; 415 } 416 p2 = savep; 417 } 418 fract = 0; 419 } 420 } 421 /* 422 * g/G in f format; if out of precision, replace digits with 423 * zeroes, note, have to round first. 424 */ 425 else if (format&GFORMAT) { 426 for (; ++p < endp && prec; --prec, *p2++ = *p); 427 /* precision ran out; round and then add zeroes */ 428 if (p < endp) { 429 if (*p > '4') { 430 for (savep = p2--; ++*p2 > '9'; 431 *p2-- = '0'); 432 p2 = savep; 433 } 434 do { 435 *p2++ = '0'; 436 } while (++p < endp); 437 fract = 0; 438 } 439 if (decpt) 440 *p2++ = '.'; 441 } 442 /* f format */ 443 else { 444 for (; ++p < endp; *p2++ = *p); 445 if (decpt) 446 *p2++ = '.'; 447 } 448 p = p2; 449 } 450 /* 451 * if no fraction, the number was zero, and if no precision, can't 452 * show anything after the decimal point. 453 */ 454 else if (!fract || !prec) { 455 *startp++ = '0'; 456 if (decpt && !(format&GFORMAT)) 457 *startp++ = '.'; 458 *startp = '\0'; 459 return(startp); 460 } 461 /* 462 * if the format is g/G, and the resulting exponent will be less than 463 * -4 use e/E format. If e/E format, compute exponent value. 464 */ 465 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 466 format |= EFORMAT; 467 if (fract) 468 for (p = startp; fract;) { 469 fract = modf(fract * 10, &tmp); 470 if (!tmp) { 471 --expcnt; 472 continue; 473 } 474 *p++ = (int)tmp + '0'; 475 break; 476 } 477 else 478 *p++ = '0'; 479 480 /* g/G format, decrement precision for first digit */ 481 if (format&GFORMAT && prec) 482 --prec; 483 484 /* add decimal after first non-zero digit */ 485 if (decpt) 486 *p++ = '.'; 487 } 488 /* 489 * f format or g/G printed as f format; don't worry about decimal 490 * point, if g/G format doesn't need it, will get stripped later. 491 */ 492 else { 493 p = startp; 494 *p++ = '0'; 495 *p++ = '.'; 496 } 497 498 /* finish out requested precision */ 499 while (fract && prec-- > 0) { 500 fract = modf(fract * 10, &tmp); 501 *p++ = (int)tmp + '0'; 502 } 503 while (prec-- > 0) 504 *p++ = '0'; 505 506 /* 507 * if any fractional value left, "round" it back up to the beginning 508 * of the number, fixing the exponent as necessary, and avoiding the 509 * decimal point. 510 */ 511 if (fract) { 512 (void)modf(fract * 10, &tmp); 513 if (tmp > 4) { 514 for (savep = p--;; *p-- = '0') { 515 if (*p == '.') 516 --p; 517 if (p == startp) { 518 *p = '1'; 519 ++expcnt; 520 break; 521 } 522 if (++*p <= '9') 523 break; 524 } 525 p = savep; 526 } 527 } 528 529 /* 530 * if a g/G format and not alternate flag, lose trailing zeroes, 531 * if e/E or g/G format, and last char is decimal point, lose it. 532 */ 533 if (!(flags&ALT)) { 534 if (format&GFORMAT) 535 for (; p[-1] == '0'; --p); 536 if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 537 --p; 538 } 539 540 /* if an e/E format, add exponent */ 541 if (format&EFORMAT) { 542 *p++ = fmtch; 543 if (--expcnt < 0) { 544 expcnt = -expcnt; 545 *p++ = '-'; 546 } 547 else 548 *p++ = '+'; 549 *p++ = expcnt / 10 + '0'; 550 *p++ = expcnt % 10 + '0'; 551 } 552 return(p); 553 } 554