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