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.10 (Berkeley) 05/11/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 120 23 #define DEFPREC 6 24 25 #define PUTC(ch) {++cnt; putc(ch, fp);} 26 27 #define LONGINT 0x01 28 #define LONGDBL 0x02 29 #define SHORTINT 0x04 30 #define GETARG(r) \ 31 r = argsize&LONGINT ? va_arg(argp, long) : \ 32 argsize&SHORTINT ? va_arg(argp, short) : va_arg(argp, int); 33 34 static int alternate; 35 static char printsign; 36 37 x_doprnt(fmt, argp, fp) 38 register char *fmt; 39 va_list argp; 40 register FILE *fp; 41 { 42 register int base, cnt; 43 register char *bp, *t; 44 register u_long reg_ulong; 45 register long reg_long; 46 double _double; 47 char argsize, padc, *_cvt(), *digs, buf[MAXBUF]; 48 int n, ladjust, width, prec, size; 49 50 digs = "0123456789abcdef"; 51 for (cnt = 0; *fmt; ++fmt) { 52 if (*fmt != '%') { 53 PUTC(*fmt); 54 continue; 55 } 56 57 alternate = ladjust = width = 0; 58 prec = -1; 59 padc = ' '; 60 argsize = printsign = '\0'; 61 62 flags: switch (*++fmt) { 63 case '#': 64 alternate = 1; 65 goto flags; 66 case '*': 67 /* 68 * ``A negative field width argument is taken as a 69 * - flag followed by a positive field width.'' 70 * -- ANSI X3J11 71 * They don't exclude field widths read from args. 72 */ 73 if ((width = va_arg(argp, int)) >= 0) 74 goto flags; 75 width = -width; 76 /*FALLTHROUGH*/ 77 case '-': 78 ladjust = 1; 79 goto flags; 80 case '+': 81 printsign = '+'; 82 goto flags; 83 case '.': 84 if (*++fmt == '*') 85 prec = va_arg(argp, int); 86 else if (isdigit(*fmt)) { 87 prec = 0; 88 do { 89 prec = 10 * prec + *fmt - '0'; 90 } while isdigit(*++fmt); 91 --fmt; 92 } 93 else { 94 prec = 0; 95 --fmt; 96 goto flags; 97 } 98 if (prec < 0) 99 prec = -1; 100 goto flags; 101 case '0': 102 padc = '0'; 103 /*FALLTHROUGH*/ 104 case '1': case '2': case '3': case '4': 105 case '5': case '6': case '7': case '8': case '9': 106 do { 107 width = 10 * width + *fmt - '0'; 108 } while isdigit(*++fmt); 109 --fmt; 110 case 'L': 111 argsize |= LONGDBL; 112 goto flags; 113 case 'h': 114 argsize |= SHORTINT; 115 goto flags; 116 case 'l': 117 argsize |= LONGINT; 118 goto flags; 119 case 'c': { 120 char ch; 121 122 ch = va_arg(argp, int); 123 PUTC(ch); 124 break; 125 } 126 case 'd': 127 case 'i': 128 GETARG(reg_long); 129 if (reg_long < 0) { 130 reg_ulong = -reg_long; 131 printsign = '-'; 132 } 133 else { 134 reg_ulong = reg_long; 135 } 136 if (printsign) 137 PUTC(printsign); 138 base = 10; 139 goto num1; 140 case 'e': 141 case 'E': 142 case 'f': 143 case 'g': 144 case 'G': 145 _double = va_arg(argp, double); 146 bp = _cvt(_double, prec, buf, buf + sizeof(buf), *fmt); 147 pbuf: size = bp - buf; 148 if (size < width && !ladjust) 149 do { 150 PUTC(padc); 151 } while (--width > size); 152 for (t = buf; t < bp; ++t) 153 PUTC(*t); 154 for (; width > size; --width) 155 PUTC(padc); 156 break; 157 case 'n': 158 *(va_arg(argp, int *)) = cnt; 159 break; 160 case 'o': 161 GETARG(reg_ulong); 162 base = 8; 163 if (!reg_ulong || !alternate) 164 goto num1; 165 bp = buf + sizeof(buf) - 1; 166 do { 167 *bp-- = digs[reg_ulong % base]; 168 reg_ulong /= base; 169 } while(reg_ulong); 170 size = &buf[sizeof(buf) - 1] - bp; 171 if (size < --width && !ladjust) 172 do { 173 PUTC(padc); 174 } while (--width > size); 175 PUTC('0'); 176 goto num2; 177 case 'p': 178 case 's': 179 if (!(bp = va_arg(argp, char *))) 180 bp = "(null)"; 181 if (width > 0 && !ladjust) { 182 char *savep; 183 184 savep = bp; 185 for (n = 0; *bp && (prec < 0 || n < prec); 186 n++, bp++); 187 bp = savep; 188 while (n++ < width) 189 PUTC(' '); 190 } 191 for (n = 0; *bp; ++bp) { 192 if (++n > prec && prec >= 0) 193 break; 194 PUTC(*bp); 195 } 196 if (n < width && ladjust) 197 do { 198 PUTC(' '); 199 } while (++n < width); 200 break; 201 case 'u': 202 GETARG(reg_ulong); 203 base = 10; 204 goto num1; 205 case 'X': 206 digs = "0123456789ABCDEF"; 207 /*FALLTHROUGH*/ 208 case 'x': 209 GETARG(reg_ulong); 210 if (alternate && reg_ulong) { 211 PUTC('0'); 212 PUTC(*fmt); 213 } 214 base = 16; 215 num1: bp = buf + sizeof(buf) - 1; 216 do { 217 *bp-- = digs[reg_ulong % base]; 218 reg_ulong /= base; 219 } while(reg_ulong); 220 size = &buf[sizeof(buf) - 1] - bp; 221 for (; size < prec; *bp-- = '0', ++size); 222 if (size < width && !ladjust) 223 do { 224 PUTC(padc); 225 } while (--width > size); 226 num2: while (++bp != &buf[MAXBUF]) 227 PUTC(*bp); 228 for (; width > size; --width) 229 PUTC(padc); 230 digs = "0123456789abcdef"; 231 break; 232 case '\0': /* "%?" prints ?, unless ? is NULL */ 233 return(ferror(fp) ? -1 : cnt); 234 default: 235 PUTC(*fmt); 236 } 237 } 238 return(ferror(fp) ? -1 : cnt); 239 } 240 241 #define EFORMAT 0x01 242 #define FFORMAT 0x02 243 #define GFORMAT 0x04 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) /* set default precision */ 258 prec = DEFPREC; 259 260 p = endp - 1; 261 if (number < 0) { /* set sign */ 262 *startp++ = '-'; 263 number = -number; 264 } 265 else if (printsign) 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, since it counts 286 * precision differently. 287 */ 288 decpt = alternate || prec > 1 || !(format&GFORMAT) && prec; 289 290 expcnt = 0; 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 run out of precision, replace digits 339 * with zeroes, note, have to round first, otherwise lose 340 * rounding point. 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 (!alternate) { 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