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