]>
Commit | Line | Data |
---|---|---|
e9ce8d39 A |
1 | /* |
2 | * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. | |
3 | * | |
4 | * @APPLE_LICENSE_HEADER_START@ | |
5 | * | |
6 | * The contents of this file constitute Original Code as defined in and | |
7 | * are subject to the Apple Public Source License Version 1.1 (the | |
8 | * "License"). You may not use this file except in compliance with the | |
9 | * License. Please obtain a copy of the License at | |
10 | * http://www.apple.com/publicsource and read it before using this file. | |
11 | * | |
12 | * This Original Code and all software distributed under the License are | |
13 | * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER | |
14 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, | |
15 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, | |
16 | * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the | |
17 | * License for the specific language governing rights and limitations | |
18 | * under the License. | |
19 | * | |
20 | * @APPLE_LICENSE_HEADER_END@ | |
21 | */ | |
22 | /* | |
23 | * Copyright (c) 1990, 1993 | |
24 | * The Regents of the University of California. All rights reserved. | |
25 | * | |
26 | * This code is derived from software contributed to Berkeley by | |
27 | * Chris Torek. | |
28 | * | |
29 | * Redistribution and use in source and binary forms, with or without | |
30 | * modification, are permitted provided that the following conditions | |
31 | * are met: | |
32 | * 1. Redistributions of source code must retain the above copyright | |
33 | * notice, this list of conditions and the following disclaimer. | |
34 | * 2. Redistributions in binary form must reproduce the above copyright | |
35 | * notice, this list of conditions and the following disclaimer in the | |
36 | * documentation and/or other materials provided with the distribution. | |
37 | * 3. All advertising materials mentioning features or use of this software | |
38 | * must display the following acknowledgement: | |
39 | * This product includes software developed by the University of | |
40 | * California, Berkeley and its contributors. | |
41 | * 4. Neither the name of the University nor the names of its contributors | |
42 | * may be used to endorse or promote products derived from this software | |
43 | * without specific prior written permission. | |
44 | * | |
45 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
46 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
47 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
48 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
49 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
50 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
51 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
52 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
53 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
54 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
55 | * SUCH DAMAGE. | |
56 | */ | |
57 | ||
58 | /* | |
59 | * Actual printf innards. | |
60 | * | |
61 | * This code is large and complicated... | |
62 | */ | |
63 | ||
64 | #include <sys/types.h> | |
65 | ||
66 | #include <limits.h> | |
67 | #include <stdio.h> | |
68 | #include <stdlib.h> | |
69 | #include <string.h> | |
5b2abdfb | 70 | #include <sys/sysctl.h> |
e9ce8d39 A |
71 | |
72 | #if __STDC__ | |
73 | #include <stdarg.h> | |
74 | #else | |
75 | #include <varargs.h> | |
76 | #endif | |
77 | ||
78 | #include "local.h" | |
79 | #include "fvwrite.h" | |
80 | ||
81 | /* Define FLOATING_POINT to get floating point. */ | |
82 | #define FLOATING_POINT | |
5b2abdfb A |
83 | union arg { |
84 | int intarg; | |
85 | unsigned int uintarg; | |
86 | long longarg; | |
87 | unsigned long ulongarg; | |
88 | quad_t quadarg; | |
89 | u_quad_t uquadarg; | |
90 | void *pvoidarg; | |
91 | char *pchararg; | |
92 | short *pshortarg; | |
93 | int *pintarg; | |
94 | long *plongarg; | |
95 | quad_t *pquadarg; | |
96 | #ifdef FLOATING_POINT | |
97 | double doublearg; | |
98 | long double longdoublearg; | |
99 | #endif | |
100 | #ifdef ALTIVEC | |
101 | unsigned char vuchararg[16]; | |
102 | signed char vchararg[16]; | |
103 | unsigned short vushortarg[8]; | |
104 | signed short vshortarg[8]; | |
105 | unsigned int vuintarg[4]; | |
106 | signed int vintarg[4]; | |
107 | float vfloatarg[4]; | |
108 | #endif | |
109 | }; | |
e9ce8d39 A |
110 | |
111 | static int __sprint __P((FILE *, struct __suio *)); | |
112 | static int __sbprintf __P((FILE *, const char *, va_list)); | |
113 | static char * __ultoa __P((u_long, char *, int, int, char *)); | |
114 | static char * __uqtoa __P((u_quad_t, char *, int, int, char *)); | |
5b2abdfb | 115 | static void __find_arguments __P((const char *, va_list, union arg **)); |
e9ce8d39 A |
116 | static void __grow_type_table __P((int, unsigned char **, int *)); |
117 | ||
5b2abdfb A |
118 | /* |
119 | * Get the argument indexed by nextarg. If the argument table is | |
120 | * built, use it to get the argument. If its not, get the next | |
121 | * argument (and arguments must be gotten sequentially). | |
122 | */ | |
123 | #define GETARG(type) \ | |
124 | ((argtable != NULL) ? *((type*)(&argtable[nextarg++])) : \ | |
125 | (nextarg++, va_arg(ap, type))) | |
126 | ||
127 | #ifdef ALTIVEC | |
128 | static void getvec(union arg *dst, const union arg *argtable, int nextarg, va_list ap) | |
129 | { | |
130 | vector unsigned char tmp; | |
131 | ||
132 | tmp = GETARG(vector unsigned char); | |
133 | memcpy( dst, &tmp, 16 ); | |
134 | } | |
135 | ||
136 | #endif | |
137 | ||
e9ce8d39 A |
138 | /* |
139 | * Flush out all the vectors defined by the given uio, | |
140 | * then reset it so that it can be reused. | |
141 | */ | |
142 | static int | |
143 | __sprint(fp, uio) | |
144 | FILE *fp; | |
145 | register struct __suio *uio; | |
146 | { | |
147 | register int err; | |
148 | ||
149 | if (uio->uio_resid == 0) { | |
150 | uio->uio_iovcnt = 0; | |
151 | return (0); | |
152 | } | |
153 | err = __sfvwrite(fp, uio); | |
154 | uio->uio_resid = 0; | |
155 | uio->uio_iovcnt = 0; | |
156 | return (err); | |
157 | } | |
158 | ||
159 | /* | |
160 | * Helper function for `fprintf to unbuffered unix file': creates a | |
161 | * temporary buffer. We only work on write-only files; this avoids | |
162 | * worries about ungetc buffers and so forth. | |
163 | */ | |
164 | static int | |
165 | __sbprintf(fp, fmt, ap) | |
166 | register FILE *fp; | |
167 | const char *fmt; | |
168 | va_list ap; | |
169 | { | |
170 | int ret; | |
171 | FILE fake; | |
172 | unsigned char buf[BUFSIZ]; | |
173 | ||
174 | /* copy the important variables */ | |
175 | fake._flags = fp->_flags & ~__SNBF; | |
176 | fake._file = fp->_file; | |
177 | fake._cookie = fp->_cookie; | |
178 | fake._write = fp->_write; | |
179 | ||
180 | /* set up the buffer */ | |
181 | fake._bf._base = fake._p = buf; | |
182 | fake._bf._size = fake._w = sizeof(buf); | |
183 | fake._lbfsize = 0; /* not actually used, but Just In Case */ | |
184 | ||
185 | /* do the work, then copy any error status */ | |
186 | ret = vfprintf(&fake, fmt, ap); | |
187 | if (ret >= 0 && fflush(&fake)) | |
188 | ret = EOF; | |
189 | if (fake._flags & __SERR) | |
190 | fp->_flags |= __SERR; | |
191 | return (ret); | |
192 | } | |
193 | ||
194 | /* | |
195 | * Macros for converting digits to letters and vice versa | |
196 | */ | |
197 | #define to_digit(c) ((c) - '0') | |
198 | #define is_digit(c) ((unsigned)to_digit(c) <= 9) | |
199 | #define to_char(n) ((n) + '0') | |
200 | ||
201 | /* | |
202 | * Convert an unsigned long to ASCII for printf purposes, returning | |
203 | * a pointer to the first character of the string representation. | |
204 | * Octal numbers can be forced to have a leading zero; hex numbers | |
205 | * use the given digits. | |
206 | */ | |
207 | static char * | |
208 | __ultoa(val, endp, base, octzero, xdigs) | |
209 | register u_long val; | |
210 | char *endp; | |
211 | int base, octzero; | |
212 | char *xdigs; | |
213 | { | |
214 | register char *cp = endp; | |
215 | register long sval; | |
216 | ||
217 | /* | |
218 | * Handle the three cases separately, in the hope of getting | |
219 | * better/faster code. | |
220 | */ | |
221 | switch (base) { | |
222 | case 10: | |
223 | if (val < 10) { /* many numbers are 1 digit */ | |
224 | *--cp = to_char(val); | |
225 | return (cp); | |
226 | } | |
227 | /* | |
228 | * On many machines, unsigned arithmetic is harder than | |
229 | * signed arithmetic, so we do at most one unsigned mod and | |
230 | * divide; this is sufficient to reduce the range of | |
231 | * the incoming value to where signed arithmetic works. | |
232 | */ | |
233 | if (val > LONG_MAX) { | |
234 | *--cp = to_char(val % 10); | |
235 | sval = val / 10; | |
236 | } else | |
237 | sval = val; | |
238 | do { | |
239 | *--cp = to_char(sval % 10); | |
240 | sval /= 10; | |
241 | } while (sval != 0); | |
242 | break; | |
243 | ||
244 | case 8: | |
245 | do { | |
246 | *--cp = to_char(val & 7); | |
247 | val >>= 3; | |
248 | } while (val); | |
249 | if (octzero && *cp != '0') | |
250 | *--cp = '0'; | |
251 | break; | |
252 | ||
253 | case 16: | |
254 | do { | |
255 | *--cp = xdigs[val & 15]; | |
256 | val >>= 4; | |
257 | } while (val); | |
258 | break; | |
259 | ||
260 | default: /* oops */ | |
261 | abort(); | |
262 | } | |
263 | return (cp); | |
264 | } | |
265 | ||
266 | /* Identical to __ultoa, but for quads. */ | |
267 | static char * | |
268 | __uqtoa(val, endp, base, octzero, xdigs) | |
269 | register u_quad_t val; | |
270 | char *endp; | |
271 | int base, octzero; | |
272 | char *xdigs; | |
273 | { | |
274 | register char *cp = endp; | |
275 | register quad_t sval; | |
276 | ||
277 | /* quick test for small values; __ultoa is typically much faster */ | |
278 | /* (perhaps instead we should run until small, then call __ultoa?) */ | |
279 | if (val <= ULONG_MAX) | |
280 | return (__ultoa((u_long)val, endp, base, octzero, xdigs)); | |
281 | switch (base) { | |
282 | case 10: | |
283 | if (val < 10) { | |
284 | *--cp = to_char(val % 10); | |
285 | return (cp); | |
286 | } | |
287 | if (val > QUAD_MAX) { | |
288 | *--cp = to_char(val % 10); | |
289 | sval = val / 10; | |
290 | } else | |
291 | sval = val; | |
292 | do { | |
293 | *--cp = to_char(sval % 10); | |
294 | sval /= 10; | |
295 | } while (sval != 0); | |
296 | break; | |
297 | ||
298 | case 8: | |
299 | do { | |
300 | *--cp = to_char(val & 7); | |
301 | val >>= 3; | |
302 | } while (val); | |
303 | if (octzero && *cp != '0') | |
304 | *--cp = '0'; | |
305 | break; | |
306 | ||
307 | case 16: | |
308 | do { | |
309 | *--cp = xdigs[val & 15]; | |
310 | val >>= 4; | |
311 | } while (val); | |
312 | break; | |
313 | ||
314 | default: | |
315 | abort(); | |
316 | } | |
317 | return (cp); | |
318 | } | |
319 | ||
320 | #ifdef FLOATING_POINT | |
321 | #include <math.h> | |
322 | #include "floatio.h" | |
323 | ||
324 | #define BUF (MAXEXP+MAXFRACT+1) /* + decimal point */ | |
325 | #define DEFPREC 6 | |
326 | ||
3b2a1fe8 | 327 | static char *cvt __P((double, int, int, char *, int *, int, int *, char **)); |
e9ce8d39 A |
328 | static int exponent __P((char *, int, int)); |
329 | ||
5b2abdfb A |
330 | #if defined(__APPLE__) |
331 | /* | |
332 | * We don't want to be dependent on any libm symbols so use the versions in Libc | |
333 | */ | |
334 | ||
335 | #undef isinf | |
336 | #undef isnan | |
337 | ||
338 | extern int isnan(double); | |
339 | extern int isinf(double); | |
340 | ||
341 | #endif /* __APPLE __ */ | |
342 | ||
e9ce8d39 A |
343 | #else /* no FLOATING_POINT */ |
344 | ||
345 | #define BUF 68 | |
346 | ||
347 | #endif /* FLOATING_POINT */ | |
348 | ||
349 | #define STATIC_ARG_TBL_SIZE 8 /* Size of static argument table. */ | |
350 | ||
351 | /* | |
352 | * Flags used during conversion. | |
353 | */ | |
354 | #define ALT 0x001 /* alternate form */ | |
355 | #define HEXPREFIX 0x002 /* add 0x or 0X prefix */ | |
356 | #define LADJUST 0x004 /* left adjustment */ | |
357 | #define LONGDBL 0x008 /* long double */ | |
358 | #define LONGINT 0x010 /* long integer */ | |
359 | #define QUADINT 0x020 /* quad integer */ | |
360 | #define SHORTINT 0x040 /* short integer */ | |
361 | #define ZEROPAD 0x080 /* zero (as opposed to blank) pad */ | |
362 | #define FPT 0x100 /* Floating point number */ | |
5b2abdfb | 363 | #define VECTOR 0x200 /* Altivec vector */ |
e9ce8d39 A |
364 | int |
365 | vfprintf(fp, fmt0, ap) | |
366 | FILE *fp; | |
367 | const char *fmt0; | |
368 | va_list ap; | |
369 | { | |
370 | register char *fmt; /* format string */ | |
371 | register int ch; /* character from fmt */ | |
372 | register int n, n2; /* handy integer (short term usage) */ | |
373 | register char *cp; /* handy char pointer (short term usage) */ | |
374 | register struct __siov *iovp;/* for PRINT macro */ | |
375 | register int flags; /* flags as above */ | |
376 | int ret; /* return value accumulator */ | |
377 | int width; /* width from format (%8d), or 0 */ | |
378 | int prec; /* precision from format (%.3d), or -1 */ | |
379 | char sign; /* sign prefix (' ', '+', '-', or \0) */ | |
380 | #ifdef FLOATING_POINT | |
381 | char softsign; /* temporary negative sign for floats */ | |
382 | double _double = 0; /* double precision arguments %[eEfgG] */ | |
383 | int expt; /* integer value of exponent */ | |
384 | int expsize = 0; /* character count for expstr */ | |
385 | int ndig; /* actual number of digits returned by cvt */ | |
386 | char expstr[7]; /* buffer for exponent string */ | |
3b2a1fe8 | 387 | char *dtoaresult; /* buffer allocated by dtoa */ |
5b2abdfb A |
388 | #endif |
389 | #ifdef ALTIVEC | |
390 | union arg vval; /* Vector argument. */ | |
391 | char *pct; /* Pointer to '%' at beginning of specifier. */ | |
392 | char vsep; /* Vector separator character. */ | |
e9ce8d39 A |
393 | #endif |
394 | u_long ulval = 0; /* integer arguments %[diouxX] */ | |
395 | u_quad_t uqval = 0; /* %q integers */ | |
396 | int base; /* base for [diouxX] conversion */ | |
397 | int dprec; /* a copy of prec if [diouxX], 0 otherwise */ | |
398 | int realsz; /* field size expanded by dprec, sign, etc */ | |
399 | int size; /* size of converted field or string */ | |
400 | int prsize; /* max size of printed field */ | |
401 | char *xdigs = NULL; /* digits for [xX] conversion */ | |
402 | #define NIOV 8 | |
403 | struct __suio uio; /* output information: summary */ | |
404 | struct __siov iov[NIOV];/* ... and individual io vectors */ | |
405 | char buf[BUF]; /* space for %c, %[diouxX], %[eEfgG] */ | |
406 | char ox[2]; /* space for 0x hex-prefix */ | |
5b2abdfb A |
407 | union arg *argtable; /* args, built due to positional arg */ |
408 | union arg statargtable [STATIC_ARG_TBL_SIZE]; | |
e9ce8d39 A |
409 | int nextarg; /* 1-based argument index */ |
410 | va_list orgap; /* original argument pointer */ | |
411 | ||
412 | /* | |
413 | * Choose PADSIZE to trade efficiency vs. size. If larger printf | |
414 | * fields occur frequently, increase PADSIZE and make the initialisers | |
415 | * below longer. | |
416 | */ | |
417 | #define PADSIZE 16 /* pad chunk size */ | |
418 | static char blanks[PADSIZE] = | |
419 | {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; | |
420 | static char zeroes[PADSIZE] = | |
421 | {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; | |
422 | ||
423 | /* | |
424 | * BEWARE, these `goto error' on error, and PAD uses `n'. | |
425 | */ | |
426 | #define PRINT(ptr, len) { \ | |
427 | iovp->iov_base = (ptr); \ | |
428 | iovp->iov_len = (len); \ | |
429 | uio.uio_resid += (len); \ | |
430 | iovp++; \ | |
431 | if (++uio.uio_iovcnt >= NIOV) { \ | |
432 | if (__sprint(fp, &uio)) \ | |
433 | goto error; \ | |
434 | iovp = iov; \ | |
435 | } \ | |
436 | } | |
437 | #define PAD(howmany, with) { \ | |
438 | if ((n = (howmany)) > 0) { \ | |
439 | while (n > PADSIZE) { \ | |
440 | PRINT(with, PADSIZE); \ | |
441 | n -= PADSIZE; \ | |
442 | } \ | |
443 | PRINT(with, n); \ | |
444 | } \ | |
445 | } | |
446 | #define FLUSH() { \ | |
447 | if (uio.uio_resid && __sprint(fp, &uio)) \ | |
448 | goto error; \ | |
449 | uio.uio_iovcnt = 0; \ | |
450 | iovp = iov; \ | |
451 | } | |
452 | ||
e9ce8d39 A |
453 | |
454 | /* | |
455 | * To extend shorts properly, we need both signed and unsigned | |
456 | * argument extraction methods. | |
457 | */ | |
458 | #define SARG() \ | |
459 | (flags&LONGINT ? GETARG(long) : \ | |
460 | flags&SHORTINT ? (long)(short)GETARG(int) : \ | |
461 | (long)GETARG(int)) | |
462 | #define UARG() \ | |
463 | (flags&LONGINT ? GETARG(u_long) : \ | |
464 | flags&SHORTINT ? (u_long)(u_short)GETARG(int) : \ | |
465 | (u_long)GETARG(u_int)) | |
466 | ||
467 | /* | |
468 | * Get * arguments, including the form *nn$. Preserve the nextarg | |
469 | * that the argument can be gotten once the type is determined. | |
470 | */ | |
471 | #define GETASTER(val) \ | |
472 | n2 = 0; \ | |
473 | cp = fmt; \ | |
474 | while (is_digit(*cp)) { \ | |
475 | n2 = 10 * n2 + to_digit(*cp); \ | |
476 | cp++; \ | |
477 | } \ | |
478 | if (*cp == '$') { \ | |
479 | int hold = nextarg; \ | |
480 | if (argtable == NULL) { \ | |
481 | argtable = statargtable; \ | |
482 | __find_arguments (fmt0, orgap, &argtable); \ | |
483 | } \ | |
484 | nextarg = n2; \ | |
485 | val = GETARG (int); \ | |
486 | nextarg = hold; \ | |
487 | fmt = ++cp; \ | |
488 | } else { \ | |
489 | val = GETARG (int); \ | |
490 | } | |
3b2a1fe8 A |
491 | #ifdef FLOATING_POINT |
492 | dtoaresult = NULL; | |
493 | #endif | |
e9ce8d39 A |
494 | /* FLOCKFILE(fp); */ |
495 | /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ | |
496 | if (cantwrite(fp)) { | |
497 | /* FUNLOCKFILE(fp); */ | |
498 | return (EOF); | |
499 | } | |
500 | ||
501 | /* optimise fprintf(stderr) (and other unbuffered Unix files) */ | |
502 | if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && | |
503 | fp->_file >= 0) { | |
504 | /* FUNLOCKFILE(fp); */ | |
505 | return (__sbprintf(fp, fmt0, ap)); | |
506 | } | |
507 | ||
508 | fmt = (char *)fmt0; | |
509 | argtable = NULL; | |
510 | nextarg = 1; | |
511 | orgap = ap; | |
512 | uio.uio_iov = iovp = iov; | |
513 | uio.uio_resid = 0; | |
514 | uio.uio_iovcnt = 0; | |
515 | ret = 0; | |
516 | ||
517 | /* | |
518 | * Scan the format for conversions (`%' character). | |
519 | */ | |
520 | for (;;) { | |
521 | for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) | |
522 | /* void */; | |
523 | if ((n = fmt - cp) != 0) { | |
524 | if ((unsigned)ret + n > INT_MAX) { | |
525 | ret = EOF; | |
526 | goto error; | |
527 | } | |
528 | PRINT(cp, n); | |
529 | ret += n; | |
530 | } | |
531 | if (ch == '\0') | |
532 | goto done; | |
5b2abdfb A |
533 | #ifdef ALTIVEC |
534 | pct = fmt; | |
535 | #endif | |
e9ce8d39 A |
536 | fmt++; /* skip over '%' */ |
537 | ||
538 | flags = 0; | |
539 | dprec = 0; | |
540 | width = 0; | |
541 | prec = -1; | |
542 | sign = '\0'; | |
5b2abdfb A |
543 | #ifdef ALTIVEC |
544 | vsep = 'X'; /* Illegal value, changed to defaults later. */ | |
545 | #endif | |
e9ce8d39 A |
546 | |
547 | rflag: ch = *fmt++; | |
548 | reswitch: switch (ch) { | |
549 | case ' ': | |
550 | /* | |
551 | * ``If the space and + flags both appear, the space | |
552 | * flag will be ignored.'' | |
553 | * -- ANSI X3J11 | |
554 | */ | |
555 | if (!sign) | |
556 | sign = ' '; | |
557 | goto rflag; | |
558 | case '#': | |
559 | flags |= ALT; | |
560 | goto rflag; | |
5b2abdfb A |
561 | #ifdef ALTIVEC |
562 | case ',': case ';': case ':': case '_': | |
563 | vsep = ch; | |
564 | goto rflag; | |
565 | #endif | |
e9ce8d39 A |
566 | case '*': |
567 | /* | |
568 | * ``A negative field width argument is taken as a | |
569 | * - flag followed by a positive field width.'' | |
570 | * -- ANSI X3J11 | |
571 | * They don't exclude field widths read from args. | |
572 | */ | |
573 | GETASTER (width); | |
574 | if (width >= 0) | |
575 | goto rflag; | |
576 | width = -width; | |
577 | /* FALLTHROUGH */ | |
578 | case '-': | |
579 | flags |= LADJUST; | |
580 | goto rflag; | |
581 | case '+': | |
582 | sign = '+'; | |
583 | goto rflag; | |
584 | case '.': | |
585 | if ((ch = *fmt++) == '*') { | |
586 | GETASTER (n); | |
587 | prec = n < 0 ? -1 : n; | |
588 | goto rflag; | |
589 | } | |
590 | n = 0; | |
591 | while (is_digit(ch)) { | |
592 | n = 10 * n + to_digit(ch); | |
593 | ch = *fmt++; | |
594 | } | |
595 | prec = n < 0 ? -1 : n; | |
596 | goto reswitch; | |
597 | case '0': | |
598 | /* | |
599 | * ``Note that 0 is taken as a flag, not as the | |
600 | * beginning of a field width.'' | |
601 | * -- ANSI X3J11 | |
602 | */ | |
603 | flags |= ZEROPAD; | |
604 | goto rflag; | |
605 | case '1': case '2': case '3': case '4': | |
606 | case '5': case '6': case '7': case '8': case '9': | |
607 | n = 0; | |
608 | do { | |
609 | n = 10 * n + to_digit(ch); | |
610 | ch = *fmt++; | |
611 | } while (is_digit(ch)); | |
612 | if (ch == '$') { | |
613 | nextarg = n; | |
614 | if (argtable == NULL) { | |
615 | argtable = statargtable; | |
616 | __find_arguments (fmt0, orgap, | |
617 | &argtable); | |
618 | } | |
619 | goto rflag; | |
620 | } | |
621 | width = n; | |
622 | goto reswitch; | |
623 | #ifdef FLOATING_POINT | |
624 | case 'L': | |
625 | flags |= LONGDBL; | |
626 | goto rflag; | |
5b2abdfb A |
627 | #endif |
628 | #ifdef ALTIVEC | |
629 | case 'v': | |
630 | flags |= VECTOR; | |
631 | goto rflag; | |
e9ce8d39 A |
632 | #endif |
633 | case 'h': | |
634 | flags |= SHORTINT; | |
635 | goto rflag; | |
636 | case 'l': | |
637 | if (flags & LONGINT) | |
638 | flags |= QUADINT; | |
639 | else | |
640 | flags |= LONGINT; | |
641 | goto rflag; | |
642 | case 'q': | |
643 | flags |= QUADINT; | |
644 | goto rflag; | |
5b2abdfb A |
645 | case 'z': |
646 | if (sizeof(size_t) == sizeof(long)) | |
647 | flags |= LONGINT; | |
648 | if (sizeof(size_t) == sizeof(quad_t)) | |
649 | flags |= QUADINT; | |
650 | goto rflag; | |
e9ce8d39 | 651 | case 'c': |
5b2abdfb A |
652 | #ifdef ALTIVEC |
653 | if (flags & VECTOR) { | |
654 | getvec(&vval, argtable, nextarg, ap); | |
655 | nextarg++; | |
656 | break; | |
657 | } | |
658 | #endif | |
e9ce8d39 A |
659 | *(cp = buf) = GETARG(int); |
660 | size = 1; | |
661 | sign = '\0'; | |
662 | break; | |
663 | case 'D': | |
664 | flags |= LONGINT; | |
665 | /*FALLTHROUGH*/ | |
666 | case 'd': | |
667 | case 'i': | |
5b2abdfb A |
668 | #ifdef ALTIVEC |
669 | if (flags & VECTOR) { | |
670 | getvec(&vval, argtable, nextarg, ap); | |
671 | break; | |
672 | } else | |
673 | #endif | |
e9ce8d39 A |
674 | if (flags & QUADINT) { |
675 | uqval = GETARG(quad_t); | |
676 | if ((quad_t)uqval < 0) { | |
677 | uqval = -uqval; | |
678 | sign = '-'; | |
679 | } | |
680 | } else { | |
681 | ulval = SARG(); | |
682 | if ((long)ulval < 0) { | |
683 | ulval = -ulval; | |
684 | sign = '-'; | |
685 | } | |
686 | } | |
687 | base = 10; | |
688 | goto number; | |
689 | #ifdef FLOATING_POINT | |
690 | case 'e': | |
691 | case 'E': | |
692 | case 'f': | |
5b2abdfb A |
693 | #ifdef ALTIVEC |
694 | if (flags & VECTOR) { | |
695 | flags |= FPT; | |
696 | getvec(&vval, argtable, nextarg, ap); | |
697 | nextarg++; | |
698 | break; | |
699 | } | |
700 | #endif | |
e9ce8d39 A |
701 | goto fp_begin; |
702 | case 'g': | |
703 | case 'G': | |
5b2abdfb A |
704 | #ifdef ALTIVEC |
705 | if (flags & VECTOR) { | |
706 | flags |= FPT; | |
707 | getvec(&vval, argtable, nextarg, ap); | |
708 | nextarg++; | |
709 | break; | |
710 | } | |
711 | #endif | |
e9ce8d39 A |
712 | if (prec == 0) |
713 | prec = 1; | |
714 | fp_begin: if (prec == -1) | |
715 | prec = DEFPREC; | |
716 | if (flags & LONGDBL) | |
717 | /* XXX this loses precision. */ | |
718 | _double = (double)GETARG(long double); | |
719 | else | |
720 | _double = GETARG(double); | |
721 | /* do this before tricky precision changes */ | |
722 | if (isinf(_double)) { | |
723 | if (_double < 0) | |
724 | sign = '-'; | |
725 | cp = "Inf"; | |
726 | size = 3; | |
727 | break; | |
728 | } | |
729 | if (isnan(_double)) { | |
730 | cp = "NaN"; | |
731 | size = 3; | |
732 | break; | |
733 | } | |
734 | flags |= FPT; | |
5b2abdfb A |
735 | if (dtoaresult != NULL) { |
736 | free(dtoaresult); | |
737 | dtoaresult = NULL; | |
738 | } | |
e9ce8d39 | 739 | cp = cvt(_double, prec, flags, &softsign, |
3b2a1fe8 | 740 | &expt, ch, &ndig, &dtoaresult); |
e9ce8d39 A |
741 | if (ch == 'g' || ch == 'G') { |
742 | if (expt <= -4 || expt > prec) | |
743 | ch = (ch == 'g') ? 'e' : 'E'; | |
744 | else | |
745 | ch = 'g'; | |
746 | } | |
747 | if (ch <= 'e') { /* 'e' or 'E' fmt */ | |
748 | --expt; | |
749 | expsize = exponent(expstr, expt, ch); | |
750 | size = expsize + ndig; | |
751 | if (ndig > 1 || flags & ALT) | |
752 | ++size; | |
753 | } else if (ch == 'f') { /* f fmt */ | |
754 | if (expt > 0) { | |
755 | size = expt; | |
756 | if (prec || flags & ALT) | |
757 | size += prec + 1; | |
758 | } else /* "0.X" */ | |
759 | size = prec + 2; | |
760 | } else if (expt >= ndig) { /* fixed g fmt */ | |
761 | size = expt; | |
762 | if (flags & ALT) | |
763 | ++size; | |
764 | } else | |
765 | size = ndig + (expt > 0 ? | |
766 | 1 : 2 - expt); | |
767 | ||
768 | if (softsign) | |
769 | sign = '-'; | |
770 | break; | |
771 | #endif /* FLOATING_POINT */ | |
772 | case 'n': | |
773 | if (flags & QUADINT) | |
774 | *GETARG(quad_t *) = ret; | |
775 | else if (flags & LONGINT) | |
776 | *GETARG(long *) = ret; | |
777 | else if (flags & SHORTINT) | |
778 | *GETARG(short *) = ret; | |
779 | else | |
780 | *GETARG(int *) = ret; | |
781 | continue; /* no output */ | |
782 | case 'O': | |
783 | flags |= LONGINT; | |
784 | /*FALLTHROUGH*/ | |
785 | case 'o': | |
5b2abdfb A |
786 | #ifdef ALTIVEC |
787 | if (flags & VECTOR) { | |
788 | getvec(&vval, argtable, nextarg, ap); | |
789 | nextarg++; | |
790 | break; | |
791 | } else | |
792 | #endif | |
e9ce8d39 A |
793 | if (flags & QUADINT) |
794 | uqval = GETARG(u_quad_t); | |
795 | else | |
796 | ulval = UARG(); | |
797 | base = 8; | |
798 | goto nosign; | |
799 | case 'p': | |
800 | /* | |
801 | * ``The argument shall be a pointer to void. The | |
802 | * value of the pointer is converted to a sequence | |
803 | * of printable characters, in an implementation- | |
804 | * defined manner.'' | |
805 | * -- ANSI X3J11 | |
806 | */ | |
5b2abdfb A |
807 | #ifdef ALTIVEC |
808 | if (flags & VECTOR) { | |
809 | getvec(&vval, argtable, nextarg, ap); | |
810 | nextarg++; | |
811 | break; | |
812 | } | |
813 | #endif | |
e9ce8d39 A |
814 | ulval = (u_long)GETARG(void *); |
815 | base = 16; | |
816 | xdigs = "0123456789abcdef"; | |
817 | flags = (flags & ~QUADINT) | HEXPREFIX; | |
818 | ch = 'x'; | |
819 | goto nosign; | |
820 | case 's': | |
821 | if ((cp = GETARG(char *)) == NULL) | |
822 | cp = "(null)"; | |
823 | if (prec >= 0) { | |
824 | /* | |
825 | * can't use strlen; can only look for the | |
826 | * NUL in the first `prec' characters, and | |
827 | * strlen() will go further. | |
828 | */ | |
829 | char *p = memchr(cp, 0, (size_t)prec); | |
830 | ||
831 | if (p != NULL) { | |
832 | size = p - cp; | |
833 | if (size > prec) | |
834 | size = prec; | |
835 | } else | |
836 | size = prec; | |
837 | } else | |
838 | size = strlen(cp); | |
839 | sign = '\0'; | |
840 | break; | |
841 | case 'U': | |
842 | flags |= LONGINT; | |
843 | /*FALLTHROUGH*/ | |
844 | case 'u': | |
5b2abdfb A |
845 | #ifdef ALTIVEC |
846 | if (flags & VECTOR) { | |
847 | getvec(&vval, argtable, nextarg, ap); | |
848 | nextarg++; | |
849 | break; | |
850 | } else | |
851 | #endif | |
e9ce8d39 A |
852 | if (flags & QUADINT) |
853 | uqval = GETARG(u_quad_t); | |
854 | else | |
855 | ulval = UARG(); | |
856 | base = 10; | |
857 | goto nosign; | |
858 | case 'X': | |
859 | xdigs = "0123456789ABCDEF"; | |
860 | goto hex; | |
861 | case 'x': | |
862 | xdigs = "0123456789abcdef"; | |
5b2abdfb A |
863 | hex: |
864 | #ifdef ALTIVEC | |
865 | if (flags & VECTOR) { | |
866 | getvec(&vval, argtable, nextarg, ap); | |
867 | nextarg++; | |
868 | break; | |
869 | } else | |
870 | #endif | |
871 | if (flags & QUADINT) | |
e9ce8d39 A |
872 | uqval = GETARG(u_quad_t); |
873 | else | |
874 | ulval = UARG(); | |
875 | base = 16; | |
876 | /* leading 0x/X only if non-zero */ | |
877 | if (flags & ALT && | |
878 | (flags & QUADINT ? uqval != 0 : ulval != 0)) | |
879 | flags |= HEXPREFIX; | |
880 | ||
881 | /* unsigned conversions */ | |
882 | nosign: sign = '\0'; | |
883 | /* | |
884 | * ``... diouXx conversions ... if a precision is | |
885 | * specified, the 0 flag will be ignored.'' | |
886 | * -- ANSI X3J11 | |
887 | */ | |
888 | number: if ((dprec = prec) >= 0) | |
889 | flags &= ~ZEROPAD; | |
890 | ||
891 | /* | |
892 | * ``The result of converting a zero value with an | |
893 | * explicit precision of zero is no characters.'' | |
894 | * -- ANSI X3J11 | |
895 | */ | |
896 | cp = buf + BUF; | |
897 | if (flags & QUADINT) { | |
898 | if (uqval != 0 || prec != 0) | |
899 | cp = __uqtoa(uqval, cp, base, | |
900 | flags & ALT, xdigs); | |
901 | } else { | |
902 | if (ulval != 0 || prec != 0) | |
903 | cp = __ultoa(ulval, cp, base, | |
904 | flags & ALT, xdigs); | |
905 | } | |
906 | size = buf + BUF - cp; | |
907 | break; | |
908 | default: /* "%?" prints ?, unless ? is NUL */ | |
909 | if (ch == '\0') | |
910 | goto done; | |
911 | /* pretend it was %c with argument ch */ | |
912 | cp = buf; | |
913 | *cp = ch; | |
914 | size = 1; | |
915 | sign = '\0'; | |
916 | break; | |
917 | } | |
918 | ||
5b2abdfb A |
919 | #ifdef ALTIVEC |
920 | if (flags & VECTOR) { | |
921 | /* | |
922 | * Do the minimum amount of work necessary to construct | |
923 | * a format specifier that can be used to recursively | |
924 | * call vfprintf() for each element in the vector. | |
925 | */ | |
926 | int i, j; /* Counter. */ | |
927 | int vcnt; /* Number of elements in vector. */ | |
928 | char *vfmt; /* Pointer to format specifier. */ | |
929 | char vfmt_buf[32]; /* Static buffer for format spec. */ | |
930 | int vwidth = 0; /* Width specified via '*'. */ | |
931 | int vprec = 0; /* Precision specified via '*'. */ | |
932 | union { /* Element. */ | |
933 | int i; | |
934 | float f; | |
935 | } velm; | |
936 | char *vstr; /* Used for asprintf(). */ | |
937 | int vlen; /* Length returned by asprintf(). */ | |
938 | ||
939 | /* | |
940 | * Set vfmt. If vfmt_buf may not be big enough, | |
941 | * malloc() space, taking care to free it later. | |
942 | */ | |
943 | if (&fmt[-1] - pct < sizeof(vfmt_buf)) | |
944 | vfmt = vfmt_buf; | |
945 | else | |
946 | vfmt = (char *)malloc(&fmt[-1] - pct + 1); | |
947 | ||
948 | /* Set the separator character, if not specified. */ | |
949 | if (vsep == 'X') { | |
950 | if (ch == 'c') | |
951 | vsep = '\0'; | |
952 | else | |
953 | vsep = ' '; | |
954 | } | |
955 | ||
956 | /* Create the format specifier. */ | |
957 | for (i = j = 0; i < &fmt[-1] - pct; i++) { | |
958 | switch (pct[i]) { | |
959 | case ',': case ';': case ':': case '_': | |
960 | case 'v': case 'h': case 'l': | |
961 | /* Ignore. */ | |
962 | break; | |
963 | case '*': | |
964 | if (pct[i - 1] != '.') | |
965 | vwidth = 1; | |
966 | else | |
967 | vprec = 1; | |
968 | /* FALLTHROUGH */ | |
969 | default: | |
970 | vfmt[j++] = pct[i]; | |
971 | } | |
972 | } | |
973 | ||
974 | /* | |
975 | * Determine the number of elements in the vector and | |
976 | * finish up the format specifier. | |
977 | */ | |
978 | if (flags & SHORTINT) { | |
979 | vfmt[j++] = 'h'; | |
980 | vcnt = 8; | |
981 | } else if (flags & LONGINT) { | |
982 | vfmt[j++] = 'l'; | |
983 | vcnt = 4; | |
984 | } else { | |
985 | switch (ch) { | |
986 | case 'e': | |
987 | case 'E': | |
988 | case 'f': | |
989 | case 'g': | |
990 | case 'G': | |
991 | vcnt = 4; | |
992 | break; | |
993 | default: | |
994 | /* | |
995 | * The default case should never | |
996 | * happen. | |
997 | */ | |
998 | case 'c': | |
999 | case 'd': | |
1000 | case 'i': | |
1001 | case 'u': | |
1002 | case 'o': | |
1003 | case 'p': | |
1004 | case 'x': | |
1005 | case 'X': | |
1006 | vcnt = 16; | |
1007 | } | |
1008 | } | |
1009 | vfmt[j++] = ch; | |
1010 | vfmt[j++] = '\0'; | |
1011 | ||
1012 | /* Get a vector element. */ | |
1013 | #define VPRINT(cnt, ind, args...) do { \ | |
1014 | if (flags & FPT) { \ | |
1015 | velm.f = vval.vfloatarg[ind]; \ | |
1016 | vlen = asprintf(&vstr, vfmt , ## args, velm.f); \ | |
1017 | } else { \ | |
1018 | switch (cnt) { \ | |
1019 | default: \ | |
1020 | /* The default case should never happen. */ \ | |
1021 | case 4: \ | |
1022 | velm.i = vval.vintarg[ind]; \ | |
1023 | break; \ | |
1024 | case 8: \ | |
1025 | velm.i = vval.vshortarg[ind]; \ | |
1026 | break; \ | |
1027 | case 16: \ | |
1028 | velm.i = vval.vchararg[ind]; \ | |
1029 | break; \ | |
1030 | } \ | |
1031 | vlen = asprintf(&vstr, vfmt , ## args, velm.i); \ | |
1032 | } \ | |
1033 | ret += vlen; \ | |
1034 | PRINT(vstr, vlen); \ | |
1035 | FLUSH(); \ | |
1036 | free(vstr); \ | |
1037 | } while (0) | |
1038 | ||
1039 | /* Actually print. */ | |
1040 | if (vwidth == 0) { | |
1041 | if (vprec == 0) { | |
1042 | /* First element. */ | |
1043 | VPRINT(vcnt, 0); | |
1044 | for (i = 1; i < vcnt; i++) { | |
1045 | /* Separator. */ | |
1046 | PRINT(&vsep, 1); | |
1047 | ||
1048 | /* Element. */ | |
1049 | VPRINT(vcnt, i); | |
1050 | } | |
1051 | } else { | |
1052 | /* First element. */ | |
1053 | VPRINT(vcnt, 0, prec); | |
1054 | for (i = 1; i < vcnt; i++) { | |
1055 | /* Separator. */ | |
1056 | PRINT(&vsep, 1); | |
1057 | ||
1058 | /* Element. */ | |
1059 | VPRINT(vcnt, i, prec); | |
1060 | } | |
1061 | } | |
1062 | } else { | |
1063 | if (vprec == 0) { | |
1064 | /* First element. */ | |
1065 | VPRINT(vcnt, 0, width); | |
1066 | for (i = 1; i < vcnt; i++) { | |
1067 | /* Separator. */ | |
1068 | PRINT(&vsep, 1); | |
1069 | ||
1070 | /* Element. */ | |
1071 | VPRINT(vcnt, i, width); | |
1072 | } | |
1073 | } else { | |
1074 | /* First element. */ | |
1075 | VPRINT(vcnt, 0, width, prec); | |
1076 | for (i = 1; i < vcnt; i++) { | |
1077 | /* Separator. */ | |
1078 | PRINT(&vsep, 1); | |
1079 | ||
1080 | /* Element. */ | |
1081 | VPRINT(vcnt, i, width, prec); | |
1082 | } | |
1083 | } | |
1084 | } | |
1085 | #undef VPRINT | |
1086 | ||
1087 | if (vfmt != vfmt_buf) | |
1088 | free(vfmt); | |
1089 | ||
1090 | continue; | |
1091 | } | |
1092 | #endif | |
e9ce8d39 A |
1093 | /* |
1094 | * All reasonable formats wind up here. At this point, `cp' | |
1095 | * points to a string which (if not flags&LADJUST) should be | |
1096 | * padded out to `width' places. If flags&ZEROPAD, it should | |
1097 | * first be prefixed by any sign or other prefix; otherwise, | |
1098 | * it should be blank padded before the prefix is emitted. | |
1099 | * After any left-hand padding and prefixing, emit zeroes | |
1100 | * required by a decimal [diouxX] precision, then print the | |
1101 | * string proper, then emit zeroes required by any leftover | |
1102 | * floating precision; finally, if LADJUST, pad with blanks. | |
1103 | * | |
1104 | * Compute actual size, so we know how much to pad. | |
1105 | * size excludes decimal prec; realsz includes it. | |
1106 | */ | |
1107 | realsz = dprec > size ? dprec : size; | |
1108 | if (sign) | |
1109 | realsz++; | |
1110 | else if (flags & HEXPREFIX) | |
1111 | realsz += 2; | |
1112 | ||
1113 | prsize = width > realsz ? width : realsz; | |
1114 | if ((unsigned)ret + prsize > INT_MAX) { | |
1115 | ret = EOF; | |
1116 | goto error; | |
1117 | } | |
1118 | ||
1119 | /* right-adjusting blank padding */ | |
1120 | if ((flags & (LADJUST|ZEROPAD)) == 0) | |
1121 | PAD(width - realsz, blanks); | |
1122 | ||
1123 | /* prefix */ | |
1124 | if (sign) { | |
1125 | PRINT(&sign, 1); | |
1126 | } else if (flags & HEXPREFIX) { | |
1127 | ox[0] = '0'; | |
1128 | ox[1] = ch; | |
1129 | PRINT(ox, 2); | |
1130 | } | |
1131 | ||
1132 | /* right-adjusting zero padding */ | |
1133 | if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD) | |
1134 | PAD(width - realsz, zeroes); | |
1135 | ||
1136 | /* leading zeroes from decimal precision */ | |
1137 | PAD(dprec - size, zeroes); | |
1138 | ||
1139 | /* the string or number proper */ | |
1140 | #ifdef FLOATING_POINT | |
1141 | if ((flags & FPT) == 0) { | |
1142 | PRINT(cp, size); | |
1143 | } else { /* glue together f_p fragments */ | |
1144 | if (ch >= 'f') { /* 'f' or 'g' */ | |
1145 | if (_double == 0) { | |
1146 | /* kludge for __dtoa irregularity */ | |
1147 | if (expt >= ndig && | |
1148 | (flags & ALT) == 0) { | |
1149 | PRINT("0", 1); | |
1150 | } else { | |
1151 | PRINT("0.", 2); | |
1152 | PAD(ndig - 1, zeroes); | |
1153 | } | |
1154 | } else if (expt <= 0) { | |
1155 | PRINT("0.", 2); | |
1156 | PAD(-expt, zeroes); | |
1157 | PRINT(cp, ndig); | |
1158 | } else if (expt >= ndig) { | |
1159 | PRINT(cp, ndig); | |
1160 | PAD(expt - ndig, zeroes); | |
1161 | if (flags & ALT) | |
1162 | PRINT(".", 1); | |
1163 | } else { | |
1164 | PRINT(cp, expt); | |
1165 | cp += expt; | |
1166 | PRINT(".", 1); | |
1167 | PRINT(cp, ndig-expt); | |
1168 | } | |
1169 | } else { /* 'e' or 'E' */ | |
1170 | if (ndig > 1 || flags & ALT) { | |
1171 | ox[0] = *cp++; | |
1172 | ox[1] = '.'; | |
1173 | PRINT(ox, 2); | |
1174 | if (_double) { | |
1175 | PRINT(cp, ndig-1); | |
1176 | } else /* 0.[0..] */ | |
1177 | /* __dtoa irregularity */ | |
1178 | PAD(ndig - 1, zeroes); | |
1179 | } else /* XeYYY */ | |
1180 | PRINT(cp, 1); | |
1181 | PRINT(expstr, expsize); | |
1182 | } | |
1183 | } | |
1184 | #else | |
1185 | PRINT(cp, size); | |
1186 | #endif | |
5b2abdfb | 1187 | |
e9ce8d39 A |
1188 | /* left-adjusting padding (always blank) */ |
1189 | if (flags & LADJUST) | |
1190 | PAD(width - realsz, blanks); | |
1191 | ||
1192 | /* finally, adjust ret */ | |
1193 | ret += prsize; | |
1194 | ||
1195 | FLUSH(); /* copy out the I/O vectors */ | |
1196 | } | |
1197 | done: | |
1198 | FLUSH(); | |
1199 | error: | |
3b2a1fe8 A |
1200 | #ifdef FLOATING_POINT |
1201 | if (dtoaresult != NULL) | |
1202 | free(dtoaresult); | |
1203 | #endif | |
e9ce8d39 A |
1204 | if (__sferror(fp)) |
1205 | ret = EOF; | |
1206 | /* FUNLOCKFILE(fp); */ | |
1207 | if ((argtable != NULL) && (argtable != statargtable)) | |
1208 | free (argtable); | |
1209 | return (ret); | |
1210 | /* NOTREACHED */ | |
1211 | } | |
1212 | ||
1213 | /* | |
1214 | * Type ids for argument type table. | |
1215 | */ | |
1216 | #define T_UNUSED 0 | |
1217 | #define T_SHORT 1 | |
1218 | #define T_U_SHORT 2 | |
1219 | #define TP_SHORT 3 | |
1220 | #define T_INT 4 | |
1221 | #define T_U_INT 5 | |
1222 | #define TP_INT 6 | |
1223 | #define T_LONG 7 | |
1224 | #define T_U_LONG 8 | |
1225 | #define TP_LONG 9 | |
1226 | #define T_QUAD 10 | |
1227 | #define T_U_QUAD 11 | |
1228 | #define TP_QUAD 12 | |
1229 | #define T_DOUBLE 13 | |
1230 | #define T_LONG_DOUBLE 14 | |
1231 | #define TP_CHAR 15 | |
1232 | #define TP_VOID 16 | |
5b2abdfb | 1233 | #define T_VECTOR 17 |
e9ce8d39 A |
1234 | |
1235 | /* | |
1236 | * Find all arguments when a positional parameter is encountered. Returns a | |
1237 | * table, indexed by argument number, of pointers to each arguments. The | |
1238 | * initial argument table should be an array of STATIC_ARG_TBL_SIZE entries. | |
3b2a1fe8 | 1239 | * It will be replaces with a malloc-ed one if it overflows. |
e9ce8d39 A |
1240 | */ |
1241 | static void | |
1242 | __find_arguments (fmt0, ap, argtable) | |
1243 | const char *fmt0; | |
1244 | va_list ap; | |
5b2abdfb | 1245 | union arg **argtable; |
e9ce8d39 A |
1246 | { |
1247 | register char *fmt; /* format string */ | |
1248 | register int ch; /* character from fmt */ | |
1249 | register int n, n2; /* handy integer (short term usage) */ | |
1250 | register char *cp; /* handy char pointer (short term usage) */ | |
1251 | register int flags; /* flags as above */ | |
1252 | int width; /* width from format (%8d), or 0 */ | |
1253 | unsigned char *typetable; /* table of types */ | |
1254 | unsigned char stattypetable [STATIC_ARG_TBL_SIZE]; | |
1255 | int tablesize; /* current size of type table */ | |
1256 | int tablemax; /* largest used index in table */ | |
1257 | int nextarg; /* 1-based argument index */ | |
1258 | ||
1259 | /* | |
1260 | * Add an argument type to the table, expanding if necessary. | |
1261 | */ | |
1262 | #define ADDTYPE(type) \ | |
1263 | ((nextarg >= tablesize) ? \ | |
1264 | __grow_type_table(nextarg, &typetable, &tablesize) : 0, \ | |
3b2a1fe8 A |
1265 | (nextarg > tablemax) ? tablemax = nextarg : 0, \ |
1266 | typetable[nextarg++] = type) | |
e9ce8d39 A |
1267 | |
1268 | #define ADDSARG() \ | |
1269 | ((flags&LONGINT) ? ADDTYPE(T_LONG) : \ | |
1270 | ((flags&SHORTINT) ? ADDTYPE(T_SHORT) : ADDTYPE(T_INT))) | |
1271 | ||
1272 | #define ADDUARG() \ | |
1273 | ((flags&LONGINT) ? ADDTYPE(T_U_LONG) : \ | |
1274 | ((flags&SHORTINT) ? ADDTYPE(T_U_SHORT) : ADDTYPE(T_U_INT))) | |
1275 | ||
1276 | /* | |
1277 | * Add * arguments to the type array. | |
1278 | */ | |
1279 | #define ADDASTER() \ | |
1280 | n2 = 0; \ | |
1281 | cp = fmt; \ | |
1282 | while (is_digit(*cp)) { \ | |
1283 | n2 = 10 * n2 + to_digit(*cp); \ | |
1284 | cp++; \ | |
1285 | } \ | |
1286 | if (*cp == '$') { \ | |
1287 | int hold = nextarg; \ | |
1288 | nextarg = n2; \ | |
1289 | ADDTYPE (T_INT); \ | |
1290 | nextarg = hold; \ | |
1291 | fmt = ++cp; \ | |
1292 | } else { \ | |
1293 | ADDTYPE (T_INT); \ | |
1294 | } | |
1295 | fmt = (char *)fmt0; | |
1296 | typetable = stattypetable; | |
1297 | tablesize = STATIC_ARG_TBL_SIZE; | |
1298 | tablemax = 0; | |
1299 | nextarg = 1; | |
1300 | memset (typetable, T_UNUSED, STATIC_ARG_TBL_SIZE); | |
1301 | ||
1302 | /* | |
1303 | * Scan the format for conversions (`%' character). | |
1304 | */ | |
1305 | for (;;) { | |
1306 | for (cp = fmt; (ch = *fmt) != '\0' && ch != '%'; fmt++) | |
1307 | /* void */; | |
1308 | if (ch == '\0') | |
1309 | goto done; | |
1310 | fmt++; /* skip over '%' */ | |
1311 | ||
1312 | flags = 0; | |
1313 | width = 0; | |
1314 | ||
1315 | rflag: ch = *fmt++; | |
1316 | reswitch: switch (ch) { | |
1317 | case ' ': | |
1318 | case '#': | |
1319 | goto rflag; | |
1320 | case '*': | |
1321 | ADDASTER (); | |
1322 | goto rflag; | |
1323 | case '-': | |
1324 | case '+': | |
1325 | goto rflag; | |
1326 | case '.': | |
1327 | if ((ch = *fmt++) == '*') { | |
1328 | ADDASTER (); | |
1329 | goto rflag; | |
1330 | } | |
1331 | while (is_digit(ch)) { | |
1332 | ch = *fmt++; | |
1333 | } | |
1334 | goto reswitch; | |
1335 | case '0': | |
1336 | goto rflag; | |
1337 | case '1': case '2': case '3': case '4': | |
1338 | case '5': case '6': case '7': case '8': case '9': | |
1339 | n = 0; | |
1340 | do { | |
1341 | n = 10 * n + to_digit(ch); | |
1342 | ch = *fmt++; | |
1343 | } while (is_digit(ch)); | |
1344 | if (ch == '$') { | |
1345 | nextarg = n; | |
1346 | goto rflag; | |
1347 | } | |
1348 | width = n; | |
1349 | goto reswitch; | |
1350 | #ifdef FLOATING_POINT | |
1351 | case 'L': | |
1352 | flags |= LONGDBL; | |
1353 | goto rflag; | |
1354 | #endif | |
1355 | case 'h': | |
1356 | flags |= SHORTINT; | |
1357 | goto rflag; | |
1358 | case 'l': | |
1359 | if (flags & LONGINT) | |
1360 | flags |= QUADINT; | |
1361 | else | |
1362 | flags |= LONGINT; | |
1363 | goto rflag; | |
1364 | case 'q': | |
1365 | flags |= QUADINT; | |
1366 | goto rflag; | |
1367 | case 'c': | |
5b2abdfb A |
1368 | #ifdef ALTIVEC |
1369 | if (flags & VECTOR) | |
1370 | ADDTYPE(T_VECTOR); | |
1371 | else | |
1372 | #endif | |
1373 | ADDTYPE(T_INT); | |
e9ce8d39 A |
1374 | break; |
1375 | case 'D': | |
1376 | flags |= LONGINT; | |
1377 | /*FALLTHROUGH*/ | |
1378 | case 'd': | |
1379 | case 'i': | |
5b2abdfb A |
1380 | #ifdef ALTIVEC |
1381 | if (flags & VECTOR) | |
1382 | ADDTYPE(T_VECTOR); | |
1383 | else | |
1384 | #endif | |
e9ce8d39 A |
1385 | if (flags & QUADINT) { |
1386 | ADDTYPE(T_QUAD); | |
1387 | } else { | |
1388 | ADDSARG(); | |
1389 | } | |
1390 | break; | |
1391 | #ifdef FLOATING_POINT | |
1392 | case 'e': | |
1393 | case 'E': | |
1394 | case 'f': | |
1395 | case 'g': | |
1396 | case 'G': | |
5b2abdfb A |
1397 | #ifdef ALTIVEC |
1398 | if (flags & VECTOR) | |
1399 | ADDTYPE(T_VECTOR); | |
1400 | else | |
1401 | #endif | |
e9ce8d39 A |
1402 | if (flags & LONGDBL) |
1403 | ADDTYPE(T_LONG_DOUBLE); | |
1404 | else | |
1405 | ADDTYPE(T_DOUBLE); | |
1406 | break; | |
1407 | #endif /* FLOATING_POINT */ | |
1408 | case 'n': | |
1409 | if (flags & QUADINT) | |
1410 | ADDTYPE(TP_QUAD); | |
1411 | else if (flags & LONGINT) | |
1412 | ADDTYPE(TP_LONG); | |
1413 | else if (flags & SHORTINT) | |
1414 | ADDTYPE(TP_SHORT); | |
1415 | else | |
1416 | ADDTYPE(TP_INT); | |
1417 | continue; /* no output */ | |
1418 | case 'O': | |
1419 | flags |= LONGINT; | |
1420 | /*FALLTHROUGH*/ | |
1421 | case 'o': | |
5b2abdfb A |
1422 | #ifdef ALTIVEC |
1423 | if (flags & VECTOR) | |
1424 | ADDTYPE(T_VECTOR); | |
1425 | else | |
1426 | #endif | |
e9ce8d39 A |
1427 | if (flags & QUADINT) |
1428 | ADDTYPE(T_U_QUAD); | |
1429 | else | |
1430 | ADDUARG(); | |
1431 | break; | |
1432 | case 'p': | |
5b2abdfb A |
1433 | #ifdef ALTIVEC |
1434 | if (flags & VECTOR) | |
1435 | ADDTYPE(T_VECTOR); | |
1436 | else | |
1437 | #endif | |
1438 | ADDTYPE(TP_VOID); | |
e9ce8d39 A |
1439 | break; |
1440 | case 's': | |
1441 | ADDTYPE(TP_CHAR); | |
1442 | break; | |
1443 | case 'U': | |
1444 | flags |= LONGINT; | |
1445 | /*FALLTHROUGH*/ | |
1446 | case 'u': | |
5b2abdfb A |
1447 | #ifdef ALTIVEC |
1448 | if (flags & VECTOR) | |
1449 | ADDTYPE(T_VECTOR); | |
1450 | else | |
1451 | #endif | |
e9ce8d39 A |
1452 | if (flags & QUADINT) |
1453 | ADDTYPE(T_U_QUAD); | |
1454 | else | |
1455 | ADDUARG(); | |
1456 | break; | |
1457 | case 'X': | |
1458 | case 'x': | |
5b2abdfb A |
1459 | #ifdef ALTIVEC |
1460 | if (flags & VECTOR) | |
1461 | ADDTYPE(T_VECTOR); | |
1462 | else | |
1463 | #endif | |
e9ce8d39 A |
1464 | if (flags & QUADINT) |
1465 | ADDTYPE(T_U_QUAD); | |
1466 | else | |
1467 | ADDUARG(); | |
1468 | break; | |
1469 | default: /* "%?" prints ?, unless ? is NUL */ | |
1470 | if (ch == '\0') | |
1471 | goto done; | |
1472 | break; | |
1473 | } | |
1474 | } | |
1475 | done: | |
1476 | /* | |
1477 | * Build the argument table. | |
1478 | */ | |
1479 | if (tablemax >= STATIC_ARG_TBL_SIZE) { | |
5b2abdfb A |
1480 | *argtable = (union arg *) |
1481 | malloc (sizeof (union arg) * (tablemax + 1)); | |
e9ce8d39 A |
1482 | } |
1483 | ||
5b2abdfb | 1484 | (*argtable) [0].intarg = NULL; |
e9ce8d39 A |
1485 | for (n = 1; n <= tablemax; n++) { |
1486 | switch (typetable [n]) { | |
1487 | case T_UNUSED: | |
5b2abdfb | 1488 | (*argtable) [n].intarg = va_arg (ap, int); |
e9ce8d39 A |
1489 | break; |
1490 | case T_SHORT: | |
5b2abdfb | 1491 | (*argtable) [n].intarg = va_arg (ap, int); |
e9ce8d39 A |
1492 | break; |
1493 | case T_U_SHORT: | |
5b2abdfb | 1494 | (*argtable) [n].intarg = va_arg (ap, int); |
e9ce8d39 A |
1495 | break; |
1496 | case TP_SHORT: | |
5b2abdfb | 1497 | (*argtable) [n].pshortarg = va_arg (ap, short *); |
e9ce8d39 A |
1498 | break; |
1499 | case T_INT: | |
5b2abdfb | 1500 | (*argtable) [n].intarg = va_arg (ap, int); |
e9ce8d39 A |
1501 | break; |
1502 | case T_U_INT: | |
5b2abdfb | 1503 | (*argtable) [n].uintarg = va_arg (ap, unsigned int); |
e9ce8d39 A |
1504 | break; |
1505 | case TP_INT: | |
5b2abdfb | 1506 | (*argtable) [n].pintarg = va_arg (ap, int *); |
e9ce8d39 A |
1507 | break; |
1508 | case T_LONG: | |
5b2abdfb | 1509 | (*argtable) [n].longarg = va_arg (ap, long); |
e9ce8d39 A |
1510 | break; |
1511 | case T_U_LONG: | |
5b2abdfb | 1512 | (*argtable) [n].ulongarg = va_arg (ap, unsigned long); |
e9ce8d39 A |
1513 | break; |
1514 | case TP_LONG: | |
5b2abdfb | 1515 | (*argtable) [n].plongarg = va_arg (ap, long *); |
e9ce8d39 A |
1516 | break; |
1517 | case T_QUAD: | |
5b2abdfb | 1518 | (*argtable) [n].quadarg = va_arg (ap, quad_t); |
e9ce8d39 A |
1519 | break; |
1520 | case T_U_QUAD: | |
5b2abdfb | 1521 | (*argtable) [n].uquadarg = va_arg (ap, u_quad_t); |
e9ce8d39 A |
1522 | break; |
1523 | case TP_QUAD: | |
5b2abdfb | 1524 | (*argtable) [n].pquadarg = va_arg (ap, quad_t *); |
e9ce8d39 | 1525 | break; |
5b2abdfb | 1526 | #ifdef FLOATING_POINT |
e9ce8d39 | 1527 | case T_DOUBLE: |
5b2abdfb | 1528 | (*argtable) [n].doublearg = va_arg (ap, double); |
e9ce8d39 A |
1529 | break; |
1530 | case T_LONG_DOUBLE: | |
5b2abdfb | 1531 | (*argtable) [n].longdoublearg = va_arg (ap, long double); |
e9ce8d39 | 1532 | break; |
5b2abdfb A |
1533 | #endif |
1534 | #ifdef ALTIVEC | |
1535 | case T_VECTOR: | |
1536 | { int tmp = 0; | |
1537 | getvec( &((*argtable) [n]), NULL, tmp, ap ); | |
1538 | } | |
1539 | #endif | |
e9ce8d39 | 1540 | case TP_CHAR: |
5b2abdfb | 1541 | (*argtable) [n].pchararg = va_arg (ap, char *); |
e9ce8d39 A |
1542 | break; |
1543 | case TP_VOID: | |
5b2abdfb | 1544 | (*argtable) [n].pvoidarg = va_arg (ap, void *); |
e9ce8d39 A |
1545 | break; |
1546 | } | |
1547 | } | |
1548 | ||
1549 | if ((typetable != NULL) && (typetable != stattypetable)) | |
1550 | free (typetable); | |
1551 | } | |
1552 | ||
1553 | /* | |
1554 | * Increase the size of the type table. | |
1555 | */ | |
1556 | static void | |
1557 | __grow_type_table (nextarg, typetable, tablesize) | |
1558 | int nextarg; | |
1559 | unsigned char **typetable; | |
1560 | int *tablesize; | |
1561 | { | |
3b2a1fe8 A |
1562 | unsigned char *const oldtable = *typetable; |
1563 | const int oldsize = *tablesize; | |
1564 | unsigned char *newtable; | |
1565 | int newsize = oldsize * 2; | |
1566 | ||
1567 | if (newsize < nextarg + 1) | |
1568 | newsize = nextarg + 1; | |
1569 | if (oldsize == STATIC_ARG_TBL_SIZE) { | |
1570 | if ((newtable = malloc (newsize)) == NULL) | |
1571 | abort(); /* XXX handle better */ | |
1572 | bcopy (oldtable, newtable, oldsize); | |
e9ce8d39 | 1573 | } else { |
3b2a1fe8 A |
1574 | if ((newtable = realloc (oldtable, newsize)) == NULL) |
1575 | abort(); /* XXX handle better */ | |
e9ce8d39 | 1576 | } |
3b2a1fe8 | 1577 | memset (&newtable [oldsize], T_UNUSED, (newsize - oldsize)); |
e9ce8d39 | 1578 | |
3b2a1fe8 | 1579 | *typetable = newtable; |
e9ce8d39 A |
1580 | *tablesize = newsize; |
1581 | } | |
1582 | ||
1583 | ||
1584 | #ifdef FLOATING_POINT | |
1585 | ||
3b2a1fe8 | 1586 | extern char *__dtoa __P((double, int, int, int *, int *, char **, char **)); |
e9ce8d39 A |
1587 | |
1588 | static char * | |
3b2a1fe8 | 1589 | cvt(value, ndigits, flags, sign, decpt, ch, length, dtoaresultp) |
e9ce8d39 A |
1590 | double value; |
1591 | int ndigits, flags, *decpt, ch, *length; | |
1592 | char *sign; | |
3b2a1fe8 | 1593 | char **dtoaresultp; |
e9ce8d39 A |
1594 | { |
1595 | int mode, dsgn; | |
1596 | char *digits, *bp, *rve; | |
1597 | ||
1598 | if (ch == 'f') | |
1599 | mode = 3; /* ndigits after the decimal point */ | |
1600 | else { | |
1601 | /* | |
1602 | * To obtain ndigits after the decimal point for the 'e' | |
1603 | * and 'E' formats, round to ndigits + 1 significant | |
1604 | * figures. | |
1605 | */ | |
1606 | if (ch == 'e' || ch == 'E') | |
1607 | ndigits++; | |
1608 | mode = 2; /* ndigits significant digits */ | |
1609 | } | |
1610 | if (value < 0) { | |
1611 | value = -value; | |
1612 | *sign = '-'; | |
1613 | } else | |
1614 | *sign = '\000'; | |
3b2a1fe8 | 1615 | digits = __dtoa(value, mode, ndigits, decpt, &dsgn, &rve, dtoaresultp); |
e9ce8d39 A |
1616 | if ((ch != 'g' && ch != 'G') || flags & ALT) { |
1617 | /* print trailing zeros */ | |
1618 | bp = digits + ndigits; | |
1619 | if (ch == 'f') { | |
1620 | if (*digits == '0' && value) | |
1621 | *decpt = -ndigits + 1; | |
1622 | bp += *decpt; | |
1623 | } | |
1624 | if (value == 0) /* kludge for __dtoa irregularity */ | |
1625 | rve = bp; | |
1626 | while (rve < bp) | |
1627 | *rve++ = '0'; | |
1628 | } | |
1629 | *length = rve - digits; | |
1630 | return (digits); | |
1631 | } | |
1632 | ||
1633 | static int | |
1634 | exponent(p0, exp, fmtch) | |
1635 | char *p0; | |
1636 | int exp, fmtch; | |
1637 | { | |
1638 | register char *p, *t; | |
1639 | char expbuf[MAXEXP]; | |
1640 | ||
1641 | p = p0; | |
1642 | *p++ = fmtch; | |
1643 | if (exp < 0) { | |
1644 | exp = -exp; | |
1645 | *p++ = '-'; | |
1646 | } | |
1647 | else | |
1648 | *p++ = '+'; | |
1649 | t = expbuf + MAXEXP; | |
1650 | if (exp > 9) { | |
1651 | do { | |
1652 | *--t = to_char(exp % 10); | |
1653 | } while ((exp /= 10) > 9); | |
1654 | *--t = to_char(exp); | |
1655 | for (; t < expbuf + MAXEXP; *p++ = *t++); | |
1656 | } | |
1657 | else { | |
1658 | *p++ = '0'; | |
1659 | *p++ = to_char(exp); | |
1660 | } | |
1661 | return (p - p0); | |
1662 | } | |
1663 | #endif /* FLOATING_POINT */ |