#define __CRT__NO_INLINE #include #include #include #include #include #include #include #include #include #include #include #include #include "evil_pformat.h" #include "evil_print.h" #include "gdtoa/gdtoa.h" /*============================================================================* * Local * *============================================================================*/ /* Helper flags for conversion. */ #define IS_C 0x0001 #define IS_S 0x0002 #define IS_L 0x0004 #define IS_LL 0x0008 #define IS_SIGNED_NUM 0x0010 #define IS_POINTER 0x0020 #define IS_HEX_FLOAT 0x0040 #define IS_SUPPRESSED 0x0080 #define USE_GROUP 0x0100 #define USE_GNU_ALLOC 0x0200 #define USE_POSIX_ALLOC 0x0400 #define IS_ALLOC_USED (USE_GNU_ALLOC | USE_POSIX_ALLOC) #ifndef __BUILD_WIDEAPI /* internal stream structure with back-buffer. */ typedef struct _IFP { __extension__ union { void *fp; const char *str; }; int bch[1024]; int is_string : 1; int back_top; int seen_eof : 1; } _IFP; static void * get_va_nth (va_list argp, unsigned int n) { va_list ap; if (!n) abort (); va_copy (ap, argp); while (--n > 0) (void) va_arg(ap, void *); return va_arg (ap, void *); } static void optimize_alloc (char **p, char *end, size_t alloc_sz) { size_t need_sz; char *h; if (!p || !*p) return; need_sz = end - *p; if (need_sz == alloc_sz) return; if ((h = (char *) realloc (*p, need_sz)) != NULL) *p = h; } static void back_ch (int c, _IFP *s, size_t *rin, int not_eof) { if (!not_eof && c == EOF) return; if (s->is_string == 0) { FILE *fp = s->fp; ungetc (c, fp); rin[0] -= 1; return; } rin[0] -= 1; s->bch[s->back_top] = c; s->back_top += 1; } static int in_ch (_IFP *s, size_t *rin) { int r; if (s->back_top) { s->back_top -= 1; r = s->bch[s->back_top]; rin[0] += 1; } else if (s->seen_eof) { return EOF; } else if (s->is_string) { const char *ps = s->str; r = ((int) *ps) & 0xff; ps++; if (r != 0) { rin[0] += 1; s->str = ps; return r; } s->seen_eof = 1; return EOF; } else { FILE *fp = (FILE *) s->fp; r = getc (fp); if (r != EOF) rin[0] += 1; else s->seen_eof = 1; } return r; } static int match_string (_IFP *s, size_t *rin, int *c, const char *str) { int ch = *c; if (*str == 0) return 1; if (*str != (char) tolower (ch)) return 0; ++str; while (*str != 0) { if ((ch = in_ch (s, rin)) == EOF) { c[0] = ch; return 0; } if (*str != (char) tolower (ch)) { c[0] = ch; return 0; } ++str; } c[0] = ch; return 1; } struct gcollect { size_t count; struct gcollect *next; char **ptrs[32]; }; static void release_ptrs (struct gcollect **pt, char **wbuf) { struct gcollect *pf; size_t cnt; if (!pt || (pf = *pt) == NULL) return; while (pf != NULL) { struct gcollect *pf_sv = pf; for (cnt = 0; cnt < pf->count; ++cnt) { free (*pf->ptrs[cnt]); *pf->ptrs[cnt] = NULL; } pf = pf->next; free (pf_sv); } *pt = NULL; if (wbuf) { free (*wbuf); *wbuf = NULL; } } static int cleanup_return (int rval, struct gcollect **pfree, char **strp, char **wbuf) { if (rval == EOF) release_ptrs (pfree, wbuf); else { if (pfree) { struct gcollect *pf = *pfree, *pf_sv; while (pf != NULL) { pf_sv = pf; pf = pf->next; free (pf_sv); } *pfree = NULL; } if (strp != NULL) { free (*strp); *strp = NULL; } if (wbuf) { free (*wbuf); *wbuf = NULL; } } return rval; } static struct gcollect * resize_gcollect (struct gcollect *pf) { struct gcollect *np; if (pf && pf->count < 32) return pf; np = malloc (sizeof (struct gcollect)); np->count = 0; np->next = pf; return np; } static char * resize_wbuf (size_t wpsz, size_t *wbuf_max_sz, char *old) { char *wbuf; size_t nsz; if (*wbuf_max_sz != wpsz) return old; nsz = (256 > (2 * wbuf_max_sz[0]) ? 256 : (2 * wbuf_max_sz[0])); if (!old) wbuf = (char *) malloc (nsz); else wbuf = (char *) realloc (old, nsz); if (!wbuf) { if (old) free (old); } else *wbuf_max_sz = nsz; return wbuf; } static int _evil_sformat (_IFP *s, const char *format, va_list argp) { const char *f = format; struct gcollect *gcollect = NULL; size_t read_in = 0, wbuf_max_sz = 0, cnt; ssize_t str_sz = 0; char *str = NULL, **pstr = NULL, *wbuf = NULL; wchar_t *wstr = NULL; int rval = 0, c = 0, ignore_ws = 0; va_list arg; unsigned char fc; unsigned int npos; int width, flags, base = 0, errno_sv; size_t wbuf_cur_sz, read_in_sv, new_sz, n; char seen_dot, seen_exp, is_neg, not_in; char *tmp_wbuf_ptr, buf[MB_LEN_MAX]; const char *lc_decimal_point, *lc_thousands_sep; mbstate_t state, cstate; union { unsigned long long ull; unsigned long ul; long long ll; long l; } cv_val; arg = argp; if (!s || s->fp == NULL || !format) { errno = EINVAL; return EOF; } memset (&state, 0, sizeof (state)); lc_decimal_point = localeconv()->decimal_point; lc_thousands_sep = localeconv()->thousands_sep; if (lc_thousands_sep != NULL && *lc_thousands_sep == 0) lc_thousands_sep = NULL; while (*f != 0) { if (!isascii ((unsigned char) *f)) { int len; if ((len = mbrlen (f, strlen (f), &state)) > 0) { do { if ((c = in_ch (s, &read_in)) == EOF || c != (unsigned char) *f++) { back_ch (c, s, &read_in, 1); return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); } } while (--len > 0); continue; } } fc = *f++; if (fc != '%') { if (isspace (fc)) ignore_ws = 1; else { if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); if (ignore_ws) { ignore_ws = 0; if (isspace (c)) { do { if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); } while (isspace (c)); } } if (c != fc) { back_ch (c, s, &read_in, 0); return cleanup_return (rval, &gcollect, pstr, &wbuf); } } continue; } width = flags = 0; npos = 0; wbuf_cur_sz = 0; if (isdigit ((unsigned char) *f)) { const char *svf = f; npos = (unsigned char) *f++ - '0'; while (isdigit ((unsigned char) *f)) npos = npos * 10 + ((unsigned char) *f++ - '0'); if (*f != '$') { npos = 0; f = svf; } else f++; } do { if (*f == '*') flags |= IS_SUPPRESSED; else if (*f == '\'') { if (lc_thousands_sep) flags |= USE_GROUP; } else if (*f == 'I') /* we don't support locale's digits (i18N), but ignore it for now silently. */ ; else break; ++f; } while (1); while (isdigit ((unsigned char) *f)) width = width * 10 + ((unsigned char) *f++ - '0'); if (!width) width = -1; switch (*f) { case 'h': ++f; flags |= (*f == 'h' ? IS_C : IS_S); if (*f == 'h') ++f; break; case 'l': ++f; flags |= (*f == 'l' ? IS_LL : 0) | IS_L; if (*f == 'l') ++f; break; case 'q': case 'L': ++f; flags |= IS_LL | IS_L; break; case 'a': if (f[1] != 's' && f[1] != 'S' && f[1] != '[') break; ++f; flags |= USE_GNU_ALLOC; break; case 'm': flags |= USE_POSIX_ALLOC; ++f; if (*f == 'l') { flags |= IS_L; f++; } break; case 'z': #ifdef _WIN64 flags |= IS_LL; #else flags |= IS_L; #endif ++f; break; case 'j': if (sizeof (uintmax_t) > sizeof (unsigned long)) flags |= IS_LL; else if (sizeof (uintmax_t) > sizeof (unsigned int)) flags |= IS_L; ++f; break; case 't': #ifdef _WIN64 flags |= IS_LL; #else flags |= IS_L; #endif ++f; break; case 0: return cleanup_return (rval, &gcollect, pstr, &wbuf); default: break; } if (*f == 0) return cleanup_return (rval, &gcollect, pstr, &wbuf); fc = *f++; if (ignore_ws || (fc != '[' && fc != 'c' && fc != 'C' && fc != 'n')) { errno_sv = errno; errno = 0; do { if ((c == EOF || (c = in_ch (s, &read_in)) == EOF) && errno == EINTR) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); } while (isspace (c)); ignore_ws = 0; errno = errno_sv; back_ch (c, s, &read_in, 0); } switch (fc) { case 'c': if ((flags & IS_L) != 0) fc = 'C'; break; case 's': if ((flags & IS_L) != 0) fc = 'S'; break; } switch (fc) { case '%': if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); if (c != fc) { back_ch (c, s, &read_in, 1); return cleanup_return (rval, &gcollect, pstr, &wbuf); } break; case 'n': if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_LL) != 0) *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = read_in; else if ((flags & IS_L) != 0) *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = read_in; else if ((flags & IS_S) != 0) *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = read_in; else if ((flags & IS_C) != 0) *(npos != 0 ? (char *) get_va_nth (argp, npos) : va_arg (arg, char *)) = read_in; else *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = read_in; } break; case 'c': if (width == -1) width = 1; if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_ALLOC_USED) != 0) { if (npos != 0) pstr = (char **) get_va_nth (argp, npos); else pstr = va_arg (arg, char **); if (!pstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); str_sz = (width > 1024 ? 1024 : width); if ((str = *pstr = (char *) malloc (str_sz)) == NULL) return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); gcollect = resize_gcollect (gcollect); gcollect->ptrs[gcollect->count++] = pstr; } else { if (npos != 0) str = (char *) get_va_nth (argp, npos); else str = va_arg (arg, char *); if (!str) return cleanup_return (rval, &gcollect, pstr, &wbuf); } } if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); if ((flags & IS_SUPPRESSED) == 0) { do { if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) { new_sz = str_sz + (str_sz >= width ? width - 1 : str_sz); while ((str = (char *) realloc (*pstr, new_sz)) == NULL && new_sz > (size_t) (str_sz + 1)) new_sz = str_sz + 1; if (!str) { release_ptrs (&gcollect, &wbuf); return EOF; } *pstr = str; str += str_sz; str_sz = new_sz; } *str++ = c; } while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); } else while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); if ((flags & IS_SUPPRESSED) == 0) { optimize_alloc (pstr, str, str_sz); pstr = NULL; ++rval; } break; case 'C': if (width == -1) width = 1; if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_ALLOC_USED) != 0) { if (npos != 0) pstr = (char **) get_va_nth (argp, npos); else pstr = va_arg (arg, char **); if (!pstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); str_sz = (width > 1024 ? 1024 : width); *pstr = (char *) malloc (str_sz * sizeof (wchar_t)); if ((wstr = (wchar_t *) *pstr) == NULL) return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); gcollect = resize_gcollect (gcollect); gcollect->ptrs[gcollect->count++] = pstr; } else { if (npos != 0) wstr = (wchar_t *) get_va_nth (argp, npos); else wstr = va_arg (arg, wchar_t *); if (!wstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); } } if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); memset (&cstate, 0, sizeof (cstate)); do { buf[0] = c; if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0 && wstr == ((wchar_t *) *pstr + str_sz)) { new_sz = str_sz + (str_sz > width ? width - 1 : str_sz); while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL && new_sz > (size_t) (str_sz + 1)) new_sz = str_sz + 1; if (!wstr) { release_ptrs (&gcollect, &wbuf); return EOF; } *pstr = (char *) wstr; wstr += str_sz; str_sz = new_sz; } while (1) { n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate); if (n == (size_t) -2) { if ((c = in_ch (s, &read_in)) == EOF) { errno = EILSEQ; return cleanup_return (rval, &gcollect, pstr, &wbuf); } buf[0] = c; continue; } if (n != 1) { errno = EILSEQ; return cleanup_return (rval, &gcollect, pstr, &wbuf); } break; } ++wstr; } while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); if ((flags & IS_SUPPRESSED) == 0) { optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); pstr = NULL; ++rval; } break; case 's': if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_ALLOC_USED) != 0) { if (npos != 0) pstr = (char **) get_va_nth (argp, npos); else pstr = va_arg (arg, char **); if (!pstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); str_sz = 100; if ((str = *pstr = (char *) malloc (100)) == NULL) return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); gcollect = resize_gcollect (gcollect); gcollect->ptrs[gcollect->count++] = pstr; } else { if (npos != 0) str = (char *) get_va_nth (argp, npos); else str = va_arg (arg, char *); if (!str) return cleanup_return (rval, &gcollect, pstr, &wbuf); } } if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); do { if (isspace (c)) { back_ch (c, s, &read_in, 1); break; } if ((flags & IS_SUPPRESSED) == 0) { *str++ = c; if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) { new_sz = str_sz * 2; while ((str = (char *) realloc (*pstr, new_sz)) == NULL && new_sz > (size_t) (str_sz + 1)) new_sz = str_sz + 1; if (!str) { if ((flags & USE_POSIX_ALLOC) == 0) { (*pstr)[str_sz - 1] = 0; pstr = NULL; ++rval; } return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); } *pstr = str; str += str_sz; str_sz = new_sz; } } } while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF); if ((flags & IS_SUPPRESSED) == 0) { *str++ = 0; optimize_alloc (pstr, str, str_sz); pstr = NULL; ++rval; } break; case 'S': if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_ALLOC_USED) != 0) { if (npos != 0) pstr = (char **) get_va_nth (argp, npos); else pstr = va_arg (arg, char **); if (!pstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); str_sz = 100; *pstr = (char *) malloc (100 * sizeof (wchar_t)); if ((wstr = (wchar_t *) *pstr) == NULL) return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); gcollect = resize_gcollect (gcollect); gcollect->ptrs[gcollect->count++] = pstr; } else { if (npos != 0) wstr = (wchar_t *) get_va_nth (argp, npos); else wstr = va_arg (arg, wchar_t *); if (!wstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); } } if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); memset (&cstate, 0, sizeof (cstate)); do { if (isspace (c)) { back_ch (c, s, &read_in, 1); break; } buf[0] = c; while (1) { n = mbrtowc ((flags & IS_SUPPRESSED) == 0 ? wstr : NULL, buf, 1, &cstate); if (n == (size_t) -2) { if ((c = in_ch (s, &read_in)) == EOF) { errno = EILSEQ; return cleanup_return (rval, &gcollect, pstr, &wbuf); } buf[0] = c; continue; } if (n != 1) { errno = EILSEQ; return cleanup_return (rval, &gcollect, pstr, &wbuf); } ++wstr; break; } if ((flags & IS_SUPPRESSED) == 0 && (flags & IS_ALLOC_USED) != 0 && wstr == ((wchar_t *) *pstr + str_sz)) { new_sz = str_sz * 2; while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL && new_sz > (size_t) (str_sz + 1)) new_sz = str_sz + 1; if (!wstr) { if ((flags & USE_POSIX_ALLOC) == 0) { ((wchar_t *) (*pstr))[str_sz - 1] = 0; pstr = NULL; ++rval; } return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); } *pstr = (char *) wstr; wstr += str_sz; str_sz = new_sz; } } while ((width <= 0 || --width > 0) && (c = in_ch (s, &read_in)) != EOF); if ((flags & IS_SUPPRESSED) == 0) { *wstr++ = 0; optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); pstr = NULL; ++rval; } break; case 'd': case 'i': case 'o': case 'p': case 'u': case 'x': case 'X': switch (fc) { case 'd': flags |= IS_SIGNED_NUM; base = 10; break; case 'i': flags |= IS_SIGNED_NUM; base = 0; break; case 'o': base = 8; break; case 'p': base = 16; flags &= ~(IS_S | IS_LL | IS_L); #ifdef _WIN64 flags |= IS_LL; #endif flags |= IS_L | IS_POINTER; break; case 'u': base = 10; break; case 'x': case 'X': base = 16; break; } if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); if (c == '+' || c == '-') { wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; if (width > 0) --width; c = in_ch (s, &read_in); } if (width != 0 && c == '0') { if (width > 0) --width; wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; c = in_ch (s, &read_in); if (width != 0 && tolower (c) == 'x') { if (!base) base = 16; if (base == 16) { if (width > 0) --width; c = in_ch (s, &read_in); } } else if (!base) base = 8; } if (!base) base = 10; while (c != EOF && width != 0) { if (base == 16) { if (!isxdigit (c)) break; } else if (!isdigit (c) || (int) (c - '0') >= base) { const char *p = lc_thousands_sep; int remain; if (base != 10 || (flags & USE_GROUP) == 0) break; remain = width > 0 ? width : INT_MAX; while ((unsigned char) *p == c && remain >= 0) { /* As our conversion routines aren't supporting thousands separators, we are filtering them here. */ ++p; if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) break; --remain; } if (*p != 0) { if (p > lc_thousands_sep) { back_ch (c, s, &read_in, 0); while (--p > lc_thousands_sep) back_ch ((unsigned char) *p, s, &read_in, 1); c = (unsigned char) *p; } break; } if (width > 0) width = remain; --wbuf_cur_sz; } wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; if (width > 0) --width; c = in_ch (s, &read_in); } if (!wbuf_cur_sz || (wbuf_cur_sz == 1 && (wbuf[0] == '+' || wbuf[0] == '-'))) { if (!wbuf_cur_sz && (flags & IS_POINTER) != 0 && match_string (s, &read_in, &c, "(nil)")) { wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = '0'; } else { back_ch (c, s, &read_in, 0); return cleanup_return (rval, &gcollect, pstr, &wbuf); } } else back_ch (c, s, &read_in, 0); wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = 0; if ((flags & IS_LL)) { if (flags & IS_SIGNED_NUM) cv_val.ll = strtoll (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); else cv_val.ull = strtoull (wbuf, &tmp_wbuf_ptr, base); } else { if (flags & IS_SIGNED_NUM) cv_val.l = strtol (wbuf, &tmp_wbuf_ptr, base/*, flags & USE_GROUP*/); else cv_val.ul = strtoul (wbuf, &tmp_wbuf_ptr, base); } if (wbuf == tmp_wbuf_ptr) return cleanup_return (rval, &gcollect, pstr, &wbuf); if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_SIGNED_NUM) != 0) { if ((flags & IS_LL) != 0) *(npos != 0 ? (long long *) get_va_nth (argp, npos) : va_arg (arg, long long *)) = cv_val.ll; else if ((flags & IS_L) != 0) *(npos != 0 ? (long *) get_va_nth (argp, npos) : va_arg (arg, long *)) = cv_val.l; else if ((flags & IS_S) != 0) *(npos != 0 ? (short *) get_va_nth (argp, npos) : va_arg (arg, short *)) = (short) cv_val.l; else if ((flags & IS_C) != 0) *(npos != 0 ? (signed char *) get_va_nth (argp, npos) : va_arg (arg, signed char *)) = (signed char) cv_val.ul; else *(npos != 0 ? (int *) get_va_nth (argp, npos) : va_arg (arg, int *)) = (int) cv_val.l; } else { if ((flags & IS_LL) != 0) *(npos != 0 ? (unsigned long long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long long *)) = cv_val.ull; else if ((flags & IS_L) != 0) *(npos != 0 ? (unsigned long *) get_va_nth (argp, npos) : va_arg (arg, unsigned long *)) = cv_val.ul; else if ((flags & IS_S) != 0) *(npos != 0 ? (unsigned short *) get_va_nth (argp, npos) : va_arg (arg, unsigned short *)) = (unsigned short) cv_val.ul; else if ((flags & IS_C) != 0) *(npos != 0 ? (unsigned char *) get_va_nth (argp, npos) : va_arg (arg, unsigned char *)) = (unsigned char) cv_val.ul; else *(npos != 0 ? (unsigned int *) get_va_nth (argp, npos) : va_arg (arg, unsigned int *)) = (unsigned int) cv_val.ul; } ++rval; } break; case 'e': case 'E': case 'f': case 'F': case 'g': case 'G': case 'a': case 'A': if (width > 0) --width; if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); seen_dot = seen_exp = 0; is_neg = (c == '-' ? 1 : 0); if (c == '-' || c == '+') { if (width == 0 || (c = in_ch (s, &read_in)) == EOF) return cleanup_return (rval, &gcollect, pstr, &wbuf); if (width > 0) --width; } if (tolower (c) == 'n') { const char *match_txt = "nan"; wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; ++match_txt; do { if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) return cleanup_return (rval, &gcollect, pstr, &wbuf); if (width > 0) --width; wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; ++match_txt; } while (*match_txt != 0); } else if (tolower (c) == 'i') { const char *match_txt = "inf"; wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; ++match_txt; do { if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) return cleanup_return (rval, &gcollect, pstr, &wbuf); if (width > 0) --width; wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; ++match_txt; } while (*match_txt != 0); if (width != 0 && (c = in_ch (s, &read_in)) != EOF && tolower (c) == 'i') { match_txt = "inity"; if (width > 0) --width; wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; ++match_txt; do { if (width == 0 || (c = in_ch (s, &read_in)) == EOF || tolower (c) != match_txt[0]) return cleanup_return (rval, &gcollect, pstr, &wbuf); if (width > 0) --width; wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; ++match_txt; } while (*match_txt != 0); } else if (width != 0 && c != EOF) back_ch (c, s, &read_in, 0); } else { not_in = 'e'; if (width != 0 && c == '0') { wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; c = in_ch (s, &read_in); if (width > 0) --width; if (width != 0 && tolower (c) == 'x') { wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; flags |= IS_HEX_FLOAT; not_in = 'p'; flags &= ~USE_GROUP; c = in_ch (s, &read_in); if (width > 0) --width; } } while (1) { if (isdigit (c) || (!seen_exp && (flags & IS_HEX_FLOAT) != 0 && isxdigit (c)) || (seen_exp && wbuf[wbuf_cur_sz - 1] == not_in && (c == '-' || c == '+'))) { wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = c; } else if (wbuf_cur_sz > 0 && !seen_exp && (char) tolower (c) == not_in) { wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = not_in; seen_exp = seen_dot = 1; } else { const char *p = lc_decimal_point; int remain = width > 0 ? width : INT_MAX; if (! seen_dot) { while ((unsigned char) *p == c && remain >= 0) { ++p; if (*p == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) break; --remain; } } if (*p == 0) { for (p = lc_decimal_point; *p != 0; ++p) { wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = (unsigned char) *p; } if (width > 0) width = remain; seen_dot = 1; } else { const char *pp = lc_thousands_sep; if (!seen_dot && (flags & USE_GROUP) != 0) { while ((pp - lc_thousands_sep) < (p - lc_decimal_point) && *pp == lc_decimal_point[(pp - lc_thousands_sep)]) ++pp; if ((pp - lc_thousands_sep) == (p - lc_decimal_point)) { while ((unsigned char) *pp == c && remain >= 0) { ++pp; if (*pp == 0 || !remain || (c = in_ch (s, &read_in)) == EOF) break; --remain; } } } if (pp != NULL && *pp == 0) { /* As our conversion routines aren't supporting thousands separators, we are filtering them here. */ if (width > 0) width = remain; } else { back_ch (c, s, &read_in, 0); break; } } } if (width == 0 || (c = in_ch (s, &read_in)) == EOF) break; if (width > 0) --width; } if (!wbuf_cur_sz || ((flags & IS_HEX_FLOAT) != 0 && wbuf_cur_sz == 2)) return cleanup_return (rval, &gcollect, pstr, &wbuf); } wbuf = resize_wbuf (wbuf_cur_sz, &wbuf_max_sz, wbuf); wbuf[wbuf_cur_sz++] = 0; if ((flags & IS_LL) != 0) { long double ld; ld = __evil_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) *(npos != 0 ? (long double *) get_va_nth (argp, npos) : va_arg (arg, long double *)) = is_neg ? -ld : ld; } else if ((flags & IS_L) != 0) { double d; d = (double) __evil_strtold (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) *(npos != 0 ? (double *) get_va_nth (argp, npos) : va_arg (arg, double *)) = is_neg ? -d : d; } else { float d = __evil_strtof (wbuf, &tmp_wbuf_ptr/*, flags & USE_GROUP*/); if ((flags & IS_SUPPRESSED) == 0 && tmp_wbuf_ptr != wbuf) *(npos != 0 ? (float *) get_va_nth (argp, npos) : va_arg (arg, float *)) = is_neg ? -d : d; } if (wbuf == tmp_wbuf_ptr) return cleanup_return (rval, &gcollect, pstr, &wbuf); if ((flags & IS_SUPPRESSED) == 0) ++rval; break; case '[': if ((flags & IS_L) != 0) { if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_ALLOC_USED) != 0) { if (npos != 0) pstr = (char **) get_va_nth (argp, npos); else pstr = va_arg (arg, char **); if (!pstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); str_sz = 100; *pstr = (char *) malloc (100 * sizeof (wchar_t)); if ((wstr = (wchar_t *) *pstr) == NULL) return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); gcollect = resize_gcollect (gcollect); gcollect->ptrs[gcollect->count++] = pstr; } else { if (npos != 0) wstr = (wchar_t *) get_va_nth (argp, npos); else wstr = va_arg (arg, wchar_t *); if (!wstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); } } } else if ((flags & IS_SUPPRESSED) == 0) { if ((flags & IS_ALLOC_USED) != 0) { if (npos != 0) pstr = (char **) get_va_nth (argp, npos); else pstr = va_arg (arg, char **); if (!pstr) return cleanup_return (rval, &gcollect, pstr, &wbuf); str_sz = 100; if ((str = *pstr = (char *) malloc (100)) == NULL) return cleanup_return (((flags & USE_POSIX_ALLOC) != 0 ? EOF : rval), &gcollect, pstr, &wbuf); gcollect = resize_gcollect (gcollect); gcollect->ptrs[gcollect->count++] = pstr; } else { if (npos != 0) str = (char *) get_va_nth (argp, npos); else str = va_arg (arg, char *); if (!str) return cleanup_return (rval, &gcollect, pstr, &wbuf); } } not_in = (*f == '^' ? 1 : 0); if (*f == '^') f++; if (width < 0) width = INT_MAX; if (wbuf_max_sz < 256) { wbuf_max_sz = 256; if (wbuf) free (wbuf); wbuf = (char *) malloc (wbuf_max_sz); } memset (wbuf, 0, 256); fc = *f; if (fc == ']' || fc == '-') { wbuf[fc] = 1; ++f; } while ((fc = *f++) != 0 && fc != ']') { if (fc == '-' && *f != 0 && *f != ']' && (unsigned char) f[-2] <= (unsigned char) *f) { for (fc = (unsigned char) f[-2]; fc < (unsigned char) *f; ++fc) wbuf[fc] = 1; } else wbuf[fc] = 1; } if (!fc) return cleanup_return (rval, &gcollect, pstr, &wbuf); if ((flags & IS_L) != 0) { read_in_sv = read_in; cnt = 0; if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); memset (&cstate, 0, sizeof (cstate)); do { if (wbuf[c] == not_in) { back_ch (c, s, &read_in, 1); break; } if ((flags & IS_SUPPRESSED) == 0) { buf[0] = c; n = mbrtowc (wstr, buf, 1, &cstate); if (n == (size_t) -2) { ++cnt; continue; } cnt = 0; ++wstr; if ((flags & IS_ALLOC_USED) != 0 && wstr == ((wchar_t *) *pstr + str_sz)) { new_sz = str_sz * 2; while ((wstr = (wchar_t *) realloc (*pstr, new_sz * sizeof (wchar_t))) == NULL && new_sz > (size_t) (str_sz + 1)) new_sz = str_sz + 1; if (!wstr) { if ((flags & USE_POSIX_ALLOC) == 0) { ((wchar_t *) (*pstr))[str_sz - 1] = 0; pstr = NULL; ++rval; } else rval = EOF; return cleanup_return (rval, &gcollect, pstr, &wbuf); } *pstr = (char *) wstr; wstr += str_sz; str_sz = new_sz; } } if (--width <= 0) break; } while ((c = in_ch (s, &read_in)) != EOF); if (cnt != 0) { errno = EILSEQ; return cleanup_return (rval, &gcollect, pstr, &wbuf); } if (read_in_sv == read_in) return cleanup_return (rval, &gcollect, pstr, &wbuf); if ((flags & IS_SUPPRESSED) == 0) { *wstr++ = 0; optimize_alloc (pstr, (char *) wstr, str_sz * sizeof (wchar_t)); pstr = NULL; ++rval; } } else { read_in_sv = read_in; if ((c = in_ch (s, &read_in)) == EOF) return cleanup_return ((!rval ? EOF : rval), &gcollect, pstr, &wbuf); do { if (wbuf[c] == not_in) { back_ch (c, s, &read_in, 1); break; } if ((flags & IS_SUPPRESSED) == 0) { *str++ = c; if ((flags & IS_ALLOC_USED) != 0 && str == (*pstr + str_sz)) { new_sz = str_sz * 2; while ((str = (char *) realloc (*pstr, new_sz)) == NULL && new_sz > (size_t) (str_sz + 1)) new_sz = str_sz + 1; if (!str) { if ((flags & USE_POSIX_ALLOC) == 0) { (*pstr)[str_sz - 1] = 0; pstr = NULL; ++rval; } else rval = EOF; return cleanup_return (rval, &gcollect, pstr, &wbuf); } *pstr = str; str += str_sz; str_sz = new_sz; } } } while (--width > 0 && (c = in_ch (s, &read_in)) != EOF); if (read_in_sv == read_in) return cleanup_return (rval, &gcollect, pstr, &wbuf); if ((flags & IS_SUPPRESSED) == 0) { *str++ = 0; optimize_alloc (pstr, str, str_sz); pstr = NULL; ++rval; } } break; default: return cleanup_return (rval, &gcollect, pstr, &wbuf); } } if (ignore_ws) { while (isspace ((c = in_ch (s, &read_in)))); back_ch (c, s, &read_in, 0); } return cleanup_return (rval, &gcollect, pstr, &wbuf); } #endif /*============================================================================* * API * *============================================================================*/ /**** printf family ****/ int __cdecl _evil_fprintf(FILE *stream, const APICHAR *fmt, ...) { register int retval; va_list argv; va_start( argv, fmt ); retval = _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stream, 0, fmt, argv ); va_end( argv ); return retval; } int __cdecl _evil_printf(const APICHAR *fmt, ...) { register int retval; va_list argv; va_start( argv, fmt ); retval = _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stdout, 0, fmt, argv ); va_end( argv ); return retval; } int __cdecl _evil_sprintf(APICHAR *buf, const APICHAR *fmt, ...) { register int retval; va_list argv; va_start( argv, fmt ); buf[retval = _evil_pformat( PFORMAT_NOLIMIT, buf, 0, fmt, argv )] = '\0'; va_end( argv ); return retval; } int __cdecl _evil_snprintf(APICHAR *buf, size_t length, const APICHAR *fmt, ...) { register int retval; va_list argv; va_start( argv, fmt ); retval = _evil_vsnprintf( buf, length, fmt, argv ); va_end( argv ); return retval; } int __cdecl _evil_vfprintf(FILE *stream, const APICHAR *fmt, va_list argv) { return _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stream, 0, fmt, argv ); } int __cdecl _evil_vprintf(const APICHAR *fmt, va_list argv) { return _evil_pformat( PFORMAT_TO_FILE | PFORMAT_NOLIMIT, stdout, 0, fmt, argv ); } int __cdecl _evil_vsprintf(APICHAR *buf, const APICHAR *fmt, va_list argv) { register int retval; buf[retval = _evil_pformat( PFORMAT_NOLIMIT, buf, 0, fmt, argv )] = '\0'; return retval; } int __cdecl _evil_vsnprintf(APICHAR *buf, size_t length, const APICHAR *fmt, va_list argv ) { register int retval; if( length == (size_t)(0) ) /* * No buffer; simply compute and return the size required, * without actually emitting any data. */ return _evil_pformat( 0, buf, 0, fmt, argv); /* If we get to here, then we have a buffer... * Emit data up to the limit of buffer length less one, * then add the requisite NUL terminator. */ retval = _evil_pformat( 0, buf, --length, fmt, argv ); buf[retval < (int) length ? retval : (int)length] = '\0'; return retval; } /**** scanf family ****/ #ifndef __BUILD_WIDEAPI int __cdecl _evil_fscanf (FILE *stream, const APICHAR *format, ...) { va_list argp; int r; va_start (argp, format); r = _evil_vfscanf (stream, format, argp); va_end (argp); return r; } int __cdecl _evil_scanf (const APICHAR *format, ...) { va_list argp; int r; va_start (argp, format); r = _evil_vfscanf (stdin, format, argp); va_end (argp); return r; } int __cdecl _evil_sscanf (const APICHAR *buf, const APICHAR *format, ...) { va_list argp; int r; va_start (argp, format); r = _evil_vsscanf (buf, format, argp); va_end (argp); return r; } int __cdecl _evil_vfscanf (FILE *s, const APICHAR *format, va_list argp) { _IFP ifp; memset (&ifp, 0, sizeof (_IFP)); ifp.fp = s; return _evil_sformat (&ifp, format, argp); } int __cdecl _evil_vscanf (const APICHAR *format, va_list argp) { return _evil_vfscanf (stdin, format, argp); } int __cdecl _evil_vsscanf (const APICHAR *s, const APICHAR *format, va_list argp) { _IFP ifp; memset (&ifp, 0, sizeof (_IFP)); ifp.str = s; ifp.is_string = 1; return _evil_sformat (&ifp, format, argp); } #endif /**** asprintf family ****/ #ifndef __BUILD_WIDEAPI int _evil_asprintf(char ** __restrict__ ret, const char * __restrict__ format, ...) { va_list ap; int len; va_start(ap,format); /* Get Length */ len = _evil_vsnprintf(NULL,0,format,ap); if (len < 0) goto _end; /* +1 for \0 terminator. */ *ret = malloc(len + 1); /* Check malloc fail*/ if (!*ret) { len = -1; goto _end; } /* Write String */ _evil_vsnprintf(*ret,len+1,format,ap); /* Terminate explicitly */ (*ret)[len] = '\0'; _end: va_end(ap); return len; } int __cdecl _evil_vasprintf(char ** __restrict__ ret, const char * __restrict__ format, va_list ap) { int len; /* Get Length */ len = _evil_vsnprintf(NULL,0,format,ap); if (len < 0) return -1; /* +1 for \0 terminator. */ *ret = malloc(len + 1); /* Check malloc fail*/ if (!*ret) return -1; /* Write String */ _evil_vsnprintf(*ret,len+1,format,ap); /* Terminate explicitly */ (*ret)[len] = '\0'; return len; } #endif