blob: 1030a62e074b9f7547995fe3ed2f73676d3abaa8 [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 Hughesc8f2c522017-10-31 13:07:51 -070045#define BUF 513 /* Maximum length of numeric string. */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070046
47/*
48 * Flags used during conversion.
49 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070050#define LONG 0x00001 /* l: long or double */
51#define LONGDBL 0x00002 /* L: long double */
52#define SHORT 0x00004 /* h: short */
53#define SHORTSHORT 0x00008 /* hh: 8 bit integer */
54#define LLONG 0x00010 /* ll: long long (+ deprecated q: quad) */
55#define POINTER 0x00020 /* p: void * (as hex) */
56#define SIZEINT 0x00040 /* z: (signed) size_t */
57#define MAXINT 0x00080 /* j: intmax_t */
58#define PTRINT 0x00100 /* t: ptrdiff_t */
59#define NOSKIP 0x00200 /* [ or c: do not skip blanks */
60#define SUPPRESS 0x00400 /* *: suppress assignment */
61#define UNSIGNED 0x00800 /* %[oupxX] conversions */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070062
63/*
64 * The following are used in numeric conversions only:
65 * SIGNOK, HAVESIGN, NDIGITS, DPTOK, and EXPOK are for floating point;
66 * SIGNOK, HAVESIGN, NDIGITS, PFXOK, and NZDIGITS are for integral.
67 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070068#define SIGNOK 0x01000 /* +/- is (still) legal */
69#define HAVESIGN 0x02000 /* sign detected */
70#define NDIGITS 0x04000 /* no digits detected */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070071
Elliott Hughesc8f2c522017-10-31 13:07:51 -070072#define DPTOK 0x08000 /* (float) decimal point is still legal */
73#define EXPOK 0x10000 /* (float) exponent (e+3, etc) still legal */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070074
Elliott Hughesc8f2c522017-10-31 13:07:51 -070075#define PFXOK 0x08000 /* 0x prefix is (still) legal */
76#define NZDIGITS 0x10000 /* no zero digits detected */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070077
78/*
79 * Conversion types.
80 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -070081#define CT_CHAR 0 /* %c conversion */
82#define CT_CCL 1 /* %[...] conversion */
83#define CT_STRING 2 /* %s conversion */
84#define CT_INT 3 /* integer, i.e., strtoimax or strtoumax */
85#define CT_FLOAT 4 /* floating, i.e., strtod */
Elliott Hughes01ae00f2014-04-29 16:28:56 -070086
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -080087// An interpretive version of __sccl from vfscanf.c --- a table of all wchar_t values would
88// be a little too expensive, and some kind of compressed version isn't worth the trouble.
89static inline bool in_ccl(wchar_t wc, const wchar_t* ccl) {
90 // Is this a negated set?
91 bool member_result = true;
92 if (*ccl == '^') {
93 member_result = false;
94 ++ccl;
95 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -070096
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -080097 // The first character may be ']' or '-' without being special.
98 if (*ccl == '-' || *ccl == ']') {
99 // A literal match?
100 if (*ccl == wc) return member_result;
101 ++ccl;
102 }
103
104 while (*ccl && *ccl != ']') {
105 // The last character may be '-' without being special.
106 if (*ccl == '-' && ccl[1] != '\0' && ccl[1] != ']') {
107 wchar_t first = *(ccl - 1);
108 wchar_t last = *(ccl + 1);
109 if (first <= last) {
110 // In the range?
111 if (wc >= first && wc <= last) return member_result;
112 ccl += 2;
113 continue;
114 }
115 // A '-' is not considered to be part of a range if the character after
116 // is not greater than the character before, so fall through...
117 }
118 // A literal match?
119 if (*ccl == wc) return member_result;
120 ++ccl;
121 }
122 return !member_result;
123}
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700124
Elliott Hughes7f0849f2016-08-26 16:17:17 -0700125#pragma GCC diagnostic push
126#pragma GCC diagnostic ignored "-Wframe-larger-than="
127
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700128/*
129 * vfwscanf
130 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700131int __vfwscanf(FILE* __restrict fp, const wchar_t* __restrict fmt, __va_list ap) {
132 wint_t c; /* character from format, or conversion */
133 size_t width; /* field width, or 0 */
134 wchar_t* p; /* points into all kinds of strings */
135 int n; /* handy integer */
136 int flags; /* flags as defined above */
137 wchar_t* p0; /* saves original value of p when necessary */
138 int nassigned; /* number of fields assigned */
139 int nconversions; /* number of conversions */
140 int nread; /* number of characters consumed from fp */
141 int base; /* base argument to strtoimax/strtouimax */
142 wchar_t buf[BUF]; /* buffer for numeric conversions */
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -0800143 const wchar_t* ccl;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700144 wint_t wi; /* handy wint_t */
145 char* mbp; /* multibyte string pointer for %c %s %[ */
146 size_t nconv; /* number of bytes in mb. conversion */
147 char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
148 mbstate_t mbs;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700149
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700150 /* `basefix' is used to avoid `if' tests in the integer scanner */
151 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 -0700152
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700153 _SET_ORIENTATION(fp, 1);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700154
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700155 nassigned = 0;
156 nconversions = 0;
157 nread = 0;
158 base = 0; /* XXX just to keep gcc happy */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700159 for (;;) {
160 c = *fmt++;
161 if (c == 0) {
162 return (nassigned);
163 }
164 if (iswspace(c)) {
165 while ((c = __fgetwc_unlock(fp)) != WEOF && iswspace(c))
166 ;
167 if (c != WEOF) __ungetwc(c, fp);
168 continue;
169 }
170 if (c != '%') goto literal;
171 width = 0;
172 flags = 0;
173 /*
174 * switch on the format. continue if done;
175 * break once format type is derived.
176 */
177 again:
178 c = *fmt++;
179 switch (c) {
180 case '%':
181 literal:
182 if ((wi = __fgetwc_unlock(fp)) == WEOF) goto input_failure;
183 if (wi != c) {
184 __ungetwc(wi, fp);
185 goto input_failure;
186 }
187 nread++;
188 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700189
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700190 case '*':
191 flags |= SUPPRESS;
192 goto again;
193 case 'j':
194 flags |= MAXINT;
195 goto again;
196 case 'L':
197 flags |= LONGDBL;
198 goto again;
199 case 'h':
200 if (*fmt == 'h') {
201 fmt++;
202 flags |= SHORTSHORT;
203 } else {
204 flags |= SHORT;
205 }
206 goto again;
207 case 'l':
208 if (*fmt == 'l') {
209 fmt++;
210 flags |= LLONG;
211 } else {
212 flags |= LONG;
213 }
214 goto again;
215 case 'q':
216 flags |= LLONG; /* deprecated */
217 goto again;
218 case 't':
219 flags |= PTRINT;
220 goto again;
221 case 'z':
222 flags |= SIZEINT;
223 goto again;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700224
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700225 case '0':
226 case '1':
227 case '2':
228 case '3':
229 case '4':
230 case '5':
231 case '6':
232 case '7':
233 case '8':
234 case '9':
235 width = width * 10 + c - '0';
236 goto again;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700237
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700238 /*
239 * Conversions.
240 * Those marked `compat' are for 4.[123]BSD compatibility.
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700241 */
242 case 'D': /* compat */
243 flags |= LONG;
244 /* FALLTHROUGH */
245 case 'd':
246 c = CT_INT;
247 base = 10;
248 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700249
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700250 case 'i':
251 c = CT_INT;
252 base = 0;
253 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700254
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700255 case 'O': /* compat */
256 flags |= LONG;
257 /* FALLTHROUGH */
258 case 'o':
259 c = CT_INT;
260 flags |= UNSIGNED;
261 base = 8;
262 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700263
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700264 case 'u':
265 c = CT_INT;
266 flags |= UNSIGNED;
267 base = 10;
268 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700269
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700270 case 'X':
271 case 'x':
272 flags |= PFXOK; /* enable 0x prefixing */
273 c = CT_INT;
274 flags |= UNSIGNED;
275 base = 16;
276 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700277
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700278 case 'e':
279 case 'E':
280 case 'f':
281 case 'F':
282 case 'g':
283 case 'G':
284 case 'a':
285 case 'A':
286 c = CT_FLOAT;
287 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700288
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700289 case 's':
290 c = CT_STRING;
291 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700292
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700293 case '[':
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -0800294 ccl = fmt;
295 if (*fmt == '^') fmt++;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700296 if (*fmt == ']') fmt++;
297 while (*fmt != '\0' && *fmt != ']') fmt++;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700298 fmt++;
299 flags |= NOSKIP;
300 c = CT_CCL;
301 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700302
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700303 case 'c':
304 flags |= NOSKIP;
305 c = CT_CHAR;
306 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700307
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700308 case 'p': /* pointer format is like hex */
309 flags |= POINTER | PFXOK;
310 c = CT_INT;
311 flags |= UNSIGNED;
312 base = 16;
313 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700314
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700315 case 'n':
316 nconversions++;
317 if (flags & SUPPRESS) continue;
318 if (flags & SHORTSHORT)
319 *va_arg(ap, signed char*) = nread;
320 else if (flags & SHORT)
321 *va_arg(ap, short*) = nread;
322 else if (flags & LONG)
323 *va_arg(ap, long*) = nread;
324 else if (flags & SIZEINT)
325 *va_arg(ap, ssize_t*) = nread;
326 else if (flags & PTRINT)
327 *va_arg(ap, ptrdiff_t*) = nread;
328 else if (flags & LLONG)
329 *va_arg(ap, long long*) = nread;
330 else if (flags & MAXINT)
331 *va_arg(ap, intmax_t*) = nread;
332 else
333 *va_arg(ap, int*) = nread;
334 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700335
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700336 /*
337 * Disgusting backwards compatibility hacks. XXX
338 */
339 case '\0': /* compat */
340 return (EOF);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700341
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700342 default: /* compat */
343 if (iswupper(c)) flags |= LONG;
344 c = CT_INT;
345 base = 10;
346 break;
347 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700348
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700349 /*
350 * Consume leading white space, except for formats
351 * that suppress this.
352 */
353 if ((flags & NOSKIP) == 0) {
354 while ((wi = __fgetwc_unlock(fp)) != WEOF && iswspace(wi)) nread++;
355 if (wi == WEOF) goto input_failure;
356 __ungetwc(wi, fp);
357 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700358
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700359 /*
360 * Do the conversion.
361 */
362 switch (c) {
363 case CT_CHAR:
364 /* scan arbitrary characters (sets NOSKIP) */
365 if (width == 0) width = 1;
366 if (flags & LONG) {
367 if (!(flags & SUPPRESS)) p = va_arg(ap, wchar_t*);
368 n = 0;
369 while (width-- != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
370 if (!(flags & SUPPRESS)) *p++ = (wchar_t)wi;
371 n++;
372 }
373 if (n == 0) goto input_failure;
374 nread += n;
375 if (!(flags & SUPPRESS)) nassigned++;
376 } else {
377 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
378 n = 0;
379 memset(&mbs, 0, sizeof(mbs));
380 while (width != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
381 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
382 nconv = wcrtomb(mbp, wi, &mbs);
383 if (nconv == (size_t)-1) goto input_failure;
384 } else {
385 nconv = wcrtomb(mbbuf, wi, &mbs);
386 if (nconv == (size_t)-1) goto input_failure;
387 if (nconv > width) {
388 __ungetwc(wi, fp);
389 break;
390 }
391 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
392 }
393 if (!(flags & SUPPRESS)) mbp += nconv;
394 width -= nconv;
395 n++;
396 }
397 if (n == 0) goto input_failure;
398 nread += n;
399 if (!(flags & SUPPRESS)) nassigned++;
400 }
401 nconversions++;
402 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700403
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700404 case CT_CCL:
405 /* scan a (nonempty) character class (sets NOSKIP) */
406 if (width == 0) width = (size_t)~0; /* `infinity' */
407 /* take only those things in the class */
408 if ((flags & SUPPRESS) && (flags & LONG)) {
409 n = 0;
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -0800410 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && in_ccl(wi, ccl)) n++;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700411 if (wi != WEOF) __ungetwc(wi, fp);
412 if (n == 0) goto match_failure;
413 } else if (flags & LONG) {
414 p0 = p = va_arg(ap, wchar_t*);
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -0800415 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && in_ccl(wi, ccl))
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700416 *p++ = (wchar_t)wi;
417 if (wi != WEOF) __ungetwc(wi, fp);
418 n = p - p0;
419 if (n == 0) goto match_failure;
420 *p = 0;
421 nassigned++;
422 } else {
423 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
424 n = 0;
425 memset(&mbs, 0, sizeof(mbs));
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -0800426 while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && in_ccl(wi, ccl)) {
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700427 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
428 nconv = wcrtomb(mbp, wi, &mbs);
429 if (nconv == (size_t)-1) goto input_failure;
430 } else {
431 nconv = wcrtomb(mbbuf, wi, &mbs);
432 if (nconv == (size_t)-1) goto input_failure;
433 if (nconv > width) break;
434 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
435 }
436 if (!(flags & SUPPRESS)) mbp += nconv;
437 width -= nconv;
438 n++;
439 }
440 if (wi != WEOF) __ungetwc(wi, fp);
Elliott Hughes0d3ba1f2017-12-06 16:41:35 -0800441 if (n == 0) goto match_failure;
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700442 if (!(flags & SUPPRESS)) {
443 *mbp = 0;
444 nassigned++;
445 }
446 }
447 nread += n;
448 nconversions++;
449 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700450
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700451 case CT_STRING:
452 /* like CCL, but zero-length string OK, & no NOSKIP */
453 if (width == 0) width = (size_t)~0;
454 if ((flags & SUPPRESS) && (flags & LONG)) {
455 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) nread++;
456 if (wi != WEOF) __ungetwc(wi, fp);
457 } else if (flags & LONG) {
458 p0 = p = va_arg(ap, wchar_t*);
459 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) {
460 *p++ = (wchar_t)wi;
461 nread++;
462 }
463 if (wi != WEOF) __ungetwc(wi, fp);
464 *p = 0;
465 nassigned++;
466 } else {
467 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
468 memset(&mbs, 0, sizeof(mbs));
469 while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && !iswspace(wi)) {
470 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
471 nconv = wcrtomb(mbp, wi, &mbs);
472 if (nconv == (size_t)-1) goto input_failure;
473 } else {
474 nconv = wcrtomb(mbbuf, wi, &mbs);
475 if (nconv == (size_t)-1) goto input_failure;
476 if (nconv > width) break;
477 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
478 }
479 if (!(flags & SUPPRESS)) mbp += nconv;
480 width -= nconv;
481 nread++;
482 }
483 if (wi != WEOF) __ungetwc(wi, fp);
484 if (!(flags & SUPPRESS)) {
485 *mbp = 0;
486 nassigned++;
487 }
488 }
489 nconversions++;
490 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700491
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700492 case CT_INT:
493 /* scan an integer as if by strtoimax/strtoumax */
494 if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
495 width = sizeof(buf) / sizeof(*buf) - 1;
496 flags |= SIGNOK | NDIGITS | NZDIGITS;
497 for (p = buf; width; width--) {
498 c = __fgetwc_unlock(fp);
499 /*
500 * Switch on the character; `goto ok'
501 * if we accept it as a part of number.
502 */
503 switch (c) {
504 /*
505 * The digit 0 is always legal, but is
506 * special. For %i conversions, if no
507 * digits (zero or nonzero) have been
508 * scanned (only signs), we will have
509 * base==0. In that case, we should set
510 * it to 8 and enable 0x prefixing.
511 * Also, if we have not scanned zero digits
512 * before this, do not turn off prefixing
513 * (someone else will turn it off if we
514 * have scanned any nonzero digits).
515 */
516 case '0':
517 if (base == 0) {
518 base = 8;
519 flags |= PFXOK;
520 }
521 if (flags & NZDIGITS)
522 flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
523 else
524 flags &= ~(SIGNOK | PFXOK | NDIGITS);
525 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700526
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700527 /* 1 through 7 always legal */
528 case '1':
529 case '2':
530 case '3':
531 case '4':
532 case '5':
533 case '6':
534 case '7':
535 base = basefix[base];
536 flags &= ~(SIGNOK | PFXOK | NDIGITS);
537 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700538
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700539 /* digits 8 and 9 ok iff decimal or hex */
540 case '8':
541 case '9':
542 base = basefix[base];
543 if (base <= 8) break; /* not legal here */
544 flags &= ~(SIGNOK | PFXOK | NDIGITS);
545 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700546
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700547 /* letters ok iff hex */
548 case 'A':
549 case 'B':
550 case 'C':
551 case 'D':
552 case 'E':
553 case 'F':
554 case 'a':
555 case 'b':
556 case 'c':
557 case 'd':
558 case 'e':
559 case 'f':
560 /* no need to fix base here */
561 if (base <= 10) break; /* not legal here */
562 flags &= ~(SIGNOK | PFXOK | NDIGITS);
563 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700564
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700565 /* sign ok only as first character */
566 case '+':
567 case '-':
568 if (flags & SIGNOK) {
569 flags &= ~SIGNOK;
570 flags |= HAVESIGN;
571 goto ok;
572 }
573 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700574
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700575 /*
576 * x ok iff flag still set and 2nd char (or
577 * 3rd char if we have a sign).
578 */
579 case 'x':
580 case 'X':
581 if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) {
582 base = 16; /* if %i */
583 flags &= ~PFXOK;
584 goto ok;
585 }
586 break;
587 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700588
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700589 /*
590 * If we got here, c is not a legal character
591 * for a number. Stop accumulating digits.
592 */
593 if (c != WEOF) __ungetwc(c, fp);
594 break;
595 ok:
596 /*
597 * c is legal: store it and look at the next.
598 */
599 *p++ = (wchar_t)c;
600 }
601 /*
602 * If we had only a sign, it is no good; push
603 * back the sign. If the number ends in `x',
604 * it was [sign] '0' 'x', so push back the x
605 * and treat it as [sign] '0'.
606 */
607 if (flags & NDIGITS) {
608 if (p > buf) __ungetwc(*--p, fp);
609 goto match_failure;
610 }
611 c = p[-1];
612 if (c == 'x' || c == 'X') {
613 --p;
614 __ungetwc(c, fp);
615 }
616 if ((flags & SUPPRESS) == 0) {
617 uintmax_t res;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700618
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700619 *p = '\0';
620 if (flags & UNSIGNED)
621 res = wcstoimax(buf, NULL, base);
622 else
623 res = wcstoumax(buf, NULL, base);
624 if (flags & POINTER)
625 *va_arg(ap, void**) = (void*)(uintptr_t)res;
626 else if (flags & MAXINT)
627 *va_arg(ap, intmax_t*) = res;
628 else if (flags & LLONG)
629 *va_arg(ap, long long*) = res;
630 else if (flags & SIZEINT)
631 *va_arg(ap, ssize_t*) = res;
632 else if (flags & PTRINT)
633 *va_arg(ap, ptrdiff_t*) = res;
634 else if (flags & LONG)
635 *va_arg(ap, long*) = res;
636 else if (flags & SHORT)
637 *va_arg(ap, short*) = res;
638 else if (flags & SHORTSHORT)
639 *va_arg(ap, signed char*) = res;
640 else
641 *va_arg(ap, int*) = res;
642 nassigned++;
643 }
644 nread += p - buf;
645 nconversions++;
646 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700647
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700648 case CT_FLOAT:
649 /* scan a floating point number as if by strtod */
650 if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
651 width = sizeof(buf) / sizeof(*buf) - 1;
652 if ((width = wparsefloat(fp, buf, buf + width)) == 0) goto match_failure;
653 if ((flags & SUPPRESS) == 0) {
654 if (flags & LONGDBL) {
655 long double res = wcstold(buf, &p);
656 *va_arg(ap, long double*) = res;
657 } else if (flags & LONG) {
658 double res = wcstod(buf, &p);
659 *va_arg(ap, double*) = res;
660 } else {
661 float res = wcstof(buf, &p);
662 *va_arg(ap, float*) = res;
663 }
664 if (p - buf != (ptrdiff_t)width) abort();
665 nassigned++;
666 }
667 nread += width;
668 nconversions++;
669 break;
670 }
671 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700672input_failure:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700673 return (nconversions != 0 ? nassigned : EOF);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700674match_failure:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700675 return (nassigned);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700676}
Elliott Hughes7f0849f2016-08-26 16:17:17 -0700677#pragma GCC diagnostic pop