1 /* $OpenBSD: vfscanf.c,v 1.21 2006/01/13 21:33:28 millert Exp $ */
2 /*-
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
34 #include <ctype.h>
35 #include <inttypes.h>
36 #include <stdarg.h>
37 #include <stddef.h>
38 #include <stdio.h>
39 #include <stdlib.h>
40 #include "local.h"
41
42 #ifdef FLOATING_POINT
43 #include "floatio.h"
44 #endif
45
46 #define BUF 513 /* Maximum length of numeric string. */
47
48 /*
49 * Flags used during conversion.
50 */
51 #define LONG 0x00001 /* l: long or double */
52 #define LONGDBL 0x00002 /* L: long double; unimplemented */
53 #define SHORT 0x00004 /* h: short */
54 #define SHORTSHORT 0x00008 /* hh: 8 bit integer */
55 #define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */
56 #define POINTER 0x00020 /* p: void * (as hex) */
57 #define SIZEINT 0x00040 /* z: (signed) size_t */
58 #define MAXINT 0x00080 /* j: intmax_t */
59 #define PTRINT 0x00100 /* t: ptrdiff_t */
60 #define NOSKIP 0x00200 /* [ or c: do not skip blanks */
61 #define SUPPRESS 0x00400 /* *: suppress assignment */
62 #define UNSIGNED 0x00800 /* %[oupxX] conversions */
63
64 /*
65 * The following are used in numeric conversions only:
66 * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
67 * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
68 */
69 #define SIGNOK 0x01000 /* +/- is (still) legal */
70 #define HAVESIGN 0x02000 /* sign detected */
71 #define NDIGITS 0x04000 /* no digits detected */
72
73 #define DPTOK 0x08000 /* (float) decimal point is still legal */
74 #define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
75
76 #define PFXOK 0x08000 /* 0x prefix is (still) legal */
77 #define NZDIGITS 0x10000 /* no zero digits detected */
78
79 /*
80 * Conversion types.
81 */
82 #define CT_CHAR 0 /* %c conversion */
83 #define CT_CCL 1 /* %[...] conversion */
84 #define CT_STRING 2 /* %s conversion */
85 #define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */
86 #define CT_FLOAT 4 /* floating, i.e., strtod */
87
88 #define u_char unsigned char
89 #define u_long unsigned long
90
91 static u_char *__sccl(char *, u_char *);
92
93 #if !defined(VFSCANF)
94 #define VFSCANF vfscanf
95 #endif
96
97 /*
98 * vfscanf
99 */
100 int
VFSCANF(FILE * fp,const char * fmt0,__va_list ap)101 VFSCANF(FILE *fp, const char *fmt0, __va_list ap)
102 {
103 u_char *fmt = (u_char *)fmt0;
104 int c; /* character from format, or conversion */
105 size_t width; /* field width, or 0 */
106 char *p; /* points into all kinds of strings */
107 int n; /* handy integer */
108 int flags; /* flags as defined above */
109 char *p0; /* saves original value of p when necessary */
110 int nassigned; /* number of fields assigned */
111 int nread; /* number of characters consumed from fp */
112 int base; /* base argument to strtoimax/strtouimax */
113 char ccltab[256]; /* character class table for %[...] */
114 char buf[BUF]; /* buffer for numeric conversions */
115
116 /* `basefix' is used to avoid `if' tests in the integer scanner */
117 static short basefix[17] =
118 { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
119
120 FLOCKFILE(fp);
121 _SET_ORIENTATION(fp, -1);
122
123 nassigned = 0;
124 nread = 0;
125 base = 0; /* XXX just to keep gcc happy */
126 for (;;) {
127 c = *fmt++;
128 if (c == 0) {
129 FUNLOCKFILE(fp);
130 return (nassigned);
131 }
132 if (isspace(c)) {
133 while ((fp->_r > 0 || __srefill(fp) == 0) &&
134 isspace(*fp->_p))
135 nread++, fp->_r--, fp->_p++;
136 continue;
137 }
138 if (c != '%')
139 goto literal;
140 width = 0;
141 flags = 0;
142 /*
143 * switch on the format. continue if done;
144 * break once format type is derived.
145 */
146 again: c = *fmt++;
147 switch (c) {
148 case '%':
149 literal:
150 if (fp->_r <= 0 && __srefill(fp))
151 goto input_failure;
152 if (*fp->_p != c)
153 goto match_failure;
154 fp->_r--, fp->_p++;
155 nread++;
156 continue;
157
158 case '*':
159 flags |= SUPPRESS;
160 goto again;
161 case 'j':
162 flags |= MAXINT;
163 goto again;
164 case 'L':
165 flags |=
166 (*fmt == 'd') ? LLONG :
167 (*fmt == 'i') ? LLONG :
168 (*fmt == 'o') ? LLONG :
169 (*fmt == 'u') ? LLONG :
170 (*fmt == 'x') ? LLONG :
171 LONGDBL;
172 goto again;
173 case 'h':
174 if (*fmt == 'h') {
175 fmt++;
176 flags |= SHORTSHORT;
177 } else {
178 flags |= SHORT;
179 }
180 goto again;
181 case 'l':
182 if (*fmt == 'l') {
183 fmt++;
184 flags |= LLONG;
185 } else {
186 flags |= LONG;
187 }
188 goto again;
189 case 'q':
190 flags |= LLONG; /* deprecated */
191 goto again;
192 case 't':
193 flags |= PTRINT;
194 goto again;
195 case 'z':
196 flags |= SIZEINT;
197 goto again;
198
199 case '0': case '1': case '2': case '3': case '4':
200 case '5': case '6': case '7': case '8': case '9':
201 width = width * 10 + c - '0';
202 goto again;
203
204 /*
205 * Conversions.
206 * Those marked `compat' are for 4.[123]BSD compatibility.
207 *
208 * (According to ANSI, E and X formats are supposed
209 * to the same as e and x. Sorry about that.)
210 */
211 case 'D': /* compat */
212 flags |= LONG;
213 /* FALLTHROUGH */
214 case 'd':
215 c = CT_INT;
216 base = 10;
217 break;
218
219 case 'i':
220 c = CT_INT;
221 base = 0;
222 break;
223
224 case 'O': /* compat */
225 flags |= LONG;
226 /* FALLTHROUGH */
227 case 'o':
228 c = CT_INT;
229 flags |= UNSIGNED;
230 base = 8;
231 break;
232
233 case 'u':
234 c = CT_INT;
235 flags |= UNSIGNED;
236 base = 10;
237 break;
238
239 case 'X':
240 case 'x':
241 flags |= PFXOK; /* enable 0x prefixing */
242 c = CT_INT;
243 flags |= UNSIGNED;
244 base = 16;
245 break;
246
247 #ifdef FLOATING_POINT
248 case 'E':
249 case 'G':
250 case 'e':
251 case 'f':
252 case 'g':
253 c = CT_FLOAT;
254 break;
255 #endif
256
257 case 's':
258 c = CT_STRING;
259 break;
260
261 case '[':
262 fmt = __sccl(ccltab, fmt);
263 flags |= NOSKIP;
264 c = CT_CCL;
265 break;
266
267 case 'c':
268 flags |= NOSKIP;
269 c = CT_CHAR;
270 break;
271
272 case 'p': /* pointer format is like hex */
273 flags |= POINTER | PFXOK;
274 c = CT_INT;
275 flags |= UNSIGNED;
276 base = 16;
277 break;
278
279 case 'n':
280 if (flags & SUPPRESS)
281 continue;
282 if (flags & SHORTSHORT)
283 *va_arg(ap, __signed char *) = nread;
284 else if (flags & SHORT)
285 *va_arg(ap, short *) = nread;
286 else if (flags & LONG)
287 *va_arg(ap, long *) = nread;
288 else if (flags & SIZEINT)
289 *va_arg(ap, ssize_t *) = nread;
290 else if (flags & PTRINT)
291 *va_arg(ap, ptrdiff_t *) = nread;
292 else if (flags & LLONG)
293 *va_arg(ap, long long *) = nread;
294 else if (flags & MAXINT)
295 *va_arg(ap, intmax_t *) = nread;
296 else
297 *va_arg(ap, int *) = nread;
298 continue;
299
300 /*
301 * Disgusting backwards compatibility hacks. XXX
302 */
303 case '\0': /* compat */
304 FUNLOCKFILE(fp);
305 return (EOF);
306
307 default: /* compat */
308 if (isupper(c))
309 flags |= LONG;
310 c = CT_INT;
311 base = 10;
312 break;
313 }
314
315 /*
316 * We have a conversion that requires input.
317 */
318 if (fp->_r <= 0 && __srefill(fp))
319 goto input_failure;
320
321 /*
322 * Consume leading white space, except for formats
323 * that suppress this.
324 */
325 if ((flags & NOSKIP) == 0) {
326 while (isspace(*fp->_p)) {
327 nread++;
328 if (--fp->_r > 0)
329 fp->_p++;
330 else if (__srefill(fp))
331 goto input_failure;
332 }
333 /*
334 * Note that there is at least one character in
335 * the buffer, so conversions that do not set NOSKIP
336 * ca no longer result in an input failure.
337 */
338 }
339
340 /*
341 * Do the conversion.
342 */
343 switch (c) {
344
345 case CT_CHAR:
346 /* scan arbitrary characters (sets NOSKIP) */
347 if (width == 0)
348 width = 1;
349 if (flags & SUPPRESS) {
350 size_t sum = 0;
351 for (;;) {
352 if ((n = fp->_r) < (int)width) {
353 sum += n;
354 width -= n;
355 fp->_p += n;
356 if (__srefill(fp)) {
357 if (sum == 0)
358 goto input_failure;
359 break;
360 }
361 } else {
362 sum += width;
363 fp->_r -= width;
364 fp->_p += width;
365 break;
366 }
367 }
368 nread += sum;
369 } else {
370 size_t r = fread((void *)va_arg(ap, char *), 1,
371 width, fp);
372
373 if (r == 0)
374 goto input_failure;
375 nread += r;
376 nassigned++;
377 }
378 break;
379
380 case CT_CCL:
381 /* scan a (nonempty) character class (sets NOSKIP) */
382 if (width == 0)
383 width = (size_t)~0; /* `infinity' */
384 /* take only those things in the class */
385 if (flags & SUPPRESS) {
386 n = 0;
387 while (ccltab[*fp->_p]) {
388 n++, fp->_r--, fp->_p++;
389 if (--width == 0)
390 break;
391 if (fp->_r <= 0 && __srefill(fp)) {
392 if (n == 0)
393 goto input_failure;
394 break;
395 }
396 }
397 if (n == 0)
398 goto match_failure;
399 } else {
400 p0 = p = va_arg(ap, char *);
401 while (ccltab[*fp->_p]) {
402 fp->_r--;
403 *p++ = *fp->_p++;
404 if (--width == 0)
405 break;
406 if (fp->_r <= 0 && __srefill(fp)) {
407 if (p == p0)
408 goto input_failure;
409 break;
410 }
411 }
412 n = p - p0;
413 if (n == 0)
414 goto match_failure;
415 *p = '\0';
416 nassigned++;
417 }
418 nread += n;
419 break;
420
421 case CT_STRING:
422 /* like CCL, but zero-length string OK, & no NOSKIP */
423 if (width == 0)
424 width = (size_t)~0;
425 if (flags & SUPPRESS) {
426 n = 0;
427 while (!isspace(*fp->_p)) {
428 n++, fp->_r--, fp->_p++;
429 if (--width == 0)
430 break;
431 if (fp->_r <= 0 && __srefill(fp))
432 break;
433 }
434 nread += n;
435 } else {
436 p0 = p = va_arg(ap, char *);
437 while (!isspace(*fp->_p)) {
438 fp->_r--;
439 *p++ = *fp->_p++;
440 if (--width == 0)
441 break;
442 if (fp->_r <= 0 && __srefill(fp))
443 break;
444 }
445 *p = '\0';
446 nread += p - p0;
447 nassigned++;
448 }
449 continue;
450
451 case CT_INT:
452 /* scan an integer as if by strtoimax/strtoumax */
453 #ifdef hardway
454 if (width == 0 || width > sizeof(buf) - 1)
455 width = sizeof(buf) - 1;
456 #else
457 /* size_t is unsigned, hence this optimisation */
458 if (--width > sizeof(buf) - 2)
459 width = sizeof(buf) - 2;
460 width++;
461 #endif
462 flags |= SIGNOK | NDIGITS | NZDIGITS;
463 for (p = buf; width; width--) {
464 c = *fp->_p;
465 /*
466 * Switch on the character; `goto ok'
467 * if we accept it as a part of number.
468 */
469 switch (c) {
470
471 /*
472 * The digit 0 is always legal, but is
473 * special. For %i conversions, if no
474 * digits (zero or nonzero) have been
475 * scanned (only signs), we will have
476 * base==0. In that case, we should set
477 * it to 8 and enable 0x prefixing.
478 * Also, if we have not scanned zero digits
479 * before this, do not turn off prefixing
480 * (someone else will turn it off if we
481 * have scanned any nonzero digits).
482 */
483 case '0':
484 if (base == 0) {
485 base = 8;
486 flags |= PFXOK;
487 }
488 if (flags & NZDIGITS)
489 flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
490 else
491 flags &= ~(SIGNOK|PFXOK|NDIGITS);
492 goto ok;
493
494 /* 1 through 7 always legal */
495 case '1': case '2': case '3':
496 case '4': case '5': case '6': case '7':
497 base = basefix[base];
498 flags &= ~(SIGNOK | PFXOK | NDIGITS);
499 goto ok;
500
501 /* digits 8 and 9 ok iff decimal or hex */
502 case '8': case '9':
503 base = basefix[base];
504 if (base <= 8)
505 break; /* not legal here */
506 flags &= ~(SIGNOK | PFXOK | NDIGITS);
507 goto ok;
508
509 /* letters ok iff hex */
510 case 'A': case 'B': case 'C':
511 case 'D': case 'E': case 'F':
512 case 'a': case 'b': case 'c':
513 case 'd': case 'e': case 'f':
514 /* no need to fix base here */
515 if (base <= 10)
516 break; /* not legal here */
517 flags &= ~(SIGNOK | PFXOK | NDIGITS);
518 goto ok;
519
520 /* sign ok only as first character */
521 case '+': case '-':
522 if (flags & SIGNOK) {
523 flags &= ~SIGNOK;
524 flags |= HAVESIGN;
525 goto ok;
526 }
527 break;
528
529 /*
530 * x ok iff flag still set and 2nd char (or
531 * 3rd char if we have a sign).
532 */
533 case 'x': case 'X':
534 if ((flags & PFXOK) && p ==
535 buf + 1 + !!(flags & HAVESIGN)) {
536 base = 16; /* if %i */
537 flags &= ~PFXOK;
538 goto ok;
539 }
540 break;
541 }
542
543 /*
544 * If we got here, c is not a legal character
545 * for a number. Stop accumulating digits.
546 */
547 break;
548 ok:
549 /*
550 * c is legal: store it and look at the next.
551 */
552 *p++ = c;
553 if (--fp->_r > 0)
554 fp->_p++;
555 else if (__srefill(fp))
556 break; /* EOF */
557 }
558 /*
559 * If we had only a sign, it is no good; push
560 * back the sign. If the number ends in `x',
561 * it was [sign] '0' 'x', so push back the x
562 * and treat it as [sign] '0'.
563 */
564 if (flags & NDIGITS) {
565 if (p > buf)
566 (void) ungetc(*(u_char *)--p, fp);
567 goto match_failure;
568 }
569 c = ((u_char *)p)[-1];
570 if (c == 'x' || c == 'X') {
571 --p;
572 (void) ungetc(c, fp);
573 }
574 if ((flags & SUPPRESS) == 0) {
575 uintmax_t res;
576
577 *p = '\0';
578 if (flags & UNSIGNED)
579 res = strtoumax(buf, NULL, base);
580 else
581 res = strtoimax(buf, NULL, base);
582 if (flags & POINTER)
583 *va_arg(ap, void **) =
584 (void *)(uintptr_t)res;
585 else if (flags & MAXINT)
586 *va_arg(ap, intmax_t *) = res;
587 else if (flags & LLONG)
588 *va_arg(ap, long long *) = res;
589 else if (flags & SIZEINT)
590 *va_arg(ap, ssize_t *) = res;
591 else if (flags & PTRINT)
592 *va_arg(ap, ptrdiff_t *) = res;
593 else if (flags & LONG)
594 *va_arg(ap, long *) = res;
595 else if (flags & SHORT)
596 *va_arg(ap, short *) = res;
597 else if (flags & SHORTSHORT)
598 *va_arg(ap, __signed char *) = res;
599 else
600 *va_arg(ap, int *) = res;
601 nassigned++;
602 }
603 nread += p - buf;
604 break;
605
606 #ifdef FLOATING_POINT
607 case CT_FLOAT:
608 /* scan a floating point number as if by strtod */
609 #ifdef hardway
610 if (width == 0 || width > sizeof(buf) - 1)
611 width = sizeof(buf) - 1;
612 #else
613 /* size_t is unsigned, hence this optimisation */
614 if (--width > sizeof(buf) - 2)
615 width = sizeof(buf) - 2;
616 width++;
617 #endif
618 flags |= SIGNOK | NDIGITS | DPTOK | EXPOK;
619 for (p = buf; width; width--) {
620 c = *fp->_p;
621 /*
622 * This code mimicks the integer conversion
623 * code, but is much simpler.
624 */
625 switch (c) {
626
627 case '0': case '1': case '2': case '3':
628 case '4': case '5': case '6': case '7':
629 case '8': case '9':
630 flags &= ~(SIGNOK | NDIGITS);
631 goto fok;
632
633 case '+': case '-':
634 if (flags & SIGNOK) {
635 flags &= ~SIGNOK;
636 goto fok;
637 }
638 break;
639 case '.':
640 if (flags & DPTOK) {
641 flags &= ~(SIGNOK | DPTOK);
642 goto fok;
643 }
644 break;
645 case 'e': case 'E':
646 /* no exponent without some digits */
647 if ((flags&(NDIGITS|EXPOK)) == EXPOK) {
648 flags =
649 (flags & ~(EXPOK|DPTOK)) |
650 SIGNOK | NDIGITS;
651 goto fok;
652 }
653 break;
654 }
655 break;
656 fok:
657 *p++ = c;
658 if (--fp->_r > 0)
659 fp->_p++;
660 else if (__srefill(fp))
661 break; /* EOF */
662 }
663 /*
664 * If no digits, might be missing exponent digits
665 * (just give back the exponent) or might be missing
666 * regular digits, but had sign and/or decimal point.
667 */
668 if (flags & NDIGITS) {
669 if (flags & EXPOK) {
670 /* no digits at all */
671 while (p > buf)
672 ungetc(*(u_char *)--p, fp);
673 goto match_failure;
674 }
675 /* just a bad exponent (e and maybe sign) */
676 c = *(u_char *)--p;
677 if (c != 'e' && c != 'E') {
678 (void) ungetc(c, fp);/* sign */
679 c = *(u_char *)--p;
680 }
681 (void) ungetc(c, fp);
682 }
683 if ((flags & SUPPRESS) == 0) {
684 double res;
685
686 *p = '\0';
687 res = strtod(buf, (char **) NULL);
688 if (flags & LONGDBL)
689 *va_arg(ap, long double *) = res;
690 else if (flags & LONG)
691 *va_arg(ap, double *) = res;
692 else
693 *va_arg(ap, float *) = res;
694 nassigned++;
695 }
696 nread += p - buf;
697 break;
698 #endif /* FLOATING_POINT */
699 }
700 }
701 input_failure:
702 if (nassigned == 0)
703 nassigned = -1;
704 match_failure:
705 FUNLOCKFILE(fp);
706 return (nassigned);
707 }
708
709 /*
710 * Fill in the given table from the scanset at the given format
711 * (just after `['). Return a pointer to the character past the
712 * closing `]'. The table has a 1 wherever characters should be
713 * considered part of the scanset.
714 */
715 static u_char *
__sccl(char * tab,u_char * fmt)716 __sccl(char *tab, u_char *fmt)
717 {
718 int c, n, v;
719
720 /* first `clear' the whole table */
721 c = *fmt++; /* first char hat => negated scanset */
722 if (c == '^') {
723 v = 1; /* default => accept */
724 c = *fmt++; /* get new first char */
725 } else
726 v = 0; /* default => reject */
727 /* should probably use memset here */
728 for (n = 0; n < 256; n++)
729 tab[n] = v;
730 if (c == 0)
731 return (fmt - 1);/* format ended before closing ] */
732
733 /*
734 * Now set the entries corresponding to the actual scanset
735 * to the opposite of the above.
736 *
737 * The first character may be ']' (or '-') without being special;
738 * the last character may be '-'.
739 */
740 v = 1 - v;
741 for (;;) {
742 tab[c] = v; /* take character c */
743 doswitch:
744 n = *fmt++; /* and examine the next */
745 switch (n) {
746
747 case 0: /* format ended too soon */
748 return (fmt - 1);
749
750 case '-':
751 /*
752 * A scanset of the form
753 * [01+-]
754 * is defined as `the digit 0, the digit 1,
755 * the character +, the character -', but
756 * the effect of a scanset such as
757 * [a-zA-Z0-9]
758 * is implementation defined. The V7 Unix
759 * scanf treats `a-z' as `the letters a through
760 * z', but treats `a-a' as `the letter a, the
761 * character -, and the letter a'.
762 *
763 * For compatibility, the `-' is not considerd
764 * to define a range if the character following
765 * it is either a close bracket (required by ANSI)
766 * or is not numerically greater than the character
767 * we just stored in the table (c).
768 */
769 n = *fmt;
770 if (n == ']' || n < c) {
771 c = '-';
772 break; /* resume the for(;;) */
773 }
774 fmt++;
775 do { /* fill in the range */
776 tab[++c] = v;
777 } while (c < n);
778 #if 1 /* XXX another disgusting compatibility hack */
779 /*
780 * Alas, the V7 Unix scanf also treats formats
781 * such as [a-c-e] as `the letters a through e'.
782 * This too is permitted by the standard....
783 */
784 goto doswitch;
785 #else
786 c = *fmt++;
787 if (c == 0)
788 return (fmt - 1);
789 if (c == ']')
790 return (fmt);
791 #endif
792 break;
793
794 case ']': /* end of scanset */
795 return (fmt);
796
797 default: /* just another character */
798 c = n;
799 break;
800 }
801 }
802 /* NOTREACHED */
803 }
804