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