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