158f0484fSRodney W. Grimes /*-
28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause
38a16b7a1SPedro F. Giffuni *
458f0484fSRodney W. Grimes * Copyright (c) 1990, 1993
558f0484fSRodney W. Grimes * The Regents of the University of California. All rights reserved.
658f0484fSRodney W. Grimes *
73c87aa1dSDavid Chisnall * Copyright (c) 2011 The FreeBSD Foundation
85b5fa75aSEd Maste *
9d9dc1603SDag-Erling Smørgrav * Copyright (c) 2023 Dag-Erling Smørgrav
10d9dc1603SDag-Erling Smørgrav *
113c87aa1dSDavid Chisnall * Portions of this software were developed by David Chisnall
123c87aa1dSDavid Chisnall * under sponsorship from the FreeBSD Foundation.
133c87aa1dSDavid Chisnall *
1458f0484fSRodney W. Grimes * This code is derived from software contributed to Berkeley by
1558f0484fSRodney W. Grimes * Chris Torek.
1658f0484fSRodney W. Grimes *
1758f0484fSRodney W. Grimes * Redistribution and use in source and binary forms, with or without
1858f0484fSRodney W. Grimes * modification, are permitted provided that the following conditions
1958f0484fSRodney W. Grimes * are met:
2058f0484fSRodney W. Grimes * 1. Redistributions of source code must retain the above copyright
2158f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer.
2258f0484fSRodney W. Grimes * 2. Redistributions in binary form must reproduce the above copyright
2358f0484fSRodney W. Grimes * notice, this list of conditions and the following disclaimer in the
2458f0484fSRodney W. Grimes * documentation and/or other materials provided with the distribution.
251d8053c5SEd Maste * 3. Neither the name of the University nor the names of its contributors
2658f0484fSRodney W. Grimes * may be used to endorse or promote products derived from this software
2758f0484fSRodney W. Grimes * without specific prior written permission.
2858f0484fSRodney W. Grimes *
2958f0484fSRodney W. Grimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
3058f0484fSRodney W. Grimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
3158f0484fSRodney W. Grimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3258f0484fSRodney W. Grimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3358f0484fSRodney W. Grimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3458f0484fSRodney W. Grimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3558f0484fSRodney W. Grimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3658f0484fSRodney W. Grimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3758f0484fSRodney W. Grimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3858f0484fSRodney W. Grimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3958f0484fSRodney W. Grimes * SUCH DAMAGE.
4058f0484fSRodney W. Grimes */
4158f0484fSRodney W. Grimes
42d201fe46SDaniel Eischen #include "namespace.h"
43946b2d00SBill Fenner #include <ctype.h>
44946b2d00SBill Fenner #include <inttypes.h>
4558f0484fSRodney W. Grimes #include <stdio.h>
4658f0484fSRodney W. Grimes #include <stdlib.h>
47946b2d00SBill Fenner #include <stddef.h>
4858f0484fSRodney W. Grimes #include <stdarg.h>
49ea295661SAndrey A. Chernov #include <string.h>
504712aa3bSTim J. Robbins #include <wchar.h>
514712aa3bSTim J. Robbins #include <wctype.h>
52d201fe46SDaniel Eischen #include "un-namespace.h"
53ea295661SAndrey A. Chernov
541daad8f5SAndrey A. Chernov #include "collate.h"
55d201fe46SDaniel Eischen #include "libc_private.h"
5658f0484fSRodney W. Grimes #include "local.h"
573c87aa1dSDavid Chisnall #include "xlocale_private.h"
5858f0484fSRodney W. Grimes
598de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
60a2a135c9SAndrey A. Chernov #include <locale.h>
61a2a135c9SAndrey A. Chernov #endif
62a2a135c9SAndrey A. Chernov
6358f0484fSRodney W. Grimes #define BUF 513 /* Maximum length of numeric string. */
6458f0484fSRodney W. Grimes
6558f0484fSRodney W. Grimes /*
6658f0484fSRodney W. Grimes * Flags used during conversion.
6758f0484fSRodney W. Grimes */
6858f0484fSRodney W. Grimes #define LONG 0x01 /* l: long or double */
698fddd060SBruce Evans #define LONGDBL 0x02 /* L: long double */
7058f0484fSRodney W. Grimes #define SHORT 0x04 /* h: short */
71946b2d00SBill Fenner #define SUPPRESS 0x08 /* *: suppress assignment */
72946b2d00SBill Fenner #define POINTER 0x10 /* p: void * (as hex) */
73946b2d00SBill Fenner #define NOSKIP 0x20 /* [ or c: do not skip blanks */
74*bce0bef3SDag-Erling Smørgrav #define FASTINT 0x200 /* wfN: int_fastN_t */
75946b2d00SBill Fenner #define LONGLONG 0x400 /* ll: long long (+ deprecated q: quad) */
76946b2d00SBill Fenner #define INTMAXT 0x800 /* j: intmax_t */
77946b2d00SBill Fenner #define PTRDIFFT 0x1000 /* t: ptrdiff_t */
78946b2d00SBill Fenner #define SIZET 0x2000 /* z: size_t */
79946b2d00SBill Fenner #define SHORTSHORT 0x4000 /* hh: char */
80946b2d00SBill Fenner #define UNSIGNED 0x8000 /* %[oupxX] conversions */
8158f0484fSRodney W. Grimes
8258f0484fSRodney W. Grimes /*
8358f0484fSRodney W. Grimes * Conversion types.
8458f0484fSRodney W. Grimes */
8558f0484fSRodney W. Grimes #define CT_CHAR 0 /* %c conversion */
8658f0484fSRodney W. Grimes #define CT_CCL 1 /* %[...] conversion */
8758f0484fSRodney W. Grimes #define CT_STRING 2 /* %s conversion */
88946b2d00SBill Fenner #define CT_INT 3 /* %[dioupxX] conversion */
89946b2d00SBill Fenner #define CT_FLOAT 4 /* %[efgEFG] conversion */
9058f0484fSRodney W. Grimes
91946b2d00SBill Fenner static const u_char *__sccl(char *, const u_char *);
9275239a01SPoul-Henning Kamp #ifndef NO_FLOATING_POINT
933c87aa1dSDavid Chisnall static int parsefloat(FILE *, char *, char *, locale_t);
9475239a01SPoul-Henning Kamp #endif
95370077c7SDavid Schultz
96af1c9c0eSTim J. Robbins __weak_reference(__vfscanf, vfscanf);
97af1c9c0eSTim J. Robbins
9858f0484fSRodney W. Grimes /*
9951300896SDavid Schultz * Conversion functions are passed a pointer to this object instead of
10051300896SDavid Schultz * a real parameter to indicate that the assignment-suppression (*)
10151300896SDavid Schultz * flag was specified. We could use a NULL pointer to indicate this,
10251300896SDavid Schultz * but that would mask bugs in applications that call scanf() with a
10351300896SDavid Schultz * NULL pointer.
10451300896SDavid Schultz */
10551300896SDavid Schultz static const int suppress;
10651300896SDavid Schultz #define SUPPRESS_PTR ((void *)&suppress)
10751300896SDavid Schultz
10851300896SDavid Schultz static const mbstate_t initial_mbs;
10951300896SDavid Schultz
11051300896SDavid Schultz /*
11151300896SDavid Schultz * The following conversion functions return the number of characters consumed,
11251300896SDavid Schultz * or -1 on input failure. Character class conversion returns 0 on match
11351300896SDavid Schultz * failure.
11451300896SDavid Schultz */
11551300896SDavid Schultz
11651300896SDavid Schultz static __inline int
convert_char(FILE * fp,char * p,int width)117671c0336SJean-Sébastien Pédron convert_char(FILE *fp, char * p, int width)
11851300896SDavid Schultz {
119d7af8cf1SDavid Schultz int n;
12051300896SDavid Schultz
12151300896SDavid Schultz if (p == SUPPRESS_PTR) {
12251300896SDavid Schultz size_t sum = 0;
12351300896SDavid Schultz for (;;) {
12451300896SDavid Schultz if ((n = fp->_r) < width) {
12551300896SDavid Schultz sum += n;
12651300896SDavid Schultz width -= n;
12751300896SDavid Schultz fp->_p += n;
12851300896SDavid Schultz if (__srefill(fp)) {
12951300896SDavid Schultz if (sum == 0)
13051300896SDavid Schultz return (-1);
13151300896SDavid Schultz break;
13251300896SDavid Schultz }
13351300896SDavid Schultz } else {
13451300896SDavid Schultz sum += width;
13551300896SDavid Schultz fp->_r -= width;
13651300896SDavid Schultz fp->_p += width;
13751300896SDavid Schultz break;
13851300896SDavid Schultz }
13951300896SDavid Schultz }
140d7af8cf1SDavid Schultz return (sum);
14151300896SDavid Schultz } else {
14251300896SDavid Schultz size_t r = __fread(p, 1, width, fp);
14351300896SDavid Schultz
14451300896SDavid Schultz if (r == 0)
14551300896SDavid Schultz return (-1);
146d7af8cf1SDavid Schultz return (r);
14751300896SDavid Schultz }
14851300896SDavid Schultz }
14951300896SDavid Schultz
15051300896SDavid Schultz static __inline int
convert_wchar(FILE * fp,wchar_t * wcp,int width,locale_t locale)151d7af8cf1SDavid Schultz convert_wchar(FILE *fp, wchar_t *wcp, int width, locale_t locale)
15251300896SDavid Schultz {
15351300896SDavid Schultz mbstate_t mbs;
15451300896SDavid Schultz int n, nread;
155d7af8cf1SDavid Schultz wint_t wi;
15651300896SDavid Schultz
15751300896SDavid Schultz mbs = initial_mbs;
15851300896SDavid Schultz n = 0;
159d7af8cf1SDavid Schultz while (width-- != 0 &&
160d7af8cf1SDavid Schultz (wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF) {
161d7af8cf1SDavid Schultz if (wcp != SUPPRESS_PTR)
162d7af8cf1SDavid Schultz *wcp++ = (wchar_t)wi;
163d7af8cf1SDavid Schultz n += nread;
16451300896SDavid Schultz }
165d7af8cf1SDavid Schultz if (n == 0)
16651300896SDavid Schultz return (-1);
167d7af8cf1SDavid Schultz return (n);
16851300896SDavid Schultz }
16951300896SDavid Schultz
17051300896SDavid Schultz static __inline int
convert_ccl(FILE * fp,char * p,int width,const char * ccltab)171671c0336SJean-Sébastien Pédron convert_ccl(FILE *fp, char * p, int width, const char *ccltab)
17251300896SDavid Schultz {
17351300896SDavid Schultz char *p0;
17451300896SDavid Schultz int n;
17551300896SDavid Schultz
17651300896SDavid Schultz if (p == SUPPRESS_PTR) {
17751300896SDavid Schultz n = 0;
17851300896SDavid Schultz while (ccltab[*fp->_p]) {
17951300896SDavid Schultz n++, fp->_r--, fp->_p++;
18051300896SDavid Schultz if (--width == 0)
18151300896SDavid Schultz break;
18251300896SDavid Schultz if (fp->_r <= 0 && __srefill(fp)) {
18351300896SDavid Schultz if (n == 0)
18451300896SDavid Schultz return (-1);
18551300896SDavid Schultz break;
18651300896SDavid Schultz }
18751300896SDavid Schultz }
18851300896SDavid Schultz } else {
18951300896SDavid Schultz p0 = p;
19051300896SDavid Schultz while (ccltab[*fp->_p]) {
19151300896SDavid Schultz fp->_r--;
19251300896SDavid Schultz *p++ = *fp->_p++;
19351300896SDavid Schultz if (--width == 0)
19451300896SDavid Schultz break;
19551300896SDavid Schultz if (fp->_r <= 0 && __srefill(fp)) {
19651300896SDavid Schultz if (p == p0)
19751300896SDavid Schultz return (-1);
19851300896SDavid Schultz break;
19951300896SDavid Schultz }
20051300896SDavid Schultz }
20151300896SDavid Schultz n = p - p0;
20251300896SDavid Schultz if (n == 0)
20351300896SDavid Schultz return (0);
20451300896SDavid Schultz *p = 0;
20551300896SDavid Schultz }
20651300896SDavid Schultz return (n);
20751300896SDavid Schultz }
20851300896SDavid Schultz
20951300896SDavid Schultz static __inline int
convert_wccl(FILE * fp,wchar_t * wcp,int width,const char * ccltab,locale_t locale)210d7af8cf1SDavid Schultz convert_wccl(FILE *fp, wchar_t *wcp, int width, const char *ccltab,
211d7af8cf1SDavid Schultz locale_t locale)
21251300896SDavid Schultz {
21351300896SDavid Schultz mbstate_t mbs;
214d7af8cf1SDavid Schultz wint_t wi;
215d7af8cf1SDavid Schultz int n, nread;
21651300896SDavid Schultz
21751300896SDavid Schultz mbs = initial_mbs;
21851300896SDavid Schultz n = 0;
219d7af8cf1SDavid Schultz if (wcp == SUPPRESS_PTR) {
220d7af8cf1SDavid Schultz while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
221d7af8cf1SDavid Schultz width-- != 0 && ccltab[wctob(wi)])
222d7af8cf1SDavid Schultz n += nread;
223d7af8cf1SDavid Schultz if (wi != WEOF)
224d7af8cf1SDavid Schultz __ungetwc(wi, fp, __get_locale());
225d7af8cf1SDavid Schultz } else {
226d7af8cf1SDavid Schultz while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
227d7af8cf1SDavid Schultz width-- != 0 && ccltab[wctob(wi)]) {
228d7af8cf1SDavid Schultz *wcp++ = (wchar_t)wi;
229d7af8cf1SDavid Schultz n += nread;
23051300896SDavid Schultz }
231d7af8cf1SDavid Schultz if (wi != WEOF)
232d7af8cf1SDavid Schultz __ungetwc(wi, fp, __get_locale());
233d7af8cf1SDavid Schultz if (n == 0)
23451300896SDavid Schultz return (0);
235d7af8cf1SDavid Schultz *wcp = 0;
236d7af8cf1SDavid Schultz }
237d7af8cf1SDavid Schultz return (n);
23851300896SDavid Schultz }
23951300896SDavid Schultz
24051300896SDavid Schultz static __inline int
convert_string(FILE * fp,char * p,int width)241671c0336SJean-Sébastien Pédron convert_string(FILE *fp, char * p, int width)
24251300896SDavid Schultz {
24351300896SDavid Schultz char *p0;
24451300896SDavid Schultz int n;
24551300896SDavid Schultz
24651300896SDavid Schultz if (p == SUPPRESS_PTR) {
24751300896SDavid Schultz n = 0;
24851300896SDavid Schultz while (!isspace(*fp->_p)) {
24951300896SDavid Schultz n++, fp->_r--, fp->_p++;
25051300896SDavid Schultz if (--width == 0)
25151300896SDavid Schultz break;
25251300896SDavid Schultz if (fp->_r <= 0 && __srefill(fp))
25351300896SDavid Schultz break;
25451300896SDavid Schultz }
25551300896SDavid Schultz } else {
25651300896SDavid Schultz p0 = p;
25751300896SDavid Schultz while (!isspace(*fp->_p)) {
25851300896SDavid Schultz fp->_r--;
25951300896SDavid Schultz *p++ = *fp->_p++;
26051300896SDavid Schultz if (--width == 0)
26151300896SDavid Schultz break;
26251300896SDavid Schultz if (fp->_r <= 0 && __srefill(fp))
26351300896SDavid Schultz break;
26451300896SDavid Schultz }
26551300896SDavid Schultz *p = 0;
26651300896SDavid Schultz n = p - p0;
26751300896SDavid Schultz }
26851300896SDavid Schultz return (n);
26951300896SDavid Schultz }
27051300896SDavid Schultz
27151300896SDavid Schultz static __inline int
convert_wstring(FILE * fp,wchar_t * wcp,int width,locale_t locale)272d7af8cf1SDavid Schultz convert_wstring(FILE *fp, wchar_t *wcp, int width, locale_t locale)
27351300896SDavid Schultz {
27451300896SDavid Schultz mbstate_t mbs;
275d7af8cf1SDavid Schultz wint_t wi;
276d7af8cf1SDavid Schultz int n, nread;
27751300896SDavid Schultz
27851300896SDavid Schultz mbs = initial_mbs;
27951300896SDavid Schultz n = 0;
280d7af8cf1SDavid Schultz if (wcp == SUPPRESS_PTR) {
281d7af8cf1SDavid Schultz while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
282d7af8cf1SDavid Schultz width-- != 0 && !iswspace(wi))
283d7af8cf1SDavid Schultz n += nread;
284d7af8cf1SDavid Schultz if (wi != WEOF)
285d7af8cf1SDavid Schultz __ungetwc(wi, fp, __get_locale());
286d7af8cf1SDavid Schultz } else {
287d7af8cf1SDavid Schultz while ((wi = __fgetwc_mbs(fp, &mbs, &nread, locale)) != WEOF &&
288d7af8cf1SDavid Schultz width-- != 0 && !iswspace(wi)) {
289d7af8cf1SDavid Schultz *wcp++ = (wchar_t)wi;
290d7af8cf1SDavid Schultz n += nread;
29151300896SDavid Schultz }
292d7af8cf1SDavid Schultz if (wi != WEOF)
293d7af8cf1SDavid Schultz __ungetwc(wi, fp, __get_locale());
294d7af8cf1SDavid Schultz *wcp = '\0';
29551300896SDavid Schultz }
296d7af8cf1SDavid Schultz return (n);
29751300896SDavid Schultz }
29851300896SDavid Schultz
299d9dc1603SDag-Erling Smørgrav enum parseint_state {
300d9dc1603SDag-Erling Smørgrav begin,
301d9dc1603SDag-Erling Smørgrav havesign,
302d9dc1603SDag-Erling Smørgrav havezero,
303d9dc1603SDag-Erling Smørgrav haveprefix,
304d9dc1603SDag-Erling Smørgrav any,
305d9dc1603SDag-Erling Smørgrav };
306d9dc1603SDag-Erling Smørgrav
307d9dc1603SDag-Erling Smørgrav static __inline int
parseint_fsm(int c,enum parseint_state * state,int * base)308d9dc1603SDag-Erling Smørgrav parseint_fsm(int c, enum parseint_state *state, int *base)
309d9dc1603SDag-Erling Smørgrav {
310d9dc1603SDag-Erling Smørgrav switch (c) {
311d9dc1603SDag-Erling Smørgrav case '+':
312d9dc1603SDag-Erling Smørgrav case '-':
313d9dc1603SDag-Erling Smørgrav if (*state == begin) {
314d9dc1603SDag-Erling Smørgrav *state = havesign;
315d9dc1603SDag-Erling Smørgrav return 1;
316d9dc1603SDag-Erling Smørgrav }
317d9dc1603SDag-Erling Smørgrav break;
318d9dc1603SDag-Erling Smørgrav case '0':
319d9dc1603SDag-Erling Smørgrav if (*state == begin || *state == havesign) {
320d9dc1603SDag-Erling Smørgrav *state = havezero;
321d9dc1603SDag-Erling Smørgrav } else {
322d9dc1603SDag-Erling Smørgrav *state = any;
323d9dc1603SDag-Erling Smørgrav }
324d9dc1603SDag-Erling Smørgrav return 1;
325d9dc1603SDag-Erling Smørgrav case '1':
326d9dc1603SDag-Erling Smørgrav case '2':
327d9dc1603SDag-Erling Smørgrav case '3':
328d9dc1603SDag-Erling Smørgrav case '4':
329d9dc1603SDag-Erling Smørgrav case '5':
330d9dc1603SDag-Erling Smørgrav case '6':
331d9dc1603SDag-Erling Smørgrav case '7':
332d9dc1603SDag-Erling Smørgrav if (*state == havezero && *base == 0) {
333d9dc1603SDag-Erling Smørgrav *base = 8;
334d9dc1603SDag-Erling Smørgrav }
335d9dc1603SDag-Erling Smørgrav /* FALL THROUGH */
336d9dc1603SDag-Erling Smørgrav case '8':
337d9dc1603SDag-Erling Smørgrav case '9':
338d9dc1603SDag-Erling Smørgrav if (*state == begin ||
339d9dc1603SDag-Erling Smørgrav *state == havesign) {
340d9dc1603SDag-Erling Smørgrav if (*base == 0) {
341d9dc1603SDag-Erling Smørgrav *base = 10;
342d9dc1603SDag-Erling Smørgrav }
343d9dc1603SDag-Erling Smørgrav }
344d9dc1603SDag-Erling Smørgrav if (*state == begin ||
345d9dc1603SDag-Erling Smørgrav *state == havesign ||
346d9dc1603SDag-Erling Smørgrav *state == havezero ||
347d9dc1603SDag-Erling Smørgrav *state == haveprefix ||
348d9dc1603SDag-Erling Smørgrav *state == any) {
349d9dc1603SDag-Erling Smørgrav if (*base > c - '0') {
350d9dc1603SDag-Erling Smørgrav *state = any;
351d9dc1603SDag-Erling Smørgrav return 1;
352d9dc1603SDag-Erling Smørgrav }
353d9dc1603SDag-Erling Smørgrav }
354d9dc1603SDag-Erling Smørgrav break;
355d9dc1603SDag-Erling Smørgrav case 'b':
356d9dc1603SDag-Erling Smørgrav if (*state == havezero) {
357d9dc1603SDag-Erling Smørgrav if (*base == 0 || *base == 2) {
358d9dc1603SDag-Erling Smørgrav *state = haveprefix;
359d9dc1603SDag-Erling Smørgrav *base = 2;
360d9dc1603SDag-Erling Smørgrav return 1;
361d9dc1603SDag-Erling Smørgrav }
362d9dc1603SDag-Erling Smørgrav }
363d9dc1603SDag-Erling Smørgrav /* FALL THROUGH */
364d9dc1603SDag-Erling Smørgrav case 'a':
365d9dc1603SDag-Erling Smørgrav case 'c':
366d9dc1603SDag-Erling Smørgrav case 'd':
367d9dc1603SDag-Erling Smørgrav case 'e':
368d9dc1603SDag-Erling Smørgrav case 'f':
369d9dc1603SDag-Erling Smørgrav if (*state == begin ||
370d9dc1603SDag-Erling Smørgrav *state == havesign ||
371d9dc1603SDag-Erling Smørgrav *state == havezero ||
372d9dc1603SDag-Erling Smørgrav *state == haveprefix ||
373d9dc1603SDag-Erling Smørgrav *state == any) {
374d9dc1603SDag-Erling Smørgrav if (*base > c - 'a' + 10) {
375d9dc1603SDag-Erling Smørgrav *state = any;
376d9dc1603SDag-Erling Smørgrav return 1;
377d9dc1603SDag-Erling Smørgrav }
378d9dc1603SDag-Erling Smørgrav }
379d9dc1603SDag-Erling Smørgrav break;
380d9dc1603SDag-Erling Smørgrav case 'B':
381d9dc1603SDag-Erling Smørgrav if (*state == havezero) {
382d9dc1603SDag-Erling Smørgrav if (*base == 0 || *base == 2) {
383d9dc1603SDag-Erling Smørgrav *state = haveprefix;
384d9dc1603SDag-Erling Smørgrav *base = 2;
385d9dc1603SDag-Erling Smørgrav return 1;
386d9dc1603SDag-Erling Smørgrav }
387d9dc1603SDag-Erling Smørgrav }
388d9dc1603SDag-Erling Smørgrav /* FALL THROUGH */
389d9dc1603SDag-Erling Smørgrav case 'A':
390d9dc1603SDag-Erling Smørgrav case 'C':
391d9dc1603SDag-Erling Smørgrav case 'D':
392d9dc1603SDag-Erling Smørgrav case 'E':
393d9dc1603SDag-Erling Smørgrav case 'F':
394d9dc1603SDag-Erling Smørgrav if (*state == begin ||
395d9dc1603SDag-Erling Smørgrav *state == havesign ||
396d9dc1603SDag-Erling Smørgrav *state == havezero ||
397d9dc1603SDag-Erling Smørgrav *state == haveprefix ||
398d9dc1603SDag-Erling Smørgrav *state == any) {
399d9dc1603SDag-Erling Smørgrav if (*base > c - 'A' + 10) {
400d9dc1603SDag-Erling Smørgrav *state = any;
401d9dc1603SDag-Erling Smørgrav return 1;
402d9dc1603SDag-Erling Smørgrav }
403d9dc1603SDag-Erling Smørgrav }
404d9dc1603SDag-Erling Smørgrav break;
405d9dc1603SDag-Erling Smørgrav case 'x':
406d9dc1603SDag-Erling Smørgrav case 'X':
407d9dc1603SDag-Erling Smørgrav if (*state == havezero) {
408d9dc1603SDag-Erling Smørgrav if (*base == 0 || *base == 16) {
409d9dc1603SDag-Erling Smørgrav *state = haveprefix;
410d9dc1603SDag-Erling Smørgrav *base = 16;
411d9dc1603SDag-Erling Smørgrav return 1;
412d9dc1603SDag-Erling Smørgrav }
413d9dc1603SDag-Erling Smørgrav }
414d9dc1603SDag-Erling Smørgrav break;
415d9dc1603SDag-Erling Smørgrav }
416d9dc1603SDag-Erling Smørgrav return 0;
417d9dc1603SDag-Erling Smørgrav }
418d9dc1603SDag-Erling Smørgrav
41951300896SDavid Schultz /*
420d9dc1603SDag-Erling Smørgrav * Read an integer, storing it in buf.
42151300896SDavid Schultz *
42251300896SDavid Schultz * Return 0 on a match failure, and the number of characters read
42351300896SDavid Schultz * otherwise.
42451300896SDavid Schultz */
42551300896SDavid Schultz static __inline int
parseint(FILE * fp,char * __restrict buf,int width,int base)426d9dc1603SDag-Erling Smørgrav parseint(FILE *fp, char * __restrict buf, int width, int base)
42751300896SDavid Schultz {
428d9dc1603SDag-Erling Smørgrav enum parseint_state state = begin;
42951300896SDavid Schultz char *p;
43051300896SDavid Schultz int c;
43151300896SDavid Schultz
43251300896SDavid Schultz for (p = buf; width; width--) {
433d9dc1603SDag-Erling Smørgrav c = __sgetc(fp);
434d9dc1603SDag-Erling Smørgrav if (c == EOF)
43551300896SDavid Schultz break;
436d9dc1603SDag-Erling Smørgrav if (!parseint_fsm(c, &state, &base))
43751300896SDavid Schultz break;
43851300896SDavid Schultz *p++ = c;
43951300896SDavid Schultz }
44051300896SDavid Schultz /*
441d9dc1603SDag-Erling Smørgrav * If we only had a sign, push it back. If we only had a 0b or 0x
442d9dc1603SDag-Erling Smørgrav * prefix (possibly preceded by a sign), we view it as "0" and
443d9dc1603SDag-Erling Smørgrav * push back the letter. In all other cases, if we stopped
444d9dc1603SDag-Erling Smørgrav * because we read a non-number character, push it back.
44551300896SDavid Schultz */
446d9dc1603SDag-Erling Smørgrav if (state == havesign) {
447d9dc1603SDag-Erling Smørgrav p--;
448d9dc1603SDag-Erling Smørgrav (void) __ungetc(*(u_char *)p, fp);
449d9dc1603SDag-Erling Smørgrav } else if (state == haveprefix) {
450d9dc1603SDag-Erling Smørgrav p--;
451d9dc1603SDag-Erling Smørgrav (void) __ungetc(c, fp);
452aca3bd16SDag-Erling Smørgrav } else if (width && c != EOF) {
45351300896SDavid Schultz (void) __ungetc(c, fp);
45451300896SDavid Schultz }
45551300896SDavid Schultz return (p - buf);
45651300896SDavid Schultz }
45751300896SDavid Schultz
45851300896SDavid Schultz /*
459d201fe46SDaniel Eischen * __vfscanf - MT-safe version
46058f0484fSRodney W. Grimes */
461ce51cf03SJames Raynard int
__vfscanf(FILE * fp,char const * fmt0,va_list ap)462d201fe46SDaniel Eischen __vfscanf(FILE *fp, char const *fmt0, va_list ap)
46358f0484fSRodney W. Grimes {
464d201fe46SDaniel Eischen int ret;
465d201fe46SDaniel Eischen
466fda0a14fSKonstantin Belousov FLOCKFILE_CANCELSAFE(fp);
4673c87aa1dSDavid Chisnall ret = __svfscanf(fp, __get_locale(), fmt0, ap);
468fda0a14fSKonstantin Belousov FUNLOCKFILE_CANCELSAFE();
4693c87aa1dSDavid Chisnall return (ret);
4703c87aa1dSDavid Chisnall }
4713c87aa1dSDavid Chisnall int
vfscanf_l(FILE * fp,locale_t locale,char const * fmt0,va_list ap)4723c87aa1dSDavid Chisnall vfscanf_l(FILE *fp, locale_t locale, char const *fmt0, va_list ap)
4733c87aa1dSDavid Chisnall {
4743c87aa1dSDavid Chisnall int ret;
4753c87aa1dSDavid Chisnall FIX_LOCALE(locale);
4763c87aa1dSDavid Chisnall
477fda0a14fSKonstantin Belousov FLOCKFILE_CANCELSAFE(fp);
4783c87aa1dSDavid Chisnall ret = __svfscanf(fp, locale, fmt0, ap);
479fda0a14fSKonstantin Belousov FUNLOCKFILE_CANCELSAFE();
480d201fe46SDaniel Eischen return (ret);
481d201fe46SDaniel Eischen }
482d201fe46SDaniel Eischen
483d201fe46SDaniel Eischen /*
484d201fe46SDaniel Eischen * __svfscanf - non-MT-safe version of __vfscanf
485d201fe46SDaniel Eischen */
486d201fe46SDaniel Eischen int
__svfscanf(FILE * fp,locale_t locale,const char * fmt0,va_list ap)4873c87aa1dSDavid Chisnall __svfscanf(FILE *fp, locale_t locale, const char *fmt0, va_list ap)
488d201fe46SDaniel Eischen {
48951300896SDavid Schultz #define GETARG(type) ((flags & SUPPRESS) ? SUPPRESS_PTR : va_arg(ap, type))
490946b2d00SBill Fenner const u_char *fmt = (const u_char *)fmt0;
491d201fe46SDaniel Eischen int c; /* character from format, or conversion */
492d201fe46SDaniel Eischen size_t width; /* field width, or 0 */
493d201fe46SDaniel Eischen int flags; /* flags as defined above */
49458f0484fSRodney W. Grimes int nassigned; /* number of fields assigned */
495e836e480SBruce Evans int nconversions; /* number of conversions */
49651300896SDavid Schultz int nr; /* characters read by the current conversion */
49758f0484fSRodney W. Grimes int nread; /* number of characters consumed from fp */
498946b2d00SBill Fenner int base; /* base argument to conversion function */
49958f0484fSRodney W. Grimes char ccltab[256]; /* character class table for %[...] */
50051300896SDavid Schultz char buf[BUF]; /* buffer for numeric conversions */
50158f0484fSRodney W. Grimes
502e74101e4STim J. Robbins ORIENT(fp, -1);
503e74101e4STim J. Robbins
50458f0484fSRodney W. Grimes nassigned = 0;
505e836e480SBruce Evans nconversions = 0;
50658f0484fSRodney W. Grimes nread = 0;
50758f0484fSRodney W. Grimes for (;;) {
50858f0484fSRodney W. Grimes c = *fmt++;
50958f0484fSRodney W. Grimes if (c == 0)
51058f0484fSRodney W. Grimes return (nassigned);
51158f0484fSRodney W. Grimes if (isspace(c)) {
5125846581cSDavid E. O'Brien while ((fp->_r > 0 || __srefill(fp) == 0) && isspace(*fp->_p))
51358f0484fSRodney W. Grimes nread++, fp->_r--, fp->_p++;
51458f0484fSRodney W. Grimes continue;
51558f0484fSRodney W. Grimes }
51658f0484fSRodney W. Grimes if (c != '%')
51758f0484fSRodney W. Grimes goto literal;
51858f0484fSRodney W. Grimes width = 0;
51958f0484fSRodney W. Grimes flags = 0;
52058f0484fSRodney W. Grimes /*
52158f0484fSRodney W. Grimes * switch on the format. continue if done;
52258f0484fSRodney W. Grimes * break once format type is derived.
52358f0484fSRodney W. Grimes */
52458f0484fSRodney W. Grimes again: c = *fmt++;
52558f0484fSRodney W. Grimes switch (c) {
52658f0484fSRodney W. Grimes case '%':
52758f0484fSRodney W. Grimes literal:
52858f0484fSRodney W. Grimes if (fp->_r <= 0 && __srefill(fp))
52958f0484fSRodney W. Grimes goto input_failure;
53058f0484fSRodney W. Grimes if (*fp->_p != c)
53158f0484fSRodney W. Grimes goto match_failure;
53258f0484fSRodney W. Grimes fp->_r--, fp->_p++;
53358f0484fSRodney W. Grimes nread++;
53458f0484fSRodney W. Grimes continue;
53558f0484fSRodney W. Grimes
53658f0484fSRodney W. Grimes case '*':
53758f0484fSRodney W. Grimes flags |= SUPPRESS;
53858f0484fSRodney W. Grimes goto again;
539946b2d00SBill Fenner case 'j':
540946b2d00SBill Fenner flags |= INTMAXT;
541946b2d00SBill Fenner goto again;
54258f0484fSRodney W. Grimes case 'l':
543946b2d00SBill Fenner if (flags & LONG) {
544946b2d00SBill Fenner flags &= ~LONG;
545946b2d00SBill Fenner flags |= LONGLONG;
546946b2d00SBill Fenner } else
54758f0484fSRodney W. Grimes flags |= LONG;
54858f0484fSRodney W. Grimes goto again;
5495e17038fSJordan K. Hubbard case 'q':
550946b2d00SBill Fenner flags |= LONGLONG; /* not quite */
551946b2d00SBill Fenner goto again;
552946b2d00SBill Fenner case 't':
553946b2d00SBill Fenner flags |= PTRDIFFT;
554946b2d00SBill Fenner goto again;
555*bce0bef3SDag-Erling Smørgrav case 'w':
556*bce0bef3SDag-Erling Smørgrav /*
557*bce0bef3SDag-Erling Smørgrav * Fixed-width integer types. On all platforms we
558*bce0bef3SDag-Erling Smørgrav * support, int8_t is equivalent to char, int16_t
559*bce0bef3SDag-Erling Smørgrav * is equivalent to short, int32_t is equivalent
560*bce0bef3SDag-Erling Smørgrav * to int, int64_t is equivalent to long long int.
561*bce0bef3SDag-Erling Smørgrav * Furthermore, int_fast8_t, int_fast16_t and
562*bce0bef3SDag-Erling Smørgrav * int_fast32_t are equivalent to int, and
563*bce0bef3SDag-Erling Smørgrav * int_fast64_t is equivalent to long long int.
564*bce0bef3SDag-Erling Smørgrav */
565*bce0bef3SDag-Erling Smørgrav flags &= ~(SHORTSHORT|SHORT|LONG|LONGLONG|SIZET|INTMAXT|PTRDIFFT);
566*bce0bef3SDag-Erling Smørgrav if (fmt[0] == 'f') {
567*bce0bef3SDag-Erling Smørgrav flags |= FASTINT;
568*bce0bef3SDag-Erling Smørgrav fmt++;
569*bce0bef3SDag-Erling Smørgrav } else {
570*bce0bef3SDag-Erling Smørgrav flags &= ~FASTINT;
571*bce0bef3SDag-Erling Smørgrav }
572*bce0bef3SDag-Erling Smørgrav if (fmt[0] == '8') {
573*bce0bef3SDag-Erling Smørgrav if (!(flags & FASTINT))
574*bce0bef3SDag-Erling Smørgrav flags |= SHORTSHORT;
575*bce0bef3SDag-Erling Smørgrav else
576*bce0bef3SDag-Erling Smørgrav /* no flag set = 32 */ ;
577*bce0bef3SDag-Erling Smørgrav fmt += 1;
578*bce0bef3SDag-Erling Smørgrav } else if (fmt[0] == '1' && fmt[1] == '6') {
579*bce0bef3SDag-Erling Smørgrav if (!(flags & FASTINT))
580*bce0bef3SDag-Erling Smørgrav flags |= SHORT;
581*bce0bef3SDag-Erling Smørgrav else
582*bce0bef3SDag-Erling Smørgrav /* no flag set = 32 */ ;
583*bce0bef3SDag-Erling Smørgrav fmt += 2;
584*bce0bef3SDag-Erling Smørgrav } else if (fmt[0] == '3' && fmt[1] == '2') {
585*bce0bef3SDag-Erling Smørgrav /* no flag set = 32 */ ;
586*bce0bef3SDag-Erling Smørgrav fmt += 2;
587*bce0bef3SDag-Erling Smørgrav } else if (fmt[0] == '6' && fmt[1] == '4') {
588*bce0bef3SDag-Erling Smørgrav flags |= LONGLONG;
589*bce0bef3SDag-Erling Smørgrav fmt += 2;
590*bce0bef3SDag-Erling Smørgrav } else {
591*bce0bef3SDag-Erling Smørgrav goto match_failure;
592*bce0bef3SDag-Erling Smørgrav }
593*bce0bef3SDag-Erling Smørgrav goto again;
594946b2d00SBill Fenner case 'z':
595946b2d00SBill Fenner flags |= SIZET;
5965e17038fSJordan K. Hubbard goto again;
59758f0484fSRodney W. Grimes case 'L':
59858f0484fSRodney W. Grimes flags |= LONGDBL;
59958f0484fSRodney W. Grimes goto again;
60058f0484fSRodney W. Grimes case 'h':
601946b2d00SBill Fenner if (flags & SHORT) {
602946b2d00SBill Fenner flags &= ~SHORT;
603946b2d00SBill Fenner flags |= SHORTSHORT;
604946b2d00SBill Fenner } else
60558f0484fSRodney W. Grimes flags |= SHORT;
60658f0484fSRodney W. Grimes goto again;
60758f0484fSRodney W. Grimes
60858f0484fSRodney W. Grimes case '0': case '1': case '2': case '3': case '4':
60958f0484fSRodney W. Grimes case '5': case '6': case '7': case '8': case '9':
61058f0484fSRodney W. Grimes width = width * 10 + c - '0';
61158f0484fSRodney W. Grimes goto again;
61258f0484fSRodney W. Grimes
61358f0484fSRodney W. Grimes /*
61458f0484fSRodney W. Grimes * Conversions.
61558f0484fSRodney W. Grimes */
616d9dc1603SDag-Erling Smørgrav case 'B':
617d9dc1603SDag-Erling Smørgrav case 'b':
618d9dc1603SDag-Erling Smørgrav c = CT_INT;
619d9dc1603SDag-Erling Smørgrav flags |= UNSIGNED;
620d9dc1603SDag-Erling Smørgrav base = 2;
621d9dc1603SDag-Erling Smørgrav break;
622d9dc1603SDag-Erling Smørgrav
62358f0484fSRodney W. Grimes case 'd':
62458f0484fSRodney W. Grimes c = CT_INT;
62558f0484fSRodney W. Grimes base = 10;
62658f0484fSRodney W. Grimes break;
62758f0484fSRodney W. Grimes
62858f0484fSRodney W. Grimes case 'i':
62958f0484fSRodney W. Grimes c = CT_INT;
63058f0484fSRodney W. Grimes base = 0;
63158f0484fSRodney W. Grimes break;
63258f0484fSRodney W. Grimes
63358f0484fSRodney W. Grimes case 'o':
63458f0484fSRodney W. Grimes c = CT_INT;
635946b2d00SBill Fenner flags |= UNSIGNED;
63658f0484fSRodney W. Grimes base = 8;
63758f0484fSRodney W. Grimes break;
63858f0484fSRodney W. Grimes
63958f0484fSRodney W. Grimes case 'u':
64058f0484fSRodney W. Grimes c = CT_INT;
641946b2d00SBill Fenner flags |= UNSIGNED;
64258f0484fSRodney W. Grimes base = 10;
64358f0484fSRodney W. Grimes break;
64458f0484fSRodney W. Grimes
645946b2d00SBill Fenner case 'X':
64658f0484fSRodney W. Grimes case 'x':
64758f0484fSRodney W. Grimes c = CT_INT;
648946b2d00SBill Fenner flags |= UNSIGNED;
64958f0484fSRodney W. Grimes base = 16;
65058f0484fSRodney W. Grimes break;
65158f0484fSRodney W. Grimes
6528de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
653370077c7SDavid Schultz case 'A': case 'E': case 'F': case 'G':
654370077c7SDavid Schultz case 'a': case 'e': case 'f': case 'g':
65558f0484fSRodney W. Grimes c = CT_FLOAT;
65658f0484fSRodney W. Grimes break;
65758f0484fSRodney W. Grimes #endif
65858f0484fSRodney W. Grimes
659946b2d00SBill Fenner case 'S':
660946b2d00SBill Fenner flags |= LONG;
661946b2d00SBill Fenner /* FALLTHROUGH */
66258f0484fSRodney W. Grimes case 's':
66358f0484fSRodney W. Grimes c = CT_STRING;
66458f0484fSRodney W. Grimes break;
66558f0484fSRodney W. Grimes
66658f0484fSRodney W. Grimes case '[':
66758f0484fSRodney W. Grimes fmt = __sccl(ccltab, fmt);
66858f0484fSRodney W. Grimes flags |= NOSKIP;
66958f0484fSRodney W. Grimes c = CT_CCL;
67058f0484fSRodney W. Grimes break;
67158f0484fSRodney W. Grimes
672946b2d00SBill Fenner case 'C':
673946b2d00SBill Fenner flags |= LONG;
674946b2d00SBill Fenner /* FALLTHROUGH */
67558f0484fSRodney W. Grimes case 'c':
67658f0484fSRodney W. Grimes flags |= NOSKIP;
67758f0484fSRodney W. Grimes c = CT_CHAR;
67858f0484fSRodney W. Grimes break;
67958f0484fSRodney W. Grimes
68058f0484fSRodney W. Grimes case 'p': /* pointer format is like hex */
681d9dc1603SDag-Erling Smørgrav flags |= POINTER;
682946b2d00SBill Fenner c = CT_INT; /* assumes sizeof(uintmax_t) */
683946b2d00SBill Fenner flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
68458f0484fSRodney W. Grimes base = 16;
68558f0484fSRodney W. Grimes break;
68658f0484fSRodney W. Grimes
68758f0484fSRodney W. Grimes case 'n':
68858f0484fSRodney W. Grimes if (flags & SUPPRESS) /* ??? */
68958f0484fSRodney W. Grimes continue;
690946b2d00SBill Fenner if (flags & SHORTSHORT)
691946b2d00SBill Fenner *va_arg(ap, char *) = nread;
692946b2d00SBill Fenner else if (flags & SHORT)
69358f0484fSRodney W. Grimes *va_arg(ap, short *) = nread;
69458f0484fSRodney W. Grimes else if (flags & LONG)
69558f0484fSRodney W. Grimes *va_arg(ap, long *) = nread;
696946b2d00SBill Fenner else if (flags & LONGLONG)
697946b2d00SBill Fenner *va_arg(ap, long long *) = nread;
698946b2d00SBill Fenner else if (flags & INTMAXT)
699946b2d00SBill Fenner *va_arg(ap, intmax_t *) = nread;
700946b2d00SBill Fenner else if (flags & SIZET)
701946b2d00SBill Fenner *va_arg(ap, size_t *) = nread;
702946b2d00SBill Fenner else if (flags & PTRDIFFT)
703946b2d00SBill Fenner *va_arg(ap, ptrdiff_t *) = nread;
70458f0484fSRodney W. Grimes else
70558f0484fSRodney W. Grimes *va_arg(ap, int *) = nread;
70658f0484fSRodney W. Grimes continue;
70758f0484fSRodney W. Grimes
708946b2d00SBill Fenner default:
709946b2d00SBill Fenner goto match_failure;
710946b2d00SBill Fenner
71158f0484fSRodney W. Grimes /*
712946b2d00SBill Fenner * Disgusting backwards compatibility hack. XXX
71358f0484fSRodney W. Grimes */
71458f0484fSRodney W. Grimes case '\0': /* compat */
71558f0484fSRodney W. Grimes return (EOF);
71658f0484fSRodney W. Grimes }
71758f0484fSRodney W. Grimes
71858f0484fSRodney W. Grimes /*
71958f0484fSRodney W. Grimes * We have a conversion that requires input.
72058f0484fSRodney W. Grimes */
72158f0484fSRodney W. Grimes if (fp->_r <= 0 && __srefill(fp))
72258f0484fSRodney W. Grimes goto input_failure;
72358f0484fSRodney W. Grimes
72458f0484fSRodney W. Grimes /*
72558f0484fSRodney W. Grimes * Consume leading white space, except for formats
72658f0484fSRodney W. Grimes * that suppress this.
72758f0484fSRodney W. Grimes */
72858f0484fSRodney W. Grimes if ((flags & NOSKIP) == 0) {
72958f0484fSRodney W. Grimes while (isspace(*fp->_p)) {
73058f0484fSRodney W. Grimes nread++;
73158f0484fSRodney W. Grimes if (--fp->_r > 0)
73258f0484fSRodney W. Grimes fp->_p++;
73358f0484fSRodney W. Grimes else if (__srefill(fp))
73458f0484fSRodney W. Grimes goto input_failure;
73558f0484fSRodney W. Grimes }
73658f0484fSRodney W. Grimes /*
73758f0484fSRodney W. Grimes * Note that there is at least one character in
73858f0484fSRodney W. Grimes * the buffer, so conversions that do not set NOSKIP
73958f0484fSRodney W. Grimes * ca no longer result in an input failure.
74058f0484fSRodney W. Grimes */
74158f0484fSRodney W. Grimes }
74258f0484fSRodney W. Grimes
74358f0484fSRodney W. Grimes /*
74458f0484fSRodney W. Grimes * Do the conversion.
74558f0484fSRodney W. Grimes */
74658f0484fSRodney W. Grimes switch (c) {
74758f0484fSRodney W. Grimes
74858f0484fSRodney W. Grimes case CT_CHAR:
74958f0484fSRodney W. Grimes /* scan arbitrary characters (sets NOSKIP) */
75058f0484fSRodney W. Grimes if (width == 0)
75158f0484fSRodney W. Grimes width = 1;
75235739e07STim J. Robbins if (flags & LONG) {
75351300896SDavid Schultz nr = convert_wchar(fp, GETARG(wchar_t *),
754d7af8cf1SDavid Schultz width, locale);
75558f0484fSRodney W. Grimes } else {
75651300896SDavid Schultz nr = convert_char(fp, GETARG(char *), width);
75758f0484fSRodney W. Grimes }
75851300896SDavid Schultz if (nr < 0)
75958f0484fSRodney W. Grimes goto input_failure;
76058f0484fSRodney W. Grimes break;
76158f0484fSRodney W. Grimes
76258f0484fSRodney W. Grimes case CT_CCL:
76358f0484fSRodney W. Grimes /* scan a (nonempty) character class (sets NOSKIP) */
76458f0484fSRodney W. Grimes if (width == 0)
765ce51cf03SJames Raynard width = (size_t)~0; /* `infinity' */
76635739e07STim J. Robbins if (flags & LONG) {
76751300896SDavid Schultz nr = convert_wccl(fp, GETARG(wchar_t *), width,
768d7af8cf1SDavid Schultz ccltab, locale);
76958f0484fSRodney W. Grimes } else {
77051300896SDavid Schultz nr = convert_ccl(fp, GETARG(char *), width,
77151300896SDavid Schultz ccltab);
77251300896SDavid Schultz }
77351300896SDavid Schultz if (nr <= 0) {
77451300896SDavid Schultz if (nr < 0)
77558f0484fSRodney W. Grimes goto input_failure;
77651300896SDavid Schultz else /* nr == 0 */
77758f0484fSRodney W. Grimes goto match_failure;
77858f0484fSRodney W. Grimes }
77958f0484fSRodney W. Grimes break;
78058f0484fSRodney W. Grimes
78158f0484fSRodney W. Grimes case CT_STRING:
78258f0484fSRodney W. Grimes /* like CCL, but zero-length string OK, & no NOSKIP */
78358f0484fSRodney W. Grimes if (width == 0)
784ce51cf03SJames Raynard width = (size_t)~0;
78535739e07STim J. Robbins if (flags & LONG) {
78651300896SDavid Schultz nr = convert_wstring(fp, GETARG(wchar_t *),
787d7af8cf1SDavid Schultz width, locale);
78858f0484fSRodney W. Grimes } else {
78951300896SDavid Schultz nr = convert_string(fp, GETARG(char *), width);
79058f0484fSRodney W. Grimes }
79151300896SDavid Schultz if (nr < 0)
79251300896SDavid Schultz goto input_failure;
79351300896SDavid Schultz break;
79458f0484fSRodney W. Grimes
79558f0484fSRodney W. Grimes case CT_INT:
796946b2d00SBill Fenner /* scan an integer as if by the conversion function */
79758f0484fSRodney W. Grimes #ifdef hardway
79858f0484fSRodney W. Grimes if (width == 0 || width > sizeof(buf) - 1)
79958f0484fSRodney W. Grimes width = sizeof(buf) - 1;
80058f0484fSRodney W. Grimes #else
80158f0484fSRodney W. Grimes /* size_t is unsigned, hence this optimisation */
80258f0484fSRodney W. Grimes if (--width > sizeof(buf) - 2)
80358f0484fSRodney W. Grimes width = sizeof(buf) - 2;
80458f0484fSRodney W. Grimes width++;
80558f0484fSRodney W. Grimes #endif
806d9dc1603SDag-Erling Smørgrav nr = parseint(fp, buf, width, base);
80751300896SDavid Schultz if (nr == 0)
80858f0484fSRodney W. Grimes goto match_failure;
80958f0484fSRodney W. Grimes if ((flags & SUPPRESS) == 0) {
810946b2d00SBill Fenner uintmax_t res;
81158f0484fSRodney W. Grimes
81251300896SDavid Schultz buf[nr] = '\0';
813946b2d00SBill Fenner if ((flags & UNSIGNED) == 0)
8143c87aa1dSDavid Chisnall res = strtoimax_l(buf, (char **)NULL, base, locale);
815946b2d00SBill Fenner else
8163c87aa1dSDavid Chisnall res = strtoumax_l(buf, (char **)NULL, base, locale);
81758f0484fSRodney W. Grimes if (flags & POINTER)
8185e17038fSJordan K. Hubbard *va_arg(ap, void **) =
819946b2d00SBill Fenner (void *)(uintptr_t)res;
820946b2d00SBill Fenner else if (flags & SHORTSHORT)
821946b2d00SBill Fenner *va_arg(ap, char *) = res;
82258f0484fSRodney W. Grimes else if (flags & SHORT)
82358f0484fSRodney W. Grimes *va_arg(ap, short *) = res;
82458f0484fSRodney W. Grimes else if (flags & LONG)
82558f0484fSRodney W. Grimes *va_arg(ap, long *) = res;
826946b2d00SBill Fenner else if (flags & LONGLONG)
827946b2d00SBill Fenner *va_arg(ap, long long *) = res;
828946b2d00SBill Fenner else if (flags & INTMAXT)
829946b2d00SBill Fenner *va_arg(ap, intmax_t *) = res;
830946b2d00SBill Fenner else if (flags & PTRDIFFT)
831946b2d00SBill Fenner *va_arg(ap, ptrdiff_t *) = res;
832946b2d00SBill Fenner else if (flags & SIZET)
833946b2d00SBill Fenner *va_arg(ap, size_t *) = res;
83458f0484fSRodney W. Grimes else
83558f0484fSRodney W. Grimes *va_arg(ap, int *) = res;
83658f0484fSRodney W. Grimes }
83758f0484fSRodney W. Grimes break;
83858f0484fSRodney W. Grimes
8398de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
84058f0484fSRodney W. Grimes case CT_FLOAT:
84158f0484fSRodney W. Grimes /* scan a floating point number as if by strtod */
84258f0484fSRodney W. Grimes if (width == 0 || width > sizeof(buf) - 1)
84358f0484fSRodney W. Grimes width = sizeof(buf) - 1;
84451300896SDavid Schultz nr = parsefloat(fp, buf, buf + width, locale);
84551300896SDavid Schultz if (nr == 0)
84658f0484fSRodney W. Grimes goto match_failure;
84758f0484fSRodney W. Grimes if ((flags & SUPPRESS) == 0) {
848370077c7SDavid Schultz if (flags & LONGDBL) {
84951300896SDavid Schultz long double res = strtold_l(buf, NULL,
85051300896SDavid Schultz locale);
8518fddd060SBruce Evans *va_arg(ap, long double *) = res;
852370077c7SDavid Schultz } else if (flags & LONG) {
85351300896SDavid Schultz double res = strtod_l(buf, NULL,
85451300896SDavid Schultz locale);
85558f0484fSRodney W. Grimes *va_arg(ap, double *) = res;
856370077c7SDavid Schultz } else {
85751300896SDavid Schultz float res = strtof_l(buf, NULL, locale);
85858f0484fSRodney W. Grimes *va_arg(ap, float *) = res;
859370077c7SDavid Schultz }
86058f0484fSRodney W. Grimes }
86158f0484fSRodney W. Grimes break;
8628de9e897SDavid Schultz #endif /* !NO_FLOATING_POINT */
86358f0484fSRodney W. Grimes }
86451300896SDavid Schultz if (!(flags & SUPPRESS))
86551300896SDavid Schultz nassigned++;
86651300896SDavid Schultz nread += nr;
86751300896SDavid Schultz nconversions++;
86858f0484fSRodney W. Grimes }
86958f0484fSRodney W. Grimes input_failure:
870e836e480SBruce Evans return (nconversions != 0 ? nassigned : EOF);
87158f0484fSRodney W. Grimes match_failure:
87258f0484fSRodney W. Grimes return (nassigned);
87358f0484fSRodney W. Grimes }
87458f0484fSRodney W. Grimes
87558f0484fSRodney W. Grimes /*
87658f0484fSRodney W. Grimes * Fill in the given table from the scanset at the given format
87758f0484fSRodney W. Grimes * (just after `['). Return a pointer to the character past the
87858f0484fSRodney W. Grimes * closing `]'. The table has a 1 wherever characters should be
87958f0484fSRodney W. Grimes * considered part of the scanset.
88058f0484fSRodney W. Grimes */
881946b2d00SBill Fenner static const u_char *
__sccl(char * tab,const u_char * fmt)88225070501SCraig Rodrigues __sccl(char *tab, const u_char *fmt)
88358f0484fSRodney W. Grimes {
8841daad8f5SAndrey A. Chernov int c, n, v, i;
8851daad8f5SAndrey A. Chernov struct xlocale_collate *table =
8861daad8f5SAndrey A. Chernov (struct xlocale_collate*)__get_locale()->components[XLC_COLLATE];
88758f0484fSRodney W. Grimes
88858f0484fSRodney W. Grimes /* first `clear' the whole table */
88958f0484fSRodney W. Grimes c = *fmt++; /* first char hat => negated scanset */
89058f0484fSRodney W. Grimes if (c == '^') {
89158f0484fSRodney W. Grimes v = 1; /* default => accept */
89258f0484fSRodney W. Grimes c = *fmt++; /* get new first char */
89358f0484fSRodney W. Grimes } else
89458f0484fSRodney W. Grimes v = 0; /* default => reject */
895628abd1bSAndrey A. Chernov
896628abd1bSAndrey A. Chernov /* XXX: Will not work if sizeof(tab*) > sizeof(char) */
897ea295661SAndrey A. Chernov (void) memset(tab, v, 256);
898628abd1bSAndrey A. Chernov
89958f0484fSRodney W. Grimes if (c == 0)
90058f0484fSRodney W. Grimes return (fmt - 1);/* format ended before closing ] */
90158f0484fSRodney W. Grimes
90258f0484fSRodney W. Grimes /*
90358f0484fSRodney W. Grimes * Now set the entries corresponding to the actual scanset
90458f0484fSRodney W. Grimes * to the opposite of the above.
90558f0484fSRodney W. Grimes *
90658f0484fSRodney W. Grimes * The first character may be ']' (or '-') without being special;
90758f0484fSRodney W. Grimes * the last character may be '-'.
90858f0484fSRodney W. Grimes */
90958f0484fSRodney W. Grimes v = 1 - v;
91058f0484fSRodney W. Grimes for (;;) {
91158f0484fSRodney W. Grimes tab[c] = v; /* take character c */
91258f0484fSRodney W. Grimes doswitch:
91358f0484fSRodney W. Grimes n = *fmt++; /* and examine the next */
91458f0484fSRodney W. Grimes switch (n) {
91558f0484fSRodney W. Grimes
91658f0484fSRodney W. Grimes case 0: /* format ended too soon */
91758f0484fSRodney W. Grimes return (fmt - 1);
91858f0484fSRodney W. Grimes
91958f0484fSRodney W. Grimes case '-':
92058f0484fSRodney W. Grimes /*
92158f0484fSRodney W. Grimes * A scanset of the form
92258f0484fSRodney W. Grimes * [01+-]
92358f0484fSRodney W. Grimes * is defined as `the digit 0, the digit 1,
92458f0484fSRodney W. Grimes * the character +, the character -', but
92558f0484fSRodney W. Grimes * the effect of a scanset such as
92658f0484fSRodney W. Grimes * [a-zA-Z0-9]
92758f0484fSRodney W. Grimes * is implementation defined. The V7 Unix
92858f0484fSRodney W. Grimes * scanf treats `a-z' as `the letters a through
92958f0484fSRodney W. Grimes * z', but treats `a-a' as `the letter a, the
93058f0484fSRodney W. Grimes * character -, and the letter a'.
93158f0484fSRodney W. Grimes *
93232223c1bSPedro F. Giffuni * For compatibility, the `-' is not considered
93358f0484fSRodney W. Grimes * to define a range if the character following
93458f0484fSRodney W. Grimes * it is either a close bracket (required by ANSI)
93558f0484fSRodney W. Grimes * or is not numerically greater than the character
93658f0484fSRodney W. Grimes * we just stored in the table (c).
93758f0484fSRodney W. Grimes */
93858f0484fSRodney W. Grimes n = *fmt;
9391daad8f5SAndrey A. Chernov if (n == ']'
9401daad8f5SAndrey A. Chernov || (table->__collate_load_error ? n < c :
94112eae8c8SAndrey A. Chernov __collate_range_cmp(n, c) < 0
9421daad8f5SAndrey A. Chernov )
9431daad8f5SAndrey A. Chernov ) {
94458f0484fSRodney W. Grimes c = '-';
94558f0484fSRodney W. Grimes break; /* resume the for(;;) */
94658f0484fSRodney W. Grimes }
94758f0484fSRodney W. Grimes fmt++;
9481daad8f5SAndrey A. Chernov /* fill in the range */
9491daad8f5SAndrey A. Chernov if (table->__collate_load_error) {
9501daad8f5SAndrey A. Chernov do {
951350498c5SAndrey A. Chernov tab[++c] = v;
952350498c5SAndrey A. Chernov } while (c < n);
9531daad8f5SAndrey A. Chernov } else {
9541daad8f5SAndrey A. Chernov for (i = 0; i < 256; i ++)
95512eae8c8SAndrey A. Chernov if (__collate_range_cmp(c, i) <= 0 &&
95612eae8c8SAndrey A. Chernov __collate_range_cmp(i, n) <= 0
9571daad8f5SAndrey A. Chernov )
9581daad8f5SAndrey A. Chernov tab[i] = v;
9591daad8f5SAndrey A. Chernov }
96058f0484fSRodney W. Grimes #if 1 /* XXX another disgusting compatibility hack */
9611daad8f5SAndrey A. Chernov c = n;
96258f0484fSRodney W. Grimes /*
96358f0484fSRodney W. Grimes * Alas, the V7 Unix scanf also treats formats
96458f0484fSRodney W. Grimes * such as [a-c-e] as `the letters a through e'.
96558f0484fSRodney W. Grimes * This too is permitted by the standard....
96658f0484fSRodney W. Grimes */
96758f0484fSRodney W. Grimes goto doswitch;
96858f0484fSRodney W. Grimes #else
96958f0484fSRodney W. Grimes c = *fmt++;
97058f0484fSRodney W. Grimes if (c == 0)
97158f0484fSRodney W. Grimes return (fmt - 1);
97258f0484fSRodney W. Grimes if (c == ']')
97358f0484fSRodney W. Grimes return (fmt);
97458f0484fSRodney W. Grimes #endif
97558f0484fSRodney W. Grimes break;
97658f0484fSRodney W. Grimes
97758f0484fSRodney W. Grimes case ']': /* end of scanset */
97858f0484fSRodney W. Grimes return (fmt);
97958f0484fSRodney W. Grimes
98058f0484fSRodney W. Grimes default: /* just another character */
98158f0484fSRodney W. Grimes c = n;
98258f0484fSRodney W. Grimes break;
98358f0484fSRodney W. Grimes }
98458f0484fSRodney W. Grimes }
98558f0484fSRodney W. Grimes /* NOTREACHED */
98658f0484fSRodney W. Grimes }
987370077c7SDavid Schultz
9888de9e897SDavid Schultz #ifndef NO_FLOATING_POINT
989370077c7SDavid Schultz static int
parsefloat(FILE * fp,char * buf,char * end,locale_t locale)9903c87aa1dSDavid Chisnall parsefloat(FILE *fp, char *buf, char *end, locale_t locale)
991370077c7SDavid Schultz {
992370077c7SDavid Schultz char *commit, *p;
993f8f57193SDavid Schultz int infnanpos = 0, decptpos = 0;
994370077c7SDavid Schultz enum {
995f8f57193SDavid Schultz S_START, S_GOTSIGN, S_INF, S_NAN, S_DONE, S_MAYBEHEX,
996f8f57193SDavid Schultz S_DIGITS, S_DECPT, S_FRAC, S_EXP, S_EXPDIGITS
997370077c7SDavid Schultz } state = S_START;
998370077c7SDavid Schultz unsigned char c;
9993c87aa1dSDavid Chisnall const char *decpt = localeconv_l(locale)->decimal_point;
1000370077c7SDavid Schultz _Bool gotmantdig = 0, ishex = 0;
1001370077c7SDavid Schultz
1002370077c7SDavid Schultz /*
1003370077c7SDavid Schultz * We set commit = p whenever the string we have read so far
1004370077c7SDavid Schultz * constitutes a valid representation of a floating point
1005370077c7SDavid Schultz * number by itself. At some point, the parse will complete
1006370077c7SDavid Schultz * or fail, and we will ungetc() back to the last commit point.
1007370077c7SDavid Schultz * To ensure that the file offset gets updated properly, it is
1008370077c7SDavid Schultz * always necessary to read at least one character that doesn't
1009370077c7SDavid Schultz * match; thus, we can't short-circuit "infinity" or "nan(...)".
1010370077c7SDavid Schultz */
1011370077c7SDavid Schultz commit = buf - 1;
1012370077c7SDavid Schultz for (p = buf; p < end; ) {
1013370077c7SDavid Schultz c = *fp->_p;
1014370077c7SDavid Schultz reswitch:
1015370077c7SDavid Schultz switch (state) {
1016370077c7SDavid Schultz case S_START:
1017370077c7SDavid Schultz state = S_GOTSIGN;
1018370077c7SDavid Schultz if (c == '-' || c == '+')
1019370077c7SDavid Schultz break;
1020370077c7SDavid Schultz else
1021370077c7SDavid Schultz goto reswitch;
1022370077c7SDavid Schultz case S_GOTSIGN:
1023370077c7SDavid Schultz switch (c) {
1024370077c7SDavid Schultz case '0':
1025370077c7SDavid Schultz state = S_MAYBEHEX;
1026370077c7SDavid Schultz commit = p;
1027370077c7SDavid Schultz break;
1028370077c7SDavid Schultz case 'I':
1029370077c7SDavid Schultz case 'i':
1030370077c7SDavid Schultz state = S_INF;
1031370077c7SDavid Schultz break;
1032370077c7SDavid Schultz case 'N':
1033370077c7SDavid Schultz case 'n':
1034370077c7SDavid Schultz state = S_NAN;
1035370077c7SDavid Schultz break;
1036370077c7SDavid Schultz default:
1037370077c7SDavid Schultz state = S_DIGITS;
1038370077c7SDavid Schultz goto reswitch;
1039370077c7SDavid Schultz }
1040370077c7SDavid Schultz break;
1041370077c7SDavid Schultz case S_INF:
1042370077c7SDavid Schultz if (infnanpos > 6 ||
1043370077c7SDavid Schultz (c != "nfinity"[infnanpos] &&
1044370077c7SDavid Schultz c != "NFINITY"[infnanpos]))
1045370077c7SDavid Schultz goto parsedone;
1046370077c7SDavid Schultz if (infnanpos == 1 || infnanpos == 6)
1047370077c7SDavid Schultz commit = p; /* inf or infinity */
1048370077c7SDavid Schultz infnanpos++;
1049370077c7SDavid Schultz break;
1050370077c7SDavid Schultz case S_NAN:
1051370077c7SDavid Schultz switch (infnanpos) {
1052370077c7SDavid Schultz case 0:
1053370077c7SDavid Schultz if (c != 'A' && c != 'a')
1054370077c7SDavid Schultz goto parsedone;
1055370077c7SDavid Schultz break;
1056370077c7SDavid Schultz case 1:
1057370077c7SDavid Schultz if (c != 'N' && c != 'n')
1058370077c7SDavid Schultz goto parsedone;
1059370077c7SDavid Schultz else
1060370077c7SDavid Schultz commit = p;
1061370077c7SDavid Schultz break;
1062370077c7SDavid Schultz case 2:
1063370077c7SDavid Schultz if (c != '(')
1064370077c7SDavid Schultz goto parsedone;
1065370077c7SDavid Schultz break;
1066370077c7SDavid Schultz default:
1067370077c7SDavid Schultz if (c == ')') {
1068370077c7SDavid Schultz commit = p;
1069f8f57193SDavid Schultz state = S_DONE;
1070370077c7SDavid Schultz } else if (!isalnum(c) && c != '_')
1071370077c7SDavid Schultz goto parsedone;
1072370077c7SDavid Schultz break;
1073370077c7SDavid Schultz }
1074370077c7SDavid Schultz infnanpos++;
1075370077c7SDavid Schultz break;
1076f8f57193SDavid Schultz case S_DONE:
1077f8f57193SDavid Schultz goto parsedone;
1078370077c7SDavid Schultz case S_MAYBEHEX:
1079370077c7SDavid Schultz state = S_DIGITS;
1080370077c7SDavid Schultz if (c == 'X' || c == 'x') {
1081370077c7SDavid Schultz ishex = 1;
1082370077c7SDavid Schultz break;
1083370077c7SDavid Schultz } else { /* we saw a '0', but no 'x' */
1084370077c7SDavid Schultz gotmantdig = 1;
1085370077c7SDavid Schultz goto reswitch;
1086370077c7SDavid Schultz }
1087370077c7SDavid Schultz case S_DIGITS:
1088f8f57193SDavid Schultz if ((ishex && isxdigit(c)) || isdigit(c)) {
1089370077c7SDavid Schultz gotmantdig = 1;
1090370077c7SDavid Schultz commit = p;
1091370077c7SDavid Schultz break;
1092f8f57193SDavid Schultz } else {
1093f8f57193SDavid Schultz state = S_DECPT;
1094f8f57193SDavid Schultz goto reswitch;
1095f8f57193SDavid Schultz }
1096f8f57193SDavid Schultz case S_DECPT:
1097f8f57193SDavid Schultz if (c == decpt[decptpos]) {
1098f8f57193SDavid Schultz if (decpt[++decptpos] == '\0') {
1099f8f57193SDavid Schultz /* We read the complete decpt seq. */
1100f8f57193SDavid Schultz state = S_FRAC;
1101f8f57193SDavid Schultz if (gotmantdig)
1102f8f57193SDavid Schultz commit = p;
1103f8f57193SDavid Schultz }
1104f8f57193SDavid Schultz break;
1105f8f57193SDavid Schultz } else if (!decptpos) {
1106f8f57193SDavid Schultz /* We didn't read any decpt characters. */
1107f8f57193SDavid Schultz state = S_FRAC;
1108f8f57193SDavid Schultz goto reswitch;
1109f8f57193SDavid Schultz } else {
1110f8f57193SDavid Schultz /*
1111f8f57193SDavid Schultz * We read part of a multibyte decimal point,
1112f8f57193SDavid Schultz * but the rest is invalid, so bail.
1113f8f57193SDavid Schultz */
1114f8f57193SDavid Schultz goto parsedone;
1115f8f57193SDavid Schultz }
1116370077c7SDavid Schultz case S_FRAC:
111727a97dffSJacques Vidrine if (((c == 'E' || c == 'e') && !ishex) ||
111827a97dffSJacques Vidrine ((c == 'P' || c == 'p') && ishex)) {
1119370077c7SDavid Schultz if (!gotmantdig)
1120370077c7SDavid Schultz goto parsedone;
1121370077c7SDavid Schultz else
1122370077c7SDavid Schultz state = S_EXP;
112327a97dffSJacques Vidrine } else if ((ishex && isxdigit(c)) || isdigit(c)) {
1124370077c7SDavid Schultz commit = p;
1125370077c7SDavid Schultz gotmantdig = 1;
1126370077c7SDavid Schultz } else
1127370077c7SDavid Schultz goto parsedone;
1128370077c7SDavid Schultz break;
1129370077c7SDavid Schultz case S_EXP:
1130370077c7SDavid Schultz state = S_EXPDIGITS;
1131370077c7SDavid Schultz if (c == '-' || c == '+')
1132370077c7SDavid Schultz break;
1133370077c7SDavid Schultz else
1134370077c7SDavid Schultz goto reswitch;
1135370077c7SDavid Schultz case S_EXPDIGITS:
1136370077c7SDavid Schultz if (isdigit(c))
1137370077c7SDavid Schultz commit = p;
1138370077c7SDavid Schultz else
1139370077c7SDavid Schultz goto parsedone;
1140370077c7SDavid Schultz break;
1141370077c7SDavid Schultz default:
1142370077c7SDavid Schultz abort();
1143370077c7SDavid Schultz }
1144370077c7SDavid Schultz *p++ = c;
1145370077c7SDavid Schultz if (--fp->_r > 0)
1146370077c7SDavid Schultz fp->_p++;
1147370077c7SDavid Schultz else if (__srefill(fp))
1148370077c7SDavid Schultz break; /* EOF */
1149370077c7SDavid Schultz }
1150370077c7SDavid Schultz
1151370077c7SDavid Schultz parsedone:
1152370077c7SDavid Schultz while (commit < --p)
1153370077c7SDavid Schultz __ungetc(*(u_char *)p, fp);
1154370077c7SDavid Schultz *++commit = '\0';
1155370077c7SDavid Schultz return (commit - buf);
1156370077c7SDavid Schultz }
1157370077c7SDavid Schultz #endif
1158