#include #include #include #include #include #include #include #include #include #include #include #include "__scanf.h" static int read(rctx_t *r) { if (--r->w < 0) return r->w = -1; if (r->u) r->u = 0; else r->read(r); return r->c; } static void unread(rctx_t *r) { //if (r->u || r->w < 0) return; if (r->w < 0) return; r->w++; r->u = 1; } #define SIZE_hh -2 #define SIZE_h -1 #define SIZE_def 0 #define SIZE_l 1 #define SIZE_ll 2 #define SIZE_L 3 static void store_int(void *dest, int size, int neg, unsigned long long i) { if (!dest) return; if (neg) i = -i; switch (size) { case SIZE_hh: *(char *)dest = i; break; case SIZE_h: *(short *)dest = i; break; case SIZE_def: *(int *)dest = i; break; case SIZE_l: *(long *)dest = i; break; case SIZE_ll: *(long long *)dest = i; break; } } static void *arg_n(va_list ap, unsigned int n) { void *p; unsigned int i; va_list ap2; va_copy(ap2, ap); for (i=n; i>1; i--) va_arg(ap2, void *); p = va_arg(ap2, void *); va_end(ap2); return p; } int __scanf(rctx_t *r, const wchar_t *fmt, va_list ap) { int mode=0; int width; int size; const wchar_t *p, *z; int c, l, t, m; long long dummy; char *s; wchar_t *wcs; mbstate_t st; int wide = r->wide; void *dest=NULL; int invert; unsigned long long i=0; int neg=0; int matches=0; long double f; int (*is_space)(int) = r->is_space; for (p=fmt; *p; ) { if (is_space(*p)) { do p++; while (is_space(*p)); do r->w=1; while (is_space(read(r))); unread(r); continue; } else if (*p != '%' || p[1] == '%') { if (*p == '%') p++; r->w = 1; if (*p++ != read(r)) goto match_fail; continue; } p++; if (mode != 1) { for (z=p; isdigit(*z); z++); if (*z != '$' && *z != '*') { if (mode == 0) mode = 1; else goto fmt_fail; } else if (*z != '*') { int pos = 0; mode = 2; for (; pw = width; if (t != 'n') { if (read(r) < 0) goto input_fail; unread(r); } switch (t) { case 'n': store_int(dest, size, 0, r->l - r->u); /* do not increment match count, etc! */ continue; case 'C': wcs = dest ? dest : (void *)&dummy; st = (mbstate_t){ 0 }; while ((c=read(r)) >= 0) { if (wide) { if (dest) *wcs++ = c; } else { char ch = c; switch (mbrtowc(wcs, &ch, 1, &st)) { case -1: goto enc_fail; case -2: break; default: if (dest) wcs++; } } } if (r->w > 0) goto match_fail; break; case 'c': s = dest ? dest : (void *)&dummy; while ((c=read(r)) >= 0) { if (wide) { if ((l=wctomb(s, c)) < 0) goto enc_fail; if (dest) s += l; } else { if (dest) *s++ = c; } } if (r->w > 0) goto match_fail; break; case '[': wcs = dest ? dest : (void *)&dummy; s = dest ? dest : (void *)&dummy; if (!wide && size == SIZE_l) st = (mbstate_t){ 0 }; if (*p == '^') p++, invert = 1; else invert = 0; if (wide) { for (m=0; (c=read(r)) >= 0; m=1) { for (z=p; *z && *z != c && (*z != ']' || z==p); z++); if (!*z) goto fmt_fail; if (*z == c && (*z != ']' || z==p)) { if (invert) break; } else { if (!invert) break; } if (size == SIZE_l) { if (dest) *wcs++ = c; } else { if ((l=wctomb(s, c)) < 0) goto enc_fail; if (dest) s += l; } } for (p++; *p && *p != ']'; p++); p++; } else { unsigned char scanset[257]; memset(scanset, invert, sizeof scanset); scanset[0] = 0; for (z=p; *z && (*z != ']' || z==p); z++) scanset[1+*z] = 1-invert; if (!*z) goto fmt_fail; p=z+1; c=0; for (m=0; scanset[(c=read(r))+1]; m=1) { if (size == SIZE_l) { char ch = c; switch (mbrtowc(wcs, &ch, 1, &st)) { case -1: goto enc_fail; case -2: break; default: if (dest) wcs++; } } else { if (dest) *s++ = c; } } } if (!m) goto match_fail; if (dest) { if (size == SIZE_l) *wcs++ = 0; else *s++ = 0; } break; default: /* read unlimited number of spaces, then reset width */ do r->w = 1; while (is_space(c = read(r))); if (c < 0) goto input_fail; unread(r); r->w = width; } switch (t) { case 'p': case 'X': t = 'x'; case 'd': case 'i': case 'o': case 'u': case 'x': i = m = neg = 0; if ((c=read(r)) == '-') neg=1; else if (c != '+') unread(r); switch (t) { case 'i': case 'x': if ((c=read(r)) != '0') { if (t == 'i') t = 'd'; unread(r); break; } if (((c=read(r))|0x20) != 'x') { if (t == 'i') { t = 'o'; /* lone 0 is valid octal */ if ((unsigned)(c-'0') >= 8) { m = 1; goto int_finish; } } unread(r); break; } t = 'x'; } } switch (t) { case 'd': case 'u': for (m=0; isdigit(c=read(r)); m=1) i = 10*i + c-'0'; goto int_finish; case 'o': for (m=0; (unsigned)(c=read(r))-'0' < 8; m=1) i = (i<<3) + c-'0'; goto int_finish; case 'x': for (m=0; ; m=1) { if (isdigit(c=read(r))) { i = (i<<4) + c-'0'; } else if ((unsigned)(c|0x20)-'a' < 6) { i = (i<<4) + (c|0x20)-'a'+10; } else break; } int_finish: if (!m) goto match_fail; store_int(dest, size, neg, i); break; case 'a': case 'e': case 'f': case 'g': f = 0.0; neg = m = 0; if ((c=read(r)) == '-') neg=1; else if (c != '+') unread(r); /* FIXME: check for INF/NAN strings here */ if (read(r)=='0' && (m=1, (read(r)|0x20) == 'x')) goto hexfloat; else unread(r); for (; isdigit(c=read(r)); m=1) f = 10.0 * f + (c-'0'); if (c=='.') { double mag = 10.0; for (; isdigit(c=read(r)); mag*=10.0) f += (c-'0')/mag; } if ((c|0x20)=='e') { int ex=0, en=0; m = 0; if ((c=read(r))=='-') en=1; else if (c!='+') unread(r); for (; isdigit(c=read(r)); m=1) if (ex < LDBL_MAX_10_EXP) ex = 10 * ex + (c-'0'); if (ex > LDBL_MAX_10_EXP) f = en ? 0 : INFINITY; else { if (en) while (ex--) f/=10.0; else while (ex--) f*=10.0; } } goto writefloat; hexfloat: m = 0; for (; isxdigit(c=read(r)); m=1) if (isdigit(c)) f = 16.0*f + (c-'0'); else f = 16.0*f + ((c|32)-'a'+10); if (c=='.') { double mag = 1/16.0; for (; isxdigit(c=read(r)); mag*=1/16.0) if (isdigit(c)) f += (c-'0')*mag; else f += ((c|32)-'a'+10)*mag; } if ((c|0x20)=='p') { int ex=0, en=0; m = 0; if ((c=read(r))=='-') en=1; else if (c!='+') unread(r); for (; isdigit(c=read(r)); m=1) if (ex < LDBL_MAX_EXP) ex = 10 * ex + (c-'0'); if (ex > LDBL_MAX_EXP) f = en ? 0 : INFINITY; else { if (en) while (ex--) f*=0.5; else while (ex--) f*=2.0; } } writefloat: if (!m) goto match_fail; if (neg) f *= -1.0; if (dest) switch (size) { case SIZE_def: *(float *)dest = f; break; case SIZE_l: *(double *)dest = f; break; case SIZE_L: *(long double *)dest = f; break; } break; case 'S': wcs = dest ? dest : (void *)&dummy; st = (mbstate_t){ 0 }; while((c=read(r)) >= 0) { if (wide) { if (is_space(c)) break; if (dest) *wcs++ = c; } else { char ch = c; if (is_space(c)) break; switch (mbrtowc(wcs, &ch, 1, &st)) { case -1: goto enc_fail; case -2: break; default: if (dest) wcs++; } } } if (dest) *wcs++ = 0; break; case 's': s = dest ? dest : (void *)&dummy; while((c=read(r)) >= 0) { if (wide) { if (is_space(c)) break; if ((l=wctomb(s, c)) < 0) goto enc_fail; if (dest) s += l; } else { if (is_space(c)) break; if (dest) *s++ = c; } } if (dest) *s++ = 0; break; } /* unread will do nothing if field width was exhausted */ unread(r); if (dest) matches++; } return matches; enc_fail: errno = EILSEQ; fmt_fail: input_fail: if (!matches) matches--; match_fail: unread(r); return matches; }