Commit | Line | Data |
---|---|---|
1e949ff4 | 1 | /*- |
7860c229 KB |
2 | * Copyright (c) 1990, 1993 |
3 | * The Regents of the University of California. All rights reserved. | |
ad0e16d0 | 4 | * |
1e949ff4 KB |
5 | * This code is derived from software contributed to Berkeley by |
6 | * Chris Torek. | |
7 | * | |
019bea33 | 8 | * %sccs.include.redist.c% |
ad0e16d0 KB |
9 | */ |
10 | ||
5f6de66c | 11 | #if defined(LIBC_SCCS) && !defined(lint) |
7860c229 | 12 | static char sccsid[] = "@(#)vfprintf.c 8.1 (Berkeley) %G%"; |
5f6de66c | 13 | #endif /* LIBC_SCCS and not lint */ |
ad0e16d0 | 14 | |
1e949ff4 KB |
15 | /* |
16 | * Actual printf innards. | |
17 | * | |
18 | * This code is large and complicated... | |
19 | */ | |
20 | ||
0d15d742 | 21 | #include <sys/types.h> |
75964b08 | 22 | |
b899ab75 | 23 | #include <limits.h> |
5f6de66c | 24 | #include <stdio.h> |
75964b08 | 25 | #include <stdlib.h> |
1e949ff4 | 26 | #include <string.h> |
75964b08 | 27 | |
1e949ff4 KB |
28 | #if __STDC__ |
29 | #include <stdarg.h> | |
30 | #else | |
31 | #include <varargs.h> | |
32 | #endif | |
75964b08 | 33 | |
1e949ff4 KB |
34 | #include "local.h" |
35 | #include "fvwrite.h" | |
ad0e16d0 | 36 | |
b999da5c | 37 | /* Define FLOATING_POINT to get floating point. */ |
1e949ff4 | 38 | #define FLOATING_POINT |
1e949ff4 KB |
39 | |
40 | /* | |
41 | * Flush out all the vectors defined by the given uio, | |
42 | * then reset it so that it can be reused. | |
43 | */ | |
4569d013 | 44 | static int |
1e949ff4 KB |
45 | __sprint(fp, uio) |
46 | FILE *fp; | |
47 | register struct __suio *uio; | |
48 | { | |
49 | register int err; | |
50 | ||
51 | if (uio->uio_resid == 0) { | |
52 | uio->uio_iovcnt = 0; | |
53 | return (0); | |
54 | } | |
55 | err = __sfvwrite(fp, uio); | |
56 | uio->uio_resid = 0; | |
57 | uio->uio_iovcnt = 0; | |
58 | return (err); | |
59 | } | |
60 | ||
61 | /* | |
62 | * Helper function for `fprintf to unbuffered unix file': creates a | |
63 | * temporary buffer. We only work on write-only files; this avoids | |
64 | * worries about ungetc buffers and so forth. | |
65 | */ | |
4569d013 | 66 | static int |
1e949ff4 KB |
67 | __sbprintf(fp, fmt, ap) |
68 | register FILE *fp; | |
4569d013 | 69 | const char *fmt; |
1e949ff4 KB |
70 | va_list ap; |
71 | { | |
72 | int ret; | |
73 | FILE fake; | |
74 | unsigned char buf[BUFSIZ]; | |
75 | ||
76 | /* copy the important variables */ | |
77 | fake._flags = fp->_flags & ~__SNBF; | |
78 | fake._file = fp->_file; | |
79 | fake._cookie = fp->_cookie; | |
80 | fake._write = fp->_write; | |
81 | ||
82 | /* set up the buffer */ | |
83 | fake._bf._base = fake._p = buf; | |
84 | fake._bf._size = fake._w = sizeof(buf); | |
85 | fake._lbfsize = 0; /* not actually used, but Just In Case */ | |
86 | ||
87 | /* do the work, then copy any error status */ | |
88 | ret = vfprintf(&fake, fmt, ap); | |
89 | if (ret >= 0 && fflush(&fake)) | |
90 | ret = EOF; | |
91 | if (fake._flags & __SERR) | |
92 | fp->_flags |= __SERR; | |
93 | return (ret); | |
94 | } | |
95 | ||
b899ab75 KB |
96 | /* |
97 | * Macros for converting digits to letters and vice versa | |
98 | */ | |
99 | #define to_digit(c) ((c) - '0') | |
100 | #define is_digit(c) ((unsigned)to_digit(c) <= 9) | |
101 | #define to_char(n) ((n) + '0') | |
102 | ||
103 | /* | |
104 | * Convert an unsigned long to ASCII for printf purposes, returning | |
105 | * a pointer to the first character of the string representation. | |
106 | * Octal numbers can be forced to have a leading zero; hex numbers | |
107 | * use the given digits. | |
108 | */ | |
109 | static char * | |
110 | __ultoa(val, endp, base, octzero, xdigs) | |
111 | register u_long val; | |
112 | char *endp; | |
113 | int base, octzero; | |
114 | char *xdigs; | |
115 | { | |
116 | register char *cp = endp; | |
117 | register long sval; | |
118 | ||
119 | /* | |
120 | * Handle the three cases separately, in the hope of getting | |
121 | * better/faster code. | |
122 | */ | |
123 | switch (base) { | |
124 | case 10: | |
125 | if (val < 10) { /* many numbers are 1 digit */ | |
126 | *--cp = to_char(val); | |
127 | return (cp); | |
128 | } | |
129 | /* | |
130 | * On many machines, unsigned arithmetic is harder than | |
131 | * signed arithmetic, so we do at most one unsigned mod and | |
132 | * divide; this is sufficient to reduce the range of | |
133 | * the incoming value to where signed arithmetic works. | |
134 | */ | |
135 | if (val > LONG_MAX) { | |
136 | *--cp = to_char(val % 10); | |
137 | sval = val / 10; | |
138 | } else | |
139 | sval = val; | |
140 | do { | |
141 | *--cp = to_char(sval % 10); | |
142 | sval /= 10; | |
143 | } while (sval != 0); | |
144 | break; | |
145 | ||
146 | case 8: | |
147 | do { | |
148 | *--cp = to_char(val & 7); | |
149 | val >>= 3; | |
150 | } while (val); | |
151 | if (octzero && *cp != '0') | |
152 | *--cp = '0'; | |
153 | break; | |
154 | ||
155 | case 16: | |
156 | do { | |
157 | *--cp = xdigs[val & 15]; | |
158 | val >>= 4; | |
159 | } while (val); | |
160 | break; | |
161 | ||
162 | default: /* oops */ | |
163 | abort(); | |
164 | } | |
165 | return (cp); | |
166 | } | |
167 | ||
168 | /* Identical to __ultoa, but for quads. */ | |
169 | static char * | |
170 | __uqtoa(val, endp, base, octzero, xdigs) | |
171 | register u_quad_t val; | |
172 | char *endp; | |
173 | int base, octzero; | |
174 | char *xdigs; | |
175 | { | |
176 | register char *cp = endp; | |
177 | register quad_t sval; | |
178 | ||
179 | /* quick test for small values; __ultoa is typically much faster */ | |
180 | /* (perhaps instead we should run until small, then call __ultoa?) */ | |
181 | if (val <= ULONG_MAX) | |
182 | return (__ultoa((u_long)val, endp, base, octzero, xdigs)); | |
183 | switch (base) { | |
184 | case 10: | |
185 | if (val < 10) { | |
186 | *--cp = to_char(val % 10); | |
187 | return (cp); | |
188 | } | |
189 | if (val > QUAD_MAX) { | |
190 | *--cp = to_char(val % 10); | |
191 | sval = val / 10; | |
192 | } else | |
193 | sval = val; | |
194 | do { | |
195 | *--cp = to_char(sval % 10); | |
196 | sval /= 10; | |
197 | } while (sval != 0); | |
198 | break; | |
199 | ||
200 | case 8: | |
201 | do { | |
202 | *--cp = to_char(val & 7); | |
203 | val >>= 3; | |
204 | } while (val); | |
205 | if (octzero && *cp != '0') | |
206 | *--cp = '0'; | |
207 | break; | |
208 | ||
209 | case 16: | |
210 | do { | |
211 | *--cp = xdigs[val & 15]; | |
212 | val >>= 4; | |
213 | } while (val); | |
214 | break; | |
215 | ||
216 | default: | |
217 | abort(); | |
218 | } | |
219 | return (cp); | |
220 | } | |
1e949ff4 KB |
221 | |
222 | #ifdef FLOATING_POINT | |
75964b08 | 223 | #include <math.h> |
1e949ff4 | 224 | #include "floatio.h" |
4569d013 | 225 | |
95a39ea1 | 226 | #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ |
1e949ff4 | 227 | #define DEFPREC 6 |
6c1ffc8c | 228 | |
75964b08 KB |
229 | static char *cvt __P((double, int, int, char *, int *, int, int *)); |
230 | static int exponent __P((char *, int, int)); | |
66d7f790 | 231 | |
1e949ff4 | 232 | #else /* no FLOATING_POINT */ |
410bd0d3 | 233 | |
b899ab75 | 234 | #define BUF 68 |
5ae640f0 | 235 | |
1e949ff4 | 236 | #endif /* FLOATING_POINT */ |
410bd0d3 | 237 | |
1e949ff4 | 238 | |
1e949ff4 KB |
239 | /* |
240 | * Flags used during conversion. | |
241 | */ | |
bf4a36d5 KB |
242 | #define ALT 0x001 /* alternate form */ |
243 | #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ | |
244 | #define LADJUST 0x004 /* left adjustment */ | |
245 | #define LONGDBL 0x008 /* long double; unimplemented */ | |
246 | #define LONGINT 0x010 /* long integer */ | |
247 | #define QUADINT 0x020 /* quad integer */ | |
248 | #define SHORTINT 0x040 /* short integer */ | |
249 | #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ | |
75964b08 | 250 | #define FPT 0x100 /* Floating point number */ |
4569d013 | 251 | int |
1e949ff4 KB |
252 | vfprintf(fp, fmt0, ap) |
253 | FILE *fp; | |
4569d013 | 254 | const char *fmt0; |
1e949ff4 | 255 | va_list ap; |
ad0e16d0 | 256 | { |
1e949ff4 | 257 | register char *fmt; /* format string */ |
b042624f | 258 | register int ch; /* character from fmt */ |
1e949ff4 KB |
259 | register int n; /* handy integer (short term usage) */ |
260 | register char *cp; /* handy char pointer (short term usage) */ | |
261 | register struct __siov *iovp;/* for PRINT macro */ | |
262 | register int flags; /* flags as above */ | |
263 | int ret; /* return value accumulator */ | |
264 | int width; /* width from format (%8d), or 0 */ | |
265 | int prec; /* precision from format (%.3d), or -1 */ | |
266 | char sign; /* sign prefix (' ', '+', '-', or \0) */ | |
267 | #ifdef FLOATING_POINT | |
268 | char softsign; /* temporary negative sign for floats */ | |
b042624f | 269 | double _double; /* double precision arguments %[eEfgG] */ |
75964b08 KB |
270 | int expt; /* integer value of exponent */ |
271 | int expsize; /* character count for expstr */ | |
272 | int ndig; /* actual number of digits returned by cvt */ | |
273 | char expstr[7]; /* buffer for exponent string */ | |
1e949ff4 | 274 | #endif |
b899ab75 KB |
275 | u_long ulval; /* integer arguments %[diouxX] */ |
276 | u_quad_t uqval; /* %q integers */ | |
277 | int base; /* base for [diouxX] conversion */ | |
1e949ff4 | 278 | int dprec; /* a copy of prec if [diouxX], 0 otherwise */ |
2c2ee7b4 | 279 | int fieldsz; /* field size expanded by sign, etc */ |
1e949ff4 | 280 | int realsz; /* field size expanded by dprec */ |
2c2ee7b4 | 281 | int size; /* size of converted field or string */ |
1e949ff4 KB |
282 | char *xdigs; /* digits for [xX] conversion */ |
283 | #define NIOV 8 | |
284 | struct __suio uio; /* output information: summary */ | |
285 | struct __siov iov[NIOV];/* ... and individual io vectors */ | |
b042624f | 286 | char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ |
1e949ff4 | 287 | char ox[2]; /* space for 0x hex-prefix */ |
5f6de66c | 288 | |
1e949ff4 | 289 | /* |
bf4a36d5 KB |
290 | * Choose PADSIZE to trade efficiency vs. size. If larger printf |
291 | * fields occur frequently, increase PADSIZE and make the initialisers | |
292 | * below longer. | |
1e949ff4 KB |
293 | */ |
294 | #define PADSIZE 16 /* pad chunk size */ | |
295 | static char blanks[PADSIZE] = | |
296 | {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; | |
297 | static char zeroes[PADSIZE] = | |
298 | {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; | |
299 | ||
300 | /* | |
301 | * BEWARE, these `goto error' on error, and PAD uses `n'. | |
302 | */ | |
303 | #define PRINT(ptr, len) { \ | |
304 | iovp->iov_base = (ptr); \ | |
305 | iovp->iov_len = (len); \ | |
306 | uio.uio_resid += (len); \ | |
307 | iovp++; \ | |
308 | if (++uio.uio_iovcnt >= NIOV) { \ | |
309 | if (__sprint(fp, &uio)) \ | |
310 | goto error; \ | |
311 | iovp = iov; \ | |
312 | } \ | |
313 | } | |
314 | #define PAD(howmany, with) { \ | |
315 | if ((n = (howmany)) > 0) { \ | |
316 | while (n > PADSIZE) { \ | |
317 | PRINT(with, PADSIZE); \ | |
318 | n -= PADSIZE; \ | |
319 | } \ | |
320 | PRINT(with, n); \ | |
321 | } \ | |
322 | } | |
323 | #define FLUSH() { \ | |
324 | if (uio.uio_resid && __sprint(fp, &uio)) \ | |
325 | goto error; \ | |
326 | uio.uio_iovcnt = 0; \ | |
327 | iovp = iov; \ | |
328 | } | |
329 | ||
330 | /* | |
331 | * To extend shorts properly, we need both signed and unsigned | |
332 | * argument extraction methods. | |
333 | */ | |
334 | #define SARG() \ | |
b899ab75 | 335 | (flags&LONGINT ? va_arg(ap, long) : \ |
1e949ff4 KB |
336 | flags&SHORTINT ? (long)(short)va_arg(ap, int) : \ |
337 | (long)va_arg(ap, int)) | |
338 | #define UARG() \ | |
b899ab75 | 339 | (flags&LONGINT ? va_arg(ap, u_long) : \ |
1e949ff4 KB |
340 | flags&SHORTINT ? (u_long)(u_short)va_arg(ap, int) : \ |
341 | (u_long)va_arg(ap, u_int)) | |
342 | ||
1e949ff4 KB |
343 | /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ |
344 | if (cantwrite(fp)) | |
87d70ec1 KB |
345 | return (EOF); |
346 | ||
1e949ff4 KB |
347 | /* optimise fprintf(stderr) (and other unbuffered Unix files) */ |
348 | if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && | |
349 | fp->_file >= 0) | |
350 | return (__sbprintf(fp, fmt0, ap)); | |
ad0e16d0 | 351 | |
1e949ff4 KB |
352 | fmt = (char *)fmt0; |
353 | uio.uio_iov = iovp = iov; | |
354 | uio.uio_resid = 0; | |
355 | uio.uio_iovcnt = 0; | |
356 | ret = 0; | |
357 | ||
358 | /* | |
359 | * Scan the format for conversions (`%' character). | |
360 | */ | |
361 | for (;;) { | |
362 | for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) | |
363 | /* void */; | |
364 | if ((n = fmt - cp) != 0) { | |
365 | PRINT(cp, n); | |
366 | ret += n; | |
367 | } | |
368 | if (ch == '\0') | |
369 | goto done; | |
370 | fmt++; /* skip over '%' */ | |
371 | ||
372 | flags = 0; | |
373 | dprec = 0; | |
1e949ff4 | 374 | width = 0; |
5f6de66c | 375 | prec = -1; |
410bd0d3 | 376 | sign = '\0'; |
5f6de66c | 377 | |
1e949ff4 KB |
378 | rflag: ch = *fmt++; |
379 | reswitch: switch (ch) { | |
410bd0d3 | 380 | case ' ': |
90041bd4 KB |
381 | /* |
382 | * ``If the space and + flags both appear, the space | |
383 | * flag will be ignored.'' | |
384 | * -- ANSI X3J11 | |
385 | */ | |
386 | if (!sign) | |
387 | sign = ' '; | |
410bd0d3 | 388 | goto rflag; |
5f6de66c | 389 | case '#': |
410bd0d3 KB |
390 | flags |= ALT; |
391 | goto rflag; | |
5f6de66c | 392 | case '*': |
66d7f790 KB |
393 | /* |
394 | * ``A negative field width argument is taken as a | |
1e949ff4 | 395 | * - flag followed by a positive field width.'' |
66d7f790 KB |
396 | * -- ANSI X3J11 |
397 | * They don't exclude field widths read from args. | |
398 | */ | |
1e949ff4 | 399 | if ((width = va_arg(ap, int)) >= 0) |
410bd0d3 | 400 | goto rflag; |
66d7f790 | 401 | width = -width; |
b042624f | 402 | /* FALLTHROUGH */ |
66d7f790 | 403 | case '-': |
410bd0d3 KB |
404 | flags |= LADJUST; |
405 | goto rflag; | |
5f6de66c | 406 | case '+': |
40233183 | 407 | sign = '+'; |
410bd0d3 | 408 | goto rflag; |
5f6de66c | 409 | case '.': |
1e949ff4 KB |
410 | if ((ch = *fmt++) == '*') { |
411 | n = va_arg(ap, int); | |
412 | prec = n < 0 ? -1 : n; | |
413 | goto rflag; | |
414 | } | |
415 | n = 0; | |
416 | while (is_digit(ch)) { | |
417 | n = 10 * n + to_digit(ch); | |
418 | ch = *fmt++; | |
66d7f790 | 419 | } |
410bd0d3 | 420 | prec = n < 0 ? -1 : n; |
1e949ff4 | 421 | goto reswitch; |
5f6de66c | 422 | case '0': |
b042624f KB |
423 | /* |
424 | * ``Note that 0 is taken as a flag, not as the | |
425 | * beginning of a field width.'' | |
426 | * -- ANSI X3J11 | |
427 | */ | |
428 | flags |= ZEROPAD; | |
429 | goto rflag; | |
5f6de66c KB |
430 | case '1': case '2': case '3': case '4': |
431 | case '5': case '6': case '7': case '8': case '9': | |
410bd0d3 | 432 | n = 0; |
5f6de66c | 433 | do { |
1e949ff4 KB |
434 | n = 10 * n + to_digit(ch); |
435 | ch = *fmt++; | |
436 | } while (is_digit(ch)); | |
410bd0d3 | 437 | width = n; |
1e949ff4 KB |
438 | goto reswitch; |
439 | #ifdef FLOATING_POINT | |
66d7f790 | 440 | case 'L': |
ad5b98a5 | 441 | flags |= LONGDBL; |
410bd0d3 | 442 | goto rflag; |
1e949ff4 | 443 | #endif |
66d7f790 | 444 | case 'h': |
410bd0d3 KB |
445 | flags |= SHORTINT; |
446 | goto rflag; | |
5f6de66c | 447 | case 'l': |
410bd0d3 KB |
448 | flags |= LONGINT; |
449 | goto rflag; | |
bf4a36d5 KB |
450 | case 'q': |
451 | flags |= QUADINT; | |
452 | goto rflag; | |
40233183 | 453 | case 'c': |
1e949ff4 | 454 | *(cp = buf) = va_arg(ap, int); |
40233183 | 455 | size = 1; |
b042624f | 456 | sign = '\0'; |
1e949ff4 | 457 | break; |
2c2ee7b4 KB |
458 | case 'D': |
459 | flags |= LONGINT; | |
460 | /*FALLTHROUGH*/ | |
a5676d90 | 461 | case 'd': |
410bd0d3 | 462 | case 'i': |
b899ab75 KB |
463 | if (flags & QUADINT) { |
464 | uqval = va_arg(ap, quad_t); | |
465 | if ((quad_t)uqval < 0) { | |
466 | uqval = -uqval; | |
467 | sign = '-'; | |
468 | } | |
469 | } else { | |
470 | ulval = SARG(); | |
471 | if ((long)ulval < 0) { | |
472 | ulval = -ulval; | |
473 | sign = '-'; | |
474 | } | |
a5676d90 | 475 | } |
b899ab75 | 476 | base = 10; |
bb5070be | 477 | goto number; |
1e949ff4 | 478 | #ifdef FLOATING_POINT |
75964b08 | 479 | case 'e': /* anomalous precision */ |
0d15d742 | 480 | case 'E': |
75964b08 KB |
481 | prec = (prec == -1) ? |
482 | DEFPREC + 1 : prec + 1; | |
483 | /* FALLTHROUGH */ | |
484 | goto fp_begin; | |
485 | case 'f': /* always print trailing zeroes */ | |
486 | if (prec != 0) | |
487 | flags |= ALT; | |
5592524e | 488 | case 'g': |
0d15d742 | 489 | case 'G': |
75964b08 KB |
490 | if (prec == -1) |
491 | prec = DEFPREC; | |
492 | fp_begin: _double = va_arg(ap, double); | |
b014cebf | 493 | /* do this before tricky precision changes */ |
c3ee6712 KB |
494 | if (isinf(_double)) { |
495 | if (_double < 0) | |
496 | sign = '-'; | |
497 | cp = "Inf"; | |
498 | size = 3; | |
499 | break; | |
500 | } | |
501 | if (isnan(_double)) { | |
502 | cp = "NaN"; | |
503 | size = 3; | |
b014cebf CT |
504 | break; |
505 | } | |
75964b08 KB |
506 | flags |= FPT; |
507 | cp = cvt(_double, prec, flags, &softsign, | |
508 | &expt, ch, &ndig); | |
509 | if (ch == 'g' || ch == 'G') { | |
510 | if (expt <= -4 || expt > prec) | |
511 | ch = (ch == 'g') ? 'e' : 'E'; | |
512 | else | |
513 | ch = 'g'; | |
514 | } | |
515 | if (ch <= 'e') { /* 'e' or 'E' fmt */ | |
516 | --expt; | |
517 | expsize = exponent(expstr, expt, ch); | |
518 | size = expsize + ndig; | |
519 | if (ndig > 1 || flags & ALT) | |
520 | ++size; | |
521 | } else if (ch == 'f') { /* f fmt */ | |
522 | if (expt > 0) { | |
523 | size = expt; | |
524 | if (prec || flags & ALT) | |
525 | size += prec + 1; | |
526 | } else /* "0.X" */ | |
527 | size = prec + 2; | |
528 | } else if (expt >= ndig) { /* fixed g fmt */ | |
529 | size = expt; | |
530 | if (flags & ALT) | |
531 | ++size; | |
532 | } else | |
533 | size = ndig + (expt > 0 ? | |
534 | 1 : 2 - expt); | |
535 | ||
90041bd4 KB |
536 | if (softsign) |
537 | sign = '-'; | |
1e949ff4 KB |
538 | break; |
539 | #endif /* FLOATING_POINT */ | |
66d7f790 | 540 | case 'n': |
bf4a36d5 KB |
541 | if (flags & QUADINT) |
542 | *va_arg(ap, quad_t *) = ret; | |
543 | else if (flags & LONGINT) | |
1e949ff4 | 544 | *va_arg(ap, long *) = ret; |
b042624f | 545 | else if (flags & SHORTINT) |
1e949ff4 | 546 | *va_arg(ap, short *) = ret; |
410bd0d3 | 547 | else |
1e949ff4 KB |
548 | *va_arg(ap, int *) = ret; |
549 | continue; /* no output */ | |
2c2ee7b4 KB |
550 | case 'O': |
551 | flags |= LONGINT; | |
552 | /*FALLTHROUGH*/ | |
ad0e16d0 | 553 | case 'o': |
b899ab75 KB |
554 | if (flags & QUADINT) |
555 | uqval = va_arg(ap, u_quad_t); | |
556 | else | |
557 | ulval = UARG(); | |
558 | base = 8; | |
bb5070be | 559 | goto nosign; |
66d7f790 | 560 | case 'p': |
2c71023f | 561 | /* |
c748d653 KB |
562 | * ``The argument shall be a pointer to void. The |
563 | * value of the pointer is converted to a sequence | |
564 | * of printable characters, in an implementation- | |
565 | * defined manner.'' | |
566 | * -- ANSI X3J11 | |
2c71023f | 567 | */ |
b899ab75 KB |
568 | ulval = (u_long)va_arg(ap, void *); |
569 | base = 16; | |
1e949ff4 | 570 | xdigs = "0123456789abcdef"; |
b899ab75 | 571 | flags = (flags & ~QUADINT) | HEXPREFIX; |
1e949ff4 | 572 | ch = 'x'; |
bb5070be | 573 | goto nosign; |
ad0e16d0 | 574 | case 's': |
1e949ff4 KB |
575 | if ((cp = va_arg(ap, char *)) == NULL) |
576 | cp = "(null)"; | |
c748d653 KB |
577 | if (prec >= 0) { |
578 | /* | |
579 | * can't use strlen; can only look for the | |
580 | * NUL in the first `prec' characters, and | |
581 | * strlen() will go further. | |
582 | */ | |
1e949ff4 | 583 | char *p = memchr(cp, 0, prec); |
c748d653 | 584 | |
1e949ff4 KB |
585 | if (p != NULL) { |
586 | size = p - cp; | |
c748d653 KB |
587 | if (size > prec) |
588 | size = prec; | |
b042624f | 589 | } else |
c748d653 | 590 | size = prec; |
b042624f | 591 | } else |
1e949ff4 | 592 | size = strlen(cp); |
b042624f | 593 | sign = '\0'; |
1e949ff4 | 594 | break; |
2c2ee7b4 KB |
595 | case 'U': |
596 | flags |= LONGINT; | |
597 | /*FALLTHROUGH*/ | |
ad0e16d0 | 598 | case 'u': |
b899ab75 KB |
599 | if (flags & QUADINT) |
600 | uqval = va_arg(ap, u_quad_t); | |
601 | else | |
602 | ulval = UARG(); | |
603 | base = 10; | |
bb5070be | 604 | goto nosign; |
ad0e16d0 | 605 | case 'X': |
1e949ff4 KB |
606 | xdigs = "0123456789ABCDEF"; |
607 | goto hex; | |
ad0e16d0 | 608 | case 'x': |
1e949ff4 | 609 | xdigs = "0123456789abcdef"; |
b899ab75 KB |
610 | hex: if (flags & QUADINT) |
611 | uqval = va_arg(ap, u_quad_t); | |
612 | else | |
613 | ulval = UARG(); | |
614 | base = 16; | |
8a91f8d6 | 615 | /* leading 0x/X only if non-zero */ |
b899ab75 KB |
616 | if (flags & ALT && |
617 | (flags & QUADINT ? uqval != 0 : ulval != 0)) | |
b042624f | 618 | flags |= HEXPREFIX; |
bb5070be KB |
619 | |
620 | /* unsigned conversions */ | |
b042624f | 621 | nosign: sign = '\0'; |
89e72465 KB |
622 | /* |
623 | * ``... diouXx conversions ... if a precision is | |
624 | * specified, the 0 flag will be ignored.'' | |
625 | * -- ANSI X3J11 | |
626 | */ | |
b042624f KB |
627 | number: if ((dprec = prec) >= 0) |
628 | flags &= ~ZEROPAD; | |
629 | ||
8a91f8d6 KB |
630 | /* |
631 | * ``The result of converting a zero value with an | |
632 | * explicit precision of zero is no characters.'' | |
633 | * -- ANSI X3J11 | |
634 | */ | |
1e949ff4 | 635 | cp = buf + BUF; |
b899ab75 KB |
636 | if (flags & QUADINT) { |
637 | if (uqval != 0 || prec != 0) | |
638 | cp = __uqtoa(uqval, cp, base, | |
639 | flags & ALT, xdigs); | |
640 | } else { | |
641 | if (ulval != 0 || prec != 0) | |
642 | cp = __ultoa(ulval, cp, base, | |
643 | flags & ALT, xdigs); | |
bb5070be | 644 | } |
1e949ff4 | 645 | size = buf + BUF - cp; |
1e949ff4 KB |
646 | break; |
647 | default: /* "%?" prints ?, unless ? is NUL */ | |
648 | if (ch == '\0') | |
649 | goto done; | |
650 | /* pretend it was %c with argument ch */ | |
651 | cp = buf; | |
652 | *cp = ch; | |
653 | size = 1; | |
654 | sign = '\0'; | |
ad0e16d0 | 655 | break; |
ad0e16d0 | 656 | } |
1e949ff4 KB |
657 | |
658 | /* | |
75964b08 KB |
659 | * All reasonable formats wind up here. At this point, `cp' |
660 | * points to a string which (if not flags&LADJUST) should be | |
661 | * padded out to `width' places. If flags&ZEROPAD, it should | |
662 | * first be prefixed by any sign or other prefix; otherwise, | |
663 | * it should be blank padded before the prefix is emitted. | |
664 | * After any left-hand padding and prefixing, emit zeroes | |
665 | * required by a decimal [diouxX] precision, then print the | |
666 | * string proper, then emit zeroes required by any leftover | |
667 | * floating precision; finally, if LADJUST, pad with blanks. | |
668 | * | |
669 | * Compute actual size, so we know how much to pad. | |
670 | * fieldsz excludes decimal prec; realsz includes it. | |
1e949ff4 | 671 | */ |
1e949ff4 | 672 | fieldsz = size; |
1e949ff4 KB |
673 | if (sign) |
674 | fieldsz++; | |
675 | else if (flags & HEXPREFIX) | |
676 | fieldsz += 2; | |
677 | realsz = dprec > fieldsz ? dprec : fieldsz; | |
678 | ||
679 | /* right-adjusting blank padding */ | |
680 | if ((flags & (LADJUST|ZEROPAD)) == 0) | |
681 | PAD(width - realsz, blanks); | |
682 | ||
683 | /* prefix */ | |
684 | if (sign) { | |
685 | PRINT(&sign, 1); | |
686 | } else if (flags & HEXPREFIX) { | |
687 | ox[0] = '0'; | |
688 | ox[1] = ch; | |
689 | PRINT(ox, 2); | |
690 | } | |
691 | ||
692 | /* right-adjusting zero padding */ | |
693 | if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) | |
694 | PAD(width - realsz, zeroes); | |
695 | ||
696 | /* leading zeroes from decimal precision */ | |
697 | PAD(dprec - fieldsz, zeroes); | |
698 | ||
699 | /* the string or number proper */ | |
1e949ff4 | 700 | #ifdef FLOATING_POINT |
75964b08 KB |
701 | if ((flags & FPT) == 0) { |
702 | PRINT(cp, size); | |
703 | } else { /* glue together f_p fragments */ | |
704 | if (ch >= 'f') { /* 'f' or 'g' */ | |
705 | if (_double == 0) { | |
706 | /* kludge for __dtoa irregularity */ | |
707 | if (prec == 0 || | |
708 | (flags & ALT) == 0) { | |
709 | PRINT("0", 1); | |
710 | } else { | |
711 | PRINT("0.", 2); | |
712 | PAD(ndig - 1, zeroes); | |
713 | } | |
714 | } else if (expt <= 0) { | |
715 | PRINT("0.", 2); | |
716 | PAD(-expt, zeroes); | |
717 | PRINT(cp, ndig); | |
718 | } else if (expt >= ndig) { | |
719 | PRINT(cp, ndig); | |
720 | PAD(expt - ndig, zeroes); | |
721 | if (flags & ALT) | |
722 | PRINT(".", 1); | |
723 | } else { | |
724 | PRINT(cp, expt); | |
725 | cp += expt; | |
726 | PRINT(".", 1); | |
727 | PRINT(cp, ndig-expt); | |
728 | } | |
729 | } else { /* 'e' or 'E' */ | |
730 | if (ndig > 1 || flags & ALT) { | |
731 | ox[0] = *cp++; | |
732 | ox[1] = '.'; | |
733 | PRINT(ox, 2); | |
734 | if (_double || flags & ALT == 0) { | |
735 | PRINT(cp, ndig-1); | |
736 | } else /* 0.[0..] */ | |
737 | /* __dtoa irregularity */ | |
738 | PAD(ndig - 1, zeroes); | |
739 | } else /* XeYYY */ | |
740 | PRINT(cp, 1); | |
741 | PRINT(expstr, expsize); | |
742 | } | |
743 | } | |
744 | #else | |
745 | PRINT(cp, size); | |
1e949ff4 | 746 | #endif |
1e949ff4 KB |
747 | /* left-adjusting padding (always blank) */ |
748 | if (flags & LADJUST) | |
749 | PAD(width - realsz, blanks); | |
750 | ||
751 | /* finally, adjust ret */ | |
752 | ret += width > realsz ? width : realsz; | |
753 | ||
754 | FLUSH(); /* copy out the I/O vectors */ | |
ad0e16d0 | 755 | } |
1e949ff4 KB |
756 | done: |
757 | FLUSH(); | |
758 | error: | |
759 | return (__sferror(fp) ? EOF : ret); | |
b042624f | 760 | /* NOTREACHED */ |
ad0e16d0 | 761 | } |
cd0a25f4 | 762 | |
1e949ff4 | 763 | #ifdef FLOATING_POINT |
4569d013 | 764 | |
75964b08 | 765 | extern char *__dtoa __P((double, int, int, int *, int *, char **)); |
1e949ff4 | 766 | |
75964b08 KB |
767 | static char * |
768 | cvt(value, ndigits, flags, sign, decpt, ch, length) | |
769 | double value; | |
770 | int ndigits, flags, *decpt, ch, *length; | |
771 | char *sign; | |
cd0a25f4 | 772 | { |
75964b08 KB |
773 | int mode, dsgn; |
774 | char *digits, *bp, *rve; | |
bb5070be | 775 | |
75964b08 KB |
776 | if (ch == 'f') |
777 | mode = 3; | |
778 | else { | |
779 | mode = 2; | |
2c2ee7b4 | 780 | } |
75964b08 KB |
781 | if (value < 0) { |
782 | value = -value; | |
783 | *sign = '-'; | |
784 | } else | |
785 | *sign = '\000'; | |
786 | digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve); | |
787 | if (flags & ALT) { /* Print trailing zeros */ | |
788 | bp = digits + ndigits; | |
789 | if (ch == 'f') { | |
790 | if (*digits == '0' && value) | |
791 | *decpt = -ndigits + 1; | |
792 | bp += *decpt; | |
2c2ee7b4 | 793 | } |
75964b08 KB |
794 | if (value == 0) /* kludge for __dtoa irregularity */ |
795 | rve = bp; | |
796 | while (rve < bp) | |
797 | *rve++ = '0'; | |
862bbfbe | 798 | } |
75964b08 KB |
799 | *length = rve - digits; |
800 | return (digits); | |
2c2ee7b4 | 801 | } |
862bbfbe | 802 | |
75964b08 KB |
803 | static int |
804 | exponent(p0, exp, fmtch) | |
805 | char *p0; | |
806 | int exp, fmtch; | |
2c2ee7b4 | 807 | { |
75964b08 | 808 | register char *p, *t; |
2c2ee7b4 | 809 | char expbuf[MAXEXP]; |
862bbfbe | 810 | |
75964b08 | 811 | p = p0; |
2c2ee7b4 KB |
812 | *p++ = fmtch; |
813 | if (exp < 0) { | |
814 | exp = -exp; | |
815 | *p++ = '-'; | |
816 | } | |
817 | else | |
818 | *p++ = '+'; | |
819 | t = expbuf + MAXEXP; | |
820 | if (exp > 9) { | |
821 | do { | |
1e949ff4 | 822 | *--t = to_char(exp % 10); |
2c2ee7b4 | 823 | } while ((exp /= 10) > 9); |
1e949ff4 | 824 | *--t = to_char(exp); |
2c2ee7b4 KB |
825 | for (; t < expbuf + MAXEXP; *p++ = *t++); |
826 | } | |
827 | else { | |
828 | *p++ = '0'; | |
1e949ff4 | 829 | *p++ = to_char(exp); |
cd0a25f4 | 830 | } |
75964b08 | 831 | return (p - p0); |
cd0a25f4 | 832 | } |
1e949ff4 | 833 | #endif /* FLOATING_POINT */ |