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