vfwscanf.c revision 125283
1757Sdg/*- 299703Sjulian * Copyright (c) 1990, 1993 3757Sdg * The Regents of the University of California. All rights reserved. 4174395Sjkoshy * 5757Sdg * This code is derived from software contributed to Berkeley by 6757Sdg * Chris Torek. 7174395Sjkoshy * 8174395Sjkoshy * Redistribution and use in source and binary forms, with or without 9174395Sjkoshy * modification, are permitted provided that the following conditions 10757Sdg * are met: 11757Sdg * 1. Redistributions of source code must retain the above copyright 12757Sdg * notice, this list of conditions and the following disclaimer. 13757Sdg * 2. Redistributions in binary form must reproduce the above copyright 14757Sdg * notice, this list of conditions and the following disclaimer in the 15757Sdg * documentation and/or other materials provided with the distribution. 16757Sdg * 3. All advertising materials mentioning features or use of this software 17757Sdg * must display the following acknowledgement: 18757Sdg * This product includes software developed by the University of 19757Sdg * California, Berkeley and its contributors. 20757Sdg * 4. Neither the name of the University nor the names of its contributors 21757Sdg * may be used to endorse or promote products derived from this software 22757Sdg * without specific prior written permission. 23757Sdg * 24757Sdg * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25757Sdg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26757Sdg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27757Sdg * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28757Sdg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29757Sdg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30757Sdg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31757Sdg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32757Sdg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33757Sdg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3450477Speter * SUCH DAMAGE. 35757Sdg */ 36757Sdg 37129653Sbde#include <sys/cdefs.h> 38133854Sobrien#if 0 39174395Sjkoshy#if defined(LIBC_SCCS) && !defined(lint) 40179279Sjbstatic char sccsid[] = "@(#)vfscanf.c 8.1 (Berkeley) 6/4/93"; 41129653Sbde#endif /* LIBC_SCCS and not lint */ 4228921Sfsmp__FBSDID("FreeBSD: src/lib/libc/stdio/vfscanf.c,v 1.35 2004/01/31 23:16:09 das Exp "); 4330786Sbde#endif 4430786Sbde__FBSDID("$FreeBSD: head/lib/libc/stdio/vfwscanf.c 125283 2004-01-31 23:18:53Z das $"); 45190620Skib 46757Sdg#include "namespace.h" 4730786Sbde#include <ctype.h> 4830786Sbde#include <inttypes.h> 49179279Sjb#include <stdio.h> 50179279Sjb#include <stdlib.h> 51179279Sjb#include <stddef.h> 52179279Sjb#include <stdarg.h> 53207570Skib#include <string.h> 54207570Skib#include <wchar.h> 55179279Sjb#include <wctype.h> 56179279Sjb#include "un-namespace.h" 57179279Sjb 58179279Sjb#include "libc_private.h" 59207570Skib#include "local.h" 60207570Skib 61179279Sjb#define FLOATING_POINT 62179279Sjb 63179279Sjb#ifdef FLOATING_POINT 64757Sdg#include <locale.h> 65174395Sjkoshy#endif 66174395Sjkoshy 67174395Sjkoshy#define BUF 513 /* Maximum length of numeric string. */ 68757Sdg 69757Sdg/* 70757Sdg * Flags used during conversion. 71757Sdg */ 72757Sdg#define LONG 0x01 /* l: long or double */ 7358717Sdillon#define LONGDBL 0x02 /* L: long double */ 7458717Sdillon#define SHORT 0x04 /* h: short */ 75114928Speter#define SUPPRESS 0x08 /* *: suppress assignment */ 76114928Speter#define POINTER 0x10 /* p: void * (as hex) */ 77114928Speter#define NOSKIP 0x20 /* [ or c: do not skip blanks */ 78114928Speter#define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */ 79114928Speter#define INTMAXT 0x800 /* j: intmax_t */ 80114928Speter#define PTRDIFFT 0x1000 /* t: ptrdiff_t */ 8158717Sdillon#define SIZET 0x2000 /* z: size_t */ 8258717Sdillon#define SHORTSHORT 0x4000 /* hh: char */ 83251988Skib#define UNSIGNED 0x8000 /* %[oupxX] conversions */ 84251988Skib 8558717Sdillon/* 86251988Skib * The following are used in integral conversions only: 87251988Skib * SIGNOK, NDIGITS, PFXOK, and NZDIGITS 88251988Skib */ 8958717Sdillon#define SIGNOK 0x40 /* +/- is (still) legal */ 9058717Sdillon#define NDIGITS 0x80 /* no digits detected */ 91251988Skib#define PFXOK 0x100 /* 0x prefix is (still) legal */ 92251988Skib#define NZDIGITS 0x200 /* no zero digits detected */ 93251988Skib#define HAVESIGN 0x10000 /* sign detected */ 94251988Skib 95251988Skib/* 96251988Skib * Conversion types. 97251988Skib */ 98251988Skib#define CT_CHAR 0 /* %c conversion */ 991321Sdg#define CT_CCL 1 /* %[...] conversion */ 1001321Sdg#define CT_STRING 2 /* %s conversion */ 1011321Sdg#define CT_INT 3 /* %[dioupxX] conversion */ 1021321Sdg#define CT_FLOAT 4 /* %[efgEFG] conversion */ 1031321Sdg 104114952Speterstatic int parsefloat(FILE *, wchar_t *, wchar_t *); 105114952Speter 106114952Speterextern int __scanfdebug; 107190620Skib 108114952Speter#define INCCL(_c) \ 109114952Speter (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) : \ 110114952Speter (wmemchr(ccls, (_c), ccle - ccls) != NULL)) 111114952Speter 112114952Speter/* 113211924Srpaulo * MT-safe version. 114211924Srpaulo */ 115211924Srpauloint 116211924Srpaulovfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap) 117114952Speter{ 118114952Speter int ret; 119114952Speter 120114952Speter FLOCKFILE(fp); 121190620Skib ORIENT(fp, 1); 122114952Speter ret = __vfwscanf(fp, fmt, ap); 123114952Speter FUNLOCKFILE(fp); 124114952Speter return (ret); 125757Sdg} 126114952Speter 127757Sdg/* 128114952Speter * Non-MT-safe version. 129757Sdg */ 130114952Speterint 131757Sdg__vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap) 132114952Speter{ 133757Sdg wint_t c; /* character from format, or conversion */ 134114952Speter size_t width; /* field width, or 0 */ 135757Sdg wchar_t *p; /* points into all kinds of strings */ 136114952Speter int n; /* handy integer */ 137114952Speter int flags; /* flags as defined above */ 138114952Speter wchar_t *p0; /* saves original value of p when necessary */ 139114952Speter int nassigned; /* number of fields assigned */ 140114952Speter int nconversions; /* number of conversions */ 141114952Speter int nread; /* number of characters consumed from fp */ 142114952Speter int base; /* base argument to conversion function */ 143114952Speter wchar_t buf[BUF]; /* buffer for numeric conversions */ 144114952Speter const wchar_t *ccls; /* character class start */ 145114952Speter const wchar_t *ccle; /* character class end */ 146114952Speter int cclcompl; /* ccl is complemented? */ 147114952Speter wint_t wi; /* handy wint_t */ 148114952Speter char *mbp; /* multibyte string pointer for %c %s %[ */ 149190620Skib size_t nconv; /* number of bytes in mb. conversion */ 150114952Speter char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */ 151147568Speter 152757Sdg /* `basefix' is used to avoid `if' tests in the integer scanner */ 153114952Speter static short basefix[17] = 154757Sdg { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }; 155275933Skib 156275933Skib nassigned = 0; 157275933Skib nconversions = 0; 158757Sdg nread = 0; 159275933Skib ccls = ccle = NULL; 160275933Skib for (;;) { 161275933Skib c = *fmt++; 1625603Sbde if (c == 0) 163114952Speter return (nassigned); 164140553Speter if (iswspace(c)) { 16558717Sdillon while ((c = __fgetwc(fp)) != WEOF && 166114928Speter iswspace(c)) 167114928Speter ; 168114928Speter if (c != WEOF) 16958717Sdillon __ungetwc(c, fp); 170757Sdg continue; 17173011Sjake } 17273011Sjake if (c != '%') 17373011Sjake goto literal; 174195486Skib width = 0; 175114928Speter flags = 0; 176114928Speter /* 177114928Speter * switch on the format. continue if done; 178195486Skib * break once format type is derived. 179216673Sjkim */ 180190620Skibagain: c = *fmt++; 181190620Skib switch (c) { 182190620Skib case '%': 183190620Skibliteral: 184114928Speter if ((wi = __fgetwc(fp)) == WEOF) 185114928Speter goto input_failure; 186195486Skib if (wi != c) { 187114928Speter __ungetwc(wi, fp); 188114952Speter goto input_failure; 189114349Speter } 190114349Speter nread++; 191114349Speter continue; 192114349Speter 193114349Speter case '*': 194114349Speter flags |= SUPPRESS; 195114349Speter goto again; 196114349Speter case 'j': 197114349Speter flags |= INTMAXT; 198114349Speter goto again; 199114349Speter case 'l': 200114349Speter if (flags & LONG) { 201114349Speter flags &= ~LONG; 202114349Speter flags |= LONGLONG; 203190620Skib } else 204209483Skib flags |= LONG; 205129623Sbde goto again; 206179279Sjb case 'q': 207179279Sjb flags |= LONGLONG; /* not quite */ 208179279Sjb goto again; 209179279Sjb case 't': 210179279Sjb flags |= PTRDIFFT; 211179279Sjb goto again; 212179279Sjb case 'z': 213298621Savg flags |= SIZET; 214298621Savg goto again; 215190620Skib case 'L': 216179279Sjb flags |= LONGDBL; 217179279Sjb goto again; 218179279Sjb case 'h': 219179279Sjb if (flags & SHORT) { 220179279Sjb flags &= ~SHORT; 221179279Sjb flags |= SHORTSHORT; 222179279Sjb } else 223179279Sjb flags |= SHORT; 224179279Sjb goto again; 225179279Sjb 226207570Skib case '0': case '1': case '2': case '3': case '4': 227179279Sjb case '5': case '6': case '7': case '8': case '9': 228179279Sjb width = width * 10 + c - '0'; 229207570Skib goto again; 230179279Sjb 231179279Sjb /* 232146461Speter * Conversions. 233146461Speter */ 234757Sdg case 'd': 235207570Skib c = CT_INT; 23673011Sjake base = 10; 237129623Sbde break; 238114349Speter 239757Sdg case 'i': 240114928Speter c = CT_INT; 241114928Speter base = 0; 242114928Speter break; 243114928Speter 244114928Speter case 'o': 245114928Speter c = CT_INT; 246114928Speter flags |= UNSIGNED; 247114928Speter base = 8; 248114928Speter break; 249195486Skib 250114928Speter case 'u': 251190620Skib c = CT_INT; 252114928Speter flags |= UNSIGNED; 253195486Skib base = 10; 254216673Sjkim break; 255190620Skib 256190620Skib case 'X': 257190620Skib case 'x': 258190620Skib flags |= PFXOK; /* enable 0x prefixing */ 259195486Skib c = CT_INT; 260114928Speter flags |= UNSIGNED; 261114928Speter base = 16; 262114952Speter break; 263190620Skib 264173659Sjhb#ifdef FLOATING_POINT 265173659Sjhb case 'A': case 'E': case 'F': case 'G': 266173659Sjhb case 'a': case 'e': case 'f': case 'g': 267173659Sjhb c = CT_FLOAT; 268173659Sjhb break; 269173659Sjhb#endif 270173659Sjhb 271173659Sjhb case 'S': 272173659Sjhb flags |= LONG; 273173659Sjhb /* FALLTHROUGH */ 274173659Sjhb case 's': 275173659Sjhb c = CT_STRING; 276173659Sjhb break; 277173659Sjhb 278173659Sjhb case '[': 279173659Sjhb ccls = fmt; 280173659Sjhb if (*fmt == '^') { 281190620Skib cclcompl = 1; 282190620Skib fmt++; 283190620Skib } else 284190620Skib cclcompl = 0; 285190620Skib if (*fmt == ']') 286209483Skib fmt++; 287114928Speter while (*fmt != '\0' && *fmt != ']') 288114928Speter fmt++; 289114928Speter ccle = fmt; 290207570Skib fmt++; 291207570Skib flags |= NOSKIP; 292173659Sjhb c = CT_CCL; 293207570Skib break; 294207570Skib 295114928Speter case 'C': 296114928Speter flags |= LONG; 297114952Speter /* FALLTHROUGH */ 298114952Speter case 'c': 299190620Skib flags |= NOSKIP; 300195486Skib c = CT_CHAR; 301114952Speter break; 302114952Speter 303114952Speter case 'p': /* pointer format is like hex */ 304195486Skib flags |= POINTER | PFXOK; 305216673Sjkim c = CT_INT; /* assumes sizeof(uintmax_t) */ 306195486Skib flags |= UNSIGNED; /* >= sizeof(uintptr_t) */ 307114952Speter base = 16; 308190620Skib break; 309190620Skib 310190620Skib case 'n': 311190620Skib nconversions++; 312114952Speter if (flags & SUPPRESS) /* ??? */ 313114952Speter continue; 314114952Speter if (flags & SHORTSHORT) 315114952Speter *va_arg(ap, char *) = nread; 316114952Speter else if (flags & SHORT) 317147677Speter *va_arg(ap, short *) = nread; 318147677Speter else if (flags & LONG) 319147677Speter *va_arg(ap, long *) = nread; 320147677Speter else if (flags & LONGLONG) 321147677Speter *va_arg(ap, long long *) = nread; 322147677Speter else if (flags & INTMAXT) 323147677Speter *va_arg(ap, intmax_t *) = nread; 324147677Speter else if (flags & SIZET) 325190620Skib *va_arg(ap, size_t *) = nread; 326275933Skib else if (flags & PTRDIFFT) 327147677Speter *va_arg(ap, ptrdiff_t *) = nread; 328147677Speter else 329147677Speter *va_arg(ap, int *) = nread; 330147677Speter continue; 331190620Skib 332147677Speter default: 333190620Skib goto match_failure; 334190620Skib 335195486Skib /* 336216673Sjkim * Disgusting backwards compatibility hack. XXX 337195486Skib */ 338190620Skib case '\0': /* compat */ 339190620Skib return (EOF); 340190620Skib } 341147677Speter 342147677Speter /* 343147677Speter * Consume leading white space, except for formats 344147677Speter * that suppress this. 345147677Speter */ 346757Sdg if ((flags & NOSKIP) == 0) { 347114349Speter while ((wi = __fgetwc(fp)) != WEOF && iswspace(wi)) 348114349Speter nread++; 349114349Speter if (wi == WEOF) 350114349Speter goto input_failure; 351220430Sjhb __ungetwc(wi, fp); 352220430Sjhb } 353220430Sjhb 3546380Ssos /* 355114349Speter * Do the conversion. 356114928Speter */ 357114349Speter switch (c) { 358122849Speter 359119924Speter case CT_CHAR: 360121103Speter /* scan arbitrary characters (sets NOSKIP) */ 361114349Speter if (width == 0) 362114349Speter width = 1; 363114928Speter if (flags & LONG) { 364114928Speter if (!(flags & SUPPRESS)) 365114928Speter p = va_arg(ap, wchar_t *); 366190620Skib n = 0; 367190620Skib while (width-- != 0 && 368190620Skib (wi = __fgetwc(fp)) != WEOF) { 369190620Skib if (!(flags & SUPPRESS)) 370195486Skib *p++ = (wchar_t)wi; 371216673Sjkim n++; 372114928Speter } 373114928Speter if (n == 0) 374114349Speter goto input_failure; 375114349Speter nread += n; 376114349Speter if (!(flags & SUPPRESS)) 377114349Speter nassigned++; 378114349Speter } else { 379114349Speter if (!(flags & SUPPRESS)) 380114349Speter mbp = va_arg(ap, char *); 381114349Speter n = 0; 382114349Speter while (width != 0 && 383114349Speter (wi = __fgetwc(fp)) != WEOF) { 384114349Speter if (width >= MB_CUR_MAX && 385114349Speter !(flags & SUPPRESS)) { 386114349Speter nconv = wcrtomb(mbp, wi, NULL); 387114349Speter if (nconv == (size_t)-1) 388114349Speter goto input_failure; 389190620Skib } else { 390209483Skib nconv = wcrtomb(mbbuf, wi, 391129623Sbde NULL); 392225475Skib if (nconv == (size_t)-1) 393225475Skib goto input_failure; 394225475Skib if (nconv > width) { 395225475Skib __ungetwc(wi, fp); 396225475Skib break; 397220452Sjhb } 398220460Skib if (!(flags & SUPPRESS)) 399220460Skib memcpy(mbp, mbbuf, 400220431Sjhb nconv); 401220452Sjhb } 402220452Sjhb if (!(flags & SUPPRESS)) 403220430Sjhb mbp += nconv; 404220430Sjhb width -= nconv; 405225575Skib n++; 406225575Skib } 407129623Sbde if (n == 0) 408220430Sjhb goto input_failure; 409220430Sjhb nread += n; 410220430Sjhb if (!(flags & SUPPRESS)) 411220430Sjhb nassigned++; 412220430Sjhb } 413220430Sjhb nconversions++; 414225575Skib break; 415220430Sjhb 416220430Sjhb case CT_CCL: 417225575Skib /* scan a (nonempty) character class (sets NOSKIP) */ 418225575Skib if (width == 0) 419225575Skib width = (size_t)~0; /* `infinity' */ 420225575Skib /* take only those things in the class */ 421225575Skib if ((flags & SUPPRESS) && (flags & LONG)) { 422225575Skib n = 0; 423225575Skib while ((wi = __fgetwc(fp)) != WEOF && 424220430Sjhb width-- != 0 && INCCL(wi)) 425220430Sjhb n++; 42673011Sjake if (wi != WEOF) 4276380Ssos __ungetwc(wi, fp); 428114349Speter if (n == 0) 429114349Speter goto match_failure; 430114349Speter } else if (flags & LONG) { 431114349Speter p0 = p = va_arg(ap, wchar_t *); 432114349Speter while ((wi = __fgetwc(fp)) != WEOF && 433114349Speter width-- != 0 && INCCL(wi)) 434114349Speter *p++ = (wchar_t)wi; 435114349Speter if (wi != WEOF) 436149526Sjkoshy __ungetwc(wi, fp); 437333370Semaste n = p - p0; 438333370Semaste if (n == 0) 439333370Semaste goto match_failure; 440333370Semaste *p = 0; 441333370Semaste nassigned++; 442333370Semaste } else { 443333370Semaste if (!(flags & SUPPRESS)) 444333370Semaste mbp = va_arg(ap, char *); 445333370Semaste n = 0; 446333370Semaste while ((wi = __fgetwc(fp)) != WEOF && 447333370Semaste width != 0 && INCCL(wi)) { 448333370Semaste if (width >= MB_CUR_MAX && 449333370Semaste !(flags & SUPPRESS)) { 450333370Semaste nconv = wcrtomb(mbp, wi, NULL); 451333370Semaste if (nconv == (size_t)-1) 452333370Semaste goto input_failure; 453333370Semaste } else { 454333370Semaste nconv = wcrtomb(mbbuf, wi, 455333370Semaste NULL); 456333370Semaste if (nconv == (size_t)-1) 457333370Semaste goto input_failure; 458333370Semaste if (nconv > width) 459333370Semaste break; 460333370Semaste if (!(flags & SUPPRESS)) 461333370Semaste memcpy(mbp, mbbuf, 462333370Semaste nconv); 463333370Semaste } 464333370Semaste if (!(flags & SUPPRESS)) 465333370Semaste mbp += nconv; 466333370Semaste width -= nconv; 467333370Semaste n++; 468333370Semaste } 469333370Semaste if (wi != WEOF) 470333370Semaste __ungetwc(wi, fp); 471333370Semaste if (!(flags & SUPPRESS)) { 472333370Semaste *mbp = 0; 473333370Semaste nassigned++; 474333370Semaste } 475333370Semaste } 476333370Semaste nread += n; 477333370Semaste nconversions++; 478333370Semaste break; 479333370Semaste 480333370Semaste case CT_STRING: 481333370Semaste /* like CCL, but zero-length string OK, & no NOSKIP */ 482333370Semaste if (width == 0) 483333370Semaste width = (size_t)~0; 484333370Semaste if ((flags & SUPPRESS) && (flags & LONG)) { 485333370Semaste while ((wi = __fgetwc(fp)) != WEOF && 486333370Semaste width-- != 0 && 487333370Semaste !iswspace(wi)) 488333370Semaste nread++; 489333370Semaste if (wi != WEOF) 490333370Semaste __ungetwc(wi, fp); 491333370Semaste } else if (flags & LONG) { 492333370Semaste p0 = p = va_arg(ap, wchar_t *); 493333370Semaste while ((wi = __fgetwc(fp)) != WEOF && 494333370Semaste width-- != 0 && 495333370Semaste !iswspace(wi)) { 496333370Semaste *p++ = (wchar_t)wi; 497333370Semaste nread++; 498333370Semaste } 499333370Semaste if (wi != WEOF) 500333370Semaste __ungetwc(wi, fp); 501333370Semaste *p = '\0'; 502333370Semaste nassigned++; 503333370Semaste } else { 504333370Semaste if (!(flags & SUPPRESS)) 505333370Semaste mbp = va_arg(ap, char *); 506333370Semaste while ((wi = __fgetwc(fp)) != WEOF && 507333370Semaste width != 0 && 508333370Semaste !iswspace(wi)) { 509333370Semaste if (width >= MB_CUR_MAX && 510333370Semaste !(flags & SUPPRESS)) { 511333370Semaste nconv = wcrtomb(mbp, wi, NULL); 512333370Semaste if (nconv == (size_t)-1) 513333370Semaste goto input_failure; 514333370Semaste } else { 515333370Semaste nconv = wcrtomb(mbbuf, wi, 516333370Semaste NULL); 517333370Semaste if (nconv == (size_t)-1) 518333370Semaste goto input_failure; 519333370Semaste if (nconv > width) 520333370Semaste break; 521333370Semaste if (!(flags & SUPPRESS)) 522333370Semaste memcpy(mbp, mbbuf, 523333370Semaste nconv); 524333370Semaste } 525333370Semaste if (!(flags & SUPPRESS)) 526333370Semaste mbp += nconv; 527333370Semaste width -= nconv; 528333370Semaste nread++; 529333370Semaste } 530333370Semaste if (wi != WEOF) 531333370Semaste __ungetwc(wi, fp); 532149526Sjkoshy if (!(flags & SUPPRESS)) { 533149526Sjkoshy *mbp = 0; 534149526Sjkoshy nassigned++; 535188065Sjkoshy } 536188065Sjkoshy } 537188065Sjkoshy nconversions++; 538188065Sjkoshy continue; 539149526Sjkoshy 540149526Sjkoshy case CT_INT: 541188065Sjkoshy /* scan an integer as if by the conversion function */ 542188065Sjkoshy if (width == 0 || width > sizeof(buf) / 543188065Sjkoshy sizeof(*buf) - 1) 544188065Sjkoshy width = sizeof(buf) / sizeof(*buf) - 1; 545174395Sjkoshy flags |= SIGNOK | NDIGITS | NZDIGITS; 546188065Sjkoshy for (p = buf; width; width--) { 547188065Sjkoshy c = __fgetwc(fp); 548188065Sjkoshy /* 549188065Sjkoshy * Switch on the character; `goto ok' 550188065Sjkoshy * if we accept it as a part of number. 551188065Sjkoshy */ 552188065Sjkoshy switch (c) { 553149526Sjkoshy 554149526Sjkoshy /* 555149526Sjkoshy * The digit 0 is always legal, but is 556149526Sjkoshy * special. For %i conversions, if no 557190620Skib * digits (zero or nonzero) have been 558149526Sjkoshy * scanned (only signs), we will have 559149526Sjkoshy * base==0. In that case, we should set 560149526Sjkoshy * it to 8 and enable 0x prefixing. 561149526Sjkoshy * Also, if we have not scanned zero digits 562149526Sjkoshy * before this, do not turn off prefixing 563149526Sjkoshy * (someone else will turn it off if we 564149526Sjkoshy * have scanned any nonzero digits). 565149526Sjkoshy */ 566149526Sjkoshy case '0': 567149526Sjkoshy if (base == 0) { 568149526Sjkoshy base = 8; 569149526Sjkoshy flags |= PFXOK; 570149526Sjkoshy } 571149526Sjkoshy if (flags & NZDIGITS) 572149526Sjkoshy flags &= ~(SIGNOK|NZDIGITS|NDIGITS); 573149526Sjkoshy else 574149526Sjkoshy flags &= ~(SIGNOK|PFXOK|NDIGITS); 575190620Skib goto ok; 576190620Skib 577190620Skib /* 1 through 7 always legal */ 578190620Skib case '1': case '2': case '3': 579190620Skib case '4': case '5': case '6': case '7': 580209483Skib base = basefix[base]; 581149526Sjkoshy flags &= ~(SIGNOK | PFXOK | NDIGITS); 582149526Sjkoshy goto ok; 583188065Sjkoshy 584188065Sjkoshy /* digits 8 and 9 ok iff decimal or hex */ 585188065Sjkoshy case '8': case '9': 586188065Sjkoshy base = basefix[base]; 587149526Sjkoshy if (base <= 8) 588149526Sjkoshy break; /* not legal here */ 589188065Sjkoshy flags &= ~(SIGNOK | PFXOK | NDIGITS); 590188065Sjkoshy goto ok; 591188065Sjkoshy 592188065Sjkoshy /* letters ok iff hex */ 593188065Sjkoshy case 'A': case 'B': case 'C': 594188065Sjkoshy case 'D': case 'E': case 'F': 595188065Sjkoshy case 'a': case 'b': case 'c': 596188065Sjkoshy case 'd': case 'e': case 'f': 597188065Sjkoshy /* no need to fix base here */ 598188065Sjkoshy if (base <= 10) 599149526Sjkoshy break; /* not legal here */ 600149526Sjkoshy flags &= ~(SIGNOK | PFXOK | NDIGITS); 601149526Sjkoshy goto ok; 602149526Sjkoshy 603149526Sjkoshy /* sign ok only as first character */ 604207570Skib case '+': case '-': 605149526Sjkoshy if (flags & SIGNOK) { 606149526Sjkoshy flags &= ~SIGNOK; 607174395Sjkoshy flags |= HAVESIGN; 608174395Sjkoshy goto ok; 609188065Sjkoshy } 610251988Skib break; 611188065Sjkoshy 612188065Sjkoshy /* 613188065Sjkoshy * x ok iff flag still set & 2nd char (or 614188065Sjkoshy * 3rd char if we have a sign). 615188065Sjkoshy */ 616188065Sjkoshy case 'x': case 'X': 617174395Sjkoshy if (flags & PFXOK && p == 618188065Sjkoshy buf + 1 + !!(flags & HAVESIGN)) { 619188065Sjkoshy base = 16; /* if %i */ 620188065Sjkoshy flags &= ~PFXOK; 621188065Sjkoshy goto ok; 622174395Sjkoshy } 623174395Sjkoshy break; 624174395Sjkoshy } 625174395Sjkoshy 626174395Sjkoshy /* 627174395Sjkoshy * If we got here, c is not a legal character 628174395Sjkoshy * for a number. Stop accumulating digits. 629174395Sjkoshy */ 630174395Sjkoshy if (c != WEOF) 631174395Sjkoshy __ungetwc(c, fp); 632174395Sjkoshy break; 633174395Sjkoshy ok: 634186076Sjkoshy /* 635186076Sjkoshy * c is legal: store it and look at the next. 636186076Sjkoshy */ 637174395Sjkoshy *p++ = (wchar_t)c; 638174395Sjkoshy } 639174395Sjkoshy /* 640174395Sjkoshy * If we had only a sign, it is no good; push 641174395Sjkoshy * back the sign. If the number ends in `x', 642174395Sjkoshy * it was [sign] '0' 'x', so push back the x 643174395Sjkoshy * and treat it as [sign] '0'. 644174395Sjkoshy */ 645186076Sjkoshy if (flags & NDIGITS) { 646174395Sjkoshy if (p > buf) 647174395Sjkoshy __ungetwc(*--p, fp); 648174395Sjkoshy goto match_failure; 649174395Sjkoshy } 650174395Sjkoshy c = p[-1]; 651174395Sjkoshy if (c == 'x' || c == 'X') { 652174395Sjkoshy --p; 653174395Sjkoshy __ungetwc(c, fp); 654174395Sjkoshy } 655174395Sjkoshy if ((flags & SUPPRESS) == 0) { 656251988Skib uintmax_t res; 657186037Sjkoshy 658186037Sjkoshy *p = 0; 659186037Sjkoshy if ((flags & UNSIGNED) == 0) 660174395Sjkoshy res = wcstoimax(buf, NULL, base); 661186037Sjkoshy else 662186037Sjkoshy res = wcstoumax(buf, NULL, base); 663186037Sjkoshy if (flags & POINTER) 664186037Sjkoshy *va_arg(ap, void **) = 665186037Sjkoshy (void *)(uintptr_t)res; 666186037Sjkoshy else if (flags & SHORTSHORT) 667174395Sjkoshy *va_arg(ap, char *) = res; 668186037Sjkoshy else if (flags & SHORT) 669187221Skib *va_arg(ap, short *) = res; 670174395Sjkoshy else if (flags & LONG) 671174395Sjkoshy *va_arg(ap, long *) = res; 672149526Sjkoshy else if (flags & LONGLONG) 673190620Skib *va_arg(ap, long long *) = res; 674251988Skib else if (flags & INTMAXT) 675188065Sjkoshy *va_arg(ap, intmax_t *) = res; 676188065Sjkoshy else if (flags & PTRDIFFT) 677188065Sjkoshy *va_arg(ap, ptrdiff_t *) = res; 678188065Sjkoshy else if (flags & SIZET) 679188065Sjkoshy *va_arg(ap, size_t *) = res; 680188065Sjkoshy else 681188065Sjkoshy *va_arg(ap, int *) = res; 682188065Sjkoshy nassigned++; 683149526Sjkoshy } 684149526Sjkoshy nread += p - buf; 685149526Sjkoshy nconversions++; 686149526Sjkoshy break; 687149526Sjkoshy 688149526Sjkoshy#ifdef FLOATING_POINT 689149526Sjkoshy case CT_FLOAT: 690149526Sjkoshy /* scan a floating point number as if by strtod */ 691149526Sjkoshy if (width == 0 || width > sizeof(buf) / 692149526Sjkoshy sizeof(*buf) - 1) 693149526Sjkoshy width = sizeof(buf) / sizeof(*buf) - 1; 694149526Sjkoshy if ((width = parsefloat(fp, buf, buf + width)) == 0) 695149526Sjkoshy goto match_failure; 696149526Sjkoshy if ((flags & SUPPRESS) == 0) { 697149526Sjkoshy if (flags & LONGDBL) { 698149526Sjkoshy long double res = wcstold(buf, &p); 699149526Sjkoshy *va_arg(ap, long double *) = res; 700207958Skib } else if (flags & LONG) { 701149526Sjkoshy double res = wcstod(buf, &p); 70224691Speter *va_arg(ap, double *) = res; 703207570Skib } else { 704207570Skib float res = wcstof(buf, &p); 705207570Skib *va_arg(ap, float *) = res; 70673011Sjake } 707129623Sbde if (__scanfdebug && p - buf != width) 708114349Speter abort(); 70924691Speter nassigned++; 710129653Sbde } 711129653Sbde nread += width; 712129653Sbde nconversions++; 713129653Sbde break; 714129653Sbde#endif /* FLOATING_POINT */ 715129653Sbde } 716129653Sbde } 717129653Sbdeinput_failure: 718129653Sbde return (nconversions != 0 ? nassigned : EOF); 719129653Sbdematch_failure: 720205014Snwhitehorn return (nassigned); 72199703Sjulian} 722129656Sbde 723129653Sbde#ifdef FLOATING_POINT 724129653Sbdestatic int 72599703Sjulianparsefloat(FILE *fp, wchar_t *buf, wchar_t *end) 726129653Sbde{ 727129653Sbde wchar_t *commit, *p; 728129653Sbde int infnanpos = 0; 729129653Sbde enum { 730129656Sbde S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX, 731129653Sbde S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS 732129653Sbde } state = S_START; 733129653Sbde wchar_t c; 734129653Sbde wchar_t decpt = (wchar_t)(unsigned char)*localeconv()->decimal_point; 735129653Sbde _Bool gotmantdig = 0, ishex = 0; 736129653Sbde 737129653Sbde /* 738129653Sbde * We set commit = p whenever the string we have read so far 739129656Sbde * constitutes a valid representation of a floating point 740129653Sbde * number by itself. At some point, the parse will complete 741129653Sbde * or fail, and we will ungetc() back to the last commit point. 742129653Sbde * To ensure that the file offset gets updated properly, it is 743204309Sattilio * always necessary to read at least one character that doesn't 744129653Sbde * match; thus, we can't short-circuit "infinity" or "nan(...)". 745129653Sbde */ 746129653Sbde commit = buf - 1; 747129653Sbde c = WEOF; 748129653Sbde for (p = buf; p < end; ) { 7493156Sbde if ((c = __fgetwc(fp)) == WEOF) 75099703Sjulian break; 75199703Sjulianreswitch: 75299703Sjulian switch (state) { 7533156Sbde case S_START: 7543156Sbde state = S_GOTSIGN; 7553156Sbde if (c == '-' || c == '+') 75699703Sjulian break; 757302041Ssephe else 75899703Sjulian goto reswitch; 759129623Sbde case S_GOTSIGN: 76099703Sjulian switch (c) { 761114349Speter case '0': 76299703Sjulian state = S_MAYBEHEX; 763114349Speter commit = p; 76499703Sjulian break; 76599703Sjulian case 'I': 76699703Sjulian case 'i': 76799703Sjulian state = S_INF; 76899703Sjulian break; 769220452Sjhb case 'N': 77099703Sjulian case 'n': 77199703Sjulian state = S_NAN; 77299703Sjulian break; 773114349Speter default: 774114349Speter state = S_DIGITS; 77599703Sjulian goto reswitch; 77699703Sjulian } 777207570Skib break; 77899703Sjulian case S_INF: 77999703Sjulian if (infnanpos > 6 || 78099703Sjulian (c != "nfinity"[infnanpos] && 78199703Sjulian c != "NFINITY"[infnanpos])) 78299703Sjulian goto parsedone; 78399703Sjulian if (infnanpos == 1 || infnanpos == 6) 78499703Sjulian commit = p; /* inf or infinity */ 78599703Sjulian infnanpos++; 78699703Sjulian break; 78799703Sjulian case S_NAN: 78899703Sjulian switch (infnanpos) { 789129623Sbde case -1: /* XXX kludge to deal with nan(...) */ 790220452Sjhb goto parsedone; 791190620Skib case 0: 792190620Skib if (c != 'A' && c != 'a') 793190620Skib goto parsedone; 794190620Skib break; 795190620Skib case 1: 796207570Skib if (c != 'N' && c != 'n') 797207570Skib goto parsedone; 798190620Skib else 799190620Skib commit = p; 800190620Skib break; 801216673Sjkim case 2: 802216634Sjkim if (c != '(') 803190620Skib goto parsedone; 804190620Skib break; 805190620Skib default: 806190620Skib if (c == ')') { 807190620Skib commit = p; 808190620Skib infnanpos = -2; 809190620Skib } else if (!iswalnum(c) && c != '_') 810207570Skib goto parsedone; 811207570Skib break; 812190620Skib } 813190620Skib infnanpos++; 814190620Skib break; 815190620Skib case S_MAYBEHEX: 816190620Skib state = S_DIGITS; 817206459Skib if (c == 'X' || c == 'x') { 818207570Skib ishex = 1; 819207570Skib break; 820190620Skib } else { /* we saw a '0', but no 'x' */ 821190620Skib gotmantdig = 1; 822190620Skib goto reswitch; 823190620Skib } 824190620Skib case S_DIGITS: 825190620Skib if ((ishex && iswxdigit(c)) || iswdigit(c)) 826267083Skib gotmantdig = 1; 827190620Skib else { 828267083Skib state = S_FRAC; 829267083Skib if (c != decpt) 830190620Skib goto reswitch; 831207570Skib } 832207570Skib if (gotmantdig) 833267083Skib commit = p; 834267083Skib break; 835267083Skib case S_FRAC: 836267083Skib if (((c == 'E' || c == 'e') && !ishex) || 837267083Skib ((c == 'P' || c == 'p') && ishex)) { 838267083Skib if (!gotmantdig) 839267083Skib goto parsedone; 840190620Skib else 841190620Skib state = S_EXP; 842267083Skib } else if ((ishex && iswxdigit(c)) || iswdigit(c)) { 843267083Skib commit = p; 844267083Skib gotmantdig = 1; 845267083Skib } else 846267083Skib goto parsedone; 847190620Skib break; 848190620Skib case S_EXP: 849190620Skib state = S_EXPDIGITS; 850190620Skib if (c == '-' || c == '+') 851267083Skib break; 852267083Skib else 853267083Skib goto reswitch; 854267083Skib case S_EXPDIGITS: 855206459Skib if (iswdigit(c)) 856207570Skib commit = p; 857267083Skib else 858207570Skib goto parsedone; 859207570Skib break; 860207570Skib default: 861190620Skib abort(); 862207570Skib } 863207570Skib *p++ = c; 864207570Skib c = WEOF; 865207570Skib } 866114349Speter 867114349Speterparsedone: 868114349Speter if (c != WEOF) 869114349Speter __ungetwc(c, fp); 870114349Speter while (commit < --p) 871114349Speter __ungetwc(*p, fp); 872114349Speter *++commit = '\0'; 873114349Speter return (commit - buf); 874114349Speter} 875114349Speter#endif 876114349Speter