]>
Commit | Line | Data |
---|---|---|
6465356a A |
1 | /*- |
2 | * Copyright (c) 2005 Poul-Henning Kamp | |
3 | * Copyright (c) 1990, 1993 | |
4 | * The Regents of the University of California. All rights reserved. | |
5 | * | |
6 | * This code is derived from software contributed to Berkeley by | |
7 | * Chris Torek. | |
8 | * | |
9 | * Redistribution and use in source and binary forms, with or without | |
10 | * modification, are permitted provided that the following conditions | |
11 | * are met: | |
12 | * 1. Redistributions of source code must retain the above copyright | |
13 | * notice, this list of conditions and the following disclaimer. | |
14 | * 2. Redistributions in binary form must reproduce the above copyright | |
15 | * notice, this list of conditions and the following disclaimer in the | |
16 | * documentation and/or other materials provided with the distribution. | |
17 | * 3. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | * | |
33 | * $FreeBSD: src/lib/libc/stdio/xprintf.c,v 1.9 2010/03/11 17:03:32 jhb Exp $ | |
34 | */ | |
35 | ||
36 | #include "namespace.h" | |
37 | #include <err.h> | |
38 | #include <sys/types.h> | |
39 | #include <stdio.h> | |
40 | #include <stddef.h> | |
41 | #include <stdlib.h> | |
42 | #include <locale.h> | |
43 | #include <stdint.h> | |
44 | #include <assert.h> | |
45 | #include <stdarg.h> | |
46 | #include <namespace.h> | |
47 | #include <string.h> | |
48 | #include <wchar.h> | |
49 | #include <errno.h> | |
50 | #include "un-namespace.h" | |
51 | ||
52 | //#define MACHTIME | |
53 | #ifdef MACHTIME | |
54 | #include <mach/mach_time.h> | |
55 | #endif // MACHTIME | |
56 | ||
57 | #ifdef XPRINTF_PERF | |
58 | #include <libkern/OSAtomic.h> | |
59 | #endif /* XPRINTF_PERF */ | |
60 | ||
61 | #include "local.h" | |
62 | #include "xprintf_private.h" | |
63 | #include "xprintf_domain.h" | |
64 | #include "fvwrite.h" | |
65 | ||
66 | /* | |
67 | * Defining XPRINTF_DEBUG allows the __private_extern__ variable __use_xprintf | |
68 | * to be set so that regular printf variants will use the extensible printf | |
69 | * code path. This is normally off, and is only used to test the extensible | |
70 | * printf code in the conformance tests. | |
71 | */ | |
72 | #ifdef XPRINTF_DEBUG | |
73 | #include <unistd.h> | |
74 | int __use_xprintf = 0; | |
75 | #endif | |
76 | ||
77 | /* private stuff -----------------------------------------------------*/ | |
78 | ||
79 | union arg { | |
80 | int intarg; | |
81 | long longarg; | |
82 | intmax_t intmaxarg; | |
83 | #ifndef NO_FLOATING_POINT | |
84 | double doublearg; | |
85 | long double longdoublearg; | |
86 | #endif | |
87 | wint_t wintarg; | |
88 | char *pchararg; | |
89 | wchar_t *pwchararg; | |
90 | void *pvoidarg; | |
91 | #ifdef VECTORS | |
92 | VECTORTYPE vectorarg; | |
93 | unsigned char vuchararg[16]; | |
94 | signed char vchararg[16]; | |
95 | unsigned short vushortarg[8]; | |
96 | signed short vshortarg[8]; | |
97 | unsigned int vuintarg[4]; | |
98 | signed int vintarg[4]; | |
99 | float vfloatarg[4]; | |
100 | #ifdef V64TYPE | |
101 | double vdoublearg[2]; | |
102 | unsigned long long vulonglongarg[2]; | |
103 | long long vlonglongarg[2]; | |
104 | #endif /* V64TYPE */ | |
105 | #endif /* VECTORS */ | |
106 | }; | |
107 | ||
108 | /* | |
109 | * Macros for converting digits to letters and vice versa | |
110 | */ | |
111 | #define to_digit(c) ((c) - '0') | |
112 | #define is_digit(c) (((unsigned)to_digit(c)) <= 9) | |
113 | ||
114 | /* various globals ---------------------------------------------------*/ | |
115 | ||
116 | __private_extern__ const char __lowercase_hex[17] = "0123456789abcdef?"; /*lint !e784 */ | |
117 | __private_extern__ const char __uppercase_hex[17] = "0123456789ABCDEF?"; /*lint !e784 */ | |
118 | ||
119 | #define PADSIZE 16 | |
120 | static char blanks[PADSIZE] = | |
121 | {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '}; | |
122 | static char zeroes[PADSIZE] = | |
123 | {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'}; | |
124 | ||
125 | /* printing and padding functions ------------------------------------*/ | |
126 | ||
127 | #define NIOV 8 | |
128 | ||
129 | struct __printf_io { | |
130 | FILE *fp; | |
131 | struct __suio uio; | |
132 | struct __siov iov[NIOV]; | |
133 | struct __siov *iovp; | |
134 | }; | |
135 | ||
136 | static void | |
137 | __printf_init(struct __printf_io *io) | |
138 | { | |
139 | ||
140 | io->uio.uio_iov = io->iovp = &io->iov[0]; | |
141 | io->uio.uio_resid = 0; | |
142 | io->uio.uio_iovcnt = 0; | |
143 | } | |
144 | ||
145 | __private_extern__ void | |
146 | __printf_flush(struct __printf_io *io) | |
147 | { | |
148 | ||
149 | __sfvwrite(io->fp, &io->uio); | |
150 | __printf_init(io); | |
151 | } | |
152 | ||
153 | __private_extern__ int | |
154 | __printf_puts(struct __printf_io *io, const void *ptr, int len) | |
155 | { | |
156 | ||
157 | ||
158 | #if 0 | |
159 | if (io->fp->_flags & __SERR) | |
160 | return (0); | |
161 | #endif | |
162 | if (len == 0) | |
163 | return (0); | |
164 | io->iovp->iov_base = __DECONST(void *, ptr); | |
165 | io->iovp->iov_len = len; | |
166 | io->uio.uio_resid += len; | |
167 | io->iovp++; | |
168 | io->uio.uio_iovcnt++; | |
169 | if (io->uio.uio_iovcnt >= NIOV) | |
170 | __printf_flush(io); | |
171 | return (len); | |
172 | } | |
173 | ||
174 | __private_extern__ int | |
175 | __printf_pad(struct __printf_io *io, int howmany, int zero) | |
176 | { | |
177 | int n; | |
178 | const char *with; | |
179 | int ret = 0; | |
180 | ||
181 | if (zero) | |
182 | with = zeroes; | |
183 | else | |
184 | with = blanks; | |
185 | ||
186 | if ((n = (howmany)) > 0) { | |
187 | while (n > PADSIZE) { | |
188 | ret += __printf_puts(io, with, PADSIZE); | |
189 | n -= PADSIZE; | |
190 | } | |
191 | ret += __printf_puts(io, with, n); | |
192 | } | |
193 | return (ret); | |
194 | } | |
195 | ||
196 | __private_extern__ int | |
197 | __printf_out(struct __printf_io *io, const struct printf_info *pi, const void *ptr, int len) | |
198 | { | |
199 | int ret = 0; | |
200 | ||
201 | if ((!pi->left) && pi->width > len) | |
202 | ret += __printf_pad(io, pi->width - len, pi->pad == '0'); | |
203 | ret += __printf_puts(io, ptr, len); | |
204 | if (pi->left && pi->width > len) | |
205 | ret += __printf_pad(io, pi->width - len, pi->pad == '0'); | |
206 | return (ret); | |
207 | } | |
208 | ||
209 | ||
210 | /* percent handling -------------------------------------------------*/ | |
211 | ||
212 | __private_extern__ int | |
213 | __printf_arginfo_pct(const struct printf_info *pi __unused, size_t n __unused, int *argt __unused) | |
214 | { | |
215 | ||
216 | return (0); | |
217 | } | |
218 | ||
219 | __private_extern__ int | |
220 | __printf_render_pct(struct __printf_io *io, const struct printf_info *pi __unused, const void *const *arg __unused) | |
221 | { | |
222 | ||
223 | return (__printf_puts(io, "%", 1)); | |
224 | } | |
225 | ||
226 | /* 'n' ---------------------------------------------------------------*/ | |
227 | ||
228 | __private_extern__ int | |
229 | __printf_arginfo_n(const struct printf_info *pi, size_t n, int *argt) | |
230 | { | |
231 | ||
232 | assert(n >= 1); | |
233 | argt[0] = PA_POINTER; | |
234 | return (1); | |
235 | } | |
236 | ||
237 | /* | |
238 | * This is a printf_render so that all output has been flushed before it | |
239 | * gets called. | |
240 | */ | |
241 | ||
242 | __private_extern__ int | |
243 | __printf_render_n(FILE *io __unused, const struct printf_info *pi, const void *const *arg) | |
244 | { | |
245 | ||
246 | if (pi->is_char) | |
247 | **((signed char **)arg[0]) = (signed char)pi->sofar; | |
248 | else if (pi->is_short) | |
249 | **((short **)arg[0]) = (short)pi->sofar; | |
250 | else if (pi->is_long) | |
251 | **((long **)arg[0]) = pi->sofar; | |
252 | else if (pi->is_long_double) | |
253 | **((long long **)arg[0]) = pi->sofar; | |
254 | else if (pi->is_intmax) | |
255 | **((intmax_t **)arg[0]) = pi->sofar; | |
256 | else if (pi->is_ptrdiff) | |
257 | **((ptrdiff_t **)arg[0]) = pi->sofar; | |
258 | else if (pi->is_quad) | |
259 | **((quad_t **)arg[0]) = pi->sofar; | |
260 | else if (pi->is_size) | |
261 | **((size_t **)arg[0]) = pi->sofar; | |
262 | else | |
263 | **((int **)arg[0]) = pi->sofar; | |
264 | ||
265 | return (0); | |
266 | } | |
267 | ||
268 | /* dynamic array handling -------------------------------------------------*/ | |
269 | #define ARRAYDELTA 8 | |
270 | ||
271 | struct array { | |
272 | #ifdef XPRINTF_PERF | |
273 | struct array *next; | |
274 | #endif /* XPRINTF_PERF */ | |
275 | void *data; | |
276 | int itemsize; | |
277 | int max; | |
278 | }; | |
279 | ||
280 | #ifdef XPRINTF_PERF | |
281 | __private_extern__ | |
282 | #else /* !XPRINTF_PERF */ | |
283 | static | |
284 | #endif /* !XPRINTF_PERF */ | |
285 | void | |
286 | arrayfree(struct array *a) | |
287 | { | |
288 | if(a) free(a->data); | |
289 | } | |
290 | ||
291 | static void * | |
292 | arrayget(struct array *a, int i) | |
293 | { | |
294 | if (i >= a->max) { | |
295 | int oldsize = a->max * a->itemsize; | |
296 | int newmax = i + ARRAYDELTA; | |
297 | int newsize = newmax * a->itemsize; | |
298 | void *newdata = realloc(a->data, newsize); | |
299 | if(!newdata) return NULL; | |
300 | bzero(newdata + oldsize, newsize - oldsize); | |
301 | a->data = newdata; | |
302 | a->max = newmax; | |
303 | } | |
304 | return a->data + i * a->itemsize; | |
305 | } | |
306 | ||
307 | static struct array * | |
308 | arrayinit(struct array *a, int itemsize) | |
309 | { | |
310 | a->data = CALLOC(ARRAYDELTA, itemsize); | |
311 | if(!a->data) return NULL; | |
312 | a->itemsize = itemsize; | |
313 | a->max = ARRAYDELTA; | |
314 | return a; | |
315 | } | |
316 | ||
317 | /* dynamic array caching -------------------------------------------------*/ | |
318 | /* | |
319 | * Normally, dynamic array structures are created on the stack, and array | |
320 | * itself is freshly allocated, and then freed when no longer needed. When | |
321 | * the XPRINTF_PERF macro is defined, the dynamic array structures associated | |
322 | * with all-in-one printf variants are not freed, but store in a cache for | |
323 | * later use (dynamic array structures used for compile/execute continue to | |
324 | * be freed after they are no longer needed). This means there should be | |
325 | * at most one structure in the cached per thread that actually used the | |
326 | * all-in-one printf variant. | |
327 | * | |
328 | * The amount of memory that is cached is fairly small, totally about 1K | |
329 | * for three structures used by a format string using ten conversion | |
330 | * specifiers. This is too small for purgeable memory. | |
331 | * | |
332 | * However, we do flush these caches in case we every are unable to allocate | |
333 | * memory, and retry the allocation, just in case. | |
334 | */ | |
335 | #ifdef XPRINTF_PERF | |
336 | static OSQueueHead arg_type_queue = OS_ATOMIC_QUEUE_INIT; | |
337 | static OSQueueHead printf_info_queue = OS_ATOMIC_QUEUE_INIT; | |
338 | static OSQueueHead union_arg_queue = OS_ATOMIC_QUEUE_INIT; | |
339 | ||
340 | #define DEFINE_DEQUEUE(which, type) \ | |
341 | static struct array * \ | |
342 | which ## _dequeue(void) \ | |
343 | { \ | |
344 | struct array *a = (struct array *)OSAtomicDequeue(&which ## _queue, offsetof(struct array, next)); \ | |
345 | \ | |
346 | if (a) { \ | |
347 | bzero(a->data, a->max * a->itemsize); \ | |
348 | return a; \ | |
349 | } \ | |
350 | a = (struct array *)MALLOC(sizeof(*a)); \ | |
351 | if (!a) return NULL; \ | |
352 | if (!arrayinit(a, sizeof(type))) { \ | |
353 | free(a); \ | |
354 | return NULL; \ | |
355 | } \ | |
356 | return a; \ | |
357 | } | |
358 | ||
359 | #define DEFINE_ENQUEUE(which) \ | |
360 | __private_extern__ void \ | |
361 | which ## _enqueue(struct array *a) \ | |
362 | { \ | |
363 | if (!a) return; \ | |
364 | OSAtomicEnqueue(&which ## _queue, a, offsetof(struct array, next)); \ | |
365 | } | |
366 | ||
367 | #define DEFINE_FLUSH(which) \ | |
368 | static void \ | |
369 | which ## _flush(void) \ | |
370 | { \ | |
371 | struct array *a; \ | |
372 | while((a = (struct array *)OSAtomicDequeue(&which ## _queue, offsetof(struct array, next))) != NULL) { \ | |
373 | arrayfree(a); \ | |
374 | free(a); \ | |
375 | } \ | |
376 | } | |
377 | ||
378 | DEFINE_DEQUEUE(arg_type, int) | |
379 | DEFINE_ENQUEUE(arg_type) | |
380 | DEFINE_FLUSH(arg_type) | |
381 | DEFINE_DEQUEUE(printf_info, struct printf_info) | |
382 | DEFINE_ENQUEUE(printf_info) | |
383 | DEFINE_FLUSH(printf_info) | |
384 | DEFINE_DEQUEUE(union_arg, union arg) | |
385 | DEFINE_ENQUEUE(union_arg) | |
386 | DEFINE_FLUSH(union_arg) | |
387 | ||
388 | static void | |
389 | flush_queues(void) | |
390 | { | |
391 | arg_type_flush(); | |
392 | printf_info_flush(); | |
393 | union_arg_flush(); | |
394 | } | |
395 | ||
396 | __private_extern__ void * | |
397 | xprintf_calloc(size_t count, size_t size) | |
398 | { | |
399 | void *x = calloc(count, size); | |
400 | if(!x) { | |
401 | flush_queues(); | |
402 | x = calloc(count, size); | |
403 | } | |
404 | return x; | |
405 | } | |
406 | ||
407 | __private_extern__ void * | |
408 | xprintf_malloc(size_t size) | |
409 | { | |
410 | void *x = malloc(size); | |
411 | if(!x) { | |
412 | flush_queues(); | |
413 | x = malloc(size); | |
414 | } | |
415 | return x; | |
416 | } | |
417 | ||
418 | #if 0 | |
419 | void | |
420 | show_queues(void) | |
421 | { | |
422 | struct array *a; | |
423 | printf("arg_type:"); | |
424 | while((a = (struct array *)OSAtomicDequeue(&arg_type_queue, offsetof(struct array, next))) != NULL) printf("\n%p", a); | |
425 | printf("\nprintf_info:"); | |
426 | while((a = (struct array *)OSAtomicDequeue(&printf_info_queue, offsetof(struct array, next))) != NULL) printf("\n%p", a); | |
427 | printf("\nunion_arg:"); | |
428 | while((a = (struct array *)OSAtomicDequeue(&union_arg_queue, offsetof(struct array, next))) != NULL) printf("\n%p", a); | |
429 | printf("\n"); | |
430 | } | |
431 | #endif | |
432 | #endif /* XPRINTF_PERF */ | |
433 | ||
434 | /* -------------------------------------------------------------------------*/ | |
435 | ||
436 | __private_extern__ int | |
437 | __printf_comp(printf_comp_t restrict pc, printf_domain_t restrict domain) | |
438 | { | |
439 | struct printf_info *pi, *pil; | |
440 | const char *fmt; | |
441 | int ch, pii; | |
442 | int *argt; | |
443 | int nextarg; | |
444 | int maxarg; | |
445 | int ret = 0; | |
446 | int n; | |
447 | #ifndef XPRINTF_PERF | |
448 | struct array piarr, argtarr; | |
449 | #endif /* XPRINTF_PERF */ | |
450 | struct array *pa, *aa; | |
451 | ||
452 | fmt = pc->fmt; | |
453 | maxarg = 0; | |
454 | nextarg = 1; | |
455 | #ifdef XPRINTF_PERF | |
456 | pa = printf_info_dequeue(); | |
457 | #else /* !XPRINTF_PERF */ | |
458 | pa = arrayinit(&piarr, sizeof(*pi)); | |
459 | #endif /* !XPRINTF_PERF */ | |
460 | if (!pa) { | |
461 | #ifdef XPRINTF_PERF | |
462 | flush_queues(); | |
463 | #endif /* XPRINTF_PERF */ | |
464 | return EOF; | |
465 | } | |
466 | #ifdef XPRINTF_PERF | |
467 | pc->pa = pa; | |
468 | aa = arg_type_dequeue(); | |
469 | #else /* !XPRINTF_PERF */ | |
470 | aa = arrayinit(&argtarr, sizeof(*argt)); | |
471 | #endif /* !XPRINTF_PERF */ | |
472 | if (!aa) { | |
473 | arrayfree(pa); | |
474 | #ifdef XPRINTF_PERF | |
475 | free(pa); | |
476 | flush_queues(); | |
477 | #endif /* XPRINTF_PERF */ | |
478 | return EOF; | |
479 | } | |
480 | #ifdef XPRINTF_PERF | |
481 | pc->aa = aa; | |
482 | #endif /* XPRINTF_PERF */ | |
483 | for (pii = 0; ; pii++) { | |
484 | pi = arrayget(pa, pii); | |
485 | if (!pi) { | |
486 | ret = EOF; | |
487 | goto error; | |
488 | } | |
489 | pil = pi; | |
490 | if (*fmt == '\0') | |
491 | break; | |
492 | pil = pi + 1; | |
493 | pi->prec = -1; | |
494 | pi->pad = ' '; | |
495 | #ifdef VECTORS | |
496 | pi->vsep = 'X'; /* Illegal value, changed to defaults later. */ | |
497 | #endif /* VECTORS */ | |
498 | pi->begin = pi->end = fmt; | |
499 | while (*fmt != '\0' && *fmt != '%') | |
500 | pi->end = ++fmt; | |
501 | if (*fmt == '\0') | |
502 | break; | |
503 | fmt++; | |
504 | for (;;) { | |
505 | pi->spec = *fmt; | |
506 | switch (pi->spec) { | |
507 | case ' ': | |
508 | /*- | |
509 | * ``If the space and + flags both appear, the space | |
510 | * flag will be ignored.'' | |
511 | * -- ANSI X3J11 | |
512 | */ | |
513 | if (pi->showsign == 0) { | |
514 | pi->space = 1; | |
515 | pi->signchar = ' '; | |
516 | } | |
517 | fmt++; | |
518 | continue; | |
519 | case '#': | |
520 | pi->alt = 1; | |
521 | fmt++; | |
522 | continue; | |
523 | #ifdef VECTORS | |
524 | case ',': case ';': case ':': case '_': | |
525 | pi->vsep = pi->spec; | |
526 | fmt++; | |
527 | continue; | |
528 | #endif /* VECTORS */ | |
529 | case '.': | |
530 | pi->prec = 0; | |
531 | fmt++; | |
532 | if (*fmt == '*') { | |
533 | fmt++; | |
534 | /* Look for *nn$ and deal with it */ | |
535 | n = 0; | |
536 | while (*fmt != '\0' && is_digit(*fmt)) { | |
537 | n *= 10; | |
538 | n += to_digit(*fmt); | |
539 | fmt++; | |
540 | } | |
541 | if (*fmt == '$') { | |
542 | if ((n + 1) > maxarg) | |
543 | maxarg = (n + 1); | |
544 | fmt++; | |
545 | } else n = nextarg++; | |
546 | pi->get_prec = n; | |
547 | argt = (int *)arrayget(aa, n); | |
548 | if (!argt) { | |
549 | ret = EOF; | |
550 | goto error; | |
551 | } | |
552 | *argt = PA_INT; | |
553 | continue; | |
554 | } | |
555 | while (*fmt != '\0' && is_digit(*fmt)) { | |
556 | pi->prec *= 10; | |
557 | pi->prec += to_digit(*fmt); | |
558 | fmt++; | |
559 | } | |
560 | continue; | |
561 | case '-': | |
562 | pi->left = 1; | |
563 | fmt++; | |
564 | continue; | |
565 | case '+': | |
566 | pi->showsign = 1; | |
567 | pi->signchar = '+'; | |
568 | fmt++; | |
569 | continue; | |
570 | case '*': | |
571 | fmt++; | |
572 | /* Look for *nn$ and deal with it */ | |
573 | n = 0; | |
574 | while (*fmt != '\0' && is_digit(*fmt)) { | |
575 | n *= 10; | |
576 | n += to_digit(*fmt); | |
577 | fmt++; | |
578 | } | |
579 | if (*fmt == '$') { | |
580 | if ((n + 1) > maxarg) | |
581 | maxarg = (n + 1); | |
582 | fmt++; | |
583 | } else n = nextarg++; | |
584 | pi->get_width = n; | |
585 | argt = (int *)arrayget(aa, n); | |
586 | if (!argt) { | |
587 | ret = EOF; | |
588 | goto error; | |
589 | } | |
590 | *argt = PA_INT; | |
591 | continue; | |
592 | case '%': | |
593 | fmt++; | |
594 | break; | |
595 | case '\'': | |
596 | pi->group = 1; | |
597 | fmt++; | |
598 | continue; | |
599 | case '0': | |
600 | /*- | |
601 | * ``Note that 0 is taken as a flag, not as the | |
602 | * beginning of a field width.'' | |
603 | * -- ANSI X3J11 | |
604 | */ | |
605 | pi->pad = '0'; | |
606 | fmt++; | |
607 | continue; | |
608 | case '1': case '2': case '3': | |
609 | case '4': case '5': case '6': | |
610 | case '7': case '8': case '9': | |
611 | n = 0; | |
612 | while (*fmt != '\0' && is_digit(*fmt)) { | |
613 | n *= 10; | |
614 | n += to_digit(*fmt); | |
615 | fmt++; | |
616 | } | |
617 | if (*fmt == '$') { | |
618 | if (nextarg > maxarg) | |
619 | maxarg = nextarg; | |
620 | nextarg = n; | |
621 | fmt++; | |
622 | } else | |
623 | pi->width = n; | |
624 | continue; | |
625 | #if 0 | |
626 | case 'D': | |
627 | case 'O': | |
628 | case 'U': | |
629 | pi->spec += ('a' - 'A'); | |
630 | pi->is_intmax = 0; | |
631 | if (pi->is_long_double || pi->is_quad) { | |
632 | pi->is_long = 0; | |
633 | pi->is_long_double = 1; | |
634 | } else { | |
635 | pi->is_long = 1; | |
636 | pi->is_long_double = 0; | |
637 | } | |
638 | fmt++; | |
639 | break; | |
640 | #endif | |
641 | case 'j': | |
642 | pi->is_intmax = 1; | |
643 | fmt++; | |
644 | continue; | |
645 | case 'q': | |
646 | pi->is_long = 0; | |
647 | pi->is_quad = 1; | |
648 | fmt++; | |
649 | continue; | |
650 | case 'L': | |
651 | pi->is_long_double = 1; | |
652 | fmt++; | |
653 | continue; | |
654 | case 'h': | |
655 | fmt++; | |
656 | if (*fmt == 'h') { | |
657 | fmt++; | |
658 | pi->is_char = 1; | |
659 | } else { | |
660 | pi->is_short = 1; | |
661 | } | |
662 | continue; | |
663 | case 'l': | |
664 | fmt++; | |
665 | if (*fmt == 'l') { | |
666 | fmt++; | |
667 | pi->is_long_double = 1; | |
668 | pi->is_quad = 0; | |
669 | } else { | |
670 | pi->is_quad = 0; | |
671 | pi->is_long = 1; | |
672 | } | |
673 | continue; | |
674 | case 't': | |
675 | pi->is_ptrdiff = 1; | |
676 | fmt++; | |
677 | continue; | |
678 | case 'v': | |
679 | #ifdef VECTORS | |
680 | pi->is_vec = 1; | |
681 | #endif /* VECTORS */ | |
682 | fmt++; | |
683 | continue; | |
684 | case 'z': | |
685 | pi->is_size = 1; | |
686 | fmt++; | |
687 | continue; | |
688 | default: | |
689 | fmt++; | |
690 | break; | |
691 | } | |
692 | if (printf_tbl_in_range(pi->spec)) { | |
693 | switch(domain->type[printf_tbl_index(pi->spec)]) { | |
694 | /* ignore PRINTF_DOMAIN_UNUSED until later */ | |
695 | case PRINTF_DOMAIN_FLAG: | |
696 | errx(1, "Unexpected flag: %c", pi->spec); | |
697 | case PRINTF_DOMAIN_GLIBC_API: | |
698 | case PRINTF_DOMAIN_FBSD_API: | |
699 | /* | |
700 | * Insure that there are always | |
701 | * __PRINTFMAXARG available. | |
702 | */ | |
703 | if (!arrayget(aa, nextarg + __PRINTFMAXARG - 1)) { | |
704 | ret = EOF; | |
705 | goto error; | |
706 | } | |
707 | pi->context = domain->tbl[printf_tbl_index(pi->spec)].context; | |
708 | pi->loc = pc->loc; | |
709 | ch = domain->tbl[printf_tbl_index(pi->spec)].arginfo( | |
710 | pi, __PRINTFMAXARG, arrayget(aa, nextarg)); | |
711 | if (ch > 0) | |
712 | pi->arg[0] = (void *)(long)nextarg; | |
713 | if (ch > 1) | |
714 | pi->arg[1] = (void *)(long)(nextarg + 1); | |
715 | nextarg += ch; | |
716 | break; | |
717 | } | |
718 | } | |
719 | break; | |
720 | } | |
721 | } | |
722 | if (nextarg > maxarg) | |
723 | maxarg = nextarg; | |
724 | pc->argt = aa->data; | |
725 | pc->pi = pa->data; | |
726 | pc->pil = pil; | |
727 | pc->maxarg = ch = maxarg; | |
728 | if (ch < 1) ch = 1; | |
729 | #ifdef XPRINTF_PERF | |
730 | pc->ua = union_arg_dequeue(); | |
731 | if (!pc->ua) { | |
732 | ret = EOF; | |
733 | goto error; | |
734 | } | |
735 | if (!arrayget(pc->ua, ch)) { | |
736 | union_arg_enqueue(pc->ua); | |
737 | ret = EOF; | |
738 | goto error; | |
739 | } | |
740 | pc->args = pc->ua->data; | |
741 | #else /* !XPRINTF_PERF */ | |
742 | pc->args = (union arg *)malloc(ch * sizeof(*pc->args)); | |
743 | if (!pc->args) { | |
744 | ret = EOF; | |
745 | goto error; | |
746 | } | |
747 | #endif /* !XPRINTF_PERF */ | |
748 | for (pi = pc->pi; pi < pil; pi++) { | |
749 | if (pi->arg[0]) pi->arg[0] = &pc->args[(long)pi->arg[0]]; | |
750 | if (pi->arg[1]) pi->arg[1] = &pc->args[(long)pi->arg[1]]; | |
751 | } | |
752 | #if 0 | |
753 | fprintf(stderr, "fmt0 <%s>\n", fmt0); | |
754 | fprintf(stderr, "pil %p\n", pil); | |
755 | #endif | |
756 | pc->domain = domain; | |
757 | ||
758 | return (ret); | |
759 | error: | |
760 | arrayfree(pa); | |
761 | arrayfree(aa); | |
762 | #ifdef XPRINTF_PERF | |
763 | free(pa); | |
764 | free(aa); | |
765 | flush_queues(); | |
766 | #endif /* XPRINTF_PERF */ | |
767 | return (ret); | |
768 | } | |
769 | ||
770 | __private_extern__ int | |
771 | __printf_exec(printf_comp_t restrict pc, FILE * restrict fp, va_list ap) | |
772 | { | |
773 | struct printf_info *pi; | |
774 | int ch; | |
775 | int ret = 0; | |
776 | int n; | |
777 | struct __printf_io io; | |
778 | ||
779 | __printf_init(&io); | |
780 | io.fp = fp; | |
781 | ||
782 | for (ch = 1; ch < pc->maxarg; ch++) { | |
783 | #if 0 | |
784 | fprintf(stderr, "arg %d %x\n", ch, pc->argt[ch]); | |
785 | #endif | |
786 | switch(pc->argt[ch]) { | |
787 | case PA_CHAR: | |
788 | pc->args[ch].intarg = (char)va_arg (ap, int); | |
789 | break; | |
790 | case PA_INT: | |
791 | pc->args[ch].intarg = va_arg (ap, int); | |
792 | break; | |
793 | case PA_INT | PA_FLAG_SHORT: | |
794 | pc->args[ch].intarg = (short)va_arg (ap, int); | |
795 | break; | |
796 | case PA_INT | PA_FLAG_LONG: | |
797 | pc->args[ch].longarg = va_arg (ap, long); | |
798 | break; | |
799 | case PA_INT | PA_FLAG_INTMAX: | |
800 | pc->args[ch].intmaxarg = va_arg (ap, intmax_t); | |
801 | break; | |
802 | case PA_INT | PA_FLAG_QUAD: | |
803 | pc->args[ch].intmaxarg = va_arg (ap, quad_t); | |
804 | break; | |
805 | case PA_INT | PA_FLAG_LONG_LONG: | |
806 | pc->args[ch].intmaxarg = va_arg (ap, long long); | |
807 | break; | |
808 | case PA_INT | PA_FLAG_SIZE: | |
809 | pc->args[ch].intmaxarg = va_arg (ap, size_t); | |
810 | break; | |
811 | case PA_INT | PA_FLAG_PTRDIFF: | |
812 | pc->args[ch].intmaxarg = (unsigned long)va_arg (ap, ptrdiff_t); | |
813 | break; | |
814 | case PA_WCHAR: | |
815 | pc->args[ch].wintarg = va_arg (ap, wint_t); | |
816 | break; | |
817 | case PA_POINTER: | |
818 | pc->args[ch].pvoidarg = va_arg (ap, void *); | |
819 | break; | |
820 | case PA_STRING: | |
821 | pc->args[ch].pchararg = va_arg (ap, char *); | |
822 | break; | |
823 | case PA_WSTRING: | |
824 | pc->args[ch].pwchararg = va_arg (ap, wchar_t *); | |
825 | break; | |
826 | case PA_DOUBLE: | |
827 | #ifndef NO_FLOATING_POINT | |
828 | pc->args[ch].doublearg = va_arg (ap, double); | |
829 | #endif | |
830 | break; | |
831 | case PA_DOUBLE | PA_FLAG_LONG_DOUBLE: | |
832 | #ifndef NO_FLOATING_POINT | |
833 | pc->args[ch].longdoublearg = va_arg (ap, long double); | |
834 | #endif | |
835 | break; | |
836 | #ifdef VECTORS | |
837 | case PA_VECTOR: | |
838 | pc->args[ch].vectorarg = va_arg (ap, VECTORTYPE); | |
839 | break; | |
840 | #endif /* VECTORS */ | |
841 | default: | |
842 | errx(1, "argtype = %x (fmt = \"%s\")\n", | |
843 | pc->argt[ch], pc->fmt); | |
844 | } | |
845 | } | |
846 | for (pi = pc->pi; pi < pc->pil; pi++) { | |
847 | #if 0 | |
848 | fprintf(stderr, "pi %p", pi); | |
849 | fprintf(stderr, " spec '%c'", pi->spec); | |
850 | fprintf(stderr, " args %d", | |
851 | ((uintptr_t)pi->arg[0] - (uintptr_t)pc->args) / sizeof pc->args[0]); | |
852 | if (pi->width) fprintf(stderr, " width %d", pi->width); | |
853 | if (pi->pad) fprintf(stderr, " pad 0x%x", pi->pad); | |
854 | if (pi->left) fprintf(stderr, " left"); | |
855 | if (pi->showsign) fprintf(stderr, " showsign"); | |
856 | if (pi->signchar) fprintf(stderr, " signchar 0x%x", pi->signchar); | |
857 | if (pi->prec != -1) fprintf(stderr, " prec %d", pi->prec); | |
858 | if (pi->is_char) fprintf(stderr, " char"); | |
859 | if (pi->is_short) fprintf(stderr, " short"); | |
860 | if (pi->is_long) fprintf(stderr, " long"); | |
861 | if (pi->is_long_double) fprintf(stderr, " long_double"); | |
862 | fprintf(stderr, "\n"); | |
863 | fprintf(stderr, "\t\"%.*s\"\n", pi->end - pi->begin, pi->begin); | |
864 | #endif | |
865 | if (pi->get_width) { | |
866 | pi->width = pc->args[pi->get_width].intarg; | |
867 | /*- | |
868 | * ``A negative field width argument is taken as a | |
869 | * - flag followed by a positive field width.'' | |
870 | * -- ANSI X3J11 | |
871 | * They don't exclude field widths read from args. | |
872 | */ | |
873 | if (pi->width < 0) { | |
874 | pi->left = 1; | |
875 | pi->width = -pi->width; | |
876 | } | |
877 | } | |
878 | if (pi->get_prec) | |
879 | pi->prec = pc->args[pi->get_prec].intarg; | |
880 | ret += __printf_puts(&io, pi->begin, pi->end - pi->begin); | |
881 | if (pi->spec) { | |
882 | if (!printf_tbl_in_range(pi->spec)) goto unused; | |
883 | switch(pc->domain->type[printf_tbl_index(pi->spec)]) { | |
884 | case PRINTF_DOMAIN_UNUSED: | |
885 | unused: | |
886 | { | |
887 | char unknown = pi->spec; | |
888 | ret += __printf_out(&io, pi, &unknown, 1); | |
889 | break; | |
890 | } | |
891 | case PRINTF_DOMAIN_GLIBC_API: | |
892 | __printf_flush(&io); | |
893 | pi->sofar = ret; | |
894 | ret += ((printf_function *)pc->domain->tbl[printf_tbl_index(pi->spec)].render)( | |
895 | fp, pi, (const void *)pi->arg); | |
896 | break; | |
897 | case PRINTF_DOMAIN_FBSD_API: | |
898 | pi->sofar = ret; | |
899 | n = ((printf_render *)pc->domain->tbl[printf_tbl_index(pi->spec)].render)( | |
900 | &io, pi, (const void *)pi->arg); | |
901 | if (n < 0) | |
902 | io.fp->_flags |= __SERR; | |
903 | else | |
904 | ret += n; | |
905 | break; | |
906 | } | |
907 | } | |
908 | } | |
909 | __printf_flush(&io); | |
910 | return (ret); | |
911 | } | |
912 | ||
913 | __private_extern__ int | |
914 | __v2printf(printf_comp_t restrict pc, printf_domain_t restrict domain, FILE * restrict fp, locale_t restrict loc, const char * restrict fmt, va_list ap) | |
915 | { | |
916 | struct _printf_compiled spc; | |
917 | int ret, saverrno; | |
918 | ||
919 | /* | |
920 | * All the printf family (including extensible printf variants) funnel | |
921 | * down to this point. So we can do common work here, and then fork | |
922 | * out to the appropriate handler. | |
923 | */ | |
924 | /* sorry, fprintf(read_only_file, "") returns EOF, not 0 */ | |
925 | if (prepwrite(fp) != 0) { | |
926 | errno = EBADF; | |
927 | return (EOF); | |
928 | } | |
929 | ORIENT(fp, -1); | |
930 | ||
931 | if (pc == XPRINTF_PLAIN) { | |
932 | NORMALIZE_LOCALE(loc); | |
933 | #ifdef XPRINTF_DEBUG | |
934 | if (!__use_xprintf) | |
935 | #endif | |
936 | return __vfprintf(fp, loc, fmt, ap); | |
937 | #ifdef XPRINTF_DEBUG | |
938 | xprintf_domain_init(); | |
939 | domain = xprintf_domain_global; | |
940 | #endif | |
941 | } else if (pc) { | |
942 | pthread_mutex_lock(&pc->mutex); | |
943 | pthread_rwlock_rdlock(&pc->domain->rwlock); | |
944 | ret = __printf_exec(pc, fp, ap); | |
945 | saverrno = errno; | |
946 | pthread_rwlock_unlock(&pc->domain->rwlock); | |
947 | pthread_mutex_unlock(&pc->mutex); | |
948 | errno = saverrno; | |
949 | return ret; | |
950 | } | |
951 | if (!domain) { | |
952 | errno = EINVAL; | |
953 | return EOF; | |
954 | } | |
955 | xprintf_domain_init(); | |
956 | bzero(&spc, sizeof(spc)); | |
957 | spc.fmt = fmt; | |
958 | DEFAULT_CURRENT_LOCALE(loc); | |
959 | XL_RETAIN(loc); | |
960 | spc.loc = loc; | |
961 | /* | |
962 | * We don't need to lock the printf_comp_t mutex, since the | |
963 | * printf_comp_t was just created on the stack, and is private. | |
964 | */ | |
965 | pthread_rwlock_rdlock(&domain->rwlock); | |
966 | if (__printf_comp(&spc, domain) < 0) { | |
967 | saverrno = errno; | |
968 | pthread_rwlock_unlock(&domain->rwlock); | |
969 | XL_RELEASE(loc); | |
970 | errno = saverrno; | |
971 | return EOF; | |
972 | } | |
973 | ret = __printf_exec(&spc, fp, ap); | |
974 | saverrno = errno; | |
975 | pthread_rwlock_unlock(&domain->rwlock); | |
976 | XL_RELEASE(loc); | |
977 | ||
978 | #ifdef XPRINTF_PERF | |
979 | printf_info_enqueue(spc.pa); | |
980 | arg_type_enqueue(spc.aa); | |
981 | union_arg_enqueue(spc.ua); | |
982 | #else /* !XPRINTF_PERF */ | |
983 | free(spc.pi); | |
984 | free(spc.argt); | |
985 | free(spc.args); | |
986 | #endif /* !XPRINTF_PERF */ | |
987 | errno = saverrno; | |
988 | return ret; | |
989 | } | |
990 | ||
991 | extern int __fflush(FILE *fp); | |
992 | ||
993 | /* | |
994 | * Helper function for `fprintf to unbuffered unix file': creates a | |
995 | * temporary buffer. We only work on write-only files; this avoids | |
996 | * worries about ungetc buffers and so forth. | |
997 | */ | |
998 | static int | |
999 | __v3printf(printf_comp_t restrict pc, printf_domain_t restrict domain, FILE * restrict fp, locale_t restrict loc, const char * restrict fmt, va_list ap) | |
1000 | { | |
1001 | int ret; | |
1002 | FILE fake; | |
1003 | struct __sFILEX extra; | |
1004 | unsigned char buf[BUFSIZ]; | |
1005 | ||
1006 | fake._extra = &extra; | |
1007 | INITEXTRA(&fake); | |
1008 | ||
1009 | /* copy the important variables */ | |
1010 | fake._flags = fp->_flags & ~__SNBF; | |
1011 | fake._file = fp->_file; | |
1012 | fake._cookie = fp->_cookie; | |
1013 | fake._write = fp->_write; | |
1014 | fake._orientation = fp->_orientation; | |
1015 | fake._mbstate = fp->_mbstate; | |
1016 | ||
1017 | /* set up the buffer */ | |
1018 | fake._bf._base = fake._p = buf; | |
1019 | fake._bf._size = fake._w = sizeof(buf); | |
1020 | fake._lbfsize = 0; /* not actually used, but Just In Case */ | |
1021 | ||
1022 | /* do the work, then copy any error status */ | |
1023 | ret = __v2printf(pc, domain, &fake, loc, fmt, ap); | |
1024 | if (ret >= 0 && __fflush(&fake)) | |
1025 | ret = EOF; | |
1026 | if (fake._flags & __SERR) | |
1027 | fp->_flags |= __SERR; | |
1028 | return (ret); | |
1029 | } | |
1030 | ||
1031 | __private_extern__ int | |
1032 | __xvprintf(printf_comp_t restrict pc, printf_domain_t restrict domain, FILE * restrict fp, locale_t restrict loc, const char * restrict fmt0, va_list ap) | |
1033 | { | |
1034 | int ret; | |
1035 | ||
1036 | /* optimise fprintf(stderr) (and other unbuffered Unix files) */ | |
1037 | if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) && | |
1038 | fp->_file >= 0) | |
1039 | ret = __v3printf(pc, domain, fp, loc, fmt0, ap); | |
1040 | else | |
1041 | ret = __v2printf(pc, domain, fp, loc, fmt0, ap); | |
1042 | return ret; | |
1043 | } | |
1044 | ||
1045 | /* extending ---------------------------------------------------------*/ | |
1046 | ||
1047 | // No global domain support | |
1048 | #if 0 | |
1049 | int | |
1050 | register_printf_function(int spec, printf_function *render, printf_arginfo_function *arginfo) | |
1051 | { | |
1052 | return register_printf_domain_function(NULL, spec, render, arginfo); | |
1053 | } | |
1054 | ||
1055 | __private_extern__ int | |
1056 | register_printf_render(int spec, printf_render *render, printf_arginfo_function *arginfo) | |
1057 | { | |
1058 | return register_printf_domain_render(NULL, spec, render, arginfo); | |
1059 | } | |
1060 | ||
1061 | int | |
1062 | register_printf_render_std(const char *specs) | |
1063 | { | |
1064 | return register_printf_domain_render_std(NULL, specs); | |
1065 | } | |
1066 | #endif | |
1067 | ||
1068 | #ifdef VECTORS | |
1069 | /* vector support ----------------------------------------------------*/ | |
1070 | ||
1071 | #define PRINTVECTOR(_io, _pi, _arg, _cnt, _type, _elem, _render, _ret) { \ | |
1072 | int i; \ | |
1073 | _type a, *ap; \ | |
1074 | a = (_type)(_arg)->_elem[0]; \ | |
1075 | ap = &a; \ | |
1076 | (_ret) += _render((_io), (_pi), (const void *)&ap); \ | |
1077 | for(i = 1; i < (_cnt); i++) { \ | |
1078 | (_ret) += __printf_puts((_io), (_pi)->begin, (_pi)->end - (_pi)->begin); \ | |
1079 | a = (_type)(_arg)->_elem[i]; \ | |
1080 | (_ret) += _render((_io), (_pi), (const void *)&ap); \ | |
1081 | } \ | |
1082 | } | |
1083 | ||
1084 | #define PRINTVECTOR_P(_io, _pi, _arg, _cnt, _elem, _render, _ret) { \ | |
1085 | int i; \ | |
1086 | void * a, *ap; \ | |
1087 | a = (void *)(uintptr_t)(_arg)->_elem[0]; \ | |
1088 | ap = &a; \ | |
1089 | (_ret) += _render((_io), (_pi), (const void *)&ap); \ | |
1090 | for(i = 1; i < (_cnt); i++) { \ | |
1091 | (_ret) += __printf_puts((_io), (_pi)->begin, (_pi)->end - (_pi)->begin); \ | |
1092 | a = (void *)(uintptr_t)(_arg)->_elem[i]; \ | |
1093 | (_ret) += _render((_io), (_pi), (const void *)&ap); \ | |
1094 | } \ | |
1095 | } | |
1096 | ||
1097 | __private_extern__ int | |
1098 | __xprintf_vector(struct __printf_io *io, const struct printf_info *pi, const void *const *arg) | |
1099 | { | |
1100 | char vsep; /* Vector separator character. */ | |
1101 | const union arg *argp; | |
1102 | int ret = 0; | |
1103 | struct printf_info info = *pi; | |
1104 | ||
1105 | argp = arg[0]; | |
1106 | vsep = pi->vsep; | |
1107 | if (vsep == 'X') { | |
1108 | if (pi->spec == 'c') | |
1109 | vsep = '\0'; | |
1110 | else | |
1111 | vsep = ' '; | |
1112 | } | |
1113 | info.begin = info.end = &vsep; | |
1114 | if (vsep) info.end++; | |
1115 | info.is_vec = 0; | |
1116 | ||
1117 | if (pi->is_short) { | |
1118 | if (pi->spec == 'p') { | |
1119 | PRINTVECTOR_P(io, &info, argp, 8, vushortarg, __printf_render_ptr, ret); | |
1120 | } else { | |
1121 | PRINTVECTOR(io, &info, argp, 8, unsigned int, vushortarg, __printf_render_int, ret); | |
1122 | } | |
1123 | } else if (pi->is_long) { | |
1124 | info.is_long = 0; | |
1125 | if (pi->spec == 'p') { | |
1126 | PRINTVECTOR_P(io, &info, argp, 4, vuintarg, __printf_render_ptr, ret); | |
1127 | } else { | |
1128 | PRINTVECTOR(io, &info, argp, 4, unsigned int, vuintarg, __printf_render_int, ret); | |
1129 | } | |
1130 | #ifdef V64TYPE | |
1131 | } else if (pi->is_long_double) { | |
1132 | switch (pi->spec) { | |
1133 | case 'a': | |
1134 | case 'A': | |
1135 | case 'e': | |
1136 | case 'E': | |
1137 | case 'f': | |
1138 | case 'g': | |
1139 | case 'G': | |
1140 | info.is_long_double = 0; | |
1141 | PRINTVECTOR(io, &info, argp, 2, double, vdoublearg, __printf_render_float, ret); | |
1142 | break; | |
1143 | case 'p': | |
1144 | info.is_long_double = 0; | |
1145 | PRINTVECTOR_P(io, &info, argp, 2, vulonglongarg, __printf_render_ptr, ret); | |
1146 | break; | |
1147 | case 'd': | |
1148 | case 'i': | |
1149 | case 'u': | |
1150 | case 'o': | |
1151 | case 'x': | |
1152 | case 'X': | |
1153 | PRINTVECTOR(io, &info, argp, 2, unsigned long long, vulonglongarg, __printf_render_int, ret); | |
1154 | break; | |
1155 | default: | |
1156 | /* | |
1157 | * The default case should never | |
1158 | * happen. | |
1159 | */ | |
1160 | case 'c': | |
1161 | info.is_long_double = 0; | |
1162 | PRINTVECTOR(io, &info, argp, 16, unsigned int, vuchararg, __printf_render_chr, ret); | |
1163 | } | |
1164 | #endif /* V64TYPE */ | |
1165 | } else { | |
1166 | switch (pi->spec) { | |
1167 | case 'a': | |
1168 | case 'A': | |
1169 | case 'e': | |
1170 | case 'E': | |
1171 | case 'f': | |
1172 | case 'g': | |
1173 | case 'G': | |
1174 | PRINTVECTOR(io, &info, argp, 4, double, vfloatarg, __printf_render_float, ret); | |
1175 | break; | |
1176 | default: | |
1177 | /* | |
1178 | * The default case should never | |
1179 | * happen. | |
1180 | */ | |
1181 | case 'p': | |
1182 | PRINTVECTOR_P(io, &info, argp, 16, vuchararg, __printf_render_ptr, ret); | |
1183 | break; | |
1184 | case 'd': | |
1185 | case 'i': | |
1186 | case 'u': | |
1187 | case 'o': | |
1188 | case 'x': | |
1189 | case 'X': | |
1190 | info.is_char = 1; | |
1191 | PRINTVECTOR(io, &info, argp, 16, unsigned int, vuchararg, __printf_render_int, ret); | |
1192 | break; | |
1193 | case 'c': | |
1194 | PRINTVECTOR(io, &info, argp, 16, unsigned int, vuchararg, __printf_render_chr, ret); | |
1195 | } | |
1196 | } | |
1197 | return ret; | |
1198 | } | |
1199 | #endif /* VECTORS */ |