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.19 (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 /* alternate form for hex; leading 0x/X */ 242 if (flags&ALT && _ulong) { 243 PUTC('0'); 244 PUTC(*fmt); 245 } 246 num: t = buf + MAXBUF - 1; 247 do { 248 *t-- = digs[_ulong % base]; 249 _ulong /= base; 250 } while(_ulong); 251 digs = "0123456789abcdef"; 252 size = buf + MAXBUF - 1 - t; 253 if (size >= prec) { 254 /* alternate form for octal; leading 0 */ 255 if (t[1] != '0' && flags&ALT && *fmt == 'o') { 256 *t-- = '0'; 257 ++size; 258 } 259 } 260 else 261 for (; size < prec; ++size) 262 *t-- = '0'; 263 if (!(flags&LADJUST)) 264 while (size++ < width) 265 PUTC(padc); 266 while (++t < buf + MAXBUF) 267 PUTC(*t); 268 for (; width > size; --width) 269 PUTC(padc); 270 break; 271 case '\0': /* "%?" prints ?, unless ? is NULL */ 272 return(cnt); 273 default: 274 PUTC(*fmt); 275 } 276 } 277 /*NOTREACHED*/ 278 } 279 280 #define EFORMAT 0x01 281 #define FFORMAT 0x02 282 #define GFORMAT 0x04 283 #define DEFPREC 6 284 285 static char * 286 _cvt(number, prec, flags, fmtch, sign, startp, endp) 287 double number; 288 register int prec; 289 int flags; 290 u_char fmtch; 291 char sign, *startp, *endp; 292 { 293 register char *p; 294 register int expcnt, format; 295 double fract, integer, tmp, modf(); 296 int decpt; 297 char *savep; 298 299 if (prec == -1) 300 prec = DEFPREC; 301 302 if (number < 0) { 303 *startp++ = '-'; 304 number = -number; 305 } 306 else if (sign) 307 *startp++ = sign; 308 309 switch(fmtch) { 310 case 'e': 311 case 'E': 312 format = EFORMAT; 313 break; 314 case 'f': 315 format = FFORMAT; 316 break; 317 case 'g': 318 case 'G': 319 format = GFORMAT; 320 fmtch -= 2; 321 } 322 323 /* 324 * if the alternate flag is set, or, at least one digit of precision 325 * was requested, add a decimal point, unless it's the g/G format 326 * in which case we require two digits of precision, as it counts 327 * precision differently. 328 */ 329 decpt = flags&ALT || prec > (format&GFORMAT ? 1 : 0); 330 331 expcnt = 0; 332 p = endp - 1; 333 fract = modf(number, &integer); 334 if (integer) { 335 register char *p2; 336 337 /* get integer part of number; count decimal places */ 338 for (; integer; ++expcnt) { 339 tmp = modf(integer / 10, &integer); 340 *p-- = (int)((tmp + .03) * 10) + '0'; 341 } 342 343 /* copy, in reverse order, to start of buffer */ 344 p2 = startp; 345 *p2++ = *++p; 346 347 /* 348 * if the format is g/G, and the resulting exponent will be 349 * greater than the precision, use e/E format. If e/E format, 350 * put in a decimal point as needed, and decrement precision 351 * count for each digit after the decimal point. 352 */ 353 if (format&GFORMAT && expcnt - 1 > prec || format&EFORMAT) { 354 if (format&GFORMAT) { 355 format |= EFORMAT; 356 357 /* first digit is precision for g/G format */ 358 if (prec) 359 --prec; 360 } 361 if (decpt) 362 *p2++ = '.'; 363 for (; ++p < endp && prec; --prec, *p2++ = *p); 364 365 /* precision ran out, round */ 366 if (p < endp) { 367 if (*p > '4') { 368 for (savep = p2--;; *p2-- = '0') { 369 if (*p2 == '.') 370 --p2; 371 if (++*p2 <= '9') 372 break; 373 } 374 p2 = savep; 375 } 376 fract = 0; 377 } 378 } 379 /* 380 * g/G in f format; if out of precision, replace digits with 381 * zeroes, note, have to round first. 382 */ 383 else if (format&GFORMAT) { 384 for (; ++p < endp && prec; --prec, *p2++ = *p); 385 /* precision ran out; round and then add zeroes */ 386 if (p < endp) { 387 if (*p > '4') { 388 for (savep = p2--; ++*p2 > '9'; 389 *p2-- = '0'); 390 p2 = savep; 391 } 392 do { 393 *p2++ = '0'; 394 } while (++p < endp); 395 fract = 0; 396 } 397 if (decpt) 398 *p2++ = '.'; 399 } 400 /* f format */ 401 else { 402 for (; ++p < endp; *p2++ = *p); 403 if (decpt) 404 *p2++ = '.'; 405 } 406 p = p2; 407 } 408 /* 409 * if no fraction, the number was zero, and if no precision, can't 410 * show anything after the decimal point. 411 */ 412 else if (!fract || !prec) { 413 *startp++ = '0'; 414 if (decpt && !(format&GFORMAT)) 415 *startp++ = '.'; 416 *startp = '\0'; 417 return(startp); 418 } 419 /* 420 * if the format is g/G, and the resulting exponent will be less than 421 * -4 use e/E format. If e/E format, compute exponent value. 422 */ 423 else if (format&GFORMAT && fract < .0001 || format&EFORMAT) { 424 format |= EFORMAT; 425 if (fract) 426 for (p = startp; fract;) { 427 fract = modf(fract * 10, &tmp); 428 if (!tmp) { 429 --expcnt; 430 continue; 431 } 432 *p++ = (int)tmp + '0'; 433 break; 434 } 435 else 436 *p++ = '0'; 437 438 /* g/G format, decrement precision for first digit */ 439 if (format&GFORMAT && prec) 440 --prec; 441 442 /* add decimal after first non-zero digit */ 443 if (decpt) 444 *p++ = '.'; 445 } 446 /* 447 * f format or g/G printed as f format; don't worry about decimal 448 * point, if g/G format doesn't need it, will get stripped later. 449 */ 450 else { 451 p = startp; 452 *p++ = '0'; 453 *p++ = '.'; 454 } 455 456 /* finish out requested precision */ 457 while (fract && prec-- > 0) { 458 fract = modf(fract * 10, &tmp); 459 *p++ = (int)tmp + '0'; 460 } 461 while (prec-- > 0) 462 *p++ = '0'; 463 464 /* 465 * if any fractional value left, "round" it back up to the beginning 466 * of the number, fixing the exponent as necessary, and avoiding the 467 * decimal point. 468 */ 469 if (fract) { 470 (void)modf(fract * 10, &tmp); 471 if (tmp > 4) { 472 for (savep = p--;; *p-- = '0') { 473 if (*p == '.') 474 --p; 475 if (p == startp) { 476 *p = '1'; 477 ++expcnt; 478 break; 479 } 480 if (++*p <= '9') 481 break; 482 } 483 p = savep; 484 } 485 } 486 487 /* 488 * if a g/G format and not alternate flag, lose trailing zeroes, 489 * if e/E or g/G format, and last char is decimal point, lose it. 490 */ 491 if (!(flags&ALT)) { 492 if (format&GFORMAT) 493 for (; p[-1] == '0'; --p); 494 if (format&(GFORMAT|EFORMAT) && p[-1] == '.') 495 --p; 496 } 497 498 /* if an e/E format, add exponent */ 499 if (format&EFORMAT) { 500 *p++ = fmtch; 501 if (--expcnt < 0) { 502 expcnt = -expcnt; 503 *p++ = '-'; 504 } 505 else 506 *p++ = '+'; 507 *p++ = expcnt / 10 + '0'; 508 *p++ = expcnt % 10 + '0'; 509 } 510 return(p); 511 } 512