1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : * Scan functions for NSPR types
40 : *
41 : * Author: Wan-Teh Chang
42 : *
43 : * Acknowledgment: The implementation is inspired by the source code
44 : * in P.J. Plauger's "The Standard C Library," Prentice-Hall, 1992.
45 : */
46 :
47 : #include <limits.h>
48 : #include <ctype.h>
49 : #include <string.h>
50 : #include <stdlib.h>
51 : #ifdef SUNOS4
52 : #include "md/sunos4.h" /* for strtoul */
53 : #endif
54 : #include "prprf.h"
55 : #include "prdtoa.h"
56 : #include "prlog.h"
57 : #include "prerror.h"
58 :
59 : /*
60 : * A function that reads a character from 'stream'.
61 : * Returns the character read, or EOF if end of stream is reached.
62 : */
63 : typedef int (*_PRGetCharFN)(void *stream);
64 :
65 : /*
66 : * A function that pushes the character 'ch' back to 'stream'.
67 : */
68 : typedef void (*_PRUngetCharFN)(void *stream, int ch);
69 :
70 : /*
71 : * The size specifier for the integer and floating point number
72 : * conversions in format control strings.
73 : */
74 : typedef enum {
75 : _PR_size_none, /* No size specifier is given */
76 : _PR_size_h, /* The 'h' specifier, suggesting "short" */
77 : _PR_size_l, /* The 'l' specifier, suggesting "long" */
78 : _PR_size_L, /* The 'L' specifier, meaning a 'long double' */
79 : _PR_size_ll /* The 'll' specifier, suggesting "long long" */
80 : } _PRSizeSpec;
81 :
82 : /*
83 : * The collection of data that is passed between the scan function
84 : * and its subordinate functions. The fields of this structure
85 : * serve as the input or output arguments for these functions.
86 : */
87 : typedef struct {
88 : _PRGetCharFN get; /* get a character from input stream */
89 : _PRUngetCharFN unget; /* unget (push back) a character */
90 : void *stream; /* argument for get and unget */
91 : va_list ap; /* the variable argument list */
92 : int nChar; /* number of characters read from 'stream' */
93 :
94 : PRBool assign; /* assign, or suppress assignment? */
95 : int width; /* field width */
96 : _PRSizeSpec sizeSpec; /* 'h', 'l', 'L', or 'll' */
97 :
98 : PRBool converted; /* is the value actually converted? */
99 : } ScanfState;
100 :
101 : #define GET(state) ((state)->nChar++, (state)->get((state)->stream))
102 : #define UNGET(state, ch) \
103 : ((state)->nChar--, (state)->unget((state)->stream, ch))
104 :
105 : /*
106 : * The following two macros, GET_IF_WITHIN_WIDTH and WITHIN_WIDTH,
107 : * are always used together.
108 : *
109 : * GET_IF_WITHIN_WIDTH calls the GET macro and assigns its return
110 : * value to 'ch' only if we have not exceeded the field width of
111 : * 'state'. Therefore, after GET_IF_WITHIN_WIDTH, the value of
112 : * 'ch' is valid only if the macro WITHIN_WIDTH evaluates to true.
113 : */
114 :
115 : #define GET_IF_WITHIN_WIDTH(state, ch) \
116 : if (--(state)->width >= 0) { \
117 : (ch) = GET(state); \
118 : }
119 : #define WITHIN_WIDTH(state) ((state)->width >= 0)
120 :
121 : /*
122 : * _pr_strtoull:
123 : * Convert a string to an unsigned 64-bit integer. The string
124 : * 'str' is assumed to be a representation of the integer in
125 : * base 'base'.
126 : *
127 : * Warning:
128 : * - Only handle base 8, 10, and 16.
129 : * - No overflow checking.
130 : */
131 :
132 : static PRUint64
133 16862 : _pr_strtoull(const char *str, char **endptr, int base)
134 : {
135 : static const int BASE_MAX = 16;
136 : static const char digits[] = "0123456789abcdef";
137 : char *digitPtr;
138 : PRUint64 x; /* return value */
139 : PRInt64 base64;
140 : const char *cPtr;
141 : PRBool negative;
142 : const char *digitStart;
143 :
144 16862 : PR_ASSERT(base == 0 || base == 8 || base == 10 || base == 16);
145 16862 : if (base < 0 || base == 1 || base > BASE_MAX) {
146 0 : if (endptr) {
147 0 : *endptr = (char *) str;
148 0 : return LL_ZERO;
149 : }
150 : }
151 :
152 16862 : cPtr = str;
153 33724 : while (isspace(*cPtr)) {
154 0 : ++cPtr;
155 : }
156 :
157 16862 : negative = PR_FALSE;
158 16862 : if (*cPtr == '-') {
159 15 : negative = PR_TRUE;
160 15 : cPtr++;
161 16847 : } else if (*cPtr == '+') {
162 0 : cPtr++;
163 : }
164 :
165 16862 : if (base == 16) {
166 0 : if (*cPtr == '0' && (cPtr[1] == 'x' || cPtr[1] == 'X')) {
167 0 : cPtr += 2;
168 : }
169 16862 : } else if (base == 0) {
170 0 : if (*cPtr != '0') {
171 0 : base = 10;
172 0 : } else if (cPtr[1] == 'x' || cPtr[1] == 'X') {
173 0 : base = 16;
174 0 : cPtr += 2;
175 : } else {
176 0 : base = 8;
177 : }
178 : }
179 16862 : PR_ASSERT(base != 0);
180 16862 : LL_I2L(base64, base);
181 16862 : digitStart = cPtr;
182 :
183 : /* Skip leading zeros */
184 33734 : while (*cPtr == '0') {
185 10 : cPtr++;
186 : }
187 :
188 16862 : LL_I2L(x, 0);
189 119835 : while ((digitPtr = (char*)memchr(digits, tolower(*cPtr), base)) != NULL) {
190 : PRUint64 d;
191 :
192 86111 : LL_I2L(d, (digitPtr - digits));
193 86111 : LL_MUL(x, x, base64);
194 86111 : LL_ADD(x, x, d);
195 86111 : cPtr++;
196 : }
197 :
198 16862 : if (cPtr == digitStart) {
199 0 : if (endptr) {
200 0 : *endptr = (char *) str;
201 : }
202 0 : return LL_ZERO;
203 : }
204 :
205 16862 : if (negative) {
206 : #ifdef HAVE_LONG_LONG
207 : /* The cast to a signed type is to avoid a compiler warning */
208 15 : x = -(PRInt64)x;
209 : #else
210 : LL_NEG(x, x);
211 : #endif
212 : }
213 :
214 16862 : if (endptr) {
215 0 : *endptr = (char *) cPtr;
216 : }
217 16862 : return x;
218 : }
219 :
220 : /*
221 : * The maximum field width (in number of characters) that is enough
222 : * (may be more than necessary) to represent a 64-bit integer or
223 : * floating point number.
224 : */
225 : #define FMAX 31
226 : #define DECIMAL_POINT '.'
227 :
228 : static PRStatus
229 18791 : GetInt(ScanfState *state, int code)
230 : {
231 : char buf[FMAX + 1], *p;
232 : int ch;
233 : static const char digits[] = "0123456789abcdefABCDEF";
234 18791 : PRBool seenDigit = PR_FALSE;
235 : int base;
236 : int dlen;
237 :
238 18791 : switch (code) {
239 : case 'd': case 'u':
240 18791 : base = 10;
241 18791 : break;
242 : case 'i':
243 0 : base = 0;
244 0 : break;
245 : case 'x': case 'X': case 'p':
246 0 : base = 16;
247 0 : break;
248 : case 'o':
249 0 : base = 8;
250 0 : break;
251 : default:
252 0 : return PR_FAILURE;
253 : }
254 18791 : if (state->width == 0 || state->width > FMAX) {
255 18791 : state->width = FMAX;
256 : }
257 18791 : p = buf;
258 18791 : GET_IF_WITHIN_WIDTH(state, ch);
259 18791 : if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
260 15 : *p++ = ch;
261 15 : GET_IF_WITHIN_WIDTH(state, ch);
262 : }
263 18791 : if (WITHIN_WIDTH(state) && ch == '0') {
264 28 : seenDigit = PR_TRUE;
265 28 : *p++ = ch;
266 28 : GET_IF_WITHIN_WIDTH(state, ch);
267 28 : if (WITHIN_WIDTH(state)
268 28 : && (ch == 'x' || ch == 'X')
269 0 : && (base == 0 || base == 16)) {
270 0 : base = 16;
271 0 : *p++ = ch;
272 0 : GET_IF_WITHIN_WIDTH(state, ch);
273 28 : } else if (base == 0) {
274 0 : base = 8;
275 : }
276 : }
277 18791 : if (base == 0 || base == 10) {
278 18791 : dlen = 10;
279 0 : } else if (base == 8) {
280 0 : dlen = 8;
281 : } else {
282 0 : PR_ASSERT(base == 16);
283 0 : dlen = 16 + 6; /* 16 digits, plus 6 in uppercase */
284 : }
285 124785 : while (WITHIN_WIDTH(state) && memchr(digits, ch, dlen)) {
286 87203 : *p++ = ch;
287 87203 : GET_IF_WITHIN_WIDTH(state, ch);
288 87203 : seenDigit = PR_TRUE;
289 : }
290 18791 : if (WITHIN_WIDTH(state)) {
291 18791 : UNGET(state, ch);
292 : }
293 18791 : if (!seenDigit) {
294 1362 : return PR_FAILURE;
295 : }
296 17429 : *p = '\0';
297 17429 : if (state->assign) {
298 17429 : if (code == 'd' || code == 'i') {
299 34604 : if (state->sizeSpec == _PR_size_ll) {
300 16862 : PRInt64 llval = _pr_strtoull(buf, NULL, base);
301 16862 : *va_arg(state->ap, PRInt64 *) = llval;
302 : } else {
303 440 : long lval = strtol(buf, NULL, base);
304 :
305 440 : if (state->sizeSpec == _PR_size_none) {
306 440 : *va_arg(state->ap, PRIntn *) = lval;
307 0 : } else if (state->sizeSpec == _PR_size_h) {
308 0 : *va_arg(state->ap, PRInt16 *) = (PRInt16)lval;
309 0 : } else if (state->sizeSpec == _PR_size_l) {
310 0 : *va_arg(state->ap, PRInt32 *) = lval;
311 : } else {
312 0 : return PR_FAILURE;
313 : }
314 : }
315 : } else {
316 127 : if (state->sizeSpec == _PR_size_ll) {
317 0 : PRUint64 llval = _pr_strtoull(buf, NULL, base);
318 0 : *va_arg(state->ap, PRUint64 *) = llval;
319 : } else {
320 127 : unsigned long lval = strtoul(buf, NULL, base);
321 :
322 127 : if (state->sizeSpec == _PR_size_none) {
323 127 : *va_arg(state->ap, PRUintn *) = lval;
324 0 : } else if (state->sizeSpec == _PR_size_h) {
325 0 : *va_arg(state->ap, PRUint16 *) = (PRUint16)lval;
326 0 : } else if (state->sizeSpec == _PR_size_l) {
327 0 : *va_arg(state->ap, PRUint32 *) = lval;
328 : } else {
329 0 : return PR_FAILURE;
330 : }
331 : }
332 : }
333 17429 : state->converted = PR_TRUE;
334 : }
335 17429 : return PR_SUCCESS;
336 : }
337 :
338 : static PRStatus
339 0 : GetFloat(ScanfState *state)
340 : {
341 : char buf[FMAX + 1], *p;
342 : int ch;
343 0 : PRBool seenDigit = PR_FALSE;
344 :
345 0 : if (state->width == 0 || state->width > FMAX) {
346 0 : state->width = FMAX;
347 : }
348 0 : p = buf;
349 0 : GET_IF_WITHIN_WIDTH(state, ch);
350 0 : if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
351 0 : *p++ = ch;
352 0 : GET_IF_WITHIN_WIDTH(state, ch);
353 : }
354 0 : while (WITHIN_WIDTH(state) && isdigit(ch)) {
355 0 : *p++ = ch;
356 0 : GET_IF_WITHIN_WIDTH(state, ch);
357 0 : seenDigit = PR_TRUE;
358 : }
359 0 : if (WITHIN_WIDTH(state) && ch == DECIMAL_POINT) {
360 0 : *p++ = ch;
361 0 : GET_IF_WITHIN_WIDTH(state, ch);
362 0 : while (WITHIN_WIDTH(state) && isdigit(ch)) {
363 0 : *p++ = ch;
364 0 : GET_IF_WITHIN_WIDTH(state, ch);
365 0 : seenDigit = PR_TRUE;
366 : }
367 : }
368 :
369 : /*
370 : * This is not robust. For example, "1.2e+" would confuse
371 : * the code below to read 'e' and '+', only to realize that
372 : * it should have stopped at "1.2". But we can't push back
373 : * more than one character, so there is nothing I can do.
374 : */
375 :
376 : /* Parse exponent */
377 0 : if (WITHIN_WIDTH(state) && (ch == 'e' || ch == 'E') && seenDigit) {
378 0 : *p++ = ch;
379 0 : GET_IF_WITHIN_WIDTH(state, ch);
380 0 : if (WITHIN_WIDTH(state) && (ch == '+' || ch == '-')) {
381 0 : *p++ = ch;
382 0 : GET_IF_WITHIN_WIDTH(state, ch);
383 : }
384 0 : while (WITHIN_WIDTH(state) && isdigit(ch)) {
385 0 : *p++ = ch;
386 0 : GET_IF_WITHIN_WIDTH(state, ch);
387 : }
388 : }
389 0 : if (WITHIN_WIDTH(state)) {
390 0 : UNGET(state, ch);
391 : }
392 0 : if (!seenDigit) {
393 0 : return PR_FAILURE;
394 : }
395 0 : *p = '\0';
396 0 : if (state->assign) {
397 0 : PRFloat64 dval = PR_strtod(buf, NULL);
398 :
399 0 : state->converted = PR_TRUE;
400 0 : if (state->sizeSpec == _PR_size_l) {
401 0 : *va_arg(state->ap, PRFloat64 *) = dval;
402 0 : } else if (state->sizeSpec == _PR_size_L) {
403 : #if defined(OSF1) || defined(IRIX)
404 : *va_arg(state->ap, double *) = dval;
405 : #else
406 0 : *va_arg(state->ap, long double *) = dval;
407 : #endif
408 : } else {
409 0 : *va_arg(state->ap, float *) = (float) dval;
410 : }
411 : }
412 0 : return PR_SUCCESS;
413 : }
414 :
415 : /*
416 : * Convert, and return the end of the conversion spec.
417 : * Return NULL on error.
418 : */
419 :
420 : static const char *
421 18903 : Convert(ScanfState *state, const char *fmt)
422 : {
423 : const char *cPtr;
424 : int ch;
425 18903 : char *cArg = NULL;
426 :
427 18903 : state->converted = PR_FALSE;
428 18903 : cPtr = fmt;
429 18903 : if (*cPtr != 'c' && *cPtr != 'n' && *cPtr != '[') {
430 : do {
431 18811 : ch = GET(state);
432 18811 : } while (isspace(ch));
433 18791 : UNGET(state, ch);
434 : }
435 18903 : switch (*cPtr) {
436 : case 'c':
437 112 : if (state->assign) {
438 112 : cArg = va_arg(state->ap, char *);
439 : }
440 112 : if (state->width == 0) {
441 112 : state->width = 1;
442 : }
443 216 : for (; state->width > 0; state->width--) {
444 112 : ch = GET(state);
445 112 : if (ch == EOF) {
446 8 : return NULL;
447 104 : } else if (state->assign) {
448 104 : *cArg++ = ch;
449 : }
450 : }
451 104 : if (state->assign) {
452 104 : state->converted = PR_TRUE;
453 : }
454 104 : break;
455 : case 'p':
456 : case 'd': case 'i': case 'o':
457 : case 'u': case 'x': case 'X':
458 18791 : if (GetInt(state, *cPtr) == PR_FAILURE) {
459 1362 : return NULL;
460 : }
461 17429 : break;
462 : case 'e': case 'E': case 'f':
463 : case 'g': case 'G':
464 0 : if (GetFloat(state) == PR_FAILURE) {
465 0 : return NULL;
466 : }
467 0 : break;
468 : case 'n':
469 : /* do not consume any input */
470 0 : if (state->assign) {
471 0 : switch (state->sizeSpec) {
472 : case _PR_size_none:
473 0 : *va_arg(state->ap, PRIntn *) = state->nChar;
474 0 : break;
475 : case _PR_size_h:
476 0 : *va_arg(state->ap, PRInt16 *) = state->nChar;
477 0 : break;
478 : case _PR_size_l:
479 0 : *va_arg(state->ap, PRInt32 *) = state->nChar;
480 0 : break;
481 : case _PR_size_ll:
482 0 : LL_I2L(*va_arg(state->ap, PRInt64 *), state->nChar);
483 0 : break;
484 : default:
485 0 : PR_ASSERT(0);
486 : }
487 : }
488 0 : break;
489 : case 's':
490 0 : if (state->width == 0) {
491 0 : state->width = INT_MAX;
492 : }
493 0 : if (state->assign) {
494 0 : cArg = va_arg(state->ap, char *);
495 : }
496 0 : for (; state->width > 0; state->width--) {
497 0 : ch = GET(state);
498 0 : if ((ch == EOF) || isspace(ch)) {
499 0 : UNGET(state, ch);
500 0 : break;
501 : }
502 0 : if (state->assign) {
503 0 : *cArg++ = ch;
504 : }
505 : }
506 0 : if (state->assign) {
507 0 : *cArg = '\0';
508 0 : state->converted = PR_TRUE;
509 : }
510 0 : break;
511 : case '%':
512 0 : ch = GET(state);
513 0 : if (ch != '%') {
514 0 : UNGET(state, ch);
515 0 : return NULL;
516 : }
517 0 : break;
518 : case '[':
519 : {
520 0 : PRBool complement = PR_FALSE;
521 : const char *closeBracket;
522 : size_t n;
523 :
524 0 : if (*++cPtr == '^') {
525 0 : complement = PR_TRUE;
526 0 : cPtr++;
527 : }
528 0 : closeBracket = strchr(*cPtr == ']' ? cPtr + 1 : cPtr, ']');
529 0 : if (closeBracket == NULL) {
530 0 : return NULL;
531 : }
532 0 : n = closeBracket - cPtr;
533 0 : if (state->width == 0) {
534 0 : state->width = INT_MAX;
535 : }
536 0 : if (state->assign) {
537 0 : cArg = va_arg(state->ap, char *);
538 : }
539 0 : for (; state->width > 0; state->width--) {
540 0 : ch = GET(state);
541 0 : if ((ch == EOF)
542 0 : || (!complement && !memchr(cPtr, ch, n))
543 0 : || (complement && memchr(cPtr, ch, n))) {
544 0 : UNGET(state, ch);
545 0 : break;
546 : }
547 0 : if (state->assign) {
548 0 : *cArg++ = ch;
549 : }
550 : }
551 0 : if (state->assign) {
552 0 : *cArg = '\0';
553 0 : state->converted = PR_TRUE;
554 : }
555 0 : cPtr = closeBracket;
556 : }
557 0 : break;
558 : default:
559 0 : return NULL;
560 : }
561 17533 : return cPtr;
562 : }
563 :
564 : static PRInt32
565 18548 : DoScanf(ScanfState *state, const char *fmt)
566 : {
567 18548 : PRInt32 nConverted = 0;
568 : const char *cPtr;
569 : int ch;
570 :
571 18548 : state->nChar = 0;
572 18548 : cPtr = fmt;
573 : while (1) {
574 36657 : if (isspace(*cPtr)) {
575 : /* white space: skip */
576 : do {
577 0 : cPtr++;
578 0 : } while (isspace(*cPtr));
579 : do {
580 0 : ch = GET(state);
581 0 : } while (isspace(ch));
582 0 : UNGET(state, ch);
583 36657 : } else if (*cPtr == '%') {
584 : /* format spec: convert */
585 18903 : cPtr++;
586 18903 : state->assign = PR_TRUE;
587 18903 : if (*cPtr == '*') {
588 0 : cPtr++;
589 0 : state->assign = PR_FALSE;
590 : }
591 18903 : for (state->width = 0; isdigit(*cPtr); cPtr++) {
592 0 : state->width = state->width * 10 + *cPtr - '0';
593 : }
594 18903 : state->sizeSpec = _PR_size_none;
595 18903 : if (*cPtr == 'h') {
596 0 : cPtr++;
597 0 : state->sizeSpec = _PR_size_h;
598 18903 : } else if (*cPtr == 'l') {
599 17556 : cPtr++;
600 17556 : if (*cPtr == 'l') {
601 17556 : cPtr++;
602 17556 : state->sizeSpec = _PR_size_ll;
603 : } else {
604 0 : state->sizeSpec = _PR_size_l;
605 : }
606 1347 : } else if (*cPtr == 'L') {
607 0 : cPtr++;
608 0 : state->sizeSpec = _PR_size_L;
609 : }
610 18903 : cPtr = Convert(state, cPtr);
611 18903 : if (cPtr == NULL) {
612 1370 : return (nConverted > 0 ? nConverted : EOF);
613 : }
614 17533 : if (state->converted) {
615 17533 : nConverted++;
616 : }
617 17533 : cPtr++;
618 : } else {
619 : /* others: must match */
620 17754 : if (*cPtr == '\0') {
621 17148 : return nConverted;
622 : }
623 606 : ch = GET(state);
624 606 : if (ch != *cPtr) {
625 30 : UNGET(state, ch);
626 30 : return nConverted;
627 : }
628 576 : cPtr++;
629 : }
630 18109 : }
631 : }
632 :
633 : static int
634 125566 : StringGetChar(void *stream)
635 : {
636 125566 : char *cPtr = *((char **) stream);
637 :
638 125566 : if (*cPtr == '\0') {
639 17171 : return EOF;
640 : } else {
641 108395 : *((char **) stream) = cPtr + 1;
642 108395 : return (unsigned char) *cPtr;
643 : }
644 : }
645 :
646 : static void
647 37612 : StringUngetChar(void *stream, int ch)
648 : {
649 37612 : char *cPtr = *((char **) stream);
650 :
651 37612 : if (ch != EOF) {
652 20449 : *((char **) stream) = cPtr - 1;
653 : }
654 37612 : }
655 :
656 : PR_IMPLEMENT(PRInt32)
657 18548 : PR_sscanf(const char *buf, const char *fmt, ...)
658 : {
659 : PRInt32 rv;
660 : ScanfState state;
661 :
662 18548 : state.get = &StringGetChar;
663 18548 : state.unget = &StringUngetChar;
664 18548 : state.stream = (void *) &buf;
665 18548 : va_start(state.ap, fmt);
666 18548 : rv = DoScanf(&state, fmt);
667 18548 : va_end(state.ap);
668 18548 : return rv;
669 : }
|