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.20 (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 if (sign) 159 PUTC(sign); 160 base = 10; 161 goto num; 162 case 'e': 163 case 'E': 164 case 'f': 165 case 'g': 166 case 'G': 167 _double = va_arg(argp, double); 168 size = _cvt(_double, prec, flags, *fmt, sign, 169 buf, buf + sizeof(buf)) - buf; 170 t = buf; 171 goto pforw; 172 case 'n': 173 if (flags&LONGDBL || flags&LONGINT) 174 *va_arg(argp, long *) = cnt; 175 else if (flags&SHORTINT) 176 *va_arg(argp, short *) = cnt; 177 else 178 *va_arg(argp, int *) = cnt; 179 break; 180 case 'o': 181 ARG(); 182 base = 8; 183 goto num; 184 case 'p': 185 /* 186 * ``The argument shall be a pointer to void. The 187 * value of the pointer is converted to a sequence 188 * of printable characters, in an implementation- 189 * defined manner.'' 190 * -- ANSI X3J11 191 */ 192 _ulong = (u_long)va_arg(argp, void *); 193 base = 16; 194 goto num; 195 case 's': 196 if (!(t = va_arg(argp, char *))) 197 t = "(null)"; 198 if (prec >= 0) { 199 /* 200 * can't use strlen; can only look for the 201 * NUL in the first `prec' characters, and 202 * strlen() will go further. 203 */ 204 char *p, *memchr(); 205 206 if (p = memchr(t, 0, prec)) { 207 size = p - t; 208 if (size > prec) 209 size = prec; 210 } 211 else 212 size = prec; 213 } 214 else 215 size = strlen(t); 216 pforw: if (!(flags&LADJUST) && width) 217 for (n = size; n++ < width;) 218 PUTC(padc); 219 if (fp->_cnt - (n = size) >= 0) { 220 cnt += n; 221 fp->_cnt -= n; 222 bcopy(t, fp->_ptr, n); 223 fp->_ptr += n; 224 } 225 else for (; n--; ++t) 226 PUTC(*t); 227 if (flags&LADJUST) 228 while (width-- > size) 229 PUTC(padc); 230 break; 231 case 'u': 232 ARG(); 233 base = 10; 234 goto num; 235 case 'X': 236 digs = "0123456789ABCDEF"; 237 /*FALLTHROUGH*/ 238 case 'x': 239 ARG(); 240 base = 16; 241 if (!_ulong) /* leading 0x/X only if non-zero */ 242 flags &= ~ALT; 243 num: t = buf + MAXBUF - 1; 244 do { 245 *t-- = digs[_ulong % base]; 246 _ulong /= base; 247 } while(_ulong); 248 digs = "0123456789abcdef"; 249 for (size = buf + MAXBUF - 1 - t; size < prec; ++size) 250 *t-- = '0'; 251 if (flags&ALT) 252 switch (base) { 253 case 16: 254 /* avoid "00000x35" */ 255 if (padc == '0') { 256 PUTC('0'); 257 PUTC(*fmt); 258 } 259 else { 260 *t-- = *fmt; 261 *t-- = '0'; 262 } 263 width -= 2; 264 break; 265 case 8: 266 if (t[1] != '0') { 267 *t-- = '0'; 268 --width; 269 } 270 break; 271 } 272 if (!(flags&LADJUST)) 273 while (size++ < width) 274 PUTC(padc); 275 while (++t < buf + MAXBUF) 276 PUTC(*t); 277 for (; width > size; --width) 278 PUTC(padc); 279 break; 280 case '\0': /* "%?" prints ?, unless ? is NULL */ 281 return(cnt); 282 default: 283 PUTC(*fmt); 284 } 285 } 286 /*NOTREACHED*/ 287 } 288 289 #define EFORMAT 0x01 290 #define FFORMAT 0x02 291 #define GFORMAT 0x04 292 #define DEFPREC 6 293 294 static char * 295 _cvt(number, prec, flags, fmtch, sign, startp, endp) 296 double number; 297 register int prec; 298 int flags; 299 u_char fmtch; 300 char sign, *startp, *endp; 301 { 302 register char *p; 303 register int expcnt, format; 304 double fract, integer, tmp, modf(); 305 int decpt; 306 char *savep; 307 308 if (prec == -1) 309 prec = DEFPREC; 310 311 if (number < 0) { 312 *startp++ = '-'; 313 number = -number; 314 } 315 else if (sign) 316 *startp++ = sign; 317 318 switch(fmtch) { 319 case 'e': 320 case 'E': 321 format = EFORMAT; 322 break; 323 case 'f': 324 format = FFORMAT; 325 break; 326 case 'g': 327 case 'G': 328 format = GFORMAT; 329 fmtch -= 2; 330 } 331 332 /* 333 * if the alternate flag is set, or, at least one digit of precision 334 * was requested, add a decimal point, unless it's the g/G format 335 * in which case we require two digits of precision, as it counts 336 * precision differently. 337 */ 338 decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 339 340 expcnt = 0; 341 p = endp - 1; 342 fract = modf(number, &integer); 343 if (integer) { 344 register char *p2; 345 346 /* get integer part of number; count decimal places */ 347 for (; integer; ++expcnt) { 348 tmp = modf(integer / 10, &integer); 349 *p-- = (int)((tmp + .03) * 10) + '0'; 350 } 351 352 /* copy, in reverse order, to start of buffer */ 353 p2 = startp; 354 *p2++ = *++p; 355 356 /* 357 * if the format is g/G, and the resulting exponent will be 358 * greater than the precision, use e/E format. If e/E format, 359 * put in a decimal point as needed, and decrement precision 360 * count for each digit after the decimal point. 361 */ 362 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 363 if (format&GFORMAT) { 364 format |= EFORMAT; 365 366 /* first digit is precision for g/G format */ 367 if (prec) 368 --prec; 369 } 370 if (decpt) 371 *p2++ = '.'; 372 for (; ++p < endp && prec; --prec, *p2++ = *p); 373 374 /* precision ran out, round */ 375 if (p < endp) { 376 if (*p > '4') { 377 for (savep = p2--;; *p2-- = '0') { 378 if (*p2 == '.') 379 --p2; 380 if (++*p2 <= '9') 381 break; 382 } 383 p2 = savep; 384 } 385 fract = 0; 386 } 387 } 388 /* 389 * g/G in f format; if out of precision, replace digits with 390 * zeroes, note, have to round first. 391 */ 392 else if (format&GFORMAT) { 393 for (; ++p < endp && prec; --prec, *p2++ = *p); 394 /* precision ran out; round and then add zeroes */ 395 if (p < endp) { 396 if (*p > '4') { 397 for (savep = p2--; ++*p2 > '9'; 398 *p2-- = '0'); 399 p2 = savep; 400 } 401 do { 402 *p2++ = '0'; 403 } while (++p < endp); 404 fract = 0; 405 } 406 if (decpt) 407 *p2++ = '.'; 408 } 409 /* f format */ 410 else { 411 for (; ++p < endp; *p2++ = *p); 412 if (decpt) 413 *p2++ = '.'; 414 } 415 p = p2; 416 } 417 /* 418 * if no fraction, the number was zero, and if no precision, can't 419 * show anything after the decimal point. 420 */ 421 else if (!fract || !prec) { 422 *startp++ = '0'; 423 if (decpt && !(format&GFORMAT)) 424 *startp++ = '.'; 425 *startp = '\0'; 426 return(startp); 427 } 428 /* 429 * if the format is g/G, and the resulting exponent will be less than 430 * -4 use e/E format. If e/E format, compute exponent value. 431 */ 432 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 433 format |= EFORMAT; 434 if (fract) 435 for (p = startp; fract;) { 436 fract = modf(fract * 10, &tmp); 437 if (!tmp) { 438 --expcnt; 439 continue; 440 } 441 *p++ = (int)tmp + '0'; 442 break; 443 } 444 else 445 *p++ = '0'; 446 447 /* g/G format, decrement precision for first digit */ 448 if (format&GFORMAT && prec) 449 --prec; 450 451 /* add decimal after first non-zero digit */ 452 if (decpt) 453 *p++ = '.'; 454 } 455 /* 456 * f format or g/G printed as f format; don't worry about decimal 457 * point, if g/G format doesn't need it, will get stripped later. 458 */ 459 else { 460 p = startp; 461 *p++ = '0'; 462 *p++ = '.'; 463 } 464 465 /* finish out requested precision */ 466 while (fract && prec-- > 0) { 467 fract = modf(fract * 10, &tmp); 468 *p++ = (int)tmp + '0'; 469 } 470 while (prec-- > 0) 471 *p++ = '0'; 472 473 /* 474 * if any fractional value left, "round" it back up to the beginning 475 * of the number, fixing the exponent as necessary, and avoiding the 476 * decimal point. 477 */ 478 if (fract) { 479 (void)modf(fract * 10, &tmp); 480 if (tmp > 4) { 481 for (savep = p--;; *p-- = '0') { 482 if (*p == '.') 483 --p; 484 if (p == startp) { 485 *p = '1'; 486 ++expcnt; 487 break; 488 } 489 if (++*p <= '9') 490 break; 491 } 492 p = savep; 493 } 494 } 495 496 /* 497 * if a g/G format and not alternate flag, lose trailing zeroes, 498 * if e/E or g/G format, and last char is decimal point, lose it. 499 */ 500 if (!(flags&ALT)) { 501 if (format&GFORMAT) 502 for (; p[-1] == '0'; --p); 503 if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 504 --p; 505 } 506 507 /* if an e/E format, add exponent */ 508 if (format&EFORMAT) { 509 *p++ = fmtch; 510 if (--expcnt < 0) { 511 expcnt = -expcnt; 512 *p++ = '-'; 513 } 514 else 515 *p++ = '+'; 516 *p++ = expcnt / 10 + '0'; 517 *p++ = expcnt % 10 + '0'; 518 } 519 return(p); 520 } 521