xref: /dragonfly/lib/libc/stdio/xprintf.c (revision 0720b42f)
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.8 2008/05/05 16:03:52 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 <string.h>
47 #include <wchar.h>
48 #include "un-namespace.h"
49 
50 #include "printf.h"
51 #include "priv_stdio.h"
52 
53 int __use_xprintf = -1;
54 
55 /* private stuff -----------------------------------------------------*/
56 
57 union arg {
58 	int			intarg;
59 	long			longarg;
60 	intmax_t 		intmaxarg;
61 #ifndef NO_FLOATING_POINT
62 	double			doublearg;
63 	long double 		longdoublearg;
64 #endif
65 	wint_t			wintarg;
66 	char			*pchararg;
67 	wchar_t			*pwchararg;
68 	void			*pvoidarg;
69 };
70 
71 /*
72  * Macros for converting digits to letters and vice versa
73  */
74 #define	to_digit(c)	((c) - '0')
75 #define	is_digit(c)	(((unsigned)to_digit(c)) <= 9)
76 
77 /* various globals ---------------------------------------------------*/
78 
79 const char __lowercase_hex[17] = "0123456789abcdef?";	/*lint !e784 */
80 const char __uppercase_hex[17] = "0123456789ABCDEF?";	/*lint !e784 */
81 
82 #define PADSIZE 16
83 static char blanks[PADSIZE] =
84 	 {' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',' '};
85 static char zeroes[PADSIZE] =
86 	 {'0','0','0','0','0','0','0','0','0','0','0','0','0','0','0','0'};
87 
88 /* printing and padding functions ------------------------------------*/
89 
90 #define NIOV 8
91 
92 struct __printf_io {
93 	FILE		*fp;
94 	struct __suio	uio;
95 	struct __siov	iov[NIOV];
96 	struct __siov	*iovp;
97 };
98 
99 static void
100 __printf_init(struct __printf_io *io)
101 {
102 
103 	io->uio.uio_iov = io->iovp = &io->iov[0];
104 	io->uio.uio_resid = 0;
105 	io->uio.uio_iovcnt = 0;
106 }
107 
108 void
109 __printf_flush(struct __printf_io *io)
110 {
111 
112 	__sfvwrite(io->fp, &io->uio);
113 	__printf_init(io);
114 }
115 
116 int
117 __printf_puts(struct __printf_io *io, const void *ptr, int len)
118 {
119 
120 	if (io->fp->pub._flags & __SERR)
121 		return (0);
122 	if (len == 0)
123 		return (0);
124 	io->iovp->iov_base = __DECONST(void *, ptr);
125 	io->iovp->iov_len = len;
126 	io->uio.uio_resid += len;
127 	io->iovp++;
128 	io->uio.uio_iovcnt++;
129 	if (io->uio.uio_iovcnt >= NIOV)
130 		__printf_flush(io);
131 	return (len);
132 }
133 
134 int
135 __printf_pad(struct __printf_io *io, int howmany, int zero)
136 {
137 	int n;
138 	const char *with;
139 	int ret = 0;
140 
141 	if (zero)
142 		with = zeroes;
143 	else
144 		with = blanks;
145 
146 	if ((n = (howmany)) > 0) {
147 		while (n > PADSIZE) {
148 			ret += __printf_puts(io, with, PADSIZE);
149 			n -= PADSIZE;
150 		}
151 		ret += __printf_puts(io, with, n);
152 	}
153 	return (ret);
154 }
155 
156 int
157 __printf_out(struct __printf_io *io, const struct printf_info *pi,
158 	     const void *ptr, int len)
159 {
160 	int ret = 0;
161 
162 	if ((!pi->left) && pi->width > len)
163 		ret += __printf_pad(io, pi->width - len, pi->pad == '0');
164 	ret += __printf_puts(io, ptr, len);
165 	if (pi->left && pi->width > len)
166 		ret += __printf_pad(io, pi->width - len, pi->pad == '0');
167 	return (ret);
168 }
169 
170 
171 /* percent handling  -------------------------------------------------*/
172 
173 static int
174 __printf_arginfo_pct(const struct printf_info *pi __unused, size_t n __unused,
175 		     int *argt __unused)
176 {
177 
178 	return (0);
179 }
180 
181 static int
182 __printf_render_pct(struct __printf_io *io,
183 		    const struct printf_info *pi __unused,
184 		    const void *const *arg __unused)
185 {
186 
187 	return (__printf_puts(io, "%", 1));
188 }
189 
190 /* 'n' ---------------------------------------------------------------*/
191 
192 static int
193 __printf_arginfo_n(const struct printf_info *pi __unused, size_t n, int *argt)
194 {
195 
196 	assert(n >= 1);
197 	argt[0] = PA_POINTER;
198 	return (1);
199 }
200 
201 /*
202  * This is a printf_render so that all output has been flushed before it
203  * gets called.
204  */
205 
206 static int
207 __printf_render_n(FILE *io __unused, const struct printf_info *pi,
208 		  const void *const *arg)
209 {
210 
211 	if (pi->is_char)
212 		**((signed char **)arg[0]) = (signed char)pi->sofar;
213 	else if (pi->is_short)
214 		**((short **)arg[0]) = (short)pi->sofar;
215 	else if (pi->is_long)
216 		**((long **)arg[0]) = pi->sofar;
217 	else if (pi->is_long_double)
218 		**((long long **)arg[0]) = pi->sofar;
219 	else if (pi->is_intmax)
220 		**((intmax_t **)arg[0]) = pi->sofar;
221 	else if (pi->is_ptrdiff)
222 		**((ptrdiff_t **)arg[0]) = pi->sofar;
223 	else if (pi->is_quad)
224 		**((quad_t **)arg[0]) = pi->sofar;
225 	else if (pi->is_size)
226 		**((size_t **)arg[0]) = pi->sofar;
227 	else
228 		**((int **)arg[0]) = pi->sofar;
229 
230 	return (0);
231 }
232 
233 /* table -------------------------------------------------------------*/
234 
235 /*lint -esym(785, printf_tbl) */
236 static struct {
237 	printf_arginfo_function	*arginfo;
238 	printf_function		*gnurender;
239 	printf_render		*render;
240 } printf_tbl[256] = {
241 	['%'] = { __printf_arginfo_pct,		NULL,	__printf_render_pct },
242 	['A'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
243 	['C'] = { __printf_arginfo_chr,		NULL,	__printf_render_chr },
244 	['E'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
245 	['F'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
246 	['G'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
247 	['S'] = { __printf_arginfo_str,		NULL,	__printf_render_str },
248 	['X'] = { __printf_arginfo_int,		NULL,	__printf_render_int },
249 	['a'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
250 	['c'] = { __printf_arginfo_chr,		NULL,	__printf_render_chr },
251 	['d'] = { __printf_arginfo_int,		NULL,	__printf_render_int },
252 	['e'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
253 	['f'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
254 	['g'] = { __printf_arginfo_float,	NULL,	__printf_render_float },
255 	['i'] = { __printf_arginfo_int,		NULL,	__printf_render_int },
256 	['n'] = { __printf_arginfo_n,		__printf_render_n, NULL },
257 	['o'] = { __printf_arginfo_int,		NULL,	__printf_render_int },
258 	['p'] = { __printf_arginfo_ptr,		NULL,	__printf_render_ptr },
259 	['q'] = { __printf_arginfo_int,		NULL,	__printf_render_int },
260 	['s'] = { __printf_arginfo_str,		NULL,	__printf_render_str },
261 	['u'] = { __printf_arginfo_int,		NULL,	__printf_render_int },
262 	['x'] = { __printf_arginfo_int,		NULL,	__printf_render_int },
263 };
264 
265 
266 static int
267 __v2printf(FILE *fp, const char *fmt0, unsigned pct, va_list ap)
268 {
269 	struct printf_info	*pi, *pil;
270 	const char		*fmt;
271 	int			ch;
272 	struct printf_info	pia[pct + 10];
273 	int			argt[pct + 10];
274 	union arg		args[pct + 10];
275 	int			nextarg;
276 	int			maxarg;
277 	int			ret = 0;
278 	int			n;
279 	struct __printf_io	io;
280 
281 	__printf_init(&io);
282 	io.fp = fp;
283 
284 	fmt = fmt0;
285 	maxarg = 0;
286 	nextarg = 1;
287 	memset(argt, 0, sizeof argt);
288 	for (pi = pia; ; pi++) {
289 		memset(pi, 0, sizeof *pi);
290 		pil = pi;
291 		if (*fmt == '\0')
292 			break;
293 		pil = pi + 1;
294 		pi->prec = -1;
295 		pi->pad = ' ';
296 		pi->begin = pi->end = fmt;
297 		while (*fmt != '\0' && *fmt != '%')
298 			pi->end = ++fmt;
299 		if (*fmt == '\0')
300 			break;
301 		fmt++;
302 		for (;;) {
303 			pi->spec = *fmt;
304 			switch (pi->spec) {
305 			case ' ':
306 				/*-
307 				 * ``If the space and + flags both appear, the space
308 				 * flag will be ignored.''
309 				 *      -- ANSI X3J11
310 				 */
311 				if (pi->showsign == 0)
312 					pi->showsign = ' ';
313 				fmt++;
314 				continue;
315 			case '#':
316 				pi->alt = 1;
317 				fmt++;
318 				continue;
319 			case '.':
320 				pi->prec = 0;
321 				fmt++;
322 				if (*fmt == '*') {
323 					fmt++;
324 					pi->get_prec = nextarg;
325 					argt[nextarg++] = PA_INT;
326 					continue;
327 				}
328 				while (*fmt != '\0' && is_digit(*fmt)) {
329 					pi->prec *= 10;
330 					pi->prec += to_digit(*fmt);
331 					fmt++;
332 				}
333 				continue;
334 			case '-':
335 				pi->left = 1;
336 				fmt++;
337 				continue;
338 			case '+':
339 				pi->showsign = '+';
340 				fmt++;
341 				continue;
342 			case '*':
343 				fmt++;
344 				pi->get_width = nextarg;
345 				argt[nextarg++] = PA_INT;
346 				continue;
347 			case '%':
348 				fmt++;
349 				break;
350 			case '\'':
351 				pi->group = 1;
352 				fmt++;
353 				continue;
354 			case '0':
355 				/*-
356 				 * ``Note that 0 is taken as a flag, not as the
357 				 * beginning of a field width.''
358 				 *      -- ANSI X3J11
359 				 */
360 				pi->pad = '0';
361 				fmt++;
362 				continue;
363 			case '1': case '2': case '3':
364 			case '4': case '5': case '6':
365 			case '7': case '8': case '9':
366 				n = 0;
367 				while (*fmt != '\0' && is_digit(*fmt)) {
368 					n *= 10;
369 					n += to_digit(*fmt);
370 					fmt++;
371 				}
372 				if (*fmt == '$') {
373 					if (nextarg > maxarg)
374 						maxarg = nextarg;
375 					nextarg = n;
376 					fmt++;
377 				} else {
378 					pi->width = n;
379 				}
380 				continue;
381 			case 'D':
382 			case 'O':
383 			case 'U':
384 				pi->spec += ('a' - 'A');
385 				pi->is_intmax = 0;
386 				if (pi->is_long_double || pi->is_quad) {
387 					pi->is_long = 0;
388 					pi->is_long_double = 1;
389 				} else {
390 					pi->is_long = 1;
391 					pi->is_long_double = 0;
392 				}
393 				fmt++;
394 				break;
395 			case 'j':
396 				pi->is_intmax = 1;
397 				fmt++;
398 				continue;
399 			case 'q':
400 				pi->is_long = 0;
401 				pi->is_quad = 1;
402 				fmt++;
403 				continue;
404 			case 'L':
405 				pi->is_long_double = 1;
406 				fmt++;
407 				continue;
408 			case 'h':
409 				fmt++;
410 				if (*fmt == 'h') {
411 					fmt++;
412 					pi->is_char = 1;
413 				} else {
414 					pi->is_short = 1;
415 				}
416 				continue;
417 			case 'l':
418 				fmt++;
419 				if (*fmt == 'l') {
420 					fmt++;
421 					pi->is_long_double = 1;
422 					pi->is_quad = 0;
423 				} else {
424 					pi->is_quad = 0;
425 					pi->is_long = 1;
426 				}
427 				continue;
428 			case 't':
429 				pi->is_ptrdiff = 1;
430 				fmt++;
431 				continue;
432 			case 'z':
433 				pi->is_size = 1;
434 				fmt++;
435 				continue;
436 			default:
437 				fmt++;
438 				break;
439 			}
440 			if (printf_tbl[pi->spec].arginfo == NULL)
441 				errx(1, "arginfo[%c] = NULL", pi->spec);
442 			ch = printf_tbl[pi->spec].arginfo(
443 			    pi, __PRINTFMAXARG, &argt[nextarg]);
444 			if (ch > 0)
445 				pi->arg[0] = &args[nextarg];
446 			if (ch > 1)
447 				pi->arg[1] = &args[nextarg + 1];
448 			nextarg += ch;
449 			break;
450 		}
451 	}
452 	if (nextarg > maxarg)
453 		maxarg = nextarg;
454 #if 0
455 	fprintf(stderr, "fmt0 <%s>\n", fmt0);
456 	fprintf(stderr, "pil %p\n", pil);
457 #endif
458 	for (ch = 1; ch < maxarg; ch++) {
459 #if 0
460 		fprintf(stderr, "arg %d %x\n", ch, argt[ch]);
461 #endif
462 		switch(argt[ch]) {
463 		case PA_CHAR:
464 			args[ch].intarg = (char)va_arg(ap, int);
465 			break;
466 		case PA_INT:
467 			args[ch].intarg = va_arg(ap, int);
468 			break;
469 		case PA_INT | PA_FLAG_SHORT:
470 			args[ch].intarg = (short)va_arg(ap, int);
471 			break;
472 		case PA_INT | PA_FLAG_LONG:
473 			args[ch].longarg = va_arg(ap, long);
474 			break;
475 		case PA_INT | PA_FLAG_INTMAX:
476 			args[ch].intmaxarg = va_arg(ap, intmax_t);
477 			break;
478 		case PA_INT | PA_FLAG_QUAD:
479 			args[ch].intmaxarg = va_arg(ap, quad_t);
480 			break;
481 		case PA_INT | PA_FLAG_LONG_LONG:
482 			args[ch].intmaxarg = va_arg(ap, long long);
483 			break;
484 		case PA_INT | PA_FLAG_SIZE:
485 			args[ch].intmaxarg = va_arg(ap, size_t);
486 			break;
487 		case PA_INT | PA_FLAG_PTRDIFF:
488 			args[ch].intmaxarg = va_arg(ap, ptrdiff_t);
489 			break;
490 		case PA_WCHAR:
491 			args[ch].wintarg = va_arg(ap, wint_t);
492 			break;
493 		case PA_POINTER:
494 			args[ch].pvoidarg = va_arg(ap, void *);
495 			break;
496 		case PA_STRING:
497 			args[ch].pchararg = va_arg(ap, char *);
498 			break;
499 		case PA_WSTRING:
500 			args[ch].pwchararg = va_arg(ap, wchar_t *);
501 			break;
502 		case PA_DOUBLE:
503 #ifndef NO_FLOATING_POINT
504 			args[ch].doublearg = va_arg(ap, double);
505 #endif
506 			break;
507 		case PA_DOUBLE | PA_FLAG_LONG_DOUBLE:
508 #ifndef NO_FLOATING_POINT
509 			args[ch].longdoublearg = va_arg(ap, long double);
510 #endif
511 			break;
512 		default:
513 			errx(1, "argtype = %x (fmt = \"%s\")\n",
514 			    argt[ch], fmt0);
515 		}
516 	}
517 	for (pi = pia; pi < pil; pi++) {
518 #if 0
519 		fprintf(stderr, "pi %p", pi);
520 		fprintf(stderr, " spec '%c'", pi->spec);
521 		fprintf(stderr, " args %d",
522 		    ((uintptr_t)pi->arg[0] - (uintptr_t)args) / sizeof args[0]);
523 		if (pi->width) fprintf(stderr, " width %d", pi->width);
524 		if (pi->pad) fprintf(stderr, " pad 0x%x", pi->pad);
525 		if (pi->left) fprintf(stderr, " left");
526 		if (pi->showsign) fprintf(stderr, " showsign");
527 		if (pi->prec != -1) fprintf(stderr, " prec %d", pi->prec);
528 		if (pi->is_char) fprintf(stderr, " char");
529 		if (pi->is_short) fprintf(stderr, " short");
530 		if (pi->is_long) fprintf(stderr, " long");
531 		if (pi->is_long_double) fprintf(stderr, " long_double");
532 		fprintf(stderr, "\n");
533 		fprintf(stderr, "\t\"%.*s\"\n", pi->end - pi->begin, pi->begin);
534 #endif
535 		if (pi->get_width) {
536 			pi->width = args[pi->get_width].intarg;
537 			/*-
538 			 * ``A negative field width argument is taken as a
539 			 * - flag followed by a positive field width.''
540 			 *      -- ANSI X3J11
541 			 * They don't exclude field widths read from args.
542 			 */
543 			if (pi->width < 0) {
544 				pi->left = 1;
545 				pi->width = -pi->width;
546 			}
547 		}
548 		if (pi->get_prec)
549 			pi->prec = args[pi->get_prec].intarg;
550 		ret += __printf_puts(&io, pi->begin, pi->end - pi->begin);
551 		if (printf_tbl[pi->spec].gnurender != NULL) {
552 			__printf_flush(&io);
553 			pi->sofar = ret;
554 			ret += printf_tbl[pi->spec].gnurender(
555 			    fp, pi, (const void *)pi->arg);
556 		} else if (printf_tbl[pi->spec].render != NULL) {
557 			pi->sofar = ret;
558 			n = printf_tbl[pi->spec].render(
559 			    &io, pi, (const void *)pi->arg);
560 			if (n < 0)
561 				io.fp->pub._flags |= __SERR;
562 			else
563 				ret += n;
564 		} else if (pi->begin == pi->end)
565 			errx(1, "render[%c] = NULL", *fmt);
566 	}
567 	__printf_flush(&io);
568 	return (ret);
569 }
570 
571 extern int	__fflush(FILE *fp);
572 
573 /*
574  * Helper function for `fprintf to unbuffered unix file': creates a
575  * temporary buffer.  We only work on write-only files; this avoids
576  * worries about ungetc buffers and so forth.
577  */
578 static int
579 __v3printf(FILE *fp, const char *fmt, int pct, va_list ap)
580 {
581 	int ret;
582 	FILE fake;
583 	unsigned char buf[BUFSIZ];
584 
585 	/* copy the important variables */
586 	fake.pub._flags = fp->pub._flags & ~__SNBF;
587 	fake.pub._fileno = fp->pub._fileno;
588 	fake._cookie = fp->_cookie;
589 	fake._write = fp->_write;
590 	memcpy(WCIO_GET(&fake), WCIO_GET(fp), sizeof(struct wchar_io_data));
591 
592 	/* set up the buffer */
593 	fake._bf._base = fake.pub._p = buf;
594 	fake._bf._size = fake.pub._w = sizeof(buf);
595 	fake.pub._lbfsize = 0;	/* not actually used, but Just In Case */
596 
597 	/* do the work, then copy any error status */
598 	ret = __v2printf(&fake, fmt, pct, ap);
599 	if (ret >= 0 && __fflush(&fake))
600 		ret = EOF;
601 	if (fake.pub._flags & __SERR)
602 		fp->pub._flags |= __SERR;
603 	return (ret);
604 }
605 
606 int
607 __xvprintf(FILE *fp, const char *fmt0, va_list ap)
608 {
609 	unsigned u;
610 	const char *p;
611 
612 	/* Count number of '%' signs handling double '%' signs */
613 	for (p = fmt0, u = 0; *p; p++) {
614 		if (*p != '%')
615 			continue;
616 		u++;
617 		if (p[1] == '%')
618 			p++;
619 	}
620 
621 	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
622 	if ((fp->pub._flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
623 	    fp->pub._fileno >= 0)
624 		return (__v3printf(fp, fmt0, u, ap));
625 	else
626 		return (__v2printf(fp, fmt0, u, ap));
627 }
628 
629 /* extending ---------------------------------------------------------*/
630 
631 int
632 register_printf_function(int spec, printf_function *render,
633 			 printf_arginfo_function *arginfo)
634 {
635 
636 	if (spec > 255 || spec < 0)
637 		return (-1);
638 	printf_tbl[spec].gnurender = render;
639 	printf_tbl[spec].arginfo = arginfo;
640 	__use_xprintf = 1;
641 	return (0);
642 }
643 
644 int
645 register_printf_render(int spec, printf_render *render,
646 		       printf_arginfo_function *arginfo)
647 {
648 
649 	if (spec > 255 || spec < 0)
650 		return (-1);
651 	printf_tbl[spec].render = render;
652 	printf_tbl[spec].arginfo = arginfo;
653 	__use_xprintf = 1;
654 	return (0);
655 }
656 
657 int
658 register_printf_render_std(const unsigned char *specs)
659 {
660 
661 	for (; *specs != '\0'; specs++) {
662 		switch (*specs) {
663 		case 'H':
664 			register_printf_render(*specs,
665 			    __printf_render_hexdump,
666 			    __printf_arginfo_hexdump);
667 			break;
668 		case 'M':
669 			register_printf_render(*specs,
670 			    __printf_render_errno,
671 			    __printf_arginfo_errno);
672 			break;
673 		case 'Q':
674 			register_printf_render(*specs,
675 			    __printf_render_quote,
676 			    __printf_arginfo_quote);
677 			break;
678 		case 'T':
679 			register_printf_render(*specs,
680 			    __printf_render_time,
681 			    __printf_arginfo_time);
682 			break;
683 		case 'V':
684 			register_printf_render(*specs,
685 			    __printf_render_vis,
686 			    __printf_arginfo_vis);
687 			break;
688 		default:
689 			return (-1);
690 		}
691 	}
692 	return (0);
693 }
694