blob: c74c6e7c8c952d2d1e893294be1a3176c450f6a5 [file] [log] [blame]
Elliott Hughesf1ada792014-05-02 17:56:56 -07001/* $OpenBSD: vfwscanf.c,v 1.4 2014/03/19 05:17:01 guenther Exp $ */
Elliott Hughes01ae00f2014-04-29 16:28:56 -07002/*-
3 * Copyright (c) 1990, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * This code is derived from software contributed to Berkeley by
7 * Chris Torek.
8 *
9 * Redistribution and use in source and binary forms, with or without
10 * modification, are permitted provided that the following conditions
11 * are met:
12 * 1. Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33
Elliott Hughes01ae00f2014-04-29 16:28:56 -070034#include <inttypes.h>
35#include <limits.h>
36#include <locale.h>
37#include <stdarg.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <string.h>
42#include <wctype.h>
43#include "local.h"
44
Elliott Hughes01ae00f2014-04-29 16:28:56 -070045#include "floatio.h"
Elliott Hughes01ae00f2014-04-29 16:28:56 -070046
Elliott Hughesc8f2c522017-10-31 13:07:51 -070047#define BUF 513 /* Maximum length of numeric string. */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070048
49/*
50 * Flags used during conversion.
51 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070052#define LONG 0x00001 /* l: long or double */
53#define LONGDBL 0x00002 /* L: long double */
54#define SHORT 0x00004 /* h: short */
55#define SHORTSHORT 0x00008 /* hh: 8 bit integer */
56#define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */
57#define POINTER 0x00020 /* p: void * (as hex) */
58#define SIZEINT 0x00040 /* z: (signed) size_t */
59#define MAXINT 0x00080 /* j: intmax_t */
60#define PTRINT 0x00100 /* t: ptrdiff_t */
61#define NOSKIP 0x00200 /* [ or c: do not skip blanks */
62#define SUPPRESS 0x00400 /* *: suppress assignment */
63#define UNSIGNED 0x00800 /* %[oupxX] conversions */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070064
65/*
66 * The following are used in numeric conversions only:
67 * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
68 * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
69 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070070#define SIGNOK 0x01000 /* +/- is (still) legal */
71#define HAVESIGN 0x02000 /* sign detected */
72#define NDIGITS 0x04000 /* no digits detected */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070073
Elliott Hughesc8f2c522017-10-31 13:07:51 -070074#define DPTOK 0x08000 /* (float) decimal point is still legal */
75#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070076
Elliott Hughesc8f2c522017-10-31 13:07:51 -070077#define PFXOK 0x08000 /* 0x prefix is (still) legal */
78#define NZDIGITS 0x10000 /* no zero digits detected */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070079
80/*
81 * Conversion types.
82 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070083#define CT_CHAR 0 /* %c conversion */
84#define CT_CCL 1 /* %[...] conversion */
85#define CT_STRING 2 /* %s conversion */
86#define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */
87#define CT_FLOAT 4 /* floating, i.e., strtod */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070088
89#define u_char unsigned char
90#define u_long unsigned long
91
Elliott Hughesc8f2c522017-10-31 13:07:51 -070092#define INCCL(_c) \
93 (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) \
94 : (wmemchr(ccls, (_c), ccle - ccls) != NULL))
Elliott Hughes01ae00f2014-04-29 16:28:56 -070095
Elliott Hughes7f0849f2016-08-26 16:17:17 -070096#pragma GCC diagnostic push
97#pragma GCC diagnostic ignored "-Wframe-larger-than="
98
Elliott Hughes01ae00f2014-04-29 16:28:56 -070099/*
100 * vfwscanf
101 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700102int __vfwscanf(FILE* __restrict fp, const wchar_t* __restrict fmt, __va_list ap) {
103 wint_t c; /* character from format, or conversion */
104 size_t width; /* field width, or 0 */
105 wchar_t* p; /* points into all kinds of strings */
106 int n; /* handy integer */
107 int flags; /* flags as defined above */
108 wchar_t* p0; /* saves original value of p when necessary */
109 int nassigned; /* number of fields assigned */
110 int nconversions; /* number of conversions */
111 int nread; /* number of characters consumed from fp */
112 int base; /* base argument to strtoimax/strtouimax */
113 wchar_t buf[BUF]; /* buffer for numeric conversions */
114 const wchar_t* ccls; /* character class start */
115 const wchar_t* ccle; /* character class end */
116 int cclcompl; /* ccl is complemented? */
117 wint_t wi; /* handy wint_t */
118 char* mbp; /* multibyte string pointer for %c %s %[ */
119 size_t nconv; /* number of bytes in mb. conversion */
120 char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
121 mbstate_t mbs;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700122
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700123 /* `basefix' is used to avoid `if' tests in the integer scanner */
124 static short basefix[17] = { 10, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 };
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700125
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700126 _SET_ORIENTATION(fp, 1);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700127
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700128 nassigned = 0;
129 nconversions = 0;
130 nread = 0;
131 base = 0; /* XXX just to keep gcc happy */
132 ccls = ccle = NULL;
133 for (;;) {
134 c = *fmt++;
135 if (c == 0) {
136 return (nassigned);
137 }
138 if (iswspace(c)) {
139 while ((c = __fgetwc_unlock(fp)) != WEOF && iswspace(c))
140 ;
141 if (c != WEOF) __ungetwc(c, fp);
142 continue;
143 }
144 if (c != '%') goto literal;
145 width = 0;
146 flags = 0;
147 /*
148 * switch on the format. continue if done;
149 * break once format type is derived.
150 */
151 again:
152 c = *fmt++;
153 switch (c) {
154 case '%':
155 literal:
156 if ((wi = __fgetwc_unlock(fp)) == WEOF) goto input_failure;
157 if (wi != c) {
158 __ungetwc(wi, fp);
159 goto input_failure;
160 }
161 nread++;
162 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700163
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700164 case '*':
165 flags |= SUPPRESS;
166 goto again;
167 case 'j':
168 flags |= MAXINT;
169 goto again;
170 case 'L':
171 flags |= LONGDBL;
172 goto again;
173 case 'h':
174 if (*fmt == 'h') {
175 fmt++;
176 flags |= SHORTSHORT;
177 } else {
178 flags |= SHORT;
179 }
180 goto again;
181 case 'l':
182 if (*fmt == 'l') {
183 fmt++;
184 flags |= LLONG;
185 } else {
186 flags |= LONG;
187 }
188 goto again;
189 case 'q':
190 flags |= LLONG; /* deprecated */
191 goto again;
192 case 't':
193 flags |= PTRINT;
194 goto again;
195 case 'z':
196 flags |= SIZEINT;
197 goto again;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700198
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700199 case '0':
200 case '1':
201 case '2':
202 case '3':
203 case '4':
204 case '5':
205 case '6':
206 case '7':
207 case '8':
208 case '9':
209 width = width * 10 + c - '0';
210 goto again;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700211
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700212 /*
213 * Conversions.
214 * Those marked `compat' are for 4.[123]BSD compatibility.
215 *
216 * (According to ANSI, E and X formats are supposed
217 * to the same as e and x. Sorry about that.)
218 */
219 case 'D': /* compat */
220 flags |= LONG;
221 /* FALLTHROUGH */
222 case 'd':
223 c = CT_INT;
224 base = 10;
225 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700226
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700227 case 'i':
228 c = CT_INT;
229 base = 0;
230 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700231
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700232 case 'O': /* compat */
233 flags |= LONG;
234 /* FALLTHROUGH */
235 case 'o':
236 c = CT_INT;
237 flags |= UNSIGNED;
238 base = 8;
239 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700240
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700241 case 'u':
242 c = CT_INT;
243 flags |= UNSIGNED;
244 base = 10;
245 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700246
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700247 case 'X':
248 case 'x':
249 flags |= PFXOK; /* enable 0x prefixing */
250 c = CT_INT;
251 flags |= UNSIGNED;
252 base = 16;
253 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700254
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700255 case 'e':
256 case 'E':
257 case 'f':
258 case 'F':
259 case 'g':
260 case 'G':
261 case 'a':
262 case 'A':
263 c = CT_FLOAT;
264 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700265
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700266 case 's':
267 c = CT_STRING;
268 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700269
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700270 case '[':
271 ccls = fmt;
272 if (*fmt == '^') {
273 cclcompl = 1;
274 fmt++;
275 } else
276 cclcompl = 0;
277 if (*fmt == ']') fmt++;
278 while (*fmt != '\0' && *fmt != ']') fmt++;
279 ccle = fmt;
280 fmt++;
281 flags |= NOSKIP;
282 c = CT_CCL;
283 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700284
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700285 case 'c':
286 flags |= NOSKIP;
287 c = CT_CHAR;
288 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700289
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700290 case 'p': /* pointer format is like hex */
291 flags |= POINTER | PFXOK;
292 c = CT_INT;
293 flags |= UNSIGNED;
294 base = 16;
295 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700296
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700297 case 'n':
298 nconversions++;
299 if (flags & SUPPRESS) continue;
300 if (flags & SHORTSHORT)
301 *va_arg(ap, signed char*) = nread;
302 else if (flags & SHORT)
303 *va_arg(ap, short*) = nread;
304 else if (flags & LONG)
305 *va_arg(ap, long*) = nread;
306 else if (flags & SIZEINT)
307 *va_arg(ap, ssize_t*) = nread;
308 else if (flags & PTRINT)
309 *va_arg(ap, ptrdiff_t*) = nread;
310 else if (flags & LLONG)
311 *va_arg(ap, long long*) = nread;
312 else if (flags & MAXINT)
313 *va_arg(ap, intmax_t*) = nread;
314 else
315 *va_arg(ap, int*) = nread;
316 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700317
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700318 /*
319 * Disgusting backwards compatibility hacks. XXX
320 */
321 case '\0': /* compat */
322 return (EOF);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700323
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700324 default: /* compat */
325 if (iswupper(c)) flags |= LONG;
326 c = CT_INT;
327 base = 10;
328 break;
329 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700330
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700331 /*
332 * Consume leading white space, except for formats
333 * that suppress this.
334 */
335 if ((flags & NOSKIP) == 0) {
336 while ((wi = __fgetwc_unlock(fp)) != WEOF && iswspace(wi)) nread++;
337 if (wi == WEOF) goto input_failure;
338 __ungetwc(wi, fp);
339 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700340
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700341 /*
342 * Do the conversion.
343 */
344 switch (c) {
345 case CT_CHAR:
346 /* scan arbitrary characters (sets NOSKIP) */
347 if (width == 0) width = 1;
348 if (flags & LONG) {
349 if (!(flags & SUPPRESS)) p = va_arg(ap, wchar_t*);
350 n = 0;
351 while (width-- != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
352 if (!(flags & SUPPRESS)) *p++ = (wchar_t)wi;
353 n++;
354 }
355 if (n == 0) goto input_failure;
356 nread += n;
357 if (!(flags & SUPPRESS)) nassigned++;
358 } else {
359 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
360 n = 0;
361 memset(&mbs, 0, sizeof(mbs));
362 while (width != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
363 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
364 nconv = wcrtomb(mbp, wi, &mbs);
365 if (nconv == (size_t)-1) goto input_failure;
366 } else {
367 nconv = wcrtomb(mbbuf, wi, &mbs);
368 if (nconv == (size_t)-1) goto input_failure;
369 if (nconv > width) {
370 __ungetwc(wi, fp);
371 break;
372 }
373 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
374 }
375 if (!(flags & SUPPRESS)) mbp += nconv;
376 width -= nconv;
377 n++;
378 }
379 if (n == 0) goto input_failure;
380 nread += n;
381 if (!(flags & SUPPRESS)) nassigned++;
382 }
383 nconversions++;
384 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700385
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700386 case CT_CCL:
387 /* scan a (nonempty) character class (sets NOSKIP) */
388 if (width == 0) width = (size_t)~0; /* `infinity' */
389 /* take only those things in the class */
390 if ((flags & SUPPRESS) && (flags & LONG)) {
391 n = 0;
392 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi)) n++;
393 if (wi != WEOF) __ungetwc(wi, fp);
394 if (n == 0) goto match_failure;
395 } else if (flags & LONG) {
396 p0 = p = va_arg(ap, wchar_t*);
397 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi))
398 *p++ = (wchar_t)wi;
399 if (wi != WEOF) __ungetwc(wi, fp);
400 n = p - p0;
401 if (n == 0) goto match_failure;
402 *p = 0;
403 nassigned++;
404 } else {
405 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
406 n = 0;
407 memset(&mbs, 0, sizeof(mbs));
408 while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && INCCL(wi)) {
409 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
410 nconv = wcrtomb(mbp, wi, &mbs);
411 if (nconv == (size_t)-1) goto input_failure;
412 } else {
413 nconv = wcrtomb(mbbuf, wi, &mbs);
414 if (nconv == (size_t)-1) goto input_failure;
415 if (nconv > width) break;
416 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
417 }
418 if (!(flags & SUPPRESS)) mbp += nconv;
419 width -= nconv;
420 n++;
421 }
422 if (wi != WEOF) __ungetwc(wi, fp);
423 if (!(flags & SUPPRESS)) {
424 *mbp = 0;
425 nassigned++;
426 }
427 }
428 nread += n;
429 nconversions++;
430 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700431
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700432 case CT_STRING:
433 /* like CCL, but zero-length string OK, & no NOSKIP */
434 if (width == 0) width = (size_t)~0;
435 if ((flags & SUPPRESS) && (flags & LONG)) {
436 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) nread++;
437 if (wi != WEOF) __ungetwc(wi, fp);
438 } else if (flags & LONG) {
439 p0 = p = va_arg(ap, wchar_t*);
440 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) {
441 *p++ = (wchar_t)wi;
442 nread++;
443 }
444 if (wi != WEOF) __ungetwc(wi, fp);
445 *p = 0;
446 nassigned++;
447 } else {
448 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
449 memset(&mbs, 0, sizeof(mbs));
450 while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && !iswspace(wi)) {
451 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
452 nconv = wcrtomb(mbp, wi, &mbs);
453 if (nconv == (size_t)-1) goto input_failure;
454 } else {
455 nconv = wcrtomb(mbbuf, wi, &mbs);
456 if (nconv == (size_t)-1) goto input_failure;
457 if (nconv > width) break;
458 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
459 }
460 if (!(flags & SUPPRESS)) mbp += nconv;
461 width -= nconv;
462 nread++;
463 }
464 if (wi != WEOF) __ungetwc(wi, fp);
465 if (!(flags & SUPPRESS)) {
466 *mbp = 0;
467 nassigned++;
468 }
469 }
470 nconversions++;
471 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700472
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700473 case CT_INT:
474 /* scan an integer as if by strtoimax/strtoumax */
475 if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
476 width = sizeof(buf) / sizeof(*buf) - 1;
477 flags |= SIGNOK | NDIGITS | NZDIGITS;
478 for (p = buf; width; width--) {
479 c = __fgetwc_unlock(fp);
480 /*
481 * Switch on the character; `goto ok'
482 * if we accept it as a part of number.
483 */
484 switch (c) {
485 /*
486 * The digit 0 is always legal, but is
487 * special. For %i conversions, if no
488 * digits (zero or nonzero) have been
489 * scanned (only signs), we will have
490 * base==0. In that case, we should set
491 * it to 8 and enable 0x prefixing.
492 * Also, if we have not scanned zero digits
493 * before this, do not turn off prefixing
494 * (someone else will turn it off if we
495 * have scanned any nonzero digits).
496 */
497 case '0':
498 if (base == 0) {
499 base = 8;
500 flags |= PFXOK;
501 }
502 if (flags & NZDIGITS)
503 flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
504 else
505 flags &= ~(SIGNOK | PFXOK | NDIGITS);
506 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700507
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700508 /* 1 through 7 always legal */
509 case '1':
510 case '2':
511 case '3':
512 case '4':
513 case '5':
514 case '6':
515 case '7':
516 base = basefix[base];
517 flags &= ~(SIGNOK | PFXOK | NDIGITS);
518 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700519
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700520 /* digits 8 and 9 ok iff decimal or hex */
521 case '8':
522 case '9':
523 base = basefix[base];
524 if (base <= 8) break; /* not legal here */
525 flags &= ~(SIGNOK | PFXOK | NDIGITS);
526 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700527
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700528 /* letters ok iff hex */
529 case 'A':
530 case 'B':
531 case 'C':
532 case 'D':
533 case 'E':
534 case 'F':
535 case 'a':
536 case 'b':
537 case 'c':
538 case 'd':
539 case 'e':
540 case 'f':
541 /* no need to fix base here */
542 if (base <= 10) break; /* not legal here */
543 flags &= ~(SIGNOK | PFXOK | NDIGITS);
544 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700545
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700546 /* sign ok only as first character */
547 case '+':
548 case '-':
549 if (flags & SIGNOK) {
550 flags &= ~SIGNOK;
551 flags |= HAVESIGN;
552 goto ok;
553 }
554 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700555
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700556 /*
557 * x ok iff flag still set and 2nd char (or
558 * 3rd char if we have a sign).
559 */
560 case 'x':
561 case 'X':
562 if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) {
563 base = 16; /* if %i */
564 flags &= ~PFXOK;
565 goto ok;
566 }
567 break;
568 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700569
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700570 /*
571 * If we got here, c is not a legal character
572 * for a number. Stop accumulating digits.
573 */
574 if (c != WEOF) __ungetwc(c, fp);
575 break;
576 ok:
577 /*
578 * c is legal: store it and look at the next.
579 */
580 *p++ = (wchar_t)c;
581 }
582 /*
583 * If we had only a sign, it is no good; push
584 * back the sign. If the number ends in `x',
585 * it was [sign] '0' 'x', so push back the x
586 * and treat it as [sign] '0'.
587 */
588 if (flags & NDIGITS) {
589 if (p > buf) __ungetwc(*--p, fp);
590 goto match_failure;
591 }
592 c = p[-1];
593 if (c == 'x' || c == 'X') {
594 --p;
595 __ungetwc(c, fp);
596 }
597 if ((flags & SUPPRESS) == 0) {
598 uintmax_t res;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700599
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700600 *p = '\0';
601 if (flags & UNSIGNED)
602 res = wcstoimax(buf, NULL, base);
603 else
604 res = wcstoumax(buf, NULL, base);
605 if (flags & POINTER)
606 *va_arg(ap, void**) = (void*)(uintptr_t)res;
607 else if (flags & MAXINT)
608 *va_arg(ap, intmax_t*) = res;
609 else if (flags & LLONG)
610 *va_arg(ap, long long*) = res;
611 else if (flags & SIZEINT)
612 *va_arg(ap, ssize_t*) = res;
613 else if (flags & PTRINT)
614 *va_arg(ap, ptrdiff_t*) = res;
615 else if (flags & LONG)
616 *va_arg(ap, long*) = res;
617 else if (flags & SHORT)
618 *va_arg(ap, short*) = res;
619 else if (flags & SHORTSHORT)
620 *va_arg(ap, signed char*) = res;
621 else
622 *va_arg(ap, int*) = res;
623 nassigned++;
624 }
625 nread += p - buf;
626 nconversions++;
627 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700628
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700629 case CT_FLOAT:
630 /* scan a floating point number as if by strtod */
631 if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
632 width = sizeof(buf) / sizeof(*buf) - 1;
633 if ((width = wparsefloat(fp, buf, buf + width)) == 0) goto match_failure;
634 if ((flags & SUPPRESS) == 0) {
635 if (flags & LONGDBL) {
636 long double res = wcstold(buf, &p);
637 *va_arg(ap, long double*) = res;
638 } else if (flags & LONG) {
639 double res = wcstod(buf, &p);
640 *va_arg(ap, double*) = res;
641 } else {
642 float res = wcstof(buf, &p);
643 *va_arg(ap, float*) = res;
644 }
645 if (p - buf != (ptrdiff_t)width) abort();
646 nassigned++;
647 }
648 nread += width;
649 nconversions++;
650 break;
651 }
652 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700653input_failure:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700654 return (nconversions != 0 ? nassigned : EOF);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700655match_failure:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700656 return (nassigned);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700657}
Elliott Hughes7f0849f2016-08-26 16:17:17 -0700658#pragma GCC diagnostic pop
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700659
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700660int vfwscanf(FILE* __restrict fp, const wchar_t* __restrict fmt, __va_list ap) {
661 int r;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700662
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700663 FLOCKFILE(fp);
664 r = __vfwscanf(fp, fmt, ap);
665 FUNLOCKFILE(fp);
666 return (r);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700667}