1*388550b0Srillig /* $NetBSD: vfwscanf.c,v 1.13 2022/04/19 20:32:16 rillig Exp $ */
2f432bbb6Schristos
3f432bbb6Schristos /*-
4f432bbb6Schristos * Copyright (c) 1990, 1993
5f432bbb6Schristos * The Regents of the University of California. All rights reserved.
6f432bbb6Schristos *
7f432bbb6Schristos * This code is derived from software contributed to Berkeley by
8f432bbb6Schristos * Chris Torek.
9f432bbb6Schristos *
10f432bbb6Schristos * Redistribution and use in source and binary forms, with or without
11f432bbb6Schristos * modification, are permitted provided that the following conditions
12f432bbb6Schristos * are met:
13f432bbb6Schristos * 1. Redistributions of source code must retain the above copyright
14f432bbb6Schristos * notice, this list of conditions and the following disclaimer.
15f432bbb6Schristos * 2. Redistributions in binary form must reproduce the above copyright
16f432bbb6Schristos * notice, this list of conditions and the following disclaimer in the
17f432bbb6Schristos * documentation and/or other materials provided with the distribution.
18f432bbb6Schristos * 3. All advertising materials mentioning features or use of this software
19f432bbb6Schristos * must display the following acknowledgement:
20f432bbb6Schristos * This product includes software developed by the University of
21f432bbb6Schristos * California, Berkeley and its contributors.
22f432bbb6Schristos * 4. Neither the name of the University nor the names of its contributors
23f432bbb6Schristos * may be used to endorse or promote products derived from this software
24f432bbb6Schristos * without specific prior written permission.
25f432bbb6Schristos *
26f432bbb6Schristos * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
27f432bbb6Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28f432bbb6Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29f432bbb6Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
30f432bbb6Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31f432bbb6Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32f432bbb6Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33f432bbb6Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34f432bbb6Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35f432bbb6Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36f432bbb6Schristos * SUCH DAMAGE.
37f432bbb6Schristos */
38f432bbb6Schristos
39f432bbb6Schristos #include <sys/cdefs.h>
40f432bbb6Schristos #if defined(LIBC_SCCS) && !defined(lint)
41f432bbb6Schristos #if 0
42f432bbb6Schristos static char sccsid[] = "@(#)ftell.c 8.2 (Berkeley) 5/4/95";
43f432bbb6Schristos __FBSDID("$FreeBSD: src/lib/libc/stdio/vfwscanf.c,v 1.12 2004/05/02 20:13:29 obrien Exp $");
44f432bbb6Schristos #else
45*388550b0Srillig __RCSID("$NetBSD: vfwscanf.c,v 1.13 2022/04/19 20:32:16 rillig Exp $");
46f432bbb6Schristos #endif
47f432bbb6Schristos #endif /* LIBC_SCCS and not lint */
48f432bbb6Schristos
49f432bbb6Schristos #include "namespace.h"
50f432bbb6Schristos #include <ctype.h>
51f432bbb6Schristos #include <inttypes.h>
52c5e820caSchristos #include <assert.h>
53f432bbb6Schristos #include <stdio.h>
54f432bbb6Schristos #include <stdlib.h>
55f432bbb6Schristos #include <stddef.h>
56f432bbb6Schristos #include <stdarg.h>
57f432bbb6Schristos #include <string.h>
58f432bbb6Schristos #include <limits.h>
59f432bbb6Schristos #include <wchar.h>
60f432bbb6Schristos #include <wctype.h>
61f432bbb6Schristos
62f432bbb6Schristos #include "reentrant.h"
63f432bbb6Schristos #include "local.h"
64f432bbb6Schristos
65f432bbb6Schristos #include <locale.h>
669790c07aSjoerg #include "setlocale_local.h"
67f432bbb6Schristos
68f432bbb6Schristos #define BUF 513 /* Maximum length of numeric string. */
69f432bbb6Schristos
70f432bbb6Schristos /*
71f432bbb6Schristos * Flags used during conversion.
72f432bbb6Schristos */
73f432bbb6Schristos #define LONG 0x01 /* l: long or double */
74f432bbb6Schristos #define LONGDBL 0x02 /* L: long double */
75f432bbb6Schristos #define SHORT 0x04 /* h: short */
76f432bbb6Schristos #define SUPPRESS 0x08 /* *: suppress assignment */
77f432bbb6Schristos #define POINTER 0x10 /* p: void * (as hex) */
78f432bbb6Schristos #define NOSKIP 0x20 /* [ or c: do not skip blanks */
79f432bbb6Schristos #define LONGLONG 0x400 /* ll: quad_t (+ deprecated q: quad) */
80f432bbb6Schristos #define INTMAXT 0x800 /* j: intmax_t */
81f432bbb6Schristos #define PTRDIFFT 0x1000 /* t: ptrdiff_t */
82f432bbb6Schristos #define SIZET 0x2000 /* z: size_t */
83f432bbb6Schristos #define SHORTSHORT 0x4000 /* hh: char */
84f432bbb6Schristos #define UNSIGNED 0x8000 /* %[oupxX] conversions */
85f432bbb6Schristos
86f432bbb6Schristos /*
87f432bbb6Schristos * The following are used in integral conversions only:
88f432bbb6Schristos * SIGNOK, NDIGITS, PFXOK, and NZDIGITS
89f432bbb6Schristos */
90f432bbb6Schristos #define SIGNOK 0x40 /* +/- is (still) legal */
91f432bbb6Schristos #define NDIGITS 0x80 /* no digits detected */
92f432bbb6Schristos #define PFXOK 0x100 /* 0x prefix is (still) legal */
93f432bbb6Schristos #define NZDIGITS 0x200 /* no zero digits detected */
94f432bbb6Schristos #define HAVESIGN 0x10000 /* sign detected */
95f432bbb6Schristos
96f432bbb6Schristos /*
97f432bbb6Schristos * Conversion types.
98f432bbb6Schristos */
99f432bbb6Schristos #define CT_CHAR 0 /* %c conversion */
100f432bbb6Schristos #define CT_CCL 1 /* %[...] conversion */
101f432bbb6Schristos #define CT_STRING 2 /* %s conversion */
102f432bbb6Schristos #define CT_INT 3 /* %[dioupxX] conversion */
103f432bbb6Schristos #define CT_FLOAT 4 /* %[efgEFG] conversion */
104f432bbb6Schristos
105f42f5177Spooka #ifndef NO_FLOATING_POINT
1069790c07aSjoerg static int parsefloat(FILE *, wchar_t *, wchar_t *, locale_t);
107f42f5177Spooka #endif
108f432bbb6Schristos
109f432bbb6Schristos #define INCCL(_c) \
110f432bbb6Schristos (cclcompl ? (wmemchr(ccls, (_c), (size_t)(ccle - ccls)) == NULL) : \
111f432bbb6Schristos (wmemchr(ccls, (_c), (size_t)(ccle - ccls)) != NULL))
112f432bbb6Schristos
113f432bbb6Schristos /*
114f432bbb6Schristos * MT-safe version.
115f432bbb6Schristos */
116f432bbb6Schristos int
vfwscanf(FILE * __restrict fp,const wchar_t * __restrict fmt,va_list ap)117f432bbb6Schristos vfwscanf(FILE * __restrict fp, const wchar_t * __restrict fmt, va_list ap)
118f432bbb6Schristos {
119e0ac190eSjoerg return vfwscanf_l(fp, _current_locale(), fmt, ap);
1209790c07aSjoerg }
1219790c07aSjoerg
1229790c07aSjoerg int
vfwscanf_l(FILE * __restrict fp,locale_t loc,const wchar_t * __restrict fmt,va_list ap)1239790c07aSjoerg vfwscanf_l(FILE * __restrict fp, locale_t loc, const wchar_t * __restrict fmt,
1249790c07aSjoerg va_list ap)
1259790c07aSjoerg {
126f432bbb6Schristos int ret;
127f432bbb6Schristos
128f432bbb6Schristos FLOCKFILE(fp);
129f432bbb6Schristos _SET_ORIENTATION(fp, 1);
1309790c07aSjoerg ret = __vfwscanf_unlocked_l(fp, loc, fmt, ap);
131f432bbb6Schristos FUNLOCKFILE(fp);
132526d9427Schristos return ret;
133f432bbb6Schristos }
134f432bbb6Schristos
1355022bd79Schristos #define SCANF_SKIP_SPACE() \
1365022bd79Schristos do { \
1375022bd79Schristos wint_t tc; \
1385022bd79Schristos \
1399790c07aSjoerg while ((tc = __fgetwc_unlock(fp)) != WEOF && iswspace_l(tc, loc)) \
1405022bd79Schristos continue; \
1415022bd79Schristos if (tc != WEOF) \
1425022bd79Schristos ungetwc(tc, fp); \
143*388550b0Srillig } while (0)
1445022bd79Schristos
145f432bbb6Schristos /*
146f432bbb6Schristos * Non-MT-safe version.
147f432bbb6Schristos */
148f432bbb6Schristos int
__vfwscanf_unlocked_l(FILE * __restrict fp,locale_t loc,const wchar_t * __restrict fmt,va_list ap)1499790c07aSjoerg __vfwscanf_unlocked_l(FILE * __restrict fp, locale_t loc,
1509790c07aSjoerg const wchar_t * __restrict fmt, va_list ap)
151f432bbb6Schristos {
152f432bbb6Schristos wint_t c; /* character from format, or conversion */
153f432bbb6Schristos size_t width; /* field width, or 0 */
154f432bbb6Schristos wchar_t *p; /* points into all kinds of strings */
155f432bbb6Schristos int n; /* handy integer */
156f432bbb6Schristos int flags; /* flags as defined above */
157f432bbb6Schristos wchar_t *p0; /* saves original value of p when necessary */
158f432bbb6Schristos int nassigned; /* number of fields assigned */
159f432bbb6Schristos int nconversions; /* number of conversions */
160c5e820caSchristos size_t nread; /* number of characters consumed from fp */
161f432bbb6Schristos int base; /* base argument to conversion function */
162f432bbb6Schristos wchar_t buf[BUF]; /* buffer for numeric conversions */
163f432bbb6Schristos const wchar_t *ccls; /* character class start */
164f432bbb6Schristos const wchar_t *ccle; /* character class end */
165f432bbb6Schristos int cclcompl; /* ccl is complemented? */
166f432bbb6Schristos wint_t wi; /* handy wint_t */
167f432bbb6Schristos char *mbp; /* multibyte string pointer for %c %s %[ */
168f432bbb6Schristos size_t nconv; /* number of bytes in mb. conversion */
169f432bbb6Schristos static const mbstate_t initial;
170f432bbb6Schristos mbstate_t mbs;
1719790c07aSjoerg char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
172f432bbb6Schristos /* `basefix' is used to avoid `if' tests in the integer scanner */
173f432bbb6Schristos static short basefix[17] =
174f432bbb6Schristos { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
175f432bbb6Schristos
176f432bbb6Schristos nassigned = 0;
177f432bbb6Schristos nconversions = 0;
178f432bbb6Schristos nread = 0;
179f432bbb6Schristos ccls = ccle = NULL;
180ef7b3cd6Slukem base = 0;
181ef7b3cd6Slukem cclcompl = 0;
182ef7b3cd6Slukem mbp = NULL;
18376fd4199Sjustin p = NULL; /* XXXgcc */
184f432bbb6Schristos for (;;) {
185f432bbb6Schristos c = *fmt++;
186f432bbb6Schristos if (c == 0)
187526d9427Schristos return nassigned;
1889790c07aSjoerg if (iswspace_l(c, loc)) {
189f432bbb6Schristos while ((c = __fgetwc_unlock(fp)) != WEOF &&
1909790c07aSjoerg iswspace_l(c, loc))
191f432bbb6Schristos ;
192f432bbb6Schristos if (c != WEOF)
193f432bbb6Schristos ungetwc(c, fp);
194f432bbb6Schristos continue;
195f432bbb6Schristos }
196f432bbb6Schristos if (c != '%')
197f432bbb6Schristos goto literal;
198f432bbb6Schristos width = 0;
199f432bbb6Schristos flags = 0;
200f432bbb6Schristos /*
201f432bbb6Schristos * switch on the format. continue if done;
202f432bbb6Schristos * break once format type is derived.
203f432bbb6Schristos */
204f432bbb6Schristos again: c = *fmt++;
205f432bbb6Schristos switch (c) {
206f432bbb6Schristos case '%':
2075022bd79Schristos SCANF_SKIP_SPACE();
208f432bbb6Schristos literal:
209f432bbb6Schristos if ((wi = __fgetwc_unlock(fp)) == WEOF)
210f432bbb6Schristos goto input_failure;
211f432bbb6Schristos if (wi != c) {
212f432bbb6Schristos ungetwc(wi, fp);
213f432bbb6Schristos goto input_failure;
214f432bbb6Schristos }
215f432bbb6Schristos nread++;
216f432bbb6Schristos continue;
217f432bbb6Schristos
218f432bbb6Schristos case '*':
219f432bbb6Schristos flags |= SUPPRESS;
220f432bbb6Schristos goto again;
221f432bbb6Schristos case 'j':
222f432bbb6Schristos flags |= INTMAXT;
223f432bbb6Schristos goto again;
224f432bbb6Schristos case 'l':
225f432bbb6Schristos if (flags & LONG) {
226f432bbb6Schristos flags &= ~LONG;
227f432bbb6Schristos flags |= LONGLONG;
228f432bbb6Schristos } else
229f432bbb6Schristos flags |= LONG;
230f432bbb6Schristos goto again;
231f432bbb6Schristos case 'q':
232f432bbb6Schristos flags |= LONGLONG; /* not quite */
233f432bbb6Schristos goto again;
234f432bbb6Schristos case 't':
235f432bbb6Schristos flags |= PTRDIFFT;
236f432bbb6Schristos goto again;
237f432bbb6Schristos case 'z':
238f432bbb6Schristos flags |= SIZET;
239f432bbb6Schristos goto again;
240f432bbb6Schristos case 'L':
241f432bbb6Schristos flags |= LONGDBL;
242f432bbb6Schristos goto again;
243f432bbb6Schristos case 'h':
244f432bbb6Schristos if (flags & SHORT) {
245f432bbb6Schristos flags &= ~SHORT;
246f432bbb6Schristos flags |= SHORTSHORT;
247f432bbb6Schristos } else
248f432bbb6Schristos flags |= SHORT;
249f432bbb6Schristos goto again;
250f432bbb6Schristos
251f432bbb6Schristos case '0': case '1': case '2': case '3': case '4':
252f432bbb6Schristos case '5': case '6': case '7': case '8': case '9':
253f432bbb6Schristos width = width * 10 + c - '0';
254f432bbb6Schristos goto again;
255f432bbb6Schristos
256f432bbb6Schristos /*
257f432bbb6Schristos * Conversions.
258f432bbb6Schristos */
259f432bbb6Schristos case 'd':
260f432bbb6Schristos c = CT_INT;
261f432bbb6Schristos base = 10;
262f432bbb6Schristos break;
263f432bbb6Schristos
264f432bbb6Schristos case 'i':
265f432bbb6Schristos c = CT_INT;
266f432bbb6Schristos base = 0;
267f432bbb6Schristos break;
268f432bbb6Schristos
269f432bbb6Schristos case 'o':
270f432bbb6Schristos c = CT_INT;
271f432bbb6Schristos flags |= UNSIGNED;
272f432bbb6Schristos base = 8;
273f432bbb6Schristos break;
274f432bbb6Schristos
275f432bbb6Schristos case 'u':
276f432bbb6Schristos c = CT_INT;
277f432bbb6Schristos flags |= UNSIGNED;
278f432bbb6Schristos base = 10;
279f432bbb6Schristos break;
280f432bbb6Schristos
281f432bbb6Schristos case 'X':
282f432bbb6Schristos case 'x':
283f432bbb6Schristos flags |= PFXOK; /* enable 0x prefixing */
284f432bbb6Schristos c = CT_INT;
285f432bbb6Schristos flags |= UNSIGNED;
286f432bbb6Schristos base = 16;
287f432bbb6Schristos break;
288f432bbb6Schristos
289f432bbb6Schristos #ifndef NO_FLOATING_POINT
290f432bbb6Schristos case 'A': case 'E': case 'F': case 'G':
291f432bbb6Schristos case 'a': case 'e': case 'f': case 'g':
292f432bbb6Schristos c = CT_FLOAT;
293f432bbb6Schristos break;
294f432bbb6Schristos #endif
295f432bbb6Schristos
296f432bbb6Schristos case 'S':
297f432bbb6Schristos flags |= LONG;
298f432bbb6Schristos /* FALLTHROUGH */
299f432bbb6Schristos case 's':
300f432bbb6Schristos c = CT_STRING;
301f432bbb6Schristos break;
302f432bbb6Schristos
303f432bbb6Schristos case '[':
304f432bbb6Schristos ccls = fmt;
305f432bbb6Schristos if (*fmt == '^') {
306f432bbb6Schristos cclcompl = 1;
307f432bbb6Schristos fmt++;
308f432bbb6Schristos } else
309f432bbb6Schristos cclcompl = 0;
310f432bbb6Schristos if (*fmt == ']')
311f432bbb6Schristos fmt++;
312f432bbb6Schristos while (*fmt != '\0' && *fmt != ']')
313f432bbb6Schristos fmt++;
314f432bbb6Schristos ccle = fmt;
315f432bbb6Schristos fmt++;
316f432bbb6Schristos flags |= NOSKIP;
317f432bbb6Schristos c = CT_CCL;
318f432bbb6Schristos break;
319f432bbb6Schristos
320f432bbb6Schristos case 'C':
321f432bbb6Schristos flags |= LONG;
322f432bbb6Schristos /* FALLTHROUGH */
323f432bbb6Schristos case 'c':
324f432bbb6Schristos flags |= NOSKIP;
325f432bbb6Schristos c = CT_CHAR;
326f432bbb6Schristos break;
327f432bbb6Schristos
328f432bbb6Schristos case 'p': /* pointer format is like hex */
329f432bbb6Schristos flags |= POINTER | PFXOK;
330f432bbb6Schristos c = CT_INT; /* assumes sizeof(uintmax_t) */
331f432bbb6Schristos flags |= UNSIGNED; /* >= sizeof(uintptr_t) */
332f432bbb6Schristos base = 16;
333f432bbb6Schristos break;
334f432bbb6Schristos
335f432bbb6Schristos case 'n':
336f432bbb6Schristos nconversions++;
337f432bbb6Schristos if (flags & SUPPRESS) /* ??? */
338f432bbb6Schristos continue;
339f432bbb6Schristos if (flags & SHORTSHORT)
340c5e820caSchristos *va_arg(ap, char *) = (char)nread;
341f432bbb6Schristos else if (flags & SHORT)
342c5e820caSchristos *va_arg(ap, short *) = (short)nread;
343f432bbb6Schristos else if (flags & LONG)
344f432bbb6Schristos *va_arg(ap, long *) = nread;
345f432bbb6Schristos else if (flags & LONGLONG)
346f432bbb6Schristos *va_arg(ap, quad_t *) = nread;
347f432bbb6Schristos else if (flags & INTMAXT)
348f432bbb6Schristos *va_arg(ap, intmax_t *) = nread;
349f432bbb6Schristos else if (flags & SIZET)
350f432bbb6Schristos *va_arg(ap, size_t *) = nread;
351f432bbb6Schristos else if (flags & PTRDIFFT)
352f432bbb6Schristos *va_arg(ap, ptrdiff_t *) = nread;
353f432bbb6Schristos else
354c5e820caSchristos *va_arg(ap, int *) = (int)nread;
355f432bbb6Schristos continue;
356f432bbb6Schristos
357f432bbb6Schristos default:
358f432bbb6Schristos goto match_failure;
359f432bbb6Schristos
360f432bbb6Schristos /*
361f432bbb6Schristos * Disgusting backwards compatibility hack. XXX
362f432bbb6Schristos */
363f432bbb6Schristos case '\0': /* compat */
364526d9427Schristos return EOF;
365f432bbb6Schristos }
366f432bbb6Schristos
367f432bbb6Schristos /*
368f432bbb6Schristos * Consume leading white space, except for formats
369f432bbb6Schristos * that suppress this.
370f432bbb6Schristos */
371f432bbb6Schristos if ((flags & NOSKIP) == 0) {
3729790c07aSjoerg while ((wi = __fgetwc_unlock(fp)) != WEOF &&
3739790c07aSjoerg iswspace_l(wi, loc))
374f432bbb6Schristos nread++;
375f432bbb6Schristos if (wi == WEOF)
376f432bbb6Schristos goto input_failure;
377f432bbb6Schristos ungetwc(wi, fp);
378f432bbb6Schristos }
379f432bbb6Schristos
380f432bbb6Schristos /*
381f432bbb6Schristos * Do the conversion.
382f432bbb6Schristos */
383f432bbb6Schristos switch (c) {
384f432bbb6Schristos
385f432bbb6Schristos case CT_CHAR:
386f432bbb6Schristos /* scan arbitrary characters (sets NOSKIP) */
387f432bbb6Schristos if (width == 0)
388f432bbb6Schristos width = 1;
389f432bbb6Schristos if (flags & LONG) {
390f432bbb6Schristos if (!(flags & SUPPRESS))
391f432bbb6Schristos p = va_arg(ap, wchar_t *);
392f432bbb6Schristos n = 0;
393f432bbb6Schristos while (width-- != 0 &&
394f432bbb6Schristos (wi = __fgetwc_unlock(fp)) != WEOF) {
395f432bbb6Schristos if (!(flags & SUPPRESS))
396f432bbb6Schristos *p++ = (wchar_t)wi;
397f432bbb6Schristos n++;
398f432bbb6Schristos }
399f432bbb6Schristos if (n == 0)
400f432bbb6Schristos goto input_failure;
401f432bbb6Schristos nread += n;
402f432bbb6Schristos if (!(flags & SUPPRESS))
403f432bbb6Schristos nassigned++;
404f432bbb6Schristos } else {
405f432bbb6Schristos if (!(flags & SUPPRESS))
406f432bbb6Schristos mbp = va_arg(ap, char *);
407f432bbb6Schristos n = 0;
408f432bbb6Schristos mbs = initial;
409f432bbb6Schristos while (width != 0 &&
410f432bbb6Schristos (wi = __fgetwc_unlock(fp)) != WEOF) {
4119790c07aSjoerg if (width >= MB_CUR_MAX_L(loc) &&
412f432bbb6Schristos !(flags & SUPPRESS)) {
4139790c07aSjoerg nconv = wcrtomb_l(mbp, wi,
4149790c07aSjoerg &mbs, loc);
415f432bbb6Schristos if (nconv == (size_t)-1)
416f432bbb6Schristos goto input_failure;
417f432bbb6Schristos } else {
4189790c07aSjoerg nconv = wcrtomb_l(mbbuf, wi,
4199790c07aSjoerg &mbs, loc);
420f432bbb6Schristos if (nconv == (size_t)-1)
421f432bbb6Schristos goto input_failure;
422f432bbb6Schristos if (nconv > width) {
423f432bbb6Schristos ungetwc(wi, fp);
424f432bbb6Schristos break;
425f432bbb6Schristos }
426f432bbb6Schristos if (!(flags & SUPPRESS))
427f432bbb6Schristos memcpy(mbp, mbbuf,
428f432bbb6Schristos nconv);
429f432bbb6Schristos }
430f432bbb6Schristos if (!(flags & SUPPRESS))
431f432bbb6Schristos mbp += nconv;
432f432bbb6Schristos width -= nconv;
433f432bbb6Schristos n++;
434f432bbb6Schristos }
435f432bbb6Schristos if (n == 0)
436f432bbb6Schristos goto input_failure;
437f432bbb6Schristos nread += n;
438f432bbb6Schristos if (!(flags & SUPPRESS))
439f432bbb6Schristos nassigned++;
440f432bbb6Schristos }
441f432bbb6Schristos nconversions++;
442f432bbb6Schristos break;
443f432bbb6Schristos
444f432bbb6Schristos case CT_CCL:
445f432bbb6Schristos /* scan a (nonempty) character class (sets NOSKIP) */
446f432bbb6Schristos if (width == 0)
447f432bbb6Schristos width = (size_t)~0; /* `infinity' */
448f432bbb6Schristos /* take only those things in the class */
449f432bbb6Schristos if ((flags & SUPPRESS) && (flags & LONG)) {
450f432bbb6Schristos n = 0;
451f432bbb6Schristos while ((wi = __fgetwc_unlock(fp)) != WEOF &&
452f432bbb6Schristos width-- != 0 && INCCL(wi))
453f432bbb6Schristos n++;
454f432bbb6Schristos if (wi != WEOF)
455f432bbb6Schristos ungetwc(wi, fp);
456f432bbb6Schristos if (n == 0)
457f432bbb6Schristos goto match_failure;
458f432bbb6Schristos } else if (flags & LONG) {
459f432bbb6Schristos p0 = p = va_arg(ap, wchar_t *);
460f432bbb6Schristos while ((wi = __fgetwc_unlock(fp)) != WEOF &&
461f432bbb6Schristos width-- != 0 && INCCL(wi))
462f432bbb6Schristos *p++ = (wchar_t)wi;
463f432bbb6Schristos if (wi != WEOF)
464f432bbb6Schristos ungetwc(wi, fp);
465c5e820caSchristos _DIAGASSERT(__type_fit(int, p - p0));
466c5e820caSchristos n = (int)(p - p0);
467f432bbb6Schristos if (n == 0)
468f432bbb6Schristos goto match_failure;
469f432bbb6Schristos *p = 0;
470f432bbb6Schristos nassigned++;
471f432bbb6Schristos } else {
472f432bbb6Schristos if (!(flags & SUPPRESS))
473f432bbb6Schristos mbp = va_arg(ap, char *);
474f432bbb6Schristos n = 0;
475f432bbb6Schristos mbs = initial;
476f432bbb6Schristos while ((wi = __fgetwc_unlock(fp)) != WEOF &&
477f432bbb6Schristos width != 0 && INCCL(wi)) {
4789790c07aSjoerg if (width >= MB_CUR_MAX_L(loc) &&
479f432bbb6Schristos !(flags & SUPPRESS)) {
4809790c07aSjoerg nconv = wcrtomb_l(mbp, wi,
4819790c07aSjoerg &mbs, loc);
482f432bbb6Schristos if (nconv == (size_t)-1)
483f432bbb6Schristos goto input_failure;
484f432bbb6Schristos } else {
4859790c07aSjoerg nconv = wcrtomb_l(mbbuf, wi,
4869790c07aSjoerg &mbs, loc);
487f432bbb6Schristos if (nconv == (size_t)-1)
488f432bbb6Schristos goto input_failure;
489f432bbb6Schristos if (nconv > width)
490f432bbb6Schristos break;
491f432bbb6Schristos if (!(flags & SUPPRESS))
492f432bbb6Schristos memcpy(mbp, mbbuf,
493f432bbb6Schristos nconv);
494f432bbb6Schristos }
495f432bbb6Schristos if (!(flags & SUPPRESS))
496f432bbb6Schristos mbp += nconv;
497f432bbb6Schristos width -= nconv;
498f432bbb6Schristos n++;
499f432bbb6Schristos }
500f432bbb6Schristos if (wi != WEOF)
501f432bbb6Schristos ungetwc(wi, fp);
502f432bbb6Schristos if (!(flags & SUPPRESS)) {
503f432bbb6Schristos *mbp = 0;
504f432bbb6Schristos nassigned++;
505f432bbb6Schristos }
506f432bbb6Schristos }
507f432bbb6Schristos nread += n;
508f432bbb6Schristos nconversions++;
509f432bbb6Schristos break;
510f432bbb6Schristos
511f432bbb6Schristos case CT_STRING:
512f432bbb6Schristos /* like CCL, but zero-length string OK, & no NOSKIP */
513f432bbb6Schristos if (width == 0)
514f432bbb6Schristos width = (size_t)~0;
515f432bbb6Schristos if ((flags & SUPPRESS) && (flags & LONG)) {
516f432bbb6Schristos while ((wi = __fgetwc_unlock(fp)) != WEOF &&
517f432bbb6Schristos width-- != 0 &&
5189790c07aSjoerg !iswspace_l(wi, loc))
519f432bbb6Schristos nread++;
520f432bbb6Schristos if (wi != WEOF)
521f432bbb6Schristos ungetwc(wi, fp);
522f432bbb6Schristos } else if (flags & LONG) {
523f432bbb6Schristos p0 = p = va_arg(ap, wchar_t *);
524f432bbb6Schristos while ((wi = __fgetwc_unlock(fp)) != WEOF &&
525f432bbb6Schristos width-- != 0 &&
5269790c07aSjoerg !iswspace_l(wi, loc)) {
527f432bbb6Schristos *p++ = (wchar_t)wi;
528f432bbb6Schristos nread++;
529f432bbb6Schristos }
530f432bbb6Schristos if (wi != WEOF)
531f432bbb6Schristos ungetwc(wi, fp);
532f432bbb6Schristos *p = '\0';
533f432bbb6Schristos nassigned++;
534f432bbb6Schristos } else {
535f432bbb6Schristos if (!(flags & SUPPRESS))
536f432bbb6Schristos mbp = va_arg(ap, char *);
537f432bbb6Schristos mbs = initial;
538f432bbb6Schristos while ((wi = __fgetwc_unlock(fp)) != WEOF &&
539f432bbb6Schristos width != 0 &&
5409790c07aSjoerg !iswspace_l(wi, loc)) {
5419790c07aSjoerg if (width >= MB_CUR_MAX_L(loc) &&
542f432bbb6Schristos !(flags & SUPPRESS)) {
5439790c07aSjoerg nconv = wcrtomb_l(mbp, wi,
5449790c07aSjoerg &mbs, loc);
545f432bbb6Schristos if (nconv == (size_t)-1)
546f432bbb6Schristos goto input_failure;
547f432bbb6Schristos } else {
5489790c07aSjoerg nconv = wcrtomb_l(mbbuf, wi,
5499790c07aSjoerg &mbs, loc);
550f432bbb6Schristos if (nconv == (size_t)-1)
551f432bbb6Schristos goto input_failure;
552f432bbb6Schristos if (nconv > width)
553f432bbb6Schristos break;
554f432bbb6Schristos if (!(flags & SUPPRESS))
555f432bbb6Schristos memcpy(mbp, mbbuf,
556f432bbb6Schristos nconv);
557f432bbb6Schristos }
558f432bbb6Schristos if (!(flags & SUPPRESS))
559f432bbb6Schristos mbp += nconv;
560f432bbb6Schristos width -= nconv;
561f432bbb6Schristos nread++;
562f432bbb6Schristos }
563f432bbb6Schristos if (wi != WEOF)
564f432bbb6Schristos ungetwc(wi, fp);
565f432bbb6Schristos if (!(flags & SUPPRESS)) {
566f432bbb6Schristos *mbp = 0;
567f432bbb6Schristos nassigned++;
568f432bbb6Schristos }
569f432bbb6Schristos }
570f432bbb6Schristos nconversions++;
571f432bbb6Schristos continue;
572f432bbb6Schristos
573f432bbb6Schristos case CT_INT:
574f432bbb6Schristos /* scan an integer as if by the conversion function */
575f432bbb6Schristos if (width == 0 || width > sizeof(buf) /
576f432bbb6Schristos sizeof(*buf) - 1)
577f432bbb6Schristos width = sizeof(buf) / sizeof(*buf) - 1;
578f432bbb6Schristos flags |= SIGNOK | NDIGITS | NZDIGITS;
579f432bbb6Schristos for (p = buf; width; width--) {
580f432bbb6Schristos c = __fgetwc_unlock(fp);
581f432bbb6Schristos /*
582f432bbb6Schristos * Switch on the character; `goto ok'
583f432bbb6Schristos * if we accept it as a part of number.
584f432bbb6Schristos */
585f432bbb6Schristos switch (c) {
586f432bbb6Schristos
587f432bbb6Schristos /*
588f432bbb6Schristos * The digit 0 is always legal, but is
589f432bbb6Schristos * special. For %i conversions, if no
590f432bbb6Schristos * digits (zero or nonzero) have been
591f432bbb6Schristos * scanned (only signs), we will have
592f432bbb6Schristos * base==0. In that case, we should set
593f432bbb6Schristos * it to 8 and enable 0x prefixing.
594f432bbb6Schristos * Also, if we have not scanned zero digits
595f432bbb6Schristos * before this, do not turn off prefixing
596f432bbb6Schristos * (someone else will turn it off if we
597f432bbb6Schristos * have scanned any nonzero digits).
598f432bbb6Schristos */
599f432bbb6Schristos case '0':
600f432bbb6Schristos if (base == 0) {
601f432bbb6Schristos base = 8;
602f432bbb6Schristos flags |= PFXOK;
603f432bbb6Schristos }
604f432bbb6Schristos if (flags & NZDIGITS)
605f432bbb6Schristos flags &= ~(SIGNOK|NZDIGITS|NDIGITS);
606f432bbb6Schristos else
607f432bbb6Schristos flags &= ~(SIGNOK|PFXOK|NDIGITS);
608f432bbb6Schristos goto ok;
609f432bbb6Schristos
610f432bbb6Schristos /* 1 through 7 always legal */
611f432bbb6Schristos case '1': case '2': case '3':
612f432bbb6Schristos case '4': case '5': case '6': case '7':
613f432bbb6Schristos base = basefix[base];
614f432bbb6Schristos flags &= ~(SIGNOK | PFXOK | NDIGITS);
615f432bbb6Schristos goto ok;
616f432bbb6Schristos
617f432bbb6Schristos /* digits 8 and 9 ok iff decimal or hex */
618f432bbb6Schristos case '8': case '9':
619f432bbb6Schristos base = basefix[base];
620f432bbb6Schristos if (base <= 8)
621f432bbb6Schristos break; /* not legal here */
622f432bbb6Schristos flags &= ~(SIGNOK | PFXOK | NDIGITS);
623f432bbb6Schristos goto ok;
624f432bbb6Schristos
625f432bbb6Schristos /* letters ok iff hex */
626f432bbb6Schristos case 'A': case 'B': case 'C':
627f432bbb6Schristos case 'D': case 'E': case 'F':
628f432bbb6Schristos case 'a': case 'b': case 'c':
629f432bbb6Schristos case 'd': case 'e': case 'f':
630f432bbb6Schristos /* no need to fix base here */
631f432bbb6Schristos if (base <= 10)
632f432bbb6Schristos break; /* not legal here */
633f432bbb6Schristos flags &= ~(SIGNOK | PFXOK | NDIGITS);
634f432bbb6Schristos goto ok;
635f432bbb6Schristos
636f432bbb6Schristos /* sign ok only as first character */
637f432bbb6Schristos case '+': case '-':
638f432bbb6Schristos if (flags & SIGNOK) {
639f432bbb6Schristos flags &= ~SIGNOK;
640f432bbb6Schristos flags |= HAVESIGN;
641f432bbb6Schristos goto ok;
642f432bbb6Schristos }
643f432bbb6Schristos break;
644f432bbb6Schristos
645f432bbb6Schristos /*
646f432bbb6Schristos * x ok iff flag still set & 2nd char (or
647f432bbb6Schristos * 3rd char if we have a sign).
648f432bbb6Schristos */
649f432bbb6Schristos case 'x': case 'X':
650f432bbb6Schristos if (flags & PFXOK && p ==
651f432bbb6Schristos buf + 1 + !!(flags & HAVESIGN)) {
652f432bbb6Schristos base = 16; /* if %i */
653f432bbb6Schristos flags &= ~PFXOK;
654f432bbb6Schristos goto ok;
655f432bbb6Schristos }
656f432bbb6Schristos break;
657f432bbb6Schristos }
658f432bbb6Schristos
659f432bbb6Schristos /*
660f432bbb6Schristos * If we got here, c is not a legal character
661f432bbb6Schristos * for a number. Stop accumulating digits.
662f432bbb6Schristos */
663f432bbb6Schristos if (c != WEOF)
664f432bbb6Schristos ungetwc(c, fp);
665f432bbb6Schristos break;
666f432bbb6Schristos ok:
667f432bbb6Schristos /*
668f432bbb6Schristos * c is legal: store it and look at the next.
669f432bbb6Schristos */
670f432bbb6Schristos *p++ = (wchar_t)c;
671f432bbb6Schristos }
672f432bbb6Schristos /*
673f432bbb6Schristos * If we had only a sign, it is no good; push
674f432bbb6Schristos * back the sign. If the number ends in `x',
675f432bbb6Schristos * it was [sign] '0' 'x', so push back the x
676f432bbb6Schristos * and treat it as [sign] '0'.
677f432bbb6Schristos */
678f432bbb6Schristos if (flags & NDIGITS) {
679f432bbb6Schristos if (p > buf)
680f432bbb6Schristos ungetwc(*--p, fp);
681f432bbb6Schristos goto match_failure;
682f432bbb6Schristos }
683f432bbb6Schristos c = p[-1];
684f432bbb6Schristos if (c == 'x' || c == 'X') {
685f432bbb6Schristos --p;
686f432bbb6Schristos ungetwc(c, fp);
687f432bbb6Schristos }
688f432bbb6Schristos if ((flags & SUPPRESS) == 0) {
689f432bbb6Schristos uintmax_t res;
690f432bbb6Schristos
691f432bbb6Schristos *p = 0;
692f432bbb6Schristos if ((flags & UNSIGNED) == 0)
6939790c07aSjoerg res = wcstoimax_l(buf, NULL, base, loc);
694f432bbb6Schristos else
6959790c07aSjoerg res = wcstoumax_l(buf, NULL, base, loc);
696f432bbb6Schristos if (flags & POINTER)
697f432bbb6Schristos *va_arg(ap, void **) =
698f432bbb6Schristos (void *)(uintptr_t)res;
699f432bbb6Schristos else if (flags & SHORTSHORT)
700f432bbb6Schristos *va_arg(ap, char *) = (char)res;
701f432bbb6Schristos else if (flags & SHORT)
702f432bbb6Schristos *va_arg(ap, short *) = (short)res;
703f432bbb6Schristos else if (flags & LONG)
704f432bbb6Schristos *va_arg(ap, long *) = (long)res;
705f432bbb6Schristos else if (flags & LONGLONG)
706f432bbb6Schristos *va_arg(ap, quad_t *) = res;
707f432bbb6Schristos else if (flags & INTMAXT)
708f432bbb6Schristos *va_arg(ap, intmax_t *) = res;
709f432bbb6Schristos else if (flags & PTRDIFFT)
710f432bbb6Schristos *va_arg(ap, ptrdiff_t *) = (ptrdiff_t)res;
711f432bbb6Schristos else if (flags & SIZET)
712f432bbb6Schristos *va_arg(ap, size_t *) = (size_t)res;
713f432bbb6Schristos else
714f432bbb6Schristos *va_arg(ap, int *) = (int)res;
715f432bbb6Schristos nassigned++;
716f432bbb6Schristos }
717c5e820caSchristos _DIAGASSERT(__type_fit(int, p - buf));
718c5e820caSchristos nread += (int)(p - buf);
719f432bbb6Schristos nconversions++;
720f432bbb6Schristos break;
721f432bbb6Schristos
722f432bbb6Schristos #ifndef NO_FLOATING_POINT
723f432bbb6Schristos case CT_FLOAT:
724f432bbb6Schristos /* scan a floating point number as if by strtod */
725f432bbb6Schristos if (width == 0 || width > sizeof(buf) /
726f432bbb6Schristos sizeof(*buf) - 1)
727f432bbb6Schristos width = sizeof(buf) / sizeof(*buf) - 1;
7289790c07aSjoerg if ((width = parsefloat(fp, buf, buf + width, loc)) == 0)
729f432bbb6Schristos goto match_failure;
730f432bbb6Schristos if ((flags & SUPPRESS) == 0) {
731f432bbb6Schristos if (flags & LONGDBL) {
7329790c07aSjoerg long double res = wcstold_l(buf, &p,
7339790c07aSjoerg loc);
734f432bbb6Schristos *va_arg(ap, long double *) = res;
735f432bbb6Schristos } else
736f432bbb6Schristos if (flags & LONG) {
7379790c07aSjoerg double res = wcstod_l(buf, &p, loc);
738f432bbb6Schristos *va_arg(ap, double *) = res;
739f432bbb6Schristos } else {
7409790c07aSjoerg float res = wcstof_l(buf, &p, loc);
741f432bbb6Schristos *va_arg(ap, float *) = res;
742f432bbb6Schristos }
743f432bbb6Schristos #ifdef DEBUG
744eb7fe437Schristos if (p - buf != (ptrdiff_t)width)
745f432bbb6Schristos abort();
746f432bbb6Schristos #endif
747f432bbb6Schristos nassigned++;
748f432bbb6Schristos }
749f432bbb6Schristos nread += width;
750f432bbb6Schristos nconversions++;
751f432bbb6Schristos break;
752f432bbb6Schristos #endif /* !NO_FLOATING_POINT */
753f432bbb6Schristos }
754f432bbb6Schristos }
755f432bbb6Schristos input_failure:
756526d9427Schristos return nconversions != 0 ? nassigned : EOF;
757f432bbb6Schristos match_failure:
758526d9427Schristos return nassigned;
759f432bbb6Schristos }
760f432bbb6Schristos
761f432bbb6Schristos #ifndef NO_FLOATING_POINT
762f432bbb6Schristos static int
parsefloat(FILE * fp,wchar_t * buf,wchar_t * end,locale_t loc)7639790c07aSjoerg parsefloat(FILE *fp, wchar_t *buf, wchar_t *end, locale_t loc)
764f432bbb6Schristos {
765f432bbb6Schristos wchar_t *commit, *p;
766f432bbb6Schristos int infnanpos = 0;
767f432bbb6Schristos enum {
768f432bbb6Schristos S_START, S_GOTSIGN, S_INF, S_NAN, S_MAYBEHEX,
769f432bbb6Schristos S_DIGITS, S_FRAC, S_EXP, S_EXPDIGITS
770f432bbb6Schristos } state = S_START;
771f432bbb6Schristos wchar_t c;
7729790c07aSjoerg wchar_t decpt = (wchar_t)(unsigned char)*localeconv_l(loc)->decimal_point;
773f432bbb6Schristos int gotmantdig = 0, ishex = 0;
774f432bbb6Schristos
775f432bbb6Schristos /*
776f432bbb6Schristos * We set commit = p whenever the string we have read so far
777f432bbb6Schristos * constitutes a valid representation of a floating point
778f432bbb6Schristos * number by itself. At some point, the parse will complete
779f432bbb6Schristos * or fail, and we will ungetc() back to the last commit point.
780f432bbb6Schristos * To ensure that the file offset gets updated properly, it is
781f432bbb6Schristos * always necessary to read at least one character that doesn't
782f432bbb6Schristos * match; thus, we can't short-circuit "infinity" or "nan(...)".
783f432bbb6Schristos */
784f432bbb6Schristos commit = buf - 1;
785f432bbb6Schristos c = WEOF;
786f432bbb6Schristos for (p = buf; p < end; ) {
787f432bbb6Schristos if ((c = __fgetwc_unlock(fp)) == WEOF)
788f432bbb6Schristos break;
789f432bbb6Schristos reswitch:
790f432bbb6Schristos switch (state) {
791f432bbb6Schristos case S_START:
792f432bbb6Schristos state = S_GOTSIGN;
793f432bbb6Schristos if (c == '-' || c == '+')
794f432bbb6Schristos break;
795f432bbb6Schristos else
796f432bbb6Schristos goto reswitch;
797f432bbb6Schristos case S_GOTSIGN:
798f432bbb6Schristos switch (c) {
799f432bbb6Schristos case '0':
800f432bbb6Schristos state = S_MAYBEHEX;
801f432bbb6Schristos commit = p;
802f432bbb6Schristos break;
803f432bbb6Schristos case 'I':
804f432bbb6Schristos case 'i':
805f432bbb6Schristos state = S_INF;
806f432bbb6Schristos break;
807f432bbb6Schristos case 'N':
808f432bbb6Schristos case 'n':
809f432bbb6Schristos state = S_NAN;
810f432bbb6Schristos break;
811f432bbb6Schristos default:
812f432bbb6Schristos state = S_DIGITS;
813f432bbb6Schristos goto reswitch;
814f432bbb6Schristos }
815f432bbb6Schristos break;
816f432bbb6Schristos case S_INF:
817f432bbb6Schristos if (infnanpos > 6 ||
818f432bbb6Schristos (c != "nfinity"[infnanpos] &&
819f432bbb6Schristos c != "NFINITY"[infnanpos]))
820f432bbb6Schristos goto parsedone;
821f432bbb6Schristos if (infnanpos == 1 || infnanpos == 6)
822f432bbb6Schristos commit = p; /* inf or infinity */
823f432bbb6Schristos infnanpos++;
824f432bbb6Schristos break;
825f432bbb6Schristos case S_NAN:
826f432bbb6Schristos switch (infnanpos) {
827f432bbb6Schristos case -1: /* XXX kludge to deal with nan(...) */
828f432bbb6Schristos goto parsedone;
829f432bbb6Schristos case 0:
830f432bbb6Schristos if (c != 'A' && c != 'a')
831f432bbb6Schristos goto parsedone;
832f432bbb6Schristos break;
833f432bbb6Schristos case 1:
834f432bbb6Schristos if (c != 'N' && c != 'n')
835f432bbb6Schristos goto parsedone;
836f432bbb6Schristos else
837f432bbb6Schristos commit = p;
838f432bbb6Schristos break;
839f432bbb6Schristos case 2:
840f432bbb6Schristos if (c != '(')
841f432bbb6Schristos goto parsedone;
842f432bbb6Schristos break;
843f432bbb6Schristos default:
844f432bbb6Schristos if (c == ')') {
845f432bbb6Schristos commit = p;
846f432bbb6Schristos infnanpos = -2;
8479790c07aSjoerg } else if (!iswalnum_l(c, loc) && c != '_')
848f432bbb6Schristos goto parsedone;
849f432bbb6Schristos break;
850f432bbb6Schristos }
851f432bbb6Schristos infnanpos++;
852f432bbb6Schristos break;
853f432bbb6Schristos case S_MAYBEHEX:
854f432bbb6Schristos state = S_DIGITS;
855f432bbb6Schristos if (c == 'X' || c == 'x') {
856f432bbb6Schristos ishex = 1;
857f432bbb6Schristos break;
858f432bbb6Schristos } else { /* we saw a '0', but no 'x' */
859f432bbb6Schristos gotmantdig = 1;
860f432bbb6Schristos goto reswitch;
861f432bbb6Schristos }
862f432bbb6Schristos case S_DIGITS:
8639790c07aSjoerg if ((ishex && iswxdigit_l(c, loc)) ||
8649790c07aSjoerg iswdigit_l(c, loc))
865f432bbb6Schristos gotmantdig = 1;
866f432bbb6Schristos else {
867f432bbb6Schristos state = S_FRAC;
868f432bbb6Schristos if (c != decpt)
869f432bbb6Schristos goto reswitch;
870f432bbb6Schristos }
871f432bbb6Schristos if (gotmantdig)
872f432bbb6Schristos commit = p;
873f432bbb6Schristos break;
874f432bbb6Schristos case S_FRAC:
875f432bbb6Schristos if (((c == 'E' || c == 'e') && !ishex) ||
876f432bbb6Schristos ((c == 'P' || c == 'p') && ishex)) {
877f432bbb6Schristos if (!gotmantdig)
878f432bbb6Schristos goto parsedone;
879f432bbb6Schristos else
880f432bbb6Schristos state = S_EXP;
8819790c07aSjoerg } else if ((ishex && iswxdigit_l(c, loc)) ||
8829790c07aSjoerg iswdigit_l(c, loc)) {
883f432bbb6Schristos commit = p;
884f432bbb6Schristos gotmantdig = 1;
885f432bbb6Schristos } else
886f432bbb6Schristos goto parsedone;
887f432bbb6Schristos break;
888f432bbb6Schristos case S_EXP:
889f432bbb6Schristos state = S_EXPDIGITS;
890f432bbb6Schristos if (c == '-' || c == '+')
891f432bbb6Schristos break;
892f432bbb6Schristos else
893f432bbb6Schristos goto reswitch;
894f432bbb6Schristos case S_EXPDIGITS:
8959790c07aSjoerg if (iswdigit_l(c, loc))
896f432bbb6Schristos commit = p;
897f432bbb6Schristos else
898f432bbb6Schristos goto parsedone;
899f432bbb6Schristos break;
900f432bbb6Schristos default:
901f432bbb6Schristos abort();
902f432bbb6Schristos }
903f432bbb6Schristos *p++ = c;
904f432bbb6Schristos c = WEOF;
905f432bbb6Schristos }
906f432bbb6Schristos
907f432bbb6Schristos parsedone:
908f432bbb6Schristos if (c != WEOF)
909f432bbb6Schristos ungetwc(c, fp);
910f432bbb6Schristos while (commit < --p)
911f432bbb6Schristos ungetwc(*p, fp);
912f432bbb6Schristos *++commit = '\0';
913c5e820caSchristos _DIAGASSERT(__type_fit(int, commit - buf));
914c5e820caSchristos return (int)(commit - buf);
915f432bbb6Schristos }
916f432bbb6Schristos #endif
917