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