1103856Stjr/*- 2103856Stjr * Copyright (c) 1990, 1993 3103856Stjr * The Regents of the University of California. All rights reserved. 4103856Stjr * 5103856Stjr * This code is derived from software contributed to Berkeley by 6103856Stjr * Chris Torek. 7103856Stjr * 8227753Stheraven * Copyright (c) 2011 The FreeBSD Foundation 9227753Stheraven * All rights reserved. 10227753Stheraven * Portions of this software were developed by David Chisnall 11227753Stheraven * under sponsorship from the FreeBSD Foundation. 12227753Stheraven * 13103856Stjr * Redistribution and use in source and binary forms, with or without 14103856Stjr * modification, are permitted provided that the following conditions 15103856Stjr * are met: 16103856Stjr * 1. Redistributions of source code must retain the above copyright 17103856Stjr * notice, this list of conditions and the following disclaimer. 18103856Stjr * 2. Redistributions in binary form must reproduce the above copyright 19103856Stjr * notice, this list of conditions and the following disclaimer in the 20103856Stjr * documentation and/or other materials provided with the distribution. 21249808Semaste * 3. Neither the name of the University nor the names of its contributors 22103856Stjr * may be used to endorse or promote products derived from this software 23103856Stjr * without specific prior written permission. 24103856Stjr * 25103856Stjr * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 26103856Stjr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 27103856Stjr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 28103856Stjr * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 29103856Stjr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 30103856Stjr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 31103856Stjr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 32103856Stjr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 33103856Stjr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 34103856Stjr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35103856Stjr * SUCH DAMAGE. 36103856Stjr */ 37103856Stjr 38103856Stjr#if 0 39103856Stjr#if defined(LIBC_SCCS) && !defined(lint) 40103856Stjrstatic char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93"; 41103856Stjr#endif /* LIBC_SCCS and not lint */ 42103856Stjr#endif 43128844Sobrien#include <sys/cdefs.h> 44103856Stjr__FBSDID("$FreeBSD: stable/10/lib/libc/stdio/vfwscanf.c 321074 2017-07-17 14:09:34Z kib $"); 45103856Stjr 46103856Stjr#include "namespace.h" 47103856Stjr#include <ctype.h> 48103856Stjr#include <inttypes.h> 49149313Sstefanf#include <limits.h> 50103856Stjr#include <stdio.h> 51103856Stjr#include <stdlib.h> 52103856Stjr#include <stddef.h> 53103856Stjr#include <stdarg.h> 54103856Stjr#include <string.h> 55103856Stjr#include <wchar.h> 56103856Stjr#include <wctype.h> 57103856Stjr#include "un-namespace.h" 58103856Stjr 59103856Stjr#include "libc_private.h" 60103856Stjr#include "local.h" 61227753Stheraven#include "xlocale_private.h" 62103856Stjr 63103856Stjr#define BUF 513 /* Maximum length of numeric string. */ 64103856Stjr 65103856Stjr/* 66103856Stjr * Flags used during conversion. 67103856Stjr */ 68103856Stjr#define LONG 0x01 /* l: long or double */ 69103856Stjr#define LONGDBL 0x02 /* L: long double */ 70103856Stjr#define SHORT 0x04 /* h: short */ 71103856Stjr#define SUPPRESS 0x08 /* *: suppress assignment */ 72103856Stjr#define POINTER 0x10 /* p: void * (as hex) */ 73103856Stjr#define NOSKIP 0x20 /* [ or c: do not skip blanks */ 74103856Stjr#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */ 75103856Stjr#define INTMAXT 0x800 /* j: intmax_t */ 76103856Stjr#define PTRDIFFT 0x1000 /* t: ptrdiff_t */ 77103856Stjr#define SIZET 0x2000 /* z: size_t */ 78103856Stjr#define SHORTSHORT 0x4000 /* hh: char */ 79103856Stjr#define UNSIGNED 0x8000 /* %[oupxX] conversions */ 80103856Stjr 81103856Stjr/* 82117249Stjr * The following are used in integral conversions only: 83117249Stjr * SIGNOK, NDIGITS, PFXOK, and NZDIGITS 84103856Stjr */ 85103856Stjr#define SIGNOK 0x40 /* +/- is (still) legal */ 86103856Stjr#define NDIGITS 0x80 /* no digits detected */ 87103856Stjr#define PFXOK 0x100 /* 0x prefix is (still) legal */ 88103856Stjr#define NZDIGITS 0x200 /* no zero digits detected */ 89125283Sdas#define HAVESIGN 0x10000 /* sign detected */ 90103856Stjr 91103856Stjr/* 92103856Stjr * Conversion types. 93103856Stjr */ 94103856Stjr#define CT_CHAR 0 /* %c conversion */ 95103856Stjr#define CT_CCL 1 /* %[...] conversion */ 96103856Stjr#define CT_STRING 2 /* %s conversion */ 97103856Stjr#define CT_INT 3 /* %[dioupxX] conversion */ 98103856Stjr#define CT_FLOAT 4 /* %[efgEFG] conversion */ 99103856Stjr 100157381Sphk#ifndef NO_FLOATING_POINT 101227753Stheravenstatic int parsefloat(FILE *, wchar_t *, wchar_t *, locale_t); 102157381Sphk#endif 103117249Stjr 104234585Sdasstruct ccl { 105234585Sdas const wchar_t *start; /* character class start */ 106234585Sdas const wchar_t *end; /* character class end */ 107234585Sdas int compl; /* ccl is complemented? */ 108234585Sdas}; 109103856Stjr 110234585Sdasstatic __inline int 111234585Sdasinccl(const struct ccl *ccl, wint_t wi) 112234585Sdas{ 113234585Sdas 114234585Sdas if (ccl->compl) { 115234585Sdas return (wmemchr(ccl->start, wi, ccl->end - ccl->start) 116234585Sdas == NULL); 117234585Sdas } else { 118234585Sdas return (wmemchr(ccl->start, wi, ccl->end - ccl->start) != NULL); 119234585Sdas } 120234585Sdas} 121234585Sdas 122234585Sdas/* 123234585Sdas * Conversion functions are passed a pointer to this object instead of 124234585Sdas * a real parameter to indicate that the assignment-suppression (*) 125234585Sdas * flag was specified. We could use a NULL pointer to indicate this, 126234585Sdas * but that would mask bugs in applications that call scanf() with a 127234585Sdas * NULL pointer. 128234585Sdas */ 129234585Sdasstatic const int suppress; 130234585Sdas#define SUPPRESS_PTR ((void *)&suppress) 131234585Sdas 132187422Sdasstatic const mbstate_t initial_mbs; 133187422Sdas 134103856Stjr/* 135234585Sdas * The following conversion functions return the number of characters consumed, 136234585Sdas * or -1 on input failure. Character class conversion returns 0 on match 137234585Sdas * failure. 138234585Sdas */ 139234585Sdas 140234585Sdasstatic __inline int 141234836Sdumbbellconvert_char(FILE *fp, char * mbp, int width, locale_t locale) 142234585Sdas{ 143234585Sdas mbstate_t mbs; 144234585Sdas size_t nconv; 145234585Sdas wint_t wi; 146234585Sdas int n; 147234585Sdas 148234585Sdas n = 0; 149234585Sdas mbs = initial_mbs; 150234825Sdas while (width-- != 0 && (wi = __fgetwc(fp, locale)) != WEOF) { 151234825Sdas if (mbp != SUPPRESS_PTR) { 152234585Sdas nconv = wcrtomb(mbp, wi, &mbs); 153234585Sdas if (nconv == (size_t)-1) 154234585Sdas return (-1); 155234825Sdas mbp += nconv; 156234585Sdas } 157234585Sdas n++; 158234585Sdas } 159234585Sdas if (n == 0) 160234585Sdas return (-1); 161234585Sdas return (n); 162234585Sdas} 163234585Sdas 164234585Sdasstatic __inline int 165234585Sdasconvert_wchar(FILE *fp, wchar_t *wcp, int width, locale_t locale) 166234585Sdas{ 167234585Sdas wint_t wi; 168234585Sdas int n; 169234585Sdas 170234585Sdas n = 0; 171234585Sdas while (width-- != 0 && (wi = __fgetwc(fp, locale)) != WEOF) { 172234585Sdas if (wcp != SUPPRESS_PTR) 173234585Sdas *wcp++ = (wchar_t)wi; 174234585Sdas n++; 175234585Sdas } 176234585Sdas if (n == 0) 177234585Sdas return (-1); 178234585Sdas return (n); 179234585Sdas} 180234585Sdas 181234585Sdasstatic __inline int 182234836Sdumbbellconvert_ccl(FILE *fp, char * mbp, int width, const struct ccl *ccl, 183234585Sdas locale_t locale) 184234585Sdas{ 185234585Sdas mbstate_t mbs; 186234585Sdas size_t nconv; 187234585Sdas wint_t wi; 188234585Sdas int n; 189234585Sdas 190234585Sdas n = 0; 191234585Sdas mbs = initial_mbs; 192234585Sdas while ((wi = __fgetwc(fp, locale)) != WEOF && 193234825Sdas width-- != 0 && inccl(ccl, wi)) { 194234825Sdas if (mbp != SUPPRESS_PTR) { 195234585Sdas nconv = wcrtomb(mbp, wi, &mbs); 196234585Sdas if (nconv == (size_t)-1) 197234585Sdas return (-1); 198234825Sdas mbp += nconv; 199234585Sdas } 200234585Sdas n++; 201234585Sdas } 202234585Sdas if (wi != WEOF) 203234585Sdas __ungetwc(wi, fp, locale); 204234585Sdas if (mbp != SUPPRESS_PTR) 205234585Sdas *mbp = 0; 206234585Sdas return (n); 207234585Sdas} 208234585Sdas 209234585Sdasstatic __inline int 210234585Sdasconvert_wccl(FILE *fp, wchar_t *wcp, int width, const struct ccl *ccl, 211234585Sdas locale_t locale) 212234585Sdas{ 213234585Sdas wchar_t *wcp0; 214234585Sdas wint_t wi; 215234585Sdas int n; 216234585Sdas 217234585Sdas if (wcp == SUPPRESS_PTR) { 218234585Sdas n = 0; 219234585Sdas while ((wi = __fgetwc(fp, locale)) != WEOF && 220234585Sdas width-- != 0 && inccl(ccl, wi)) 221234585Sdas n++; 222234585Sdas if (wi != WEOF) 223234585Sdas __ungetwc(wi, fp, locale); 224234585Sdas } else { 225234585Sdas wcp0 = wcp; 226234585Sdas while ((wi = __fgetwc(fp, locale)) != WEOF && 227234585Sdas width-- != 0 && inccl(ccl, wi)) 228234585Sdas *wcp++ = (wchar_t)wi; 229234585Sdas if (wi != WEOF) 230234585Sdas __ungetwc(wi, fp, locale); 231234585Sdas n = wcp - wcp0; 232234585Sdas if (n == 0) 233234585Sdas return (0); 234234585Sdas *wcp = 0; 235234585Sdas } 236234585Sdas return (n); 237234585Sdas} 238234585Sdas 239234585Sdasstatic __inline int 240234836Sdumbbellconvert_string(FILE *fp, char * mbp, int width, locale_t locale) 241234585Sdas{ 242234585Sdas mbstate_t mbs; 243234585Sdas size_t nconv; 244234585Sdas wint_t wi; 245234585Sdas int nread; 246234585Sdas 247234585Sdas mbs = initial_mbs; 248234585Sdas nread = 0; 249234825Sdas while ((wi = __fgetwc(fp, locale)) != WEOF && width-- != 0 && 250234585Sdas !iswspace(wi)) { 251234825Sdas if (mbp != SUPPRESS_PTR) { 252234585Sdas nconv = wcrtomb(mbp, wi, &mbs); 253234585Sdas if (nconv == (size_t)-1) 254234585Sdas return (-1); 255234825Sdas mbp += nconv; 256234585Sdas } 257234585Sdas nread++; 258234585Sdas } 259234585Sdas if (wi != WEOF) 260234585Sdas __ungetwc(wi, fp, locale); 261234585Sdas if (mbp != SUPPRESS_PTR) 262234585Sdas *mbp = 0; 263234585Sdas return (nread); 264234585Sdas} 265234585Sdas 266234585Sdasstatic __inline int 267234585Sdasconvert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale) 268234585Sdas{ 269234585Sdas wchar_t *wcp0; 270234585Sdas wint_t wi; 271234585Sdas int nread; 272234585Sdas 273234585Sdas nread = 0; 274234585Sdas if (wcp == SUPPRESS_PTR) { 275234585Sdas while ((wi = __fgetwc(fp, locale)) != WEOF && 276234585Sdas width-- != 0 && !iswspace(wi)) 277234585Sdas nread++; 278234585Sdas if (wi != WEOF) 279234585Sdas __ungetwc(wi, fp, locale); 280234585Sdas } else { 281234585Sdas wcp0 = wcp; 282234585Sdas while ((wi = __fgetwc(fp, locale)) != WEOF && 283234585Sdas width-- != 0 && !iswspace(wi)) { 284234585Sdas *wcp++ = (wchar_t)wi; 285234585Sdas nread++; 286234585Sdas } 287234585Sdas if (wi != WEOF) 288234585Sdas __ungetwc(wi, fp, locale); 289234585Sdas *wcp = '\0'; 290234585Sdas } 291234585Sdas return (nread); 292234585Sdas} 293234585Sdas 294234585Sdas/* 295234585Sdas * Read an integer, storing it in buf. The only relevant bit in the 296234585Sdas * flags argument is PFXOK. 297234585Sdas * 298234585Sdas * Return 0 on a match failure, and the number of characters read 299234585Sdas * otherwise. 300234585Sdas */ 301234585Sdasstatic __inline int 302234585Sdasparseint(FILE *fp, wchar_t *buf, int width, int base, int flags, 303234585Sdas locale_t locale) 304234585Sdas{ 305234585Sdas /* `basefix' is used to avoid `if' tests */ 306234585Sdas static const short basefix[17] = 307234585Sdas { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 308234585Sdas wchar_t *wcp; 309234585Sdas int c; 310234585Sdas 311234585Sdas flags |= SIGNOK | NDIGITS | NZDIGITS; 312234585Sdas for (wcp = buf; width; width--) { 313234585Sdas c = __fgetwc(fp, locale); 314234585Sdas /* 315234585Sdas * Switch on the character; `goto ok' if we accept it 316234585Sdas * as a part of number. 317234585Sdas */ 318234585Sdas switch (c) { 319234585Sdas 320234585Sdas /* 321234585Sdas * The digit 0 is always legal, but is special. For 322234585Sdas * %i conversions, if no digits (zero or nonzero) have 323234585Sdas * been scanned (only signs), we will have base==0. 324234585Sdas * In that case, we should set it to 8 and enable 0x 325234585Sdas * prefixing. Also, if we have not scanned zero 326234585Sdas * digits before this, do not turn off prefixing 327234585Sdas * (someone else will turn it off if we have scanned 328234585Sdas * any nonzero digits). 329234585Sdas */ 330234585Sdas case '0': 331234585Sdas if (base == 0) { 332234585Sdas base = 8; 333234585Sdas flags |= PFXOK; 334234585Sdas } 335234585Sdas if (flags & NZDIGITS) 336234585Sdas flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 337234585Sdas else 338234585Sdas flags &= ~(SIGNOK|PFXOK|NDIGITS); 339234585Sdas goto ok; 340234585Sdas 341234585Sdas /* 1 through 7 always legal */ 342234585Sdas case '1': case '2': case '3': 343234585Sdas case '4': case '5': case '6': case '7': 344234585Sdas base = basefix[base]; 345234585Sdas flags &= ~(SIGNOK | PFXOK | NDIGITS); 346234585Sdas goto ok; 347234585Sdas 348234585Sdas /* digits 8 and 9 ok iff decimal or hex */ 349234585Sdas case '8': case '9': 350234585Sdas base = basefix[base]; 351234585Sdas if (base <= 8) 352234585Sdas break; /* not legal here */ 353234585Sdas flags &= ~(SIGNOK | PFXOK | NDIGITS); 354234585Sdas goto ok; 355234585Sdas 356234585Sdas /* letters ok iff hex */ 357234585Sdas case 'A': case 'B': case 'C': 358234585Sdas case 'D': case 'E': case 'F': 359234585Sdas case 'a': case 'b': case 'c': 360234585Sdas case 'd': case 'e': case 'f': 361234585Sdas /* no need to fix base here */ 362234585Sdas if (base <= 10) 363234585Sdas break; /* not legal here */ 364234585Sdas flags &= ~(SIGNOK | PFXOK | NDIGITS); 365234585Sdas goto ok; 366234585Sdas 367234585Sdas /* sign ok only as first character */ 368234585Sdas case '+': case '-': 369234585Sdas if (flags & SIGNOK) { 370234585Sdas flags &= ~SIGNOK; 371234585Sdas flags |= HAVESIGN; 372234585Sdas goto ok; 373234585Sdas } 374234585Sdas break; 375234836Sdumbbell 376234585Sdas /* 377234585Sdas * x ok iff flag still set & 2nd char (or 3rd char if 378234585Sdas * we have a sign). 379234585Sdas */ 380234585Sdas case 'x': case 'X': 381234585Sdas if (flags & PFXOK && wcp == 382234585Sdas buf + 1 + !!(flags & HAVESIGN)) { 383234585Sdas base = 16; /* if %i */ 384234585Sdas flags &= ~PFXOK; 385234585Sdas goto ok; 386234585Sdas } 387234585Sdas break; 388234585Sdas } 389234585Sdas 390234585Sdas /* 391234585Sdas * If we got here, c is not a legal character for a 392234585Sdas * number. Stop accumulating digits. 393234585Sdas */ 394234585Sdas if (c != WEOF) 395234585Sdas __ungetwc(c, fp, locale); 396234585Sdas break; 397234585Sdas ok: 398234585Sdas /* 399234585Sdas * c is legal: store it and look at the next. 400234585Sdas */ 401234585Sdas *wcp++ = (wchar_t)c; 402234585Sdas } 403234585Sdas /* 404234585Sdas * If we had only a sign, it is no good; push back the sign. 405234585Sdas * If the number ends in `x', it was [sign] '0' 'x', so push 406234585Sdas * back the x and treat it as [sign] '0'. 407234585Sdas */ 408234585Sdas if (flags & NDIGITS) { 409234585Sdas if (wcp > buf) 410234585Sdas __ungetwc(*--wcp, fp, locale); 411234585Sdas return (0); 412234585Sdas } 413234585Sdas c = wcp[-1]; 414234585Sdas if (c == 'x' || c == 'X') { 415234585Sdas --wcp; 416234585Sdas __ungetwc(c, fp, locale); 417234585Sdas } 418234585Sdas return (wcp - buf); 419234585Sdas} 420234585Sdas 421234585Sdas/* 422103856Stjr * MT-safe version. 423103856Stjr */ 424103856Stjrint 425227753Stheravenvfwscanf_l(FILE * __restrict fp, locale_t locale, 426227753Stheraven const wchar_t * __restrict fmt, va_list ap) 427103856Stjr{ 428103856Stjr int ret; 429227753Stheraven FIX_LOCALE(locale); 430103856Stjr 431321074Skib FLOCKFILE_CANCELSAFE(fp); 432103856Stjr ORIENT(fp, 1); 433227753Stheraven ret = __vfwscanf(fp, locale, fmt, ap); 434321074Skib FUNLOCKFILE_CANCELSAFE(); 435103856Stjr return (ret); 436103856Stjr} 437227753Stheravenint 438227753Stheravenvfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap) 439227753Stheraven{ 440227753Stheraven return vfwscanf_l(fp, __get_locale(), fmt, ap); 441227753Stheraven} 442103856Stjr 443103856Stjr/* 444103856Stjr * Non-MT-safe version. 445103856Stjr */ 446103856Stjrint 447227753Stheraven__vfwscanf(FILE * __restrict fp, locale_t locale, 448227753Stheraven const wchar_t * __restrict fmt, va_list ap) 449103856Stjr{ 450234585Sdas#define GETARG(type) ((flags & SUPPRESS) ? SUPPRESS_PTR : va_arg(ap, type)) 451103856Stjr wint_t c; /* character from format, or conversion */ 452103856Stjr size_t width; /* field width, or 0 */ 453103856Stjr int flags; /* flags as defined above */ 454103856Stjr int nassigned; /* number of fields assigned */ 455103856Stjr int nconversions; /* number of conversions */ 456234585Sdas int nr; /* characters read by the current conversion */ 457103856Stjr int nread; /* number of characters consumed from fp */ 458103856Stjr int base; /* base argument to conversion function */ 459234585Sdas struct ccl ccl; /* character class info */ 460103856Stjr wchar_t buf[BUF]; /* buffer for numeric conversions */ 461103856Stjr wint_t wi; /* handy wint_t */ 462103856Stjr 463103856Stjr nassigned = 0; 464103856Stjr nconversions = 0; 465103856Stjr nread = 0; 466234585Sdas ccl.start = ccl.end = NULL; 467103856Stjr for (;;) { 468103856Stjr c = *fmt++; 469103856Stjr if (c == 0) 470103856Stjr return (nassigned); 471103856Stjr if (iswspace(c)) { 472227753Stheraven while ((c = __fgetwc(fp, locale)) != WEOF && 473227753Stheraven iswspace_l(c, locale)) 474234588Sdas nread++; 475103856Stjr if (c != WEOF) 476227753Stheraven __ungetwc(c, fp, locale); 477103856Stjr continue; 478103856Stjr } 479103856Stjr if (c != '%') 480103856Stjr goto literal; 481103856Stjr width = 0; 482103856Stjr flags = 0; 483103856Stjr /* 484103856Stjr * switch on the format. continue if done; 485103856Stjr * break once format type is derived. 486103856Stjr */ 487103856Stjragain: c = *fmt++; 488103856Stjr switch (c) { 489103856Stjr case '%': 490103856Stjrliteral: 491227753Stheraven if ((wi = __fgetwc(fp, locale)) == WEOF) 492103856Stjr goto input_failure; 493103856Stjr if (wi != c) { 494227753Stheraven __ungetwc(wi, fp, locale); 495103856Stjr goto input_failure; 496103856Stjr } 497103856Stjr nread++; 498103856Stjr continue; 499103856Stjr 500103856Stjr case '*': 501103856Stjr flags |= SUPPRESS; 502103856Stjr goto again; 503103856Stjr case 'j': 504103856Stjr flags |= INTMAXT; 505103856Stjr goto again; 506103856Stjr case 'l': 507103856Stjr if (flags & LONG) { 508103856Stjr flags &= ~LONG; 509103856Stjr flags |= LONGLONG; 510103856Stjr } else 511103856Stjr flags |= LONG; 512103856Stjr goto again; 513103856Stjr case 'q': 514103856Stjr flags |= LONGLONG; /* not quite */ 515103856Stjr goto again; 516103856Stjr case 't': 517103856Stjr flags |= PTRDIFFT; 518103856Stjr goto again; 519103856Stjr case 'z': 520103856Stjr flags |= SIZET; 521103856Stjr goto again; 522103856Stjr case 'L': 523103856Stjr flags |= LONGDBL; 524103856Stjr goto again; 525103856Stjr case 'h': 526103856Stjr if (flags & SHORT) { 527103856Stjr flags &= ~SHORT; 528103856Stjr flags |= SHORTSHORT; 529103856Stjr } else 530103856Stjr flags |= SHORT; 531103856Stjr goto again; 532103856Stjr 533103856Stjr case '0': case '1': case '2': case '3': case '4': 534103856Stjr case '5': case '6': case '7': case '8': case '9': 535103856Stjr width = width * 10 + c - '0'; 536103856Stjr goto again; 537103856Stjr 538103856Stjr /* 539103856Stjr * Conversions. 540103856Stjr */ 541103856Stjr case 'd': 542103856Stjr c = CT_INT; 543103856Stjr base = 10; 544103856Stjr break; 545103856Stjr 546103856Stjr case 'i': 547103856Stjr c = CT_INT; 548103856Stjr base = 0; 549103856Stjr break; 550103856Stjr 551103856Stjr case 'o': 552103856Stjr c = CT_INT; 553103856Stjr flags |= UNSIGNED; 554103856Stjr base = 8; 555103856Stjr break; 556103856Stjr 557103856Stjr case 'u': 558103856Stjr c = CT_INT; 559103856Stjr flags |= UNSIGNED; 560103856Stjr base = 10; 561103856Stjr break; 562103856Stjr 563103856Stjr case 'X': 564103856Stjr case 'x': 565103856Stjr flags |= PFXOK; /* enable 0x prefixing */ 566103856Stjr c = CT_INT; 567103856Stjr flags |= UNSIGNED; 568103856Stjr base = 16; 569103856Stjr break; 570103856Stjr 571128822Sdas#ifndef NO_FLOATING_POINT 572117249Stjr case 'A': case 'E': case 'F': case 'G': 573117249Stjr case 'a': case 'e': case 'f': case 'g': 574103856Stjr c = CT_FLOAT; 575103856Stjr break; 576103856Stjr#endif 577103856Stjr 578103856Stjr case 'S': 579103856Stjr flags |= LONG; 580103856Stjr /* FALLTHROUGH */ 581103856Stjr case 's': 582103856Stjr c = CT_STRING; 583103856Stjr break; 584103856Stjr 585103856Stjr case '[': 586234585Sdas ccl.start = fmt; 587103856Stjr if (*fmt == '^') { 588234585Sdas ccl.compl = 1; 589103856Stjr fmt++; 590103856Stjr } else 591234585Sdas ccl.compl = 0; 592103856Stjr if (*fmt == ']') 593103856Stjr fmt++; 594103856Stjr while (*fmt != '\0' && *fmt != ']') 595103856Stjr fmt++; 596234585Sdas ccl.end = fmt; 597103856Stjr fmt++; 598103856Stjr flags |= NOSKIP; 599103856Stjr c = CT_CCL; 600103856Stjr break; 601103856Stjr 602103856Stjr case 'C': 603103856Stjr flags |= LONG; 604103856Stjr /* FALLTHROUGH */ 605103856Stjr case 'c': 606103856Stjr flags |= NOSKIP; 607103856Stjr c = CT_CHAR; 608103856Stjr break; 609103856Stjr 610103856Stjr case 'p': /* pointer format is like hex */ 611103856Stjr flags |= POINTER | PFXOK; 612103856Stjr c = CT_INT; /* assumes sizeof(uintmax_t) */ 613103856Stjr flags |= UNSIGNED; /* >= sizeof(uintptr_t) */ 614103856Stjr base = 16; 615103856Stjr break; 616103856Stjr 617103856Stjr case 'n': 618103856Stjr if (flags & SUPPRESS) /* ??? */ 619103856Stjr continue; 620103856Stjr if (flags & SHORTSHORT) 621103856Stjr *va_arg(ap, char *) = nread; 622103856Stjr else if (flags & SHORT) 623103856Stjr *va_arg(ap, short *) = nread; 624103856Stjr else if (flags & LONG) 625103856Stjr *va_arg(ap, long *) = nread; 626103856Stjr else if (flags & LONGLONG) 627103856Stjr *va_arg(ap, long long *) = nread; 628103856Stjr else if (flags & INTMAXT) 629103856Stjr *va_arg(ap, intmax_t *) = nread; 630103856Stjr else if (flags & SIZET) 631103856Stjr *va_arg(ap, size_t *) = nread; 632103856Stjr else if (flags & PTRDIFFT) 633103856Stjr *va_arg(ap, ptrdiff_t *) = nread; 634103856Stjr else 635103856Stjr *va_arg(ap, int *) = nread; 636103856Stjr continue; 637103856Stjr 638103856Stjr default: 639103856Stjr goto match_failure; 640103856Stjr 641103856Stjr /* 642103856Stjr * Disgusting backwards compatibility hack. XXX 643103856Stjr */ 644103856Stjr case '\0': /* compat */ 645103856Stjr return (EOF); 646103856Stjr } 647103856Stjr 648103856Stjr /* 649103856Stjr * Consume leading white space, except for formats 650103856Stjr * that suppress this. 651103856Stjr */ 652103856Stjr if ((flags & NOSKIP) == 0) { 653227753Stheraven while ((wi = __fgetwc(fp, locale)) != WEOF && iswspace(wi)) 654103856Stjr nread++; 655103856Stjr if (wi == WEOF) 656103856Stjr goto input_failure; 657227753Stheraven __ungetwc(wi, fp, locale); 658103856Stjr } 659103856Stjr 660103856Stjr /* 661103856Stjr * Do the conversion. 662103856Stjr */ 663103856Stjr switch (c) { 664103856Stjr 665103856Stjr case CT_CHAR: 666103856Stjr /* scan arbitrary characters (sets NOSKIP) */ 667103856Stjr if (width == 0) 668103856Stjr width = 1; 669105317Stjr if (flags & LONG) { 670234585Sdas nr = convert_wchar(fp, GETARG(wchar_t *), width, 671234585Sdas locale); 672103856Stjr } else { 673234585Sdas nr = convert_char(fp, GETARG(char *), width, 674234585Sdas locale); 675103856Stjr } 676234585Sdas if (nr < 0) 677234585Sdas goto input_failure; 678103856Stjr break; 679103856Stjr 680103856Stjr case CT_CCL: 681103856Stjr /* scan a (nonempty) character class (sets NOSKIP) */ 682103856Stjr if (width == 0) 683103856Stjr width = (size_t)~0; /* `infinity' */ 684103856Stjr /* take only those things in the class */ 685234585Sdas if (flags & LONG) { 686234585Sdas nr = convert_wccl(fp, GETARG(wchar_t *), width, 687234585Sdas &ccl, locale); 688103856Stjr } else { 689234585Sdas nr = convert_ccl(fp, GETARG(char *), width, 690234585Sdas &ccl, locale); 691103856Stjr } 692234585Sdas if (nr <= 0) { 693234585Sdas if (nr < 0) 694234585Sdas goto input_failure; 695234585Sdas else /* nr == 0 */ 696234585Sdas goto match_failure; 697234585Sdas } 698103856Stjr break; 699103856Stjr 700103856Stjr case CT_STRING: 701103856Stjr /* like CCL, but zero-length string OK, & no NOSKIP */ 702103856Stjr if (width == 0) 703103856Stjr width = (size_t)~0; 704234585Sdas if (flags & LONG) { 705234585Sdas nr = convert_wstring(fp, GETARG(wchar_t *), 706234585Sdas width, locale); 707103856Stjr } else { 708234585Sdas nr = convert_string(fp, GETARG(char *), width, 709234585Sdas locale); 710103856Stjr } 711234585Sdas if (nr < 0) 712234585Sdas goto input_failure; 713234585Sdas break; 714103856Stjr 715103856Stjr case CT_INT: 716103856Stjr /* scan an integer as if by the conversion function */ 717117250Stjr if (width == 0 || width > sizeof(buf) / 718117250Stjr sizeof(*buf) - 1) 719117250Stjr width = sizeof(buf) / sizeof(*buf) - 1; 720103856Stjr 721234585Sdas nr = parseint(fp, buf, width, base, flags, locale); 722234585Sdas if (nr == 0) 723103856Stjr goto match_failure; 724103856Stjr if ((flags & SUPPRESS) == 0) { 725103856Stjr uintmax_t res; 726103856Stjr 727234585Sdas buf[nr] = L'\0'; 728103856Stjr if ((flags & UNSIGNED) == 0) 729103856Stjr res = wcstoimax(buf, NULL, base); 730103856Stjr else 731103856Stjr res = wcstoumax(buf, NULL, base); 732103856Stjr if (flags & POINTER) 733103856Stjr *va_arg(ap, void **) = 734103856Stjr (void *)(uintptr_t)res; 735103856Stjr else if (flags & SHORTSHORT) 736103856Stjr *va_arg(ap, char *) = res; 737103856Stjr else if (flags & SHORT) 738103856Stjr *va_arg(ap, short *) = res; 739103856Stjr else if (flags & LONG) 740103856Stjr *va_arg(ap, long *) = res; 741103856Stjr else if (flags & LONGLONG) 742103856Stjr *va_arg(ap, long long *) = res; 743103856Stjr else if (flags & INTMAXT) 744103856Stjr *va_arg(ap, intmax_t *) = res; 745103856Stjr else if (flags & PTRDIFFT) 746103856Stjr *va_arg(ap, ptrdiff_t *) = res; 747103856Stjr else if (flags & SIZET) 748103856Stjr *va_arg(ap, size_t *) = res; 749103856Stjr else 750103856Stjr *va_arg(ap, int *) = res; 751103856Stjr } 752103856Stjr break; 753103856Stjr 754128822Sdas#ifndef NO_FLOATING_POINT 755103856Stjr case CT_FLOAT: 756103856Stjr /* scan a floating point number as if by strtod */ 757117250Stjr if (width == 0 || width > sizeof(buf) / 758117250Stjr sizeof(*buf) - 1) 759117250Stjr width = sizeof(buf) / sizeof(*buf) - 1; 760234585Sdas nr = parsefloat(fp, buf, buf + width, locale); 761234585Sdas if (nr == 0) 762117249Stjr goto match_failure; 763103856Stjr if ((flags & SUPPRESS) == 0) { 764117249Stjr if (flags & LONGDBL) { 765234585Sdas long double res = wcstold(buf, NULL); 766103856Stjr *va_arg(ap, long double *) = res; 767117249Stjr } else if (flags & LONG) { 768234585Sdas double res = wcstod(buf, NULL); 769103856Stjr *va_arg(ap, double *) = res; 770117249Stjr } else { 771234585Sdas float res = wcstof(buf, NULL); 772103856Stjr *va_arg(ap, float *) = res; 773117249Stjr } 774103856Stjr } 775103856Stjr break; 776128822Sdas#endif /* !NO_FLOATING_POINT */ 777103856Stjr } 778234585Sdas if (!(flags & SUPPRESS)) 779234585Sdas nassigned++; 780234585Sdas nread += nr; 781234585Sdas nconversions++; 782103856Stjr } 783103856Stjrinput_failure: 784103856Stjr return (nconversions != 0 ? nassigned : EOF); 785103856Stjrmatch_failure: 786103856Stjr return (nassigned); 787103856Stjr} 788117249Stjr 789128822Sdas#ifndef NO_FLOATING_POINT 790117249Stjrstatic int 791227753Stheravenparsefloat(FILE *fp, wchar_t *buf, wchar_t *end, locale_t locale) 792117249Stjr{ 793187422Sdas mbstate_t mbs; 794187422Sdas size_t nconv; 795117249Stjr wchar_t *commit, *p; 796117249Stjr int infnanpos = 0; 797117249Stjr enum { 798187422Sdas S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX, 799117249Stjr S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS 800117249Stjr } state = S_START; 801117249Stjr wchar_t c; 802187422Sdas wchar_t decpt; 803117249Stjr _Bool gotmantdig = 0, ishex = 0; 804117249Stjr 805187422Sdas mbs = initial_mbs; 806187422Sdas nconv = mbrtowc(&decpt, localeconv()->decimal_point, MB_CUR_MAX, &mbs); 807187422Sdas if (nconv == (size_t)-1 || nconv == (size_t)-2) 808187422Sdas decpt = '.'; /* failsafe */ 809187422Sdas 810117249Stjr /* 811117249Stjr * We set commit = p whenever the string we have read so far 812117249Stjr * constitutes a valid representation of a floating point 813117249Stjr * number by itself. At some point, the parse will complete 814117249Stjr * or fail, and we will ungetc() back to the last commit point. 815117249Stjr * To ensure that the file offset gets updated properly, it is 816117249Stjr * always necessary to read at least one character that doesn't 817117249Stjr * match; thus, we can't short-circuit "infinity" or "nan(...)". 818117249Stjr */ 819117249Stjr commit = buf - 1; 820117249Stjr c = WEOF; 821117249Stjr for (p = buf; p < end; ) { 822227753Stheraven if ((c = __fgetwc(fp, locale)) == WEOF) 823117249Stjr break; 824117249Stjrreswitch: 825117249Stjr switch (state) { 826117249Stjr case S_START: 827117249Stjr state = S_GOTSIGN; 828117249Stjr if (c == '-' || c == '+') 829117249Stjr break; 830117249Stjr else 831117249Stjr goto reswitch; 832117249Stjr case S_GOTSIGN: 833117249Stjr switch (c) { 834117249Stjr case '0': 835117249Stjr state = S_MAYBEHEX; 836117249Stjr commit = p; 837117249Stjr break; 838117249Stjr case 'I': 839117249Stjr case 'i': 840117249Stjr state = S_INF; 841117249Stjr break; 842117249Stjr case 'N': 843117249Stjr case 'n': 844117249Stjr state = S_NAN; 845117249Stjr break; 846117249Stjr default: 847117249Stjr state = S_DIGITS; 848117249Stjr goto reswitch; 849117249Stjr } 850117249Stjr break; 851117249Stjr case S_INF: 852117249Stjr if (infnanpos > 6 || 853117249Stjr (c != "nfinity"[infnanpos] && 854117249Stjr c != "NFINITY"[infnanpos])) 855117249Stjr goto parsedone; 856117249Stjr if (infnanpos == 1 || infnanpos == 6) 857117249Stjr commit = p; /* inf or infinity */ 858117249Stjr infnanpos++; 859117249Stjr break; 860117249Stjr case S_NAN: 861117249Stjr switch (infnanpos) { 862117249Stjr case 0: 863117249Stjr if (c != 'A' && c != 'a') 864117249Stjr goto parsedone; 865117249Stjr break; 866117249Stjr case 1: 867117249Stjr if (c != 'N' && c != 'n') 868117249Stjr goto parsedone; 869117249Stjr else 870117249Stjr commit = p; 871117249Stjr break; 872117249Stjr case 2: 873117249Stjr if (c != '(') 874117249Stjr goto parsedone; 875117249Stjr break; 876117249Stjr default: 877117249Stjr if (c == ')') { 878117249Stjr commit = p; 879187422Sdas state = S_DONE; 880117249Stjr } else if (!iswalnum(c) && c != '_') 881117249Stjr goto parsedone; 882117249Stjr break; 883117249Stjr } 884117249Stjr infnanpos++; 885117249Stjr break; 886187422Sdas case S_DONE: 887187422Sdas goto parsedone; 888117249Stjr case S_MAYBEHEX: 889117249Stjr state = S_DIGITS; 890117249Stjr if (c == 'X' || c == 'x') { 891117249Stjr ishex = 1; 892117249Stjr break; 893117249Stjr } else { /* we saw a '0', but no 'x' */ 894117249Stjr gotmantdig = 1; 895117249Stjr goto reswitch; 896117249Stjr } 897117249Stjr case S_DIGITS: 898124175Snectar if ((ishex && iswxdigit(c)) || iswdigit(c)) 899117249Stjr gotmantdig = 1; 900117249Stjr else { 901117249Stjr state = S_FRAC; 902117249Stjr if (c != decpt) 903117249Stjr goto reswitch; 904117249Stjr } 905117249Stjr if (gotmantdig) 906117249Stjr commit = p; 907117249Stjr break; 908117249Stjr case S_FRAC: 909124175Snectar if (((c == 'E' || c == 'e') && !ishex) || 910124175Snectar ((c == 'P' || c == 'p') && ishex)) { 911117249Stjr if (!gotmantdig) 912117249Stjr goto parsedone; 913117249Stjr else 914117249Stjr state = S_EXP; 915124175Snectar } else if ((ishex && iswxdigit(c)) || iswdigit(c)) { 916117249Stjr commit = p; 917117249Stjr gotmantdig = 1; 918117249Stjr } else 919117249Stjr goto parsedone; 920117249Stjr break; 921117249Stjr case S_EXP: 922117249Stjr state = S_EXPDIGITS; 923117249Stjr if (c == '-' || c == '+') 924117249Stjr break; 925117249Stjr else 926117249Stjr goto reswitch; 927117249Stjr case S_EXPDIGITS: 928117249Stjr if (iswdigit(c)) 929117249Stjr commit = p; 930117249Stjr else 931117249Stjr goto parsedone; 932117249Stjr break; 933117249Stjr default: 934117249Stjr abort(); 935117249Stjr } 936117249Stjr *p++ = c; 937117249Stjr c = WEOF; 938117249Stjr } 939117249Stjr 940117249Stjrparsedone: 941117249Stjr if (c != WEOF) 942227753Stheraven __ungetwc(c, fp, locale); 943117249Stjr while (commit < --p) 944227753Stheraven __ungetwc(*p, fp, locale); 945117249Stjr *++commit = '\0'; 946117249Stjr return (commit - buf); 947117249Stjr} 948117249Stjr#endif 949