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