Home | History | Annotate | Download | only in Stdio
      1 /** @file
      2     Implementation of scanf internals for <stdio.h>.
      3 
      4     Copyright (c) 2010 - 2014, Intel Corporation. All rights reserved.<BR>
      5     This program and the accompanying materials are licensed and made available
      6     under the terms and conditions of the BSD License that accompanies this
      7     distribution.  The full text of the license may be found at
      8     http://opensource.org/licenses/bsd-license.
      9 
     10     THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     11     WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     12 
     13     Copyright (c) 1990, 1993
     14     The Regents of the University of California.  All rights reserved.
     15 
     16     This code is derived from software contributed to Berkeley by
     17     Chris Torek.
     18 
     19     Redistribution and use in source and binary forms, with or without
     20     modification, are permitted provided that the following conditions
     21     are met:
     22       - Redistributions of source code must retain the above copyright
     23         notice, this list of conditions and the following disclaimer.
     24       - Redistributions in binary form must reproduce the above copyright
     25         notice, this list of conditions and the following disclaimer in the
     26         documentation and/or other materials provided with the distribution.
     27       - Neither the name of the University nor the names of its contributors
     28         may be used to endorse or promote products derived from this software
     29         without specific prior written permission.
     30 
     31     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     32     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     33     IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     34     ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
     35     LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     36     CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     37     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     38     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     39     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     40     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     41     POSSIBILITY OF SUCH DAMAGE.
     42 
     43     NetBSD: vfscanf.c,v 1.37.4.1 2007/05/07 19:49:08 pavel Exp
     44     FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.41 2007/01/09 00:28:07 imp Exp
     45     vfscanf.c 8.1 (Berkeley) 6/4/93
     46 **/
     47 #include  <LibConfig.h>
     48 
     49 #include "namespace.h"
     50 #include <assert.h>
     51 #include <ctype.h>
     52 #include  <errno.h>
     53 #include <inttypes.h>
     54 #include <stdio.h>
     55 #include <stdlib.h>
     56 #include <stddef.h>
     57 #include <stdarg.h>
     58 #include <string.h>
     59 #include  <sys/types.h>
     60 #include <wchar.h>
     61 #include <wctype.h>
     62 
     63 #include "reentrant.h"
     64 #include "local.h"
     65 
     66 #ifndef NO_FLOATING_POINT
     67 #include <locale.h>
     68 #endif
     69 
     70 /*
     71  * Provide an external name for vfscanf.  Note, EFI uses the normal
     72  * namespace.h method; stdio routines explicitly use the internal name
     73  * __svfscanf.
     74  */
     75 #ifdef __weak_alias
     76 __weak_alias(vfscanf,__svfscanf)
     77 #endif
     78 
     79 #define BUF   513 /* Maximum length of numeric string. */
     80 
     81 /*
     82  * Flags used during conversion.
     83  */
     84 #define LONG        0x0001  /* l: long or double */
     85 #define LONGDBL     0x0002  /* L: long double */
     86 #define SHORT       0x0004  /* h: short */
     87 #define SUPPRESS    0x0008  /* *: suppress assignment */
     88 #define POINTER     0x0010  /* p: void * (as hex) */
     89 #define NOSKIP      0x0020  /* [ or c: do not skip blanks */
     90 #define LONGLONG    0x0400  /* ll: long long (+ deprecated q: quad) */
     91 #define INTMAXT     0x0800  /* j: intmax_t */
     92 #define PTRDIFFT    0x1000  /* t: ptrdiff_t */
     93 #define SIZET       0x2000  /* z: size_t */
     94 #define SHORTSHORT  0x4000  /* hh: char */
     95 #define UNSIGNED    0x8000  /* %[oupxX] conversions */
     96 
     97 /*
     98  * The following are used in integral conversions only:
     99  * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
    100  */
    101 #define SIGNOK      0x00040 /* +/- is (still) legal */
    102 #define NDIGITS     0x00080 /* no digits detected */
    103 #define PFXOK       0x00100 /* 0x prefix is (still) legal */
    104 #define NZDIGITS    0x00200 /* no zero digits detected */
    105 #define HAVESIGN    0x10000 /* sign detected */
    106 
    107 /*
    108  * Conversion types.
    109  */
    110 #define CT_CHAR     0 /* %c conversion */
    111 #define CT_CCL      1 /* %[...] conversion */
    112 #define CT_STRING   2 /* %s conversion */
    113 #define CT_INT      3 /* %[dioupxX] conversion */
    114 #define CT_FLOAT    4 /* %[efgEFG] conversion */
    115 
    116 static const u_char  *__sccl(char *, const u_char *);
    117 #ifndef NO_FLOATING_POINT
    118   static int            parsefloat(FILE *, char *, char *);
    119 #endif
    120 
    121 int __scanfdebug = 0;
    122 
    123 #define __collate_load_error /*CONSTCOND*/0
    124 static int
    125 __collate_range_cmp(int c1, int c2)
    126 {
    127   static char s1[2] = { 0 };
    128   static char s2[2] = { 0 };
    129 
    130   s1[0] = (char)c1;
    131   s2[0] = (char)c2;
    132   return strcoll(s1, s2);
    133 }
    134 
    135 
    136 /*
    137  * __svfscanf - MT-safe version
    138  */
    139 int
    140 __svfscanf(FILE *fp, char const *fmt0, va_list ap)
    141 {
    142   int ret;
    143 
    144   if(fp == NULL) {
    145     errno = EINVAL;
    146     return (EOF);
    147   }
    148   FLOCKFILE(fp);
    149   ret = __svfscanf_unlocked(fp, fmt0, ap);
    150   FUNLOCKFILE(fp);
    151   return (ret);
    152 }
    153 
    154 /*
    155  * __svfscanf_unlocked - non-MT-safe version of __svfscanf
    156  */
    157 int
    158 __svfscanf_unlocked(FILE *fp, const char *fmt0, va_list ap)
    159 {
    160           const u_char     *fmt     = (const u_char *)fmt0;
    161                 int         c;              /* character from format, or conversion */
    162                 size_t      width;          /* field width, or 0 */
    163                 char       *p;              /* points into all kinds of strings */
    164                 size_t      n;              /* handy size_t */
    165                 int         flags;          /* flags as defined above */
    166                 char       *p0;             /* saves original value of p when necessary */
    167                 int         nassigned;      /* number of fields assigned */
    168                 int         nconversions;   /* number of conversions */
    169                 int         nread;          /* number of characters consumed from fp */
    170                 int         base;           /* base argument to conversion function */
    171                 char        ccltab[256];    /* character class table for %[...] */
    172                 char        buf[BUF];       /* buffer for numeric and mb conversions */
    173                 wchar_t    *wcp;            /* handy wide character pointer */
    174                 size_t      nconv;          /* length of multibyte sequence converted */
    175   static const  mbstate_t   initial = { 0 };
    176                 mbstate_t   mbs;
    177 
    178   /* `basefix' is used to avoid `if' tests in the integer scanner */
    179   static const short basefix[17] =
    180     { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
    181 
    182   _DIAGASSERT(fp != NULL);
    183   _DIAGASSERT(fmt0 != NULL);
    184   if(fp == NULL) {
    185     errno = EINVAL;
    186     return (EOF);
    187   }
    188 
    189   _SET_ORIENTATION(fp, -1);
    190 
    191 //Print(L"%a( %d, \"%a\", ...)\n", __func__, fp->_file, fmt0);
    192   nassigned = 0;
    193   nconversions = 0;
    194   nread = 0;
    195   base = 0;
    196   for (;;) {
    197     c = (unsigned char)*fmt++;
    198     if (c == 0)
    199       return (nassigned);
    200     if (isspace(c)) {
    201       while ((fp->_r > 0 || __srefill(fp) == 0) &&
    202           isspace(*fp->_p))
    203         nread++, fp->_r--, fp->_p++;
    204       continue;
    205     }
    206 //Print(L"%a: %d\n", __func__, __LINE__);
    207     if (c != '%')
    208       goto literal;
    209     width = 0;
    210     flags = 0;
    211     /*
    212      * switch on the format.  continue if done;
    213      * break once format type is derived.
    214      */
    215 again:    c = *fmt++;
    216 //Print(L"%a: %d\n", __func__, __LINE__);
    217     switch (c) {
    218     case '%':
    219 literal:
    220 //Print(L"%a: %d\n", __func__, __LINE__);
    221       if (fp->_r <= 0 && __srefill(fp))
    222         goto input_failure;
    223       if (*fp->_p != c)
    224         goto match_failure;
    225       fp->_r--, fp->_p++;
    226       nread++;
    227       continue;
    228 
    229     case '*':
    230       flags |= SUPPRESS;
    231       goto again;
    232     case 'j':
    233       flags |= INTMAXT;
    234       goto again;
    235     case 'l':
    236       if (flags & LONG) {
    237         flags &= ~LONG;
    238         flags |= LONGLONG;
    239       } else
    240         flags |= LONG;
    241       goto again;
    242     case 'q':
    243       flags |= LONGLONG;  /* not quite */
    244       goto again;
    245     case 't':
    246       flags |= PTRDIFFT;
    247       goto again;
    248     case 'z':
    249       flags |= SIZET;
    250       goto again;
    251     case 'L':
    252       flags |= LONGDBL;
    253       goto again;
    254     case 'h':
    255       if (flags & SHORT) {
    256         flags &= ~SHORT;
    257         flags |= SHORTSHORT;
    258       } else
    259         flags |= SHORT;
    260       goto again;
    261 
    262     case '0': case '1': case '2': case '3': case '4':
    263     case '5': case '6': case '7': case '8': case '9':
    264       width = width * 10 + c - '0';
    265       goto again;
    266 
    267     /*
    268      * Conversions.
    269      */
    270     case 'd':
    271       c = CT_INT;
    272       base = 10;
    273       break;
    274 
    275     case 'i':
    276       c = CT_INT;
    277       base = 0;
    278       break;
    279 
    280     case 'o':
    281       c = CT_INT;
    282       flags |= UNSIGNED;
    283       base = 8;
    284       break;
    285 
    286     case 'u':
    287       c = CT_INT;
    288       flags |= UNSIGNED;
    289       base = 10;
    290       break;
    291 
    292     case 'X':
    293     case 'x':
    294       flags |= PFXOK; /* enable 0x prefixing */
    295       c = CT_INT;
    296       flags |= UNSIGNED;
    297       base = 16;
    298       break;
    299 
    300 #ifndef NO_FLOATING_POINT
    301     case 'A': case 'E': case 'F': case 'G':
    302     case 'a': case 'e': case 'f': case 'g':
    303       c = CT_FLOAT;
    304       break;
    305 #endif
    306 
    307     case 'S':
    308       flags |= LONG;
    309       /* FALLTHROUGH */
    310     case 's':
    311       c = CT_STRING;
    312       break;
    313 
    314     case '[':
    315       fmt = __sccl(ccltab, fmt);
    316       flags |= NOSKIP;
    317       c = CT_CCL;
    318       break;
    319 
    320     case 'C':
    321       flags |= LONG;
    322       /* FALLTHROUGH */
    323     case 'c':
    324       flags |= NOSKIP;
    325       c = CT_CHAR;
    326       break;
    327 
    328     case 'p': /* pointer format is like hex */
    329       flags |= POINTER | PFXOK;
    330       c = CT_INT;   /* assumes sizeof(uintmax_t) */
    331       flags |= UNSIGNED;  /*      >= sizeof(uintptr_t) */
    332       base = 16;
    333       break;
    334 
    335     case 'n':
    336       nconversions++;
    337       if (flags & SUPPRESS) /* ??? */
    338         continue;
    339       if (flags & SHORTSHORT)
    340         *va_arg(ap, char *) = (char)nread;
    341       else if (flags & SHORT)
    342         *va_arg(ap, short *) = (short)nread;
    343       else if (flags & LONG)
    344         *va_arg(ap, long *) = nread;
    345       else if (flags & LONGLONG)
    346         *va_arg(ap, long long *) = nread;
    347       else if (flags & INTMAXT)
    348         *va_arg(ap, intmax_t *) = nread;
    349       else if (flags & SIZET)
    350         *va_arg(ap, size_t *) = nread;
    351       else if (flags & PTRDIFFT)
    352         *va_arg(ap, ptrdiff_t *) = nread;
    353       else
    354         *va_arg(ap, int *) = nread;
    355       continue;
    356 
    357     default:
    358       goto match_failure;
    359 
    360     /*
    361      * Disgusting backwards compatibility hack. XXX
    362      */
    363     case '\0':  /* compat */
    364       return (EOF);
    365     }
    366 //Print(L"%a: %d\n", __func__, __LINE__);
    367 
    368     /*
    369      * We have a conversion that requires input.
    370      */
    371     if (fp->_r <= 0 && __srefill(fp))
    372     {
    373 //Print(L"%a: %d\n", __func__, __LINE__);
    374       goto input_failure;
    375     }
    376 
    377     /*
    378      * Consume leading white space, except for formats
    379      * that suppress this.
    380      */
    381     if ((flags & NOSKIP) == 0) {
    382       while (isspace(*fp->_p)) {
    383         nread++;
    384         if (--fp->_r > 0)
    385           fp->_p++;
    386         else if (__srefill(fp))
    387         {
    388 //Print(L"%a: %d\n", __func__, __LINE__);
    389           goto input_failure;
    390         }
    391       }
    392       /*
    393        * Note that there is at least one character in
    394        * the buffer, so conversions that do not set NOSKIP
    395        * ca no longer result in an input failure.
    396        */
    397     }
    398 
    399     /*
    400      * Do the conversion.
    401      */
    402 //Print(L"%a: %d\n", __func__, __LINE__);
    403     switch (c) {
    404 
    405     case CT_CHAR:
    406       /* scan arbitrary characters (sets NOSKIP) */
    407       if (width == 0)
    408         width = 1;
    409       if (flags & LONG) {
    410         if ((flags & SUPPRESS) == 0)
    411           wcp = va_arg(ap, wchar_t *);
    412         else
    413           wcp = NULL;
    414         n = 0;
    415         while (width != 0) {
    416           if (n == MB_CUR_MAX) {
    417             fp->_flags |= __SERR;
    418             goto input_failure;
    419           }
    420           buf[n++] = *fp->_p;
    421           fp->_p++;
    422           fp->_r--;
    423           mbs = initial;
    424           nconv = mbrtowc(wcp, buf, n, &mbs);
    425           if (nconv == (size_t)-1) {
    426             fp->_flags |= __SERR;
    427             goto input_failure;
    428           }
    429           if (nconv == 0 && !(flags & SUPPRESS))
    430             *wcp = L'\0';
    431           if (nconv != (size_t)-2) {
    432             nread += (int)n;
    433             width--;
    434             if (!(flags & SUPPRESS))
    435               wcp++;
    436             n = 0;
    437           }
    438           if (fp->_r <= 0 && __srefill(fp)) {
    439             if (n != 0) {
    440               fp->_flags |= __SERR;
    441               goto input_failure;
    442             }
    443             break;
    444           }
    445         }
    446         if (!(flags & SUPPRESS))
    447           nassigned++;
    448       } else if (flags & SUPPRESS) {
    449         size_t sum = 0;
    450         for (;;) {
    451           if ((n = fp->_r) < width) {
    452             sum += n;
    453             width -= n;
    454             fp->_p += n;
    455             if (__srefill(fp)) {
    456               if (sum == 0)
    457                   goto input_failure;
    458               break;
    459             }
    460           } else {
    461             sum += width;
    462             fp->_r -= (int)width;
    463             fp->_p += width;
    464             break;
    465           }
    466         }
    467         nread += (int)sum;
    468       } else {
    469         size_t r = fread(va_arg(ap, char *), 1,
    470             width, fp);
    471 
    472         if (r == 0)
    473           goto input_failure;
    474         nread += (int)r;
    475         nassigned++;
    476       }
    477       nconversions++;
    478       break;
    479 
    480     case CT_CCL:
    481       /* scan a (nonempty) character class (sets NOSKIP) */
    482       if (width == 0)
    483         width = (size_t)~0; /* `infinity' */
    484       /* take only those things in the class */
    485       if (flags & LONG) {
    486         wchar_t twc;
    487         int nchars;
    488 
    489         if ((flags & SUPPRESS) == 0)
    490           wcp = va_arg(ap, wchar_t *);
    491         else
    492           wcp = &twc;
    493         n = 0;
    494         nchars = 0;
    495         while (width != 0) {
    496           if (n == MB_CUR_MAX) {
    497             fp->_flags |= __SERR;
    498             goto input_failure;
    499           }
    500           buf[n++] = *fp->_p;
    501           fp->_p++;
    502           fp->_r--;
    503           mbs = initial;
    504           nconv = mbrtowc(wcp, buf, n, &mbs);
    505           if (nconv == (size_t)-1) {
    506             fp->_flags |= __SERR;
    507             goto input_failure;
    508           }
    509           if (nconv == 0)
    510             *wcp = L'\0';
    511           if (nconv != (size_t)-2) {
    512             if (wctob(*wcp) != EOF &&
    513                 !ccltab[wctob(*wcp)]) {
    514               while (n != 0) {
    515                 n--;
    516                 (void)ungetc(buf[n],
    517                     fp);
    518               }
    519               break;
    520             }
    521             nread += (int)n;
    522             width--;
    523             if (!(flags & SUPPRESS))
    524               wcp++;
    525             nchars++;
    526             n = 0;
    527           }
    528           if (fp->_r <= 0 && __srefill(fp)) {
    529             if (n != 0) {
    530               fp->_flags |= __SERR;
    531               goto input_failure;
    532             }
    533             break;
    534           }
    535         }
    536         if (n != 0) {
    537           fp->_flags |= __SERR;
    538           goto input_failure;
    539         }
    540         n = nchars;
    541         if (n == 0)
    542           goto match_failure;
    543         if (!(flags & SUPPRESS)) {
    544           *wcp = L'\0';
    545           nassigned++;
    546         }
    547       } else if (flags & SUPPRESS) {
    548         n = 0;
    549         while (ccltab[*fp->_p]) {
    550           n++, fp->_r--, fp->_p++;
    551           if (--width == 0)
    552             break;
    553           if (fp->_r <= 0 && __srefill(fp)) {
    554             if (n == 0)
    555               goto input_failure;
    556             break;
    557           }
    558         }
    559         if (n == 0)
    560           goto match_failure;
    561       } else {
    562         p0 = p = va_arg(ap, char *);
    563         while (ccltab[*fp->_p]) {
    564           fp->_r--;
    565           *p++ = *fp->_p++;
    566           if (--width == 0)
    567             break;
    568           if (fp->_r <= 0 && __srefill(fp)) {
    569             if (p == p0)
    570               goto input_failure;
    571             break;
    572           }
    573         }
    574         n = p - p0;
    575         if (n == 0)
    576           goto match_failure;
    577         *p = 0;
    578         nassigned++;
    579       }
    580       nread += (int)n;
    581       nconversions++;
    582       break;
    583 
    584     case CT_STRING:
    585       /* like CCL, but zero-length string OK, & no NOSKIP */
    586       if (width == 0)
    587         width = (size_t)~0;
    588       if (flags & LONG) {
    589         wchar_t twc;
    590 
    591         if ((flags & SUPPRESS) == 0)
    592           wcp = va_arg(ap, wchar_t *);
    593         else
    594           wcp = &twc;
    595         n = 0;
    596         while (!isspace(*fp->_p) && width != 0) {
    597           if (n == MB_CUR_MAX) {
    598             fp->_flags |= __SERR;
    599             goto input_failure;
    600           }
    601           buf[n++] = *fp->_p;
    602           fp->_p++;
    603           fp->_r--;
    604           mbs = initial;
    605           nconv = mbrtowc(wcp, buf, n, &mbs);
    606           if (nconv == (size_t)-1) {
    607             fp->_flags |= __SERR;
    608             goto input_failure;
    609           }
    610           if (nconv == 0)
    611             *wcp = L'\0';
    612           if (nconv != (size_t)-2) {
    613             if (iswspace(*wcp)) {
    614               while (n != 0) {
    615                 n--;
    616                 (void)ungetc(buf[n],
    617                     fp);
    618               }
    619               break;
    620             }
    621             nread += (int)n;
    622             width--;
    623             if (!(flags & SUPPRESS))
    624               wcp++;
    625             n = 0;
    626           }
    627           if (fp->_r <= 0 && __srefill(fp)) {
    628             if (n != 0) {
    629               fp->_flags |= __SERR;
    630               goto input_failure;
    631             }
    632             break;
    633           }
    634         }
    635         if (!(flags & SUPPRESS)) {
    636           *wcp = L'\0';
    637           nassigned++;
    638         }
    639       } else if (flags & SUPPRESS) {
    640         n = 0;
    641         while (!isspace(*fp->_p)) {
    642           n++, fp->_r--, fp->_p++;
    643           if (--width == 0)
    644             break;
    645           if (fp->_r <= 0 && __srefill(fp))
    646             break;
    647         }
    648         nread += (int)n;
    649       } else {
    650         p0 = p = va_arg(ap, char *);
    651         while (!isspace(*fp->_p)) {
    652           fp->_r--;
    653           *p++ = *fp->_p++;
    654           if (--width == 0)
    655             break;
    656           if (fp->_r <= 0 && __srefill(fp))
    657             break;
    658         }
    659         *p = 0;
    660         nread += (int)(p - p0);
    661         nassigned++;
    662       }
    663       nconversions++;
    664       continue;
    665 
    666     case CT_INT:
    667 //Print(L"%a: %d\n", __func__, __LINE__);
    668       /* scan an integer as if by the conversion function */
    669 #ifdef hardway
    670       if (width == 0 || width > sizeof(buf) - 1)
    671         width = sizeof(buf) - 1;
    672 #else
    673       /* size_t is unsigned, hence this optimisation */
    674       if (--width > sizeof(buf) - 2)
    675         width = sizeof(buf) - 2;
    676       width++;
    677 #endif
    678       flags |= SIGNOK | NDIGITS | NZDIGITS;
    679       for (p = buf; width; width--) {
    680         c = *fp->_p;
    681         /*
    682          * Switch on the character; `goto ok'
    683          * if we accept it as a part of number.
    684          */
    685         switch (c) {
    686 
    687         /*
    688          * The digit 0 is always legal, but is
    689          * special.  For %i conversions, if no
    690          * digits (zero or nonzero) have been
    691          * scanned (only signs), we will have
    692          * base==0.  In that case, we should set
    693          * it to 8 and enable 0x prefixing.
    694          * Also, if we have not scanned zero digits
    695          * before this, do not turn off prefixing
    696          * (someone else will turn it off if we
    697          * have scanned any nonzero digits).
    698          */
    699         case '0':
    700           if (base == 0) {
    701             base = 8;
    702             flags |= PFXOK;
    703           }
    704           if (flags & NZDIGITS)
    705               flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
    706           else
    707               flags &= ~(SIGNOK|PFXOK|NDIGITS);
    708           goto ok;
    709 
    710         /* 1 through 7 always legal */
    711         case '1': case '2': case '3':
    712         case '4': case '5': case '6': case '7':
    713           base = basefix[base];
    714           flags &= ~(SIGNOK | PFXOK | NDIGITS);
    715           goto ok;
    716 
    717         /* digits 8 and 9 ok iff decimal or hex */
    718         case '8': case '9':
    719           base = basefix[base];
    720           if (base <= 8)
    721             break;  /* not legal here */
    722           flags &= ~(SIGNOK | PFXOK | NDIGITS);
    723           goto ok;
    724 
    725         /* letters ok iff hex */
    726         case 'A': case 'B': case 'C':
    727         case 'D': case 'E': case 'F':
    728         case 'a': case 'b': case 'c':
    729         case 'd': case 'e': case 'f':
    730           /* no need to fix base here */
    731           if (base <= 10)
    732             break;  /* not legal here */
    733           flags &= ~(SIGNOK | PFXOK | NDIGITS);
    734           goto ok;
    735 
    736         /* sign ok only as first character */
    737         case '+': case '-':
    738           if (flags & SIGNOK) {
    739             flags &= ~SIGNOK;
    740             flags |= HAVESIGN;
    741             goto ok;
    742           }
    743           break;
    744 
    745         /*
    746          * x ok iff flag still set & 2nd char (or
    747          * 3rd char if we have a sign).
    748          */
    749         case 'x': case 'X':
    750           if (flags & PFXOK && p ==
    751               buf + 1 + !!(flags & HAVESIGN)) {
    752             base = 16;  /* if %i */
    753             flags &= ~PFXOK;
    754             goto ok;
    755           }
    756           break;
    757         }
    758 
    759         /*
    760          * If we got here, c is not a legal character
    761          * for a number.  Stop accumulating digits.
    762          */
    763         break;
    764     ok:
    765         /*
    766          * c is legal: store it and look at the next.
    767          */
    768         *p++ = (char)c;
    769         if (--fp->_r > 0)
    770           fp->_p++;
    771         else if (__srefill(fp))
    772           break;    /* EOF */
    773       }
    774       /*
    775        * If we had only a sign, it is no good; push
    776        * back the sign.  If the number ends in `x',
    777        * it was [sign] '0' 'x', so push back the x
    778        * and treat it as [sign] '0'.
    779        */
    780       if (flags & NDIGITS) {
    781         if (p > buf)
    782           (void)ungetc(*(u_char *)--p, fp);
    783         goto match_failure;
    784       }
    785       c = ((u_char *)p)[-1];
    786       if (c == 'x' || c == 'X') {
    787         --p;
    788         (void)ungetc(c, fp);
    789       }
    790       if ((flags & SUPPRESS) == 0) {
    791         //uintmax_t res;
    792         // Use a union to get around the truncation warnings.
    793         union {
    794           uintmax_t   umax;
    795           intmax_t    imax;
    796           void       *vp;
    797           ptrdiff_t   pdt;
    798           size_t      sz;
    799           long long   ll;
    800           long        lo;
    801           int         in;
    802           short       hw;
    803           char        ch;
    804         } res;
    805 
    806         *p = 0;
    807         if ((flags & UNSIGNED) == 0)
    808             res.imax = strtoimax(buf, (char **)NULL, base);
    809         else
    810             res.umax = strtoumax(buf, (char **)NULL, base);
    811         if (flags & POINTER)
    812           *va_arg(ap, void **) = res.vp;
    813               //(void *)((uintptr_t)res);
    814         else if (flags & SHORTSHORT)
    815           *va_arg(ap, char *) = res.ch;
    816         else if (flags & SHORT)
    817           *va_arg(ap, short *) = res.hw;
    818         else if (flags & LONG)
    819           *va_arg(ap, long *) = res.lo;
    820         else if (flags & LONGLONG)
    821           *va_arg(ap, long long *) = res.ll;
    822         else if (flags & INTMAXT)
    823           *va_arg(ap, intmax_t *) = res.imax;
    824         else if (flags & PTRDIFFT)
    825           *va_arg(ap, ptrdiff_t *) = res.pdt;
    826               //(ptrdiff_t)res;
    827         else if (flags & SIZET)
    828           *va_arg(ap, size_t *) = res.sz;
    829         else
    830           *va_arg(ap, int *) = res.in;
    831         nassigned++;
    832       }
    833       nread += (int)(p - buf);
    834       nconversions++;
    835 //Print(L"%a: %d\n", __func__, __LINE__);
    836       break;
    837 
    838 #ifndef NO_FLOATING_POINT
    839     case CT_FLOAT:
    840       /* scan a floating point number as if by strtod */
    841       if (width == 0 || width > sizeof(buf) - 1)
    842         width = sizeof(buf) - 1;
    843       if ((width = parsefloat(fp, buf, buf + width)) == 0)
    844         goto match_failure;
    845       if ((flags & SUPPRESS) == 0) {
    846         if (flags & LONGDBL) {
    847           long double res = strtold(buf, &p);
    848           *va_arg(ap, long double *) = res;
    849         } else if (flags & LONG) {
    850           double res = strtod(buf, &p);
    851           *va_arg(ap, double *) = res;
    852         } else {
    853           float res = strtof(buf, &p);
    854           *va_arg(ap, float *) = res;
    855         }
    856         if (__scanfdebug && p - buf != (ptrdiff_t)width)
    857           abort();
    858         nassigned++;
    859       }
    860       nread += (int)width;
    861       nconversions++;
    862       break;
    863 #endif /* !NO_FLOATING_POINT */
    864     }
    865   }
    866 input_failure:
    867 //Print(L"%a: %d\n", __func__, __LINE__);
    868   return (nconversions != 0 ? nassigned : EOF);
    869 match_failure:
    870   return (nassigned);
    871 }
    872 
    873 /*
    874  * Fill in the given table from the scanset at the given format
    875  * (just after `[').  Return a pointer to the character past the
    876  * closing `]'.  The table has a 1 wherever characters should be
    877  * considered part of the scanset.
    878  */
    879 static const u_char *
    880 __sccl(char *tab, const u_char *fmt)
    881 {
    882   int c, n, v, i;
    883 
    884   _DIAGASSERT(tab != NULL);
    885   _DIAGASSERT(fmt != NULL);
    886   /* first `clear' the whole table */
    887   c = *fmt++;   /* first char hat => negated scanset */
    888   if (c == '^') {
    889     v = 1;    /* default => accept */
    890     c = *fmt++; /* get new first char */
    891   } else
    892     v = 0;    /* default => reject */
    893 
    894   /* XXX: Will not work if sizeof(tab*) > sizeof(char) */
    895   (void)memset(tab, v, 256);
    896 
    897   if (c == 0)
    898     return (fmt - 1);/* format ended before closing ] */
    899 
    900   /*
    901    * Now set the entries corresponding to the actual scanset
    902    * to the opposite of the above.
    903    *
    904    * The first character may be ']' (or '-') without being special;
    905    * the last character may be '-'.
    906    */
    907   v = 1 - v;
    908   for (;;) {
    909     tab[c] = (char)v;   /* take character c */
    910 doswitch:
    911     n = *fmt++;   /* and examine the next */
    912     switch (n) {
    913 
    914     case 0:     /* format ended too soon */
    915       return (fmt - 1);
    916 
    917     case '-':
    918       /*
    919        * A scanset of the form
    920        *  [01+-]
    921        * is defined as `the digit 0, the digit 1,
    922        * the character +, the character -', but
    923        * the effect of a scanset such as
    924        *  [a-zA-Z0-9]
    925        * is implementation defined.  The V7 Unix
    926        * scanf treats `a-z' as `the letters a through
    927        * z', but treats `a-a' as `the letter a, the
    928        * character -, and the letter a'.
    929        *
    930        * For compatibility, the `-' is not considerd
    931        * to define a range if the character following
    932        * it is either a close bracket (required by ANSI)
    933        * or is not numerically greater than the character
    934        * we just stored in the table (c).
    935        */
    936       n = *fmt;
    937       if (n == ']' || (__collate_load_error ? n < c :
    938           __collate_range_cmp(n, c) < 0)) {
    939         c = '-';
    940         break;  /* resume the for(;;) */
    941       }
    942       fmt++;
    943       /* fill in the range */
    944       if (__collate_load_error) {
    945         do
    946           tab[++c] = (char)v;
    947         while (c < n);
    948       } else {
    949         for (i = 0; i < 256; i ++)
    950           if (__collate_range_cmp(c, i) < 0 &&
    951               __collate_range_cmp(i, n) <= 0)
    952             tab[i] = (char)v;
    953       }
    954 #if 1 /* XXX another disgusting compatibility hack */
    955       c = n;
    956       /*
    957        * Alas, the V7 Unix scanf also treats formats
    958        * such as [a-c-e] as `the letters a through e'.
    959        * This too is permitted by the standard....
    960        */
    961       goto doswitch;
    962 #else
    963       c = *fmt++;
    964       if (c == 0)
    965         return (fmt - 1);
    966       if (c == ']')
    967         return (fmt);
    968 #endif
    969 
    970     case ']':   /* end of scanset */
    971       return (fmt);
    972 
    973     default:    /* just another character */
    974       c = n;
    975       break;
    976     }
    977   }
    978   /* NOTREACHED */
    979 }
    980 
    981 #ifndef NO_FLOATING_POINT
    982 static int
    983 parsefloat(FILE *fp, char *buf, char *end)
    984 {
    985   char *commit, *p;
    986   int infnanpos = 0;
    987   enum {
    988     S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
    989     S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
    990   } state = S_START;
    991   unsigned char c;
    992   char decpt = *localeconv()->decimal_point;
    993   _Bool gotmantdig = 0, ishex = 0;
    994 
    995   if(fp == NULL) {
    996     errno = EINVAL;
    997     return (EOF);
    998   }
    999 
   1000   /*
   1001    * We set commit = p whenever the string we have read so far
   1002    * constitutes a valid representation of a floating point
   1003    * number by itself.  At some point, the parse will complete
   1004    * or fail, and we will ungetc() back to the last commit point.
   1005    * To ensure that the file offset gets updated properly, it is
   1006    * always necessary to read at least one character that doesn't
   1007    * match; thus, we can't short-circuit "infinity" or "nan(...)".
   1008    */
   1009   commit = buf - 1;
   1010   for (p = buf; p < end; ) {
   1011     c = *fp->_p;
   1012 reswitch:
   1013     switch (state) {
   1014     case S_START:
   1015       state = S_GOTSIGN;
   1016       if (c == '-' || c == '+')
   1017         break;
   1018       else
   1019         goto reswitch;
   1020     case S_GOTSIGN:
   1021       switch (c) {
   1022       case '0':
   1023         state = S_MAYBEHEX;
   1024         commit = p;
   1025         break;
   1026       case 'I':
   1027       case 'i':
   1028         state = S_INF;
   1029         break;
   1030       case 'N':
   1031       case 'n':
   1032         state = S_NAN;
   1033         break;
   1034       default:
   1035         state = S_DIGITS;
   1036         goto reswitch;
   1037       }
   1038       break;
   1039     case S_INF:
   1040       if (infnanpos > 6 ||
   1041           (c != "nfinity"[infnanpos] &&
   1042            c != "NFINITY"[infnanpos]))
   1043         goto parsedone;
   1044       if (infnanpos == 1 || infnanpos == 6)
   1045         commit = p; /* inf or infinity */
   1046       infnanpos++;
   1047       break;
   1048     case S_NAN:
   1049       switch (infnanpos) {
   1050       case -1:  /* XXX kludge to deal with nan(...) */
   1051         goto parsedone;
   1052       case 0:
   1053         if (c != 'A' && c != 'a')
   1054           goto parsedone;
   1055         break;
   1056       case 1:
   1057         if (c != 'N' && c != 'n')
   1058           goto parsedone;
   1059         else
   1060           commit = p;
   1061         break;
   1062       case 2:
   1063         if (c != '(')
   1064           goto parsedone;
   1065         break;
   1066       default:
   1067         if (c == ')') {
   1068           commit = p;
   1069           infnanpos = -2;
   1070         } else if (!isalnum(c) && c != '_')
   1071           goto parsedone;
   1072         break;
   1073       }
   1074       infnanpos++;
   1075       break;
   1076     case S_MAYBEHEX:
   1077       state = S_DIGITS;
   1078       if (c == 'X' || c == 'x') {
   1079         ishex = 1;
   1080         break;
   1081       } else {  /* we saw a '0', but no 'x' */
   1082         gotmantdig = 1;
   1083         goto reswitch;
   1084       }
   1085     case S_DIGITS:
   1086       if ((ishex && isxdigit(c)) || isdigit(c))
   1087         gotmantdig = 1;
   1088       else {
   1089         state = S_FRAC;
   1090         if (c != decpt)
   1091           goto reswitch;
   1092       }
   1093       if (gotmantdig)
   1094         commit = p;
   1095       break;
   1096     case S_FRAC:
   1097       if (((c == 'E' || c == 'e') && !ishex) ||
   1098           ((c == 'P' || c == 'p') && ishex)) {
   1099         if (!gotmantdig)
   1100           goto parsedone;
   1101         else
   1102           state = S_EXP;
   1103       } else if ((ishex && isxdigit(c)) || isdigit(c)) {
   1104         commit = p;
   1105         gotmantdig = 1;
   1106       } else
   1107         goto parsedone;
   1108       break;
   1109     case S_EXP:
   1110       state = S_EXPDIGITS;
   1111       if (c == '-' || c == '+')
   1112         break;
   1113       else
   1114         goto reswitch;
   1115     case S_EXPDIGITS:
   1116       if (isdigit(c))
   1117         commit = p;
   1118       else
   1119         goto parsedone;
   1120       break;
   1121     default:
   1122       abort();
   1123     }
   1124     *p++ = c;
   1125     if (--fp->_r > 0)
   1126       fp->_p++;
   1127     else if (__srefill(fp))
   1128       break;  /* EOF */
   1129   }
   1130 
   1131 parsedone:
   1132   while (commit < --p)
   1133     (void)ungetc(*(u_char *)p, fp);
   1134   *++commit = '\0';
   1135   return (int)(commit - buf);
   1136 }
   1137 #endif
   1138