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
91static u_char *__sccl(char *, u_char *);
92
93#if !defined(VFSCANF)
94#define VFSCANF	vfscanf
95#endif
96
97/*
98 * vfscanf
99 */
100int
101VFSCANF(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		 */
146again:		c = *fmt++;
147		switch (c) {
148		case '%':
149literal:
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	}
701input_failure:
702	if (nassigned == 0)
703		nassigned = -1;
704match_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 */
715static u_char *
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 */
743doswitch:
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