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