blob: 206f4a210f9f44d88daf7dfb0e92181bad3de11c [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
87#define u_char unsigned char
88#define u_long unsigned long
89
Elliott Hughesc8f2c522017-10-31 13:07:51 -070090#define INCCL(_c) \
91 (cclcompl ? (wmemchr(ccls, (_c), ccle - ccls) == NULL) \
92 : (wmemchr(ccls, (_c), ccle - ccls) != NULL))
Elliott Hughes01ae00f2014-04-29 16:28:56 -070093
Elliott Hughes7f0849f2016-08-26 16:17:17 -070094#pragma GCC diagnostic push
95#pragma GCC diagnostic ignored "-Wframe-larger-than="
96
Elliott Hughes01ae00f2014-04-29 16:28:56 -070097/*
98 * vfwscanf
99 */
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700100int __vfwscanf(FILE* __restrict fp, const wchar_t* __restrict fmt, __va_list ap) {
101 wint_t c; /* character from format, or conversion */
102 size_t width; /* field width, or 0 */
103 wchar_t* p; /* points into all kinds of strings */
104 int n; /* handy integer */
105 int flags; /* flags as defined above */
106 wchar_t* p0; /* saves original value of p when necessary */
107 int nassigned; /* number of fields assigned */
108 int nconversions; /* number of conversions */
109 int nread; /* number of characters consumed from fp */
110 int base; /* base argument to strtoimax/strtouimax */
111 wchar_t buf[BUF]; /* buffer for numeric conversions */
112 const wchar_t* ccls; /* character class start */
113 const wchar_t* ccle; /* character class end */
114 int cclcompl; /* ccl is complemented? */
115 wint_t wi; /* handy wint_t */
116 char* mbp; /* multibyte string pointer for %c %s %[ */
117 size_t nconv; /* number of bytes in mb. conversion */
118 char mbbuf[MB_LEN_MAX]; /* temporary mb. character buffer */
119 mbstate_t mbs;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700120
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700121 /* `basefix' is used to avoid `if' tests in the integer scanner */
122 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 -0700123
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700124 _SET_ORIENTATION(fp, 1);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700125
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700126 nassigned = 0;
127 nconversions = 0;
128 nread = 0;
129 base = 0; /* XXX just to keep gcc happy */
130 ccls = ccle = NULL;
131 for (;;) {
132 c = *fmt++;
133 if (c == 0) {
134 return (nassigned);
135 }
136 if (iswspace(c)) {
137 while ((c = __fgetwc_unlock(fp)) != WEOF && iswspace(c))
138 ;
139 if (c != WEOF) __ungetwc(c, fp);
140 continue;
141 }
142 if (c != '%') goto literal;
143 width = 0;
144 flags = 0;
145 /*
146 * switch on the format. continue if done;
147 * break once format type is derived.
148 */
149 again:
150 c = *fmt++;
151 switch (c) {
152 case '%':
153 literal:
154 if ((wi = __fgetwc_unlock(fp)) == WEOF) goto input_failure;
155 if (wi != c) {
156 __ungetwc(wi, fp);
157 goto input_failure;
158 }
159 nread++;
160 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700161
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700162 case '*':
163 flags |= SUPPRESS;
164 goto again;
165 case 'j':
166 flags |= MAXINT;
167 goto again;
168 case 'L':
169 flags |= LONGDBL;
170 goto again;
171 case 'h':
172 if (*fmt == 'h') {
173 fmt++;
174 flags |= SHORTSHORT;
175 } else {
176 flags |= SHORT;
177 }
178 goto again;
179 case 'l':
180 if (*fmt == 'l') {
181 fmt++;
182 flags |= LLONG;
183 } else {
184 flags |= LONG;
185 }
186 goto again;
187 case 'q':
188 flags |= LLONG; /* deprecated */
189 goto again;
190 case 't':
191 flags |= PTRINT;
192 goto again;
193 case 'z':
194 flags |= SIZEINT;
195 goto again;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700196
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700197 case '0':
198 case '1':
199 case '2':
200 case '3':
201 case '4':
202 case '5':
203 case '6':
204 case '7':
205 case '8':
206 case '9':
207 width = width * 10 + c - '0';
208 goto again;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700209
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700210 /*
211 * Conversions.
212 * Those marked `compat' are for 4.[123]BSD compatibility.
213 *
214 * (According to ANSI, E and X formats are supposed
215 * to the same as e and x. Sorry about that.)
216 */
217 case 'D': /* compat */
218 flags |= LONG;
219 /* FALLTHROUGH */
220 case 'd':
221 c = CT_INT;
222 base = 10;
223 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700224
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700225 case 'i':
226 c = CT_INT;
227 base = 0;
228 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700229
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700230 case 'O': /* compat */
231 flags |= LONG;
232 /* FALLTHROUGH */
233 case 'o':
234 c = CT_INT;
235 flags |= UNSIGNED;
236 base = 8;
237 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700238
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700239 case 'u':
240 c = CT_INT;
241 flags |= UNSIGNED;
242 base = 10;
243 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700244
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700245 case 'X':
246 case 'x':
247 flags |= PFXOK; /* enable 0x prefixing */
248 c = CT_INT;
249 flags |= UNSIGNED;
250 base = 16;
251 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700252
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700253 case 'e':
254 case 'E':
255 case 'f':
256 case 'F':
257 case 'g':
258 case 'G':
259 case 'a':
260 case 'A':
261 c = CT_FLOAT;
262 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700263
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700264 case 's':
265 c = CT_STRING;
266 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700267
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700268 case '[':
269 ccls = fmt;
270 if (*fmt == '^') {
271 cclcompl = 1;
272 fmt++;
273 } else
274 cclcompl = 0;
275 if (*fmt == ']') fmt++;
276 while (*fmt != '\0' && *fmt != ']') fmt++;
277 ccle = fmt;
278 fmt++;
279 flags |= NOSKIP;
280 c = CT_CCL;
281 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700282
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700283 case 'c':
284 flags |= NOSKIP;
285 c = CT_CHAR;
286 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700287
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700288 case 'p': /* pointer format is like hex */
289 flags |= POINTER | PFXOK;
290 c = CT_INT;
291 flags |= UNSIGNED;
292 base = 16;
293 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700294
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700295 case 'n':
296 nconversions++;
297 if (flags & SUPPRESS) continue;
298 if (flags & SHORTSHORT)
299 *va_arg(ap, signed char*) = nread;
300 else if (flags & SHORT)
301 *va_arg(ap, short*) = nread;
302 else if (flags & LONG)
303 *va_arg(ap, long*) = nread;
304 else if (flags & SIZEINT)
305 *va_arg(ap, ssize_t*) = nread;
306 else if (flags & PTRINT)
307 *va_arg(ap, ptrdiff_t*) = nread;
308 else if (flags & LLONG)
309 *va_arg(ap, long long*) = nread;
310 else if (flags & MAXINT)
311 *va_arg(ap, intmax_t*) = nread;
312 else
313 *va_arg(ap, int*) = nread;
314 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700315
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700316 /*
317 * Disgusting backwards compatibility hacks. XXX
318 */
319 case '\0': /* compat */
320 return (EOF);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700321
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700322 default: /* compat */
323 if (iswupper(c)) flags |= LONG;
324 c = CT_INT;
325 base = 10;
326 break;
327 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700328
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700329 /*
330 * Consume leading white space, except for formats
331 * that suppress this.
332 */
333 if ((flags & NOSKIP) == 0) {
334 while ((wi = __fgetwc_unlock(fp)) != WEOF && iswspace(wi)) nread++;
335 if (wi == WEOF) goto input_failure;
336 __ungetwc(wi, fp);
337 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700338
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700339 /*
340 * Do the conversion.
341 */
342 switch (c) {
343 case CT_CHAR:
344 /* scan arbitrary characters (sets NOSKIP) */
345 if (width == 0) width = 1;
346 if (flags & LONG) {
347 if (!(flags & SUPPRESS)) p = va_arg(ap, wchar_t*);
348 n = 0;
349 while (width-- != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
350 if (!(flags & SUPPRESS)) *p++ = (wchar_t)wi;
351 n++;
352 }
353 if (n == 0) goto input_failure;
354 nread += n;
355 if (!(flags & SUPPRESS)) nassigned++;
356 } else {
357 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
358 n = 0;
359 memset(&mbs, 0, sizeof(mbs));
360 while (width != 0 && (wi = __fgetwc_unlock(fp)) != WEOF) {
361 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
362 nconv = wcrtomb(mbp, wi, &mbs);
363 if (nconv == (size_t)-1) goto input_failure;
364 } else {
365 nconv = wcrtomb(mbbuf, wi, &mbs);
366 if (nconv == (size_t)-1) goto input_failure;
367 if (nconv > width) {
368 __ungetwc(wi, fp);
369 break;
370 }
371 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
372 }
373 if (!(flags & SUPPRESS)) mbp += nconv;
374 width -= nconv;
375 n++;
376 }
377 if (n == 0) goto input_failure;
378 nread += n;
379 if (!(flags & SUPPRESS)) nassigned++;
380 }
381 nconversions++;
382 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700383
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700384 case CT_CCL:
385 /* scan a (nonempty) character class (sets NOSKIP) */
386 if (width == 0) width = (size_t)~0; /* `infinity' */
387 /* take only those things in the class */
388 if ((flags & SUPPRESS) && (flags & LONG)) {
389 n = 0;
390 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi)) n++;
391 if (wi != WEOF) __ungetwc(wi, fp);
392 if (n == 0) goto match_failure;
393 } else if (flags & LONG) {
394 p0 = p = va_arg(ap, wchar_t*);
395 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && INCCL(wi))
396 *p++ = (wchar_t)wi;
397 if (wi != WEOF) __ungetwc(wi, fp);
398 n = p - p0;
399 if (n == 0) goto match_failure;
400 *p = 0;
401 nassigned++;
402 } else {
403 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
404 n = 0;
405 memset(&mbs, 0, sizeof(mbs));
406 while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && INCCL(wi)) {
407 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
408 nconv = wcrtomb(mbp, wi, &mbs);
409 if (nconv == (size_t)-1) goto input_failure;
410 } else {
411 nconv = wcrtomb(mbbuf, wi, &mbs);
412 if (nconv == (size_t)-1) goto input_failure;
413 if (nconv > width) break;
414 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
415 }
416 if (!(flags & SUPPRESS)) mbp += nconv;
417 width -= nconv;
418 n++;
419 }
420 if (wi != WEOF) __ungetwc(wi, fp);
421 if (!(flags & SUPPRESS)) {
422 *mbp = 0;
423 nassigned++;
424 }
425 }
426 nread += n;
427 nconversions++;
428 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700429
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700430 case CT_STRING:
431 /* like CCL, but zero-length string OK, & no NOSKIP */
432 if (width == 0) width = (size_t)~0;
433 if ((flags & SUPPRESS) && (flags & LONG)) {
434 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) nread++;
435 if (wi != WEOF) __ungetwc(wi, fp);
436 } else if (flags & LONG) {
437 p0 = p = va_arg(ap, wchar_t*);
438 while ((wi = __fgetwc_unlock(fp)) != WEOF && width-- != 0 && !iswspace(wi)) {
439 *p++ = (wchar_t)wi;
440 nread++;
441 }
442 if (wi != WEOF) __ungetwc(wi, fp);
443 *p = 0;
444 nassigned++;
445 } else {
446 if (!(flags & SUPPRESS)) mbp = va_arg(ap, char*);
447 memset(&mbs, 0, sizeof(mbs));
448 while ((wi = __fgetwc_unlock(fp)) != WEOF && width != 0 && !iswspace(wi)) {
449 if (width >= MB_CUR_MAX && !(flags & SUPPRESS)) {
450 nconv = wcrtomb(mbp, wi, &mbs);
451 if (nconv == (size_t)-1) goto input_failure;
452 } else {
453 nconv = wcrtomb(mbbuf, wi, &mbs);
454 if (nconv == (size_t)-1) goto input_failure;
455 if (nconv > width) break;
456 if (!(flags & SUPPRESS)) memcpy(mbp, mbbuf, nconv);
457 }
458 if (!(flags & SUPPRESS)) mbp += nconv;
459 width -= nconv;
460 nread++;
461 }
462 if (wi != WEOF) __ungetwc(wi, fp);
463 if (!(flags & SUPPRESS)) {
464 *mbp = 0;
465 nassigned++;
466 }
467 }
468 nconversions++;
469 continue;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700470
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700471 case CT_INT:
472 /* scan an integer as if by strtoimax/strtoumax */
473 if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
474 width = sizeof(buf) / sizeof(*buf) - 1;
475 flags |= SIGNOK | NDIGITS | NZDIGITS;
476 for (p = buf; width; width--) {
477 c = __fgetwc_unlock(fp);
478 /*
479 * Switch on the character; `goto ok'
480 * if we accept it as a part of number.
481 */
482 switch (c) {
483 /*
484 * The digit 0 is always legal, but is
485 * special. For %i conversions, if no
486 * digits (zero or nonzero) have been
487 * scanned (only signs), we will have
488 * base==0. In that case, we should set
489 * it to 8 and enable 0x prefixing.
490 * Also, if we have not scanned zero digits
491 * before this, do not turn off prefixing
492 * (someone else will turn it off if we
493 * have scanned any nonzero digits).
494 */
495 case '0':
496 if (base == 0) {
497 base = 8;
498 flags |= PFXOK;
499 }
500 if (flags & NZDIGITS)
501 flags &= ~(SIGNOK | NZDIGITS | NDIGITS);
502 else
503 flags &= ~(SIGNOK | PFXOK | NDIGITS);
504 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700505
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700506 /* 1 through 7 always legal */
507 case '1':
508 case '2':
509 case '3':
510 case '4':
511 case '5':
512 case '6':
513 case '7':
514 base = basefix[base];
515 flags &= ~(SIGNOK | PFXOK | NDIGITS);
516 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700517
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700518 /* digits 8 and 9 ok iff decimal or hex */
519 case '8':
520 case '9':
521 base = basefix[base];
522 if (base <= 8) break; /* not legal here */
523 flags &= ~(SIGNOK | PFXOK | NDIGITS);
524 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700525
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700526 /* letters ok iff hex */
527 case 'A':
528 case 'B':
529 case 'C':
530 case 'D':
531 case 'E':
532 case 'F':
533 case 'a':
534 case 'b':
535 case 'c':
536 case 'd':
537 case 'e':
538 case 'f':
539 /* no need to fix base here */
540 if (base <= 10) break; /* not legal here */
541 flags &= ~(SIGNOK | PFXOK | NDIGITS);
542 goto ok;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700543
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700544 /* sign ok only as first character */
545 case '+':
546 case '-':
547 if (flags & SIGNOK) {
548 flags &= ~SIGNOK;
549 flags |= HAVESIGN;
550 goto ok;
551 }
552 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700553
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700554 /*
555 * x ok iff flag still set and 2nd char (or
556 * 3rd char if we have a sign).
557 */
558 case 'x':
559 case 'X':
560 if ((flags & PFXOK) && p == buf + 1 + !!(flags & HAVESIGN)) {
561 base = 16; /* if %i */
562 flags &= ~PFXOK;
563 goto ok;
564 }
565 break;
566 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700567
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700568 /*
569 * If we got here, c is not a legal character
570 * for a number. Stop accumulating digits.
571 */
572 if (c != WEOF) __ungetwc(c, fp);
573 break;
574 ok:
575 /*
576 * c is legal: store it and look at the next.
577 */
578 *p++ = (wchar_t)c;
579 }
580 /*
581 * If we had only a sign, it is no good; push
582 * back the sign. If the number ends in `x',
583 * it was [sign] '0' 'x', so push back the x
584 * and treat it as [sign] '0'.
585 */
586 if (flags & NDIGITS) {
587 if (p > buf) __ungetwc(*--p, fp);
588 goto match_failure;
589 }
590 c = p[-1];
591 if (c == 'x' || c == 'X') {
592 --p;
593 __ungetwc(c, fp);
594 }
595 if ((flags & SUPPRESS) == 0) {
596 uintmax_t res;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700597
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700598 *p = '\0';
599 if (flags & UNSIGNED)
600 res = wcstoimax(buf, NULL, base);
601 else
602 res = wcstoumax(buf, NULL, base);
603 if (flags & POINTER)
604 *va_arg(ap, void**) = (void*)(uintptr_t)res;
605 else if (flags & MAXINT)
606 *va_arg(ap, intmax_t*) = res;
607 else if (flags & LLONG)
608 *va_arg(ap, long long*) = res;
609 else if (flags & SIZEINT)
610 *va_arg(ap, ssize_t*) = res;
611 else if (flags & PTRINT)
612 *va_arg(ap, ptrdiff_t*) = res;
613 else if (flags & LONG)
614 *va_arg(ap, long*) = res;
615 else if (flags & SHORT)
616 *va_arg(ap, short*) = res;
617 else if (flags & SHORTSHORT)
618 *va_arg(ap, signed char*) = res;
619 else
620 *va_arg(ap, int*) = res;
621 nassigned++;
622 }
623 nread += p - buf;
624 nconversions++;
625 break;
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700626
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700627 case CT_FLOAT:
628 /* scan a floating point number as if by strtod */
629 if (width == 0 || width > sizeof(buf) / sizeof(*buf) - 1)
630 width = sizeof(buf) / sizeof(*buf) - 1;
631 if ((width = wparsefloat(fp, buf, buf + width)) == 0) goto match_failure;
632 if ((flags & SUPPRESS) == 0) {
633 if (flags & LONGDBL) {
634 long double res = wcstold(buf, &p);
635 *va_arg(ap, long double*) = res;
636 } else if (flags & LONG) {
637 double res = wcstod(buf, &p);
638 *va_arg(ap, double*) = res;
639 } else {
640 float res = wcstof(buf, &p);
641 *va_arg(ap, float*) = res;
642 }
643 if (p - buf != (ptrdiff_t)width) abort();
644 nassigned++;
645 }
646 nread += width;
647 nconversions++;
648 break;
649 }
650 }
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700651input_failure:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700652 return (nconversions != 0 ? nassigned : EOF);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700653match_failure:
Elliott Hughesc8f2c522017-10-31 13:07:51 -0700654 return (nassigned);
Elliott Hughes01ae00f2014-04-29 16:28:56 -0700655}
Elliott Hughes7f0849f2016-08-26 16:17:17 -0700656#pragma GCC diagnostic pop