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