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