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