]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - lib/libc/stdio/xprintf.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / lib / libc / stdio / xprintf.c
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$
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