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