diff options
author | Alon Zakai <alonzakai@gmail.com> | 2014-06-02 16:16:43 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2014-06-02 16:16:43 -0700 |
commit | acb8d1e6339fdae6285b90c5cd0740a079a3e2ed (patch) | |
tree | 918bfabd8b19ce35cda0a1f9afdbf913084e55cb | |
parent | c9823a494a96e2058f391a1778ad199f733c0423 (diff) | |
parent | 894fdda0c1f7f70939229a91a4ba9df977b37218 (diff) |
Merge pull request #2364 from juj/musl_stdio
Musl stdio
23 files changed, 1403 insertions, 128 deletions
diff --git a/src/library.js b/src/library.js index fb1a8998..120def05 100644 --- a/src/library.js +++ b/src/library.js @@ -2802,34 +2802,6 @@ LibraryManager.library = { var stdin = {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}; return _fscanf(stdin, format, varargs); }, - sscanf__deps: ['_scanString'], - sscanf: function(s, format, varargs) { - // int sscanf(const char *restrict s, const char *restrict format, ... ); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html - var index = 0; - function get() { return {{{ makeGetValue('s', 'index++', 'i8') }}}; }; - function unget() { index--; }; - return __scanString(format, get, unget, varargs); - }, - snprintf__deps: ['_formatString', 'malloc'], - snprintf: function(s, n, format, varargs) { - // int snprintf(char *restrict s, size_t n, const char *restrict format, ...); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html - var result = __formatString(format, varargs); - var limit = (n === undefined) ? result.length - : Math.min(result.length, Math.max(n - 1, 0)); - if (s < 0) { - s = -s; - var buf = _malloc(limit+1); - {{{ makeSetValue('s', '0', 'buf', 'i8*') }}}; - s = buf; - } - for (var i = 0; i < limit; i++) { - {{{ makeSetValue('s', 'i', 'result[i]', 'i8') }}}; - } - if (limit < n || (n === undefined)) {{{ makeSetValue('s', 'i', '0', 'i8') }}}; - return result.length; - }, fprintf__deps: ['fwrite', '_formatString'], fprintf: function(stream, format, varargs) { // int fprintf(FILE *restrict stream, const char *restrict format, ...); @@ -2847,16 +2819,6 @@ LibraryManager.library = { var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; return _fprintf(stdout, format, varargs); }, - sprintf__deps: ['snprintf'], - sprintf: function(s, format, varargs) { - // int sprintf(char *restrict s, const char *restrict format, ...); - // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html - return _snprintf(s, undefined, format, varargs); - }, - asprintf__deps: ['sprintf'], - asprintf: function(s, format, varargs) { - return _sprintf(-s, format, varargs); - }, dprintf__deps: ['_formatString', 'write'], dprintf: function(fd, format, varargs) { var result = __formatString(format, varargs); @@ -2868,14 +2830,10 @@ LibraryManager.library = { #if TARGET_X86 // va_arg is just like our varargs vfprintf: 'fprintf', - vsnprintf: 'snprintf', vprintf: 'printf', - vsprintf: 'sprintf', - vasprintf: 'asprintf', vdprintf: 'dprintf', vscanf: 'scanf', vfscanf: 'fscanf', - vsscanf: 'sscanf', #endif #if TARGET_ASMJS_UNKNOWN_EMSCRIPTEN @@ -2884,22 +2842,10 @@ LibraryManager.library = { vfprintf: function(s, f, va_arg) { return _fprintf(s, f, {{{ makeGetValue('va_arg', 0, '*') }}}); }, - vsnprintf__deps: ['snprintf'], - vsnprintf: function(s, n, format, va_arg) { - return _snprintf(s, n, format, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, vprintf__deps: ['printf'], vprintf: function(format, va_arg) { return _printf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); }, - vsprintf__deps: ['sprintf'], - vsprintf: function(s, format, va_arg) { - return _sprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, - vasprintf__deps: ['asprintf'], - vasprintf: function(s, format, va_arg) { - return _asprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, vdprintf__deps: ['dprintf'], vdprintf: function (fd, format, va_arg) { return _dprintf(fd, format, {{{ makeGetValue('va_arg', 0, '*') }}}); @@ -2912,10 +2858,6 @@ LibraryManager.library = { vfscanf: function(s, format, va_arg) { return _fscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); }, - vsscanf__deps: ['sscanf'], - vsscanf: function(s, format, va_arg) { - return _sscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); - }, #endif // ========================================================================== @@ -4509,23 +4451,6 @@ LibraryManager.library = { {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'float') }}}; return x - {{{ makeGetValue('intpart', 0, 'float') }}}; }, - frexp: function(x, exp_addr) { - var sig = 0, exp_ = 0; - if (x !== 0) { - var sign = 1; - if (x < 0) { - x = -x; - sign = -1; - } - var raw_exp = Math.log(x)/Math.log(2); - exp_ = Math.ceil(raw_exp); - if (exp_ === raw_exp) exp_ += 1; - sig = sign*x/Math.pow(2, exp_); - } - {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}}; - return sig; - }, - frexpf: 'frexp', finite: function(x) { return isFinite(x); }, diff --git a/src/preamble.js b/src/preamble.js index bba2fc46..431a3c27 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -41,13 +41,13 @@ var ACCEPTABLE_SAFE_HEAP_ERRORS = 0; function SAFE_HEAP_ACCESS(dest, type, store, ignore, storeValue) { //if (dest === A_NUMBER) Module.print ([dest, type, store, ignore, storeValue] + ' ' + stackTrace()); // Something like this may be useful, in debugging - assert(dest > 0, 'segmentation fault'); + if (dest <= 0) abort('segmentation fault ' + (store ? ('storing value ' + storeValue) : 'loading') + ' type ' + type + ' at address ' + dest); #if USE_TYPED_ARRAYS // When using typed arrays, reads over the top of TOTAL_MEMORY will fail silently, so we must // correct that by growing TOTAL_MEMORY as needed. Without typed arrays, memory is a normal // JS array so it will work (potentially slowly, depending on the engine). - assert(ignore || dest < Math.max(DYNAMICTOP, STATICTOP)); + if (!ignore && dest >= Math.max(DYNAMICTOP, STATICTOP)) abort('segmentation fault ' + (store ? ('storing value ' + storeValue) : 'loading') + ' type ' + type + ' at address ' + dest + '. Heap ends at address ' + Math.max(DYNAMICTOP, STATICTOP)); assert(ignore || DYNAMICTOP <= TOTAL_MEMORY); #endif diff --git a/system/lib/libc.symbols b/system/lib/libc.symbols index d889d509..28e959d9 100644 --- a/system/lib/libc.symbols +++ b/system/lib/libc.symbols @@ -56,6 +56,9 @@ W bulk_free W calloc W free + T frexp + T frexpf + T frexpl W independent_calloc W independent_comalloc T isdigit @@ -72,14 +75,18 @@ W malloc_usable_size W mallopt W memalign + T MUSL_vfprintf W posix_memalign W pvalloc W realloc W realloc_in_place T scalbn T scalbnl + T memchr T memcmp T memcpy + T snprintf + T sprintf T strtod T strtoull T strtoll @@ -101,3 +108,7 @@ T strtold_l T tolower W valloc + T vsnprintf + T vsprintf + T wcrtomb + T wctomb diff --git a/system/lib/libc/musl/src/internal/stdio_impl.h b/system/lib/libc/musl/src/internal/stdio_impl.h index 6bcd44dc..0e10e30d 100644 --- a/system/lib/libc/musl/src/internal/stdio_impl.h +++ b/system/lib/libc/musl/src/internal/stdio_impl.h @@ -95,4 +95,8 @@ int __fmodeflags(const char *); FILE *__fopen_rb_ca(const char *, FILE *, unsigned char *, size_t); int __fclose_ca(FILE *); +// XXX Emscripten: musl-specific vfscanf and vfprintf live parallel to JS handwritten vfscanf and vfprintf, so musl ones are prefixed. +int MUSL_vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap); +int MUSL_vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap); + #endif diff --git a/system/lib/libc/musl/src/math/frexp.c b/system/lib/libc/musl/src/math/frexp.c new file mode 100644 index 00000000..27b6266e --- /dev/null +++ b/system/lib/libc/musl/src/math/frexp.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stdint.h> + +double frexp(double x, int *e) +{ + union { double d; uint64_t i; } y = { x }; + int ee = y.i>>52 & 0x7ff; + + if (!ee) { + if (x) { + x = frexp(x*0x1p64, e); + *e -= 64; + } else *e = 0; + return x; + } else if (ee == 0x7ff) { + return x; + } + + *e = ee - 0x3fe; + y.i &= 0x800fffffffffffffull; + y.i |= 0x3fe0000000000000ull; + return y.d; +} diff --git a/system/lib/libc/musl/src/math/frexpf.c b/system/lib/libc/musl/src/math/frexpf.c new file mode 100644 index 00000000..07870975 --- /dev/null +++ b/system/lib/libc/musl/src/math/frexpf.c @@ -0,0 +1,23 @@ +#include <math.h> +#include <stdint.h> + +float frexpf(float x, int *e) +{ + union { float f; uint32_t i; } y = { x }; + int ee = y.i>>23 & 0xff; + + if (!ee) { + if (x) { + x = frexpf(x*0x1p64, e); + *e -= 64; + } else *e = 0; + return x; + } else if (ee == 0xff) { + return x; + } + + *e = ee - 0x7e; + y.i &= 0x807ffffful; + y.i |= 0x3f000000ul; + return y.f; +} diff --git a/system/lib/libc/musl/src/math/frexpl.c b/system/lib/libc/musl/src/math/frexpl.c new file mode 100644 index 00000000..f9d90a6d --- /dev/null +++ b/system/lib/libc/musl/src/math/frexpl.c @@ -0,0 +1,37 @@ +#include <math.h> +#include <stdint.h> +#include <float.h> + +#if LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 + +/* This version is for 80-bit little endian long double */ + +long double frexpl(long double x, int *e) +{ + union { long double ld; uint16_t hw[5]; } y = { x }; + int ee = y.hw[4]&0x7fff; + + if (!ee) { + if (x) { + x = frexpl(x*0x1p64, e); + *e -= 64; + } else *e = 0; + return x; + } else if (ee == 0x7fff) { + return x; + } + + *e = ee - 0x3ffe; + y.hw[4] &= 0x8000; + y.hw[4] |= 0x3ffe; + return y.ld; +} + +#else + +long double frexpl(long double x, int *e) +{ + return frexp(x, e); +} + +#endif diff --git a/system/lib/libc/musl/src/stdio/__string_read.c b/system/lib/libc/musl/src/stdio/__string_read.c new file mode 100644 index 00000000..7b50a7e1 --- /dev/null +++ b/system/lib/libc/musl/src/stdio/__string_read.c @@ -0,0 +1,16 @@ +#include "stdio_impl.h" +#include <string.h> + +size_t __string_read(FILE *f, unsigned char *buf, size_t len) +{ + char *src = f->cookie; + size_t k = len+256; + char *end = memchr(src, 0, k); + if (end) k = end-src; + if (k < len) len = k; + memcpy(buf, src, len); + f->rpos = (void *)(src+len); + f->rend = (void *)(src+k); + f->cookie = src+k; + return len; +} diff --git a/system/lib/libc/musl/src/stdio/asprintf.c b/system/lib/libc/musl/src/stdio/asprintf.c new file mode 100644 index 00000000..4ec83534 --- /dev/null +++ b/system/lib/libc/musl/src/stdio/asprintf.c @@ -0,0 +1,13 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> + +int asprintf(char **s, const char *fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vasprintf(s, fmt, ap); + va_end(ap); + return ret; +} diff --git a/system/lib/libc/musl/src/stdio/fwrite.c b/system/lib/libc/musl/src/stdio/fwrite.c new file mode 100644 index 00000000..bf68b794 --- /dev/null +++ b/system/lib/libc/musl/src/stdio/fwrite.c @@ -0,0 +1,41 @@ +#include "stdio_impl.h" +#include <string.h> + +size_t __fwritex(const unsigned char *restrict s, size_t l, FILE *restrict f) +{ + size_t i=0; + + if (!f->wend && __towrite(f)) return 0; + + if (l > f->wend - f->wpos) return f->write(f, s, l); + + if (f->lbf >= 0) { + /* Match /^(.*\n|)/ */ + for (i=l; i && s[i-1] != '\n'; i--); + if (i) { + if (f->write(f, s, i) < i) + return i; + s += i; + l -= i; + } + } + + memcpy(f->wpos, s, l); + f->wpos += l; + return l+i; +} + +// XXX Emscripten: Not used, since we are not currently using musl file IO. +#if 0 +size_t fwrite(const void *restrict src, size_t size, size_t nmemb, FILE *restrict f) +{ + size_t k, l = size*nmemb; + if (!l) return l; + FLOCK(f); + k = __fwritex(src, l, f); + FUNLOCK(f); + return k==l ? nmemb : k/size; +} +#endif + +weak_alias(fwrite, fwrite_unlocked); diff --git a/system/lib/libc/musl/src/stdio/snprintf.c b/system/lib/libc/musl/src/stdio/snprintf.c new file mode 100644 index 00000000..771503b2 --- /dev/null +++ b/system/lib/libc/musl/src/stdio/snprintf.c @@ -0,0 +1,13 @@ +#include <stdio.h> +#include <stdarg.h> + +int snprintf(char *restrict s, size_t n, const char *restrict fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsnprintf(s, n, fmt, ap); + va_end(ap); + return ret; +} + diff --git a/system/lib/libc/musl/src/stdio/sprintf.c b/system/lib/libc/musl/src/stdio/sprintf.c new file mode 100644 index 00000000..9dff524c --- /dev/null +++ b/system/lib/libc/musl/src/stdio/sprintf.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdarg.h> + +int sprintf(char *restrict s, const char *restrict fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsprintf(s, fmt, ap); + va_end(ap); + return ret; +} diff --git a/system/lib/libc/musl/src/stdio/sscanf.c b/system/lib/libc/musl/src/stdio/sscanf.c new file mode 100644 index 00000000..8a2302ff --- /dev/null +++ b/system/lib/libc/musl/src/stdio/sscanf.c @@ -0,0 +1,15 @@ +#include <stdio.h> +#include <stdarg.h> +#include "libc.h" + +int sscanf(const char *restrict s, const char *restrict fmt, ...) +{ + int ret; + va_list ap; + va_start(ap, fmt); + ret = vsscanf(s, fmt, ap); + va_end(ap); + return ret; +} + +weak_alias(sscanf,__isoc99_sscanf); diff --git a/system/lib/libc/musl/src/stdio/vasprintf.c b/system/lib/libc/musl/src/stdio/vasprintf.c new file mode 100644 index 00000000..68b7246b --- /dev/null +++ b/system/lib/libc/musl/src/stdio/vasprintf.c @@ -0,0 +1,28 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> + +#define GUESS 240U + +int vasprintf(char **s, const char *fmt, va_list ap) +{ + va_list ap2; + char *a; + int l=GUESS; + + if (!(a=malloc(GUESS))) return -1; + + va_copy(ap2, ap); + l=vsnprintf(a, GUESS, fmt, ap2); + va_end(ap2); + + if (l<GUESS) { + char *b = realloc(a, l+1U); + *s = b ? b : a; + return l; + } + free(a); + if (l<0 || !(*s=malloc(l+1U))) return -1; + return vsnprintf(*s, l+1U, fmt, ap); +} diff --git a/system/lib/libc/musl/src/stdio/vfprintf.c b/system/lib/libc/musl/src/stdio/vfprintf.c new file mode 100644 index 00000000..750ad1e3 --- /dev/null +++ b/system/lib/libc/musl/src/stdio/vfprintf.c @@ -0,0 +1,685 @@ +#include "stdio_impl.h" +#include <errno.h> +#include <ctype.h> +#include <limits.h> +#include <string.h> +#include <stdarg.h> +#include <wchar.h> +#include <inttypes.h> +#include <math.h> +#include <float.h> + +/* Some useful macros */ + +#define MAX(a,b) ((a)>(b) ? (a) : (b)) +#define MIN(a,b) ((a)<(b) ? (a) : (b)) +#define CONCAT2(x,y) x ## y +#define CONCAT(x,y) CONCAT2(x,y) + +/* Convenient bit representation for modifier flags, which all fall + * within 31 codepoints of the space character. */ + +#define ALT_FORM (1U<<'#'-' ') +#define ZERO_PAD (1U<<'0'-' ') +#define LEFT_ADJ (1U<<'-'-' ') +#define PAD_POS (1U<<' '-' ') +#define MARK_POS (1U<<'+'-' ') +#define GROUPED (1U<<'\''-' ') + +#define FLAGMASK (ALT_FORM|ZERO_PAD|LEFT_ADJ|PAD_POS|MARK_POS|GROUPED) + +#if UINT_MAX == ULONG_MAX +#define LONG_IS_INT +#endif + +#if SIZE_MAX != ULONG_MAX || UINTMAX_MAX != ULLONG_MAX +#define ODD_TYPES +#endif + +/* State machine to accept length modifiers + conversion specifiers. + * Result is 0 on failure, or an argument type to pop on success. */ + +enum { + BARE, LPRE, LLPRE, HPRE, HHPRE, BIGLPRE, + ZTPRE, JPRE, + STOP, + PTR, INT, UINT, ULLONG, +#ifndef LONG_IS_INT + LONG, ULONG, +#else +#define LONG INT +#define ULONG UINT +#endif + SHORT, USHORT, CHAR, UCHAR, +#ifdef ODD_TYPES + LLONG, SIZET, IMAX, UMAX, PDIFF, UIPTR, +#else +#define LLONG ULLONG +#define SIZET ULONG +#define IMAX LLONG +#define UMAX ULLONG +#define PDIFF LONG +#define UIPTR ULONG +#endif + DBL, LDBL, + NOARG, + MAXSTATE +}; + +#define S(x) [(x)-'A'] + +static const unsigned char states[]['z'-'A'+1] = { + { /* 0: bare types */ + S('d') = INT, S('i') = INT, + S('o') = UINT, S('u') = UINT, S('x') = UINT, S('X') = UINT, + S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, + S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, + S('c') = CHAR, S('C') = INT, + S('s') = PTR, S('S') = PTR, S('p') = UIPTR, S('n') = PTR, + S('m') = NOARG, + S('l') = LPRE, S('h') = HPRE, S('L') = BIGLPRE, + S('z') = ZTPRE, S('j') = JPRE, S('t') = ZTPRE, + }, { /* 1: l-prefixed */ + S('d') = LONG, S('i') = LONG, + S('o') = ULONG, S('u') = ULONG, S('x') = ULONG, S('X') = ULONG, + S('e') = DBL, S('f') = DBL, S('g') = DBL, S('a') = DBL, + S('E') = DBL, S('F') = DBL, S('G') = DBL, S('A') = DBL, + S('c') = INT, S('s') = PTR, S('n') = PTR, + S('l') = LLPRE, + }, { /* 2: ll-prefixed */ + S('d') = LLONG, S('i') = LLONG, + S('o') = ULLONG, S('u') = ULLONG, + S('x') = ULLONG, S('X') = ULLONG, + S('n') = PTR, + }, { /* 3: h-prefixed */ + S('d') = SHORT, S('i') = SHORT, + S('o') = USHORT, S('u') = USHORT, + S('x') = USHORT, S('X') = USHORT, + S('n') = PTR, + S('h') = HHPRE, + }, { /* 4: hh-prefixed */ + S('d') = CHAR, S('i') = CHAR, + S('o') = UCHAR, S('u') = UCHAR, + S('x') = UCHAR, S('X') = UCHAR, + S('n') = PTR, + }, { /* 5: L-prefixed */ + S('e') = LDBL, S('f') = LDBL, S('g') = LDBL, S('a') = LDBL, + S('E') = LDBL, S('F') = LDBL, S('G') = LDBL, S('A') = LDBL, + S('n') = PTR, + }, { /* 6: z- or t-prefixed (assumed to be same size) */ + S('d') = PDIFF, S('i') = PDIFF, + S('o') = SIZET, S('u') = SIZET, + S('x') = SIZET, S('X') = SIZET, + S('n') = PTR, + }, { /* 7: j-prefixed */ + S('d') = IMAX, S('i') = IMAX, + S('o') = UMAX, S('u') = UMAX, + S('x') = UMAX, S('X') = UMAX, + S('n') = PTR, + } +}; + +#define OOB(x) ((unsigned)(x)-'A' > 'z'-'A') + +union arg +{ + uintmax_t i; + long double f; + void *p; +}; + +static void pop_arg(union arg *arg, int type, va_list *ap) +{ + /* Give the compiler a hint for optimizing the switch. */ + if ((unsigned)type > MAXSTATE) return; + switch (type) { + case PTR: arg->p = va_arg(*ap, void *); + break; case INT: arg->i = va_arg(*ap, int); + break; case UINT: arg->i = va_arg(*ap, unsigned int); +#ifndef LONG_IS_INT + break; case LONG: arg->i = va_arg(*ap, long); + break; case ULONG: arg->i = va_arg(*ap, unsigned long); +#endif + break; case ULLONG: arg->i = va_arg(*ap, unsigned long long); + break; case SHORT: arg->i = (short)va_arg(*ap, int); + break; case USHORT: arg->i = (unsigned short)va_arg(*ap, int); + break; case CHAR: arg->i = (signed char)va_arg(*ap, int); + break; case UCHAR: arg->i = (unsigned char)va_arg(*ap, int); +#ifdef ODD_TYPES + break; case LLONG: arg->i = va_arg(*ap, long long); + break; case SIZET: arg->i = va_arg(*ap, size_t); + break; case IMAX: arg->i = va_arg(*ap, intmax_t); + break; case UMAX: arg->i = va_arg(*ap, uintmax_t); + break; case PDIFF: arg->i = va_arg(*ap, ptrdiff_t); + break; case UIPTR: arg->i = (uintptr_t)va_arg(*ap, void *); +#endif + break; case DBL: arg->f = va_arg(*ap, double); + break; case LDBL: arg->f = va_arg(*ap, long double); + } +} + +static void out(FILE *f, const char *s, size_t l) +{ + __fwritex((void *)s, l, f); +} + +static void pad(FILE *f, char c, int w, int l, int fl) +{ + char pad[256]; + if (fl & (LEFT_ADJ | ZERO_PAD) || l >= w) return; + l = w - l; + memset(pad, c, l>sizeof pad ? sizeof pad : l); + for (; l >= sizeof pad; l -= sizeof pad) + out(f, pad, sizeof pad); + out(f, pad, l); +} + +static const char xdigits[16] = { + "0123456789ABCDEF" +}; + +static char *fmt_x(uintmax_t x, char *s, int lower) +{ + for (; x; x>>=4) *--s = xdigits[(x&15)]|lower; + return s; +} + +static char *fmt_o(uintmax_t x, char *s) +{ + for (; x; x>>=3) *--s = '0' + (x&7); + return s; +} + +static char *fmt_u(uintmax_t x, char *s) +{ + unsigned long y; + for ( ; x>ULONG_MAX; x/=10) *--s = '0' + x%10; + for (y=x; y; y/=10) *--s = '0' + y%10; + return s; +} + +/* Do not override this check. The floating point printing code below + * depends on the float.h constants being right. If they are wrong, it + * may overflow the stack. */ +#if LDBL_MANT_DIG == 53 +typedef char compiler_defines_long_double_incorrectly[9-(int)sizeof(long double)]; +#endif + +static int fmt_fp(FILE *f, long double y, int w, int p, int fl, int t) +{ + uint32_t big[(LDBL_MAX_EXP+LDBL_MANT_DIG)/9+1]; + uint32_t *a, *d, *r, *z; + int e2=0, e, i, j, l; + char buf[9+LDBL_MANT_DIG/4], *s; + const char *prefix="-0X+0X 0X-0x+0x 0x"; + int pl; + char ebuf0[3*sizeof(int)], *ebuf=&ebuf0[3*sizeof(int)], *estr; + + pl=1; + if (signbit(y)) { + y=-y; + } else if (fl & MARK_POS) { + prefix+=3; + } else if (fl & PAD_POS) { + prefix+=6; + } else prefix++, pl=0; + + if (!isfinite(y)) { + char *s = (t&32)?"inf":"INF"; + if (y!=y) s=(t&32)?"nan":"NAN", pl=0; + pad(f, ' ', w, 3+pl, fl&~ZERO_PAD); + out(f, prefix, pl); + out(f, s, 3); + pad(f, ' ', w, 3+pl, fl^LEFT_ADJ); + return MAX(w, 3+pl); + } + + y = frexpl(y, &e2) * 2; + if (y) e2--; + + if ((t|32)=='a') { + long double round = 8.0; + int re; + + if (t&32) prefix += 9; + pl += 2; + + if (p<0 || p>=LDBL_MANT_DIG/4-1) re=0; + else re=LDBL_MANT_DIG/4-1-p; + + if (re) { + while (re--) round*=16; + if (*prefix=='-') { + y=-y; + y-=round; + y+=round; + y=-y; + } else { + y+=round; + y-=round; + } + } + + estr=fmt_u(e2<0 ? -e2 : e2, ebuf); + if (estr==ebuf) *--estr='0'; + *--estr = (e2<0 ? '-' : '+'); + *--estr = t+('p'-'a'); + + s=buf; + do { + int x=y; + *s++=xdigits[x]|(t&32); + y=16*(y-x); + if (s-buf==1 && (y||p>0||(fl&ALT_FORM))) *s++='.'; + } while (y); + + if (p && s-buf-2 < p) + l = (p+2) + (ebuf-estr); + else + l = (s-buf) + (ebuf-estr); + + pad(f, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+l, fl^ZERO_PAD); + out(f, buf, s-buf); + pad(f, '0', l-(ebuf-estr)-(s-buf), 0, 0); + out(f, estr, ebuf-estr); + pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + return MAX(w, pl+l); + } + if (p<0) p=6; + + if (y) y *= 0x1p28, e2-=28; + + if (e2<0) a=r=z=big; + else a=r=z=big+sizeof(big)/sizeof(*big) - LDBL_MANT_DIG - 1; + + do { + *z = y; + y = 1000000000*(y-*z++); + } while (y); + + while (e2>0) { + uint32_t carry=0; + int sh=MIN(29,e2); + for (d=z-1; d>=a; d--) { + uint64_t x = ((uint64_t)*d<<sh)+carry; + *d = x % 1000000000; + carry = x / 1000000000; + } + if (!z[-1] && z>a) z--; + if (carry) *--a = carry; + e2-=sh; + } + while (e2<0) { + uint32_t carry=0, *b; + int sh=MIN(9,-e2); + for (d=a; d<z; d++) { + uint32_t rm = *d & (1<<sh)-1; + *d = (*d>>sh) + carry; + carry = (1000000000>>sh) * rm; + } + if (!*a) a++; + if (carry) *z++ = carry; + /* Avoid (slow!) computation past requested precision */ + b = (t|32)=='f' ? r : a; + if (z-b > 2+p/9) z = b+2+p/9; + e2+=sh; + } + + if (a<z) for (i=10, e=9*(r-a); *a>=i; i*=10, e++); + else e=0; + + /* Perform rounding: j is precision after the radix (possibly neg) */ + j = p - ((t|32)!='f')*e - ((t|32)=='g' && p); + if (j < 9*(z-r-1)) { + uint32_t x; + /* We avoid C's broken division of negative numbers */ + d = r + 1 + ((j+9*LDBL_MAX_EXP)/9 - LDBL_MAX_EXP); + j += 9*LDBL_MAX_EXP; + j %= 9; + for (i=10, j++; j<9; i*=10, j++); + x = *d % i; + /* Are there any significant digits past j? */ + if (x || d+1!=z) { + long double round = CONCAT(0x1p,LDBL_MANT_DIG); + long double small; + if (*d/i & 1) round += 2; + if (x<i/2) small=0x0.8p0; + else if (x==i/2 && d+1==z) small=0x1.0p0; + else small=0x1.8p0; + if (pl && *prefix=='-') round*=-1, small*=-1; + *d -= x; + /* Decide whether to round by probing round+small */ + if (round+small != round) { + *d = *d + i; + while (*d > 999999999) { + *d--=0; + (*d)++; + } + if (d<a) a=d; + for (i=10, e=9*(r-a); *a>=i; i*=10, e++); + } + } + if (z>d+1) z=d+1; + for (; !z[-1] && z>a; z--); + } + + if ((t|32)=='g') { + if (!p) p++; + if (p>e && e>=-4) { + t--; + p-=e+1; + } else { + t-=2; + p--; + } + if (!(fl&ALT_FORM)) { + /* Count trailing zeros in last place */ + if (z>a && z[-1]) for (i=10, j=0; z[-1]%i==0; i*=10, j++); + else j=9; + if ((t|32)=='f') + p = MIN(p,MAX(0,9*(z-r-1)-j)); + else + p = MIN(p,MAX(0,9*(z-r-1)+e-j)); + } + } + l = 1 + p + (p || (fl&ALT_FORM)); + if ((t|32)=='f') { + if (e>0) l+=e; + } else { + estr=fmt_u(e<0 ? -e : e, ebuf); + while(ebuf-estr<2) *--estr='0'; + *--estr = (e<0 ? '-' : '+'); + *--estr = t; + l += ebuf-estr; + } + + pad(f, ' ', w, pl+l, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+l, fl^ZERO_PAD); + + if ((t|32)=='f') { + if (a>r) a=r; + for (d=a; d<=r; d++) { + char *s = fmt_u(*d, buf+9); + if (d!=a) while (s>buf) *--s='0'; + else if (s==buf+9) *--s='0'; + out(f, s, buf+9-s); + } + if (p || (fl&ALT_FORM)) out(f, ".", 1); + for (; d<z && p>0; d++, p-=9) { + char *s = fmt_u(*d, buf+9); + while (s>buf) *--s='0'; + out(f, s, MIN(9,p)); + } + pad(f, '0', p+9, 9, 0); + } else { + if (z<=a) z=a+1; + for (d=a; d<z && p>=0; d++) { + char *s = fmt_u(*d, buf+9); + if (s==buf+9) *--s='0'; + if (d!=a) while (s>buf) *--s='0'; + else { + out(f, s++, 1); + if (p>0||(fl&ALT_FORM)) out(f, ".", 1); + } + out(f, s, MIN(buf+9-s, p)); + p -= buf+9-s; + } + pad(f, '0', p+18, 18, 0); + out(f, estr, ebuf-estr); + } + + pad(f, ' ', w, pl+l, fl^LEFT_ADJ); + + return MAX(w, pl+l); +} + +static int getint(char **s) { + int i; + for (i=0; isdigit(**s); (*s)++) + i = 10*i + (**s-'0'); + return i; +} + +static int printf_core(FILE *f, const char *fmt, va_list *ap, union arg *nl_arg, int *nl_type) +{ + char *a, *z, *s=(char *)fmt; + unsigned l10n=0, fl; + int w, p; + union arg arg; + int argpos; + unsigned st, ps; + int cnt=0, l=0; + int i; + char buf[sizeof(uintmax_t)*3+3+LDBL_MANT_DIG/4]; + const char *prefix; + int t, pl; + wchar_t wc[2], *ws; + char mb[4]; + + for (;;) { + /* Update output count, end loop when fmt is exhausted */ + if (cnt >= 0) { + if (l > INT_MAX - cnt) { + errno = EOVERFLOW; + cnt = -1; + } else cnt += l; + } + if (!*s) break; + + /* Handle literal text and %% format specifiers */ + for (a=s; *s && *s!='%'; s++); + for (z=s; s[0]=='%' && s[1]=='%'; z++, s+=2); + l = z-a; + if (f) out(f, a, l); + if (l) continue; + + if (isdigit(s[1]) && s[2]=='$') { + l10n=1; + argpos = s[1]-'0'; + s+=3; + } else { + argpos = -1; + s++; + } + + /* Read modifier flags */ + for (fl=0; (unsigned)*s-' '<32 && (FLAGMASK&(1U<<*s-' ')); s++) + fl |= 1U<<*s-' '; + + /* Read field width */ + if (*s=='*') { + if (isdigit(s[1]) && s[2]=='$') { + l10n=1; + nl_type[s[1]-'0'] = INT; + w = nl_arg[s[1]-'0'].i; + s+=3; + } else if (!l10n) { + w = f ? va_arg(*ap, int) : 0; + s++; + } else return -1; + if (w<0) fl|=LEFT_ADJ, w=-w; + } else if ((w=getint(&s))<0) return -1; + + /* Read precision */ + if (*s=='.' && s[1]=='*') { + if (isdigit(s[2]) && s[3]=='$') { + nl_type[s[2]-'0'] = INT; + p = nl_arg[s[2]-'0'].i; + s+=4; + } else if (!l10n) { + p = f ? va_arg(*ap, int) : 0; + s+=2; + } else return -1; + } else if (*s=='.') { + s++; + p = getint(&s); + } else p = -1; + + /* Format specifier state machine */ + st=0; + do { + if (OOB(*s)) return -1; + ps=st; + st=states[st]S(*s++); + } while (st-1<STOP); + if (!st) return -1; + + /* Check validity of argument type (nl/normal) */ + if (st==NOARG) { + if (argpos>=0) return -1; + else if (!f) continue; + } else { + if (argpos>=0) nl_type[argpos]=st, arg=nl_arg[argpos]; + else if (f) pop_arg(&arg, st, ap); + else return 0; + } + + if (!f) continue; + + z = buf + sizeof(buf); + prefix = "-+ 0X0x"; + pl = 0; + t = s[-1]; + + /* Transform ls,lc -> S,C */ + if (ps && (t&15)==3) t&=~32; + + /* - and 0 flags are mutually exclusive */ + if (fl & LEFT_ADJ) fl &= ~ZERO_PAD; + + switch(t) { + case 'n': + switch(ps) { + case BARE: *(int *)arg.p = cnt; break; + case LPRE: *(long *)arg.p = cnt; break; + case LLPRE: *(long long *)arg.p = cnt; break; + case HPRE: *(unsigned short *)arg.p = cnt; break; + case HHPRE: *(unsigned char *)arg.p = cnt; break; + case ZTPRE: *(size_t *)arg.p = cnt; break; + case JPRE: *(uintmax_t *)arg.p = cnt; break; + } + continue; + case 'p': + p = MAX(p, 2*sizeof(void*)); + t = 'x'; + fl |= ALT_FORM; + case 'x': case 'X': + a = fmt_x(arg.i, z, t&32); + if (arg.i && (fl & ALT_FORM)) prefix+=(t>>4), pl=2; + if (0) { + case 'o': + a = fmt_o(arg.i, z); + if ((fl&ALT_FORM) && arg.i) prefix+=5, pl=1; + } if (0) { + case 'd': case 'i': + pl=1; + if (arg.i>INTMAX_MAX) { + arg.i=-arg.i; + } else if (fl & MARK_POS) { + prefix++; + } else if (fl & PAD_POS) { + prefix+=2; + } else pl=0; + case 'u': + a = fmt_u(arg.i, z); + } + if (p>=0) fl &= ~ZERO_PAD; + if (!arg.i && !p) { + a=z; + break; + } + p = MAX(p, z-a + !arg.i); + break; + case 'c': + *(a=z-(p=1))=arg.i; + fl &= ~ZERO_PAD; + break; + case 'm': + if (1) a = strerror(errno); else + case 's': + a = arg.p ? arg.p : "(null)"; + z = memchr(a, 0, p); + if (!z) z=a+p; + else p=z-a; + fl &= ~ZERO_PAD; + break; + case 'C': + wc[0] = arg.i; + wc[1] = 0; + arg.p = wc; + p = -1; + case 'S': + ws = arg.p; + for (i=l=0; i<0U+p && *ws && (l=wctomb(mb, *ws++))>=0 && l<=0U+p-i; i+=l); + if (l<0) return -1; + p = i; + pad(f, ' ', w, p, fl); + ws = arg.p; + for (i=0; i<0U+p && *ws && i+(l=wctomb(mb, *ws++))<=p; i+=l) + out(f, mb, l); + pad(f, ' ', w, p, fl^LEFT_ADJ); + l = w>p ? w : p; + continue; + case 'e': case 'f': case 'g': case 'a': + case 'E': case 'F': case 'G': case 'A': + l = fmt_fp(f, arg.f, w, p, fl, t); + continue; + } + + if (p < z-a) p = z-a; + if (w < pl+p) w = pl+p; + + pad(f, ' ', w, pl+p, fl); + out(f, prefix, pl); + pad(f, '0', w, pl+p, fl^ZERO_PAD); + pad(f, '0', p, z-a, 0); + out(f, a, z-a); + pad(f, ' ', w, pl+p, fl^LEFT_ADJ); + + l = w; + } + + if (f) return cnt; + if (!l10n) return 0; + + for (i=1; i<=NL_ARGMAX && nl_type[i]; i++) + pop_arg(nl_arg+i, nl_type[i], ap); + for (; i<=NL_ARGMAX && !nl_type[i]; i++); + if (i<=NL_ARGMAX) return -1; + return 1; +} + +//int vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) +int MUSL_vfprintf(FILE *restrict f, const char *restrict fmt, va_list ap) /// XXX Emscripten: Only use musl-specific vfprintf when called from within sprintf. +{ + va_list ap2; + int nl_type[NL_ARGMAX+1] = {0}; + union arg nl_arg[NL_ARGMAX+1]; + unsigned char internal_buf[80], *saved_buf = 0; + int ret; + + va_copy(ap2, ap); + if (printf_core(0, fmt, &ap2, nl_arg, nl_type) < 0) return -1; + + FLOCK(f); + if (!f->buf_size) { + saved_buf = f->buf; + f->wpos = f->wbase = f->buf = internal_buf; + f->buf_size = sizeof internal_buf; + f->wend = internal_buf + sizeof internal_buf; + } + ret = printf_core(f, fmt, &ap2, nl_arg, nl_type); + if (saved_buf) { + f->write(f, 0, 0); + if (!f->wpos) ret = -1; + f->buf = saved_buf; + f->buf_size = 0; + f->wpos = f->wbase = f->wend = 0; + } + FUNLOCK(f); + va_end(ap2); + return ret; +} diff --git a/system/lib/libc/musl/src/stdio/vfscanf.c b/system/lib/libc/musl/src/stdio/vfscanf.c new file mode 100644 index 00000000..5373eea6 --- /dev/null +++ b/system/lib/libc/musl/src/stdio/vfscanf.c @@ -0,0 +1,332 @@ +#include <stdlib.h> +#include <stdarg.h> +#include <ctype.h> +#include <wchar.h> +#include <wctype.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <math.h> +#include <float.h> +#include <inttypes.h> + +#include "stdio_impl.h" +#include "shgetc.h" +#include "intscan.h" +#include "floatscan.h" + +#define SIZE_hh -2 +#define SIZE_h -1 +#define SIZE_def 0 +#define SIZE_l 1 +#define SIZE_L 2 +#define SIZE_ll 3 + +static void store_int(void *dest, int size, unsigned long long i) +{ + if (!dest) return; + 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 vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap) +int MUSL_vfscanf(FILE *restrict f, const char *restrict fmt, va_list ap) // XXX Emscripten: Only use musl-specific vfscanf when called from within sscanf. +{ + int width; + int size; + int alloc; + int base; + const unsigned char *p; + int c, t; + char *s; + wchar_t *wcs; + mbstate_t st; + void *dest=NULL; + int invert; + int matches=0; + unsigned long long x; + long double y; + off_t pos = 0; + unsigned char scanset[257]; + size_t i, k; + wchar_t wc; + + FLOCK(f); + + for (p=(const unsigned char *)fmt; *p; p++) { + + alloc = 0; + + if (isspace(*p)) { + while (isspace(p[1])) p++; + shlim(f, 0); + while (isspace(shgetc(f))); + shunget(f); + pos += shcnt(f); + continue; + } + if (*p != '%' || p[1] == '%') { + p += *p=='%'; + shlim(f, 0); + c = shgetc(f); + if (c!=*p) { + shunget(f); + if (c<0) goto input_fail; + goto match_fail; + } + pos++; + continue; + } + + p++; + if (*p=='*') { + dest = 0; p++; + } else if (isdigit(*p) && p[1]=='$') { + dest = arg_n(ap, *p-'0'); p+=2; + } else { + dest = va_arg(ap, void *); + } + + for (width=0; isdigit(*p); p++) { + width = 10*width + *p - '0'; + } + + if (*p=='m') { + alloc = !!dest; + p++; + } else { + alloc = 0; + } + + size = SIZE_def; + switch (*p++) { + case 'h': + if (*p == 'h') p++, size = SIZE_hh; + else size = SIZE_h; + break; + case 'l': + if (*p == 'l') p++, size = SIZE_ll; + else size = SIZE_l; + break; + case 'j': + size = SIZE_ll; + break; + case 'z': + case 't': + size = SIZE_l; + break; + case 'L': + size = SIZE_L; + break; + case 'd': case 'i': case 'o': case 'u': case 'x': + case 'a': case 'e': case 'f': case 'g': + case 'A': case 'E': case 'F': case 'G': case 'X': + case 's': case 'c': case '[': + case 'S': case 'C': + case 'p': case 'n': + p--; + break; + default: + goto fmt_fail; + } + + t = *p; + + /* C or S */ + if ((t&0x2f) == 3) { + t |= 32; + size = SIZE_l; + } + + switch (t) { + case 'c': + if (width < 1) width = 1; + case '[': + break; + case 'n': + store_int(dest, size, pos); + /* do not increment match count, etc! */ + continue; + default: + shlim(f, 0); + while (isspace(shgetc(f))); + shunget(f); + pos += shcnt(f); + } + + shlim(f, width); + if (shgetc(f) < 0) goto input_fail; + shunget(f); + + switch (t) { + case 's': + case 'c': + case '[': + if (t == 'c' || t == 's') { + memset(scanset, -1, sizeof scanset); + scanset[0] = 0; + if (t == 's') { + scanset[1+'\t'] = 0; + scanset[1+'\n'] = 0; + scanset[1+'\v'] = 0; + scanset[1+'\f'] = 0; + scanset[1+'\r'] = 0; + scanset[1+' '] = 0; + } + } else { + if (*++p == '^') p++, invert = 1; + else invert = 0; + memset(scanset, invert, sizeof scanset); + scanset[0] = 0; + if (*p == '-') p++, scanset[1+'-'] = 1-invert; + else if (*p == ']') p++, scanset[1+']'] = 1-invert; + for (; *p != ']'; p++) { + if (!*p) goto fmt_fail; + if (*p=='-' && p[1] && p[1] != ']') + for (c=p++[-1]; c<*p; c++) + scanset[1+c] = 1-invert; + scanset[1+*p] = 1-invert; + } + } + wcs = 0; + s = 0; + i = 0; + k = t=='c' ? width+1U : 31; + if (size == SIZE_l) { + if (alloc) { + wcs = malloc(k*sizeof(wchar_t)); + if (!wcs) goto alloc_fail; + } else { + wcs = dest; + } + st = (mbstate_t){0}; + while (scanset[(c=shgetc(f))+1]) { + switch (mbrtowc(&wc, &(char){c}, 1, &st)) { + case -1: + goto input_fail; + case -2: + continue; + } + if (wcs) wcs[i++] = wc; + if (alloc && i==k) { + k+=k+1; + wchar_t *tmp = realloc(wcs, k*sizeof(wchar_t)); + if (!tmp) goto alloc_fail; + wcs = tmp; + } + } + if (!mbsinit(&st)) goto input_fail; + } else if (alloc) { + s = malloc(k); + if (!s) goto alloc_fail; + while (scanset[(c=shgetc(f))+1]) { + s[i++] = c; + if (i==k) { + k+=k+1; + char *tmp = realloc(s, k); + if (!tmp) goto alloc_fail; + s = tmp; + } + } + } else if ((s = dest)) { + while (scanset[(c=shgetc(f))+1]) + s[i++] = c; + } else { + while (scanset[(c=shgetc(f))+1]); + } + shunget(f); + if (!shcnt(f)) goto match_fail; + if (t == 'c' && shcnt(f) != width) goto match_fail; + if (alloc) { + if (size == SIZE_l) *(wchar_t **)dest = wcs; + else *(char **)dest = s; + } + if (t != 'c') { + if (wcs) wcs[i] = 0; + if (s) s[i] = 0; + } + break; + case 'p': + case 'X': + case 'x': + base = 16; + goto int_common; + case 'o': + base = 8; + goto int_common; + case 'd': + case 'u': + base = 10; + goto int_common; + case 'i': + base = 0; + int_common: + x = __intscan(f, base, 0, ULLONG_MAX); + if (!shcnt(f)) goto match_fail; + if (t=='p' && dest) *(void **)dest = (void *)(uintptr_t)x; + else store_int(dest, size, x); + break; + case 'a': case 'A': + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + y = __floatscan(f, size, 0); + if (!shcnt(f)) goto match_fail; + if (dest) switch (size) { + case SIZE_def: + *(float *)dest = y; + break; + case SIZE_l: + *(double *)dest = y; + break; + case SIZE_L: + *(long double *)dest = y; + break; + } + break; + } + + pos += shcnt(f); + if (dest) matches++; + } + if (0) { +fmt_fail: +alloc_fail: +input_fail: + if (!matches) matches--; +match_fail: + if (alloc) { + free(s); + free(wcs); + } + } + FUNLOCK(f); + return matches; +} diff --git a/system/lib/libc/musl/src/stdio/vsnprintf.c b/system/lib/libc/musl/src/stdio/vsnprintf.c new file mode 100644 index 00000000..9dcc84af --- /dev/null +++ b/system/lib/libc/musl/src/stdio/vsnprintf.c @@ -0,0 +1,45 @@ +#include "stdio_impl.h" +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <stdint.h> + +static size_t sn_write(FILE *f, const unsigned char *s, size_t l) +{ + size_t k = f->wend - f->wpos; + if (k > l) k = l; + memcpy(f->wpos, s, k); + f->wpos += k; + /* pretend to succeed, but discard extra data */ + return l; +} + +// XXX Emscripten Call to musl-specific vfprintf for better asm.js performance, instead of the handwritten js function. +#define vfprintf MUSL_vfprintf + +int vsnprintf(char *restrict s, size_t n, const char *restrict fmt, va_list ap) +{ + int r; + char b; + FILE f = { .lbf = EOF, .write = sn_write, .lock = -1 }; + + if (n-1 > INT_MAX-1) { + if (n) { + errno = EOVERFLOW; + return -1; + } + s = &b; + n = 1; + } + + /* Ensure pointers don't wrap if "infinite" n is passed in */ + if (n > (char *)0+SIZE_MAX-s-1) n = (char *)0+SIZE_MAX-s-1; + f.buf_size = n; + f.buf = f.wpos = (void *)s; + f.wbase = f.wend = (void *)(s+n); + r = vfprintf(&f, fmt, ap); + + /* Null-terminate, overwriting last char if dest buffer is full */ + if (n) f.wpos[-(f.wpos == f.wend)] = 0; + return r; +} diff --git a/system/lib/libc/musl/src/stdio/vsprintf.c b/system/lib/libc/musl/src/stdio/vsprintf.c new file mode 100644 index 00000000..c57349d4 --- /dev/null +++ b/system/lib/libc/musl/src/stdio/vsprintf.c @@ -0,0 +1,7 @@ +#include <stdio.h> +#include <limits.h> + +int vsprintf(char *restrict s, const char *restrict fmt, va_list ap) +{ + return vsnprintf(s, INT_MAX, fmt, ap); +} diff --git a/system/lib/libc/musl/src/stdio/vsscanf.c b/system/lib/libc/musl/src/stdio/vsscanf.c new file mode 100644 index 00000000..6492f78b --- /dev/null +++ b/system/lib/libc/musl/src/stdio/vsscanf.c @@ -0,0 +1,20 @@ +#include "stdio_impl.h" +#include "libc.h" + +static size_t do_read(FILE *f, unsigned char *buf, size_t len) +{ + return __string_read(f, buf, len); +} + +#define vfscanf MUSL_vfscanf // XXX Emscripten: Call into musl version of vfscanf for asm.js performance, not the handwritten js version. + +int vsscanf(const char *restrict s, const char *restrict fmt, va_list ap) +{ + FILE f = { + .buf = (void *)s, .cookie = (void *)s, + .read = do_read, .lock = -1 + }; + return vfscanf(&f, fmt, ap); +} + +weak_alias(vsscanf,__isoc99_vsscanf); diff --git a/system/lib/libcextra.symbols b/system/lib/libcextra.symbols index 17e524e6..1ab849bd 100644 --- a/system/lib/libcextra.symbols +++ b/system/lib/libcextra.symbols @@ -21,6 +21,7 @@ T __wcscoll_l T __wcsxfrm_l W __wctype_l + T asprintf T atoll T bcmp T bcopy @@ -113,7 +114,6 @@ T mbstowcs T mbtowc T memccpy - T memchr T memmem T mempcpy W memrchr @@ -131,6 +131,7 @@ T rindex T scalbnf D signgam + T sscanf T stpcpy T strcasecmp_l T strcasestr @@ -176,8 +177,11 @@ T towlower_l T towupper T towupper_l + T vasprintf T verr T verrx + T MUSL_vfscanf + T vsscanf T vfwprintf T vswprintf T vwarn @@ -187,7 +191,6 @@ T warnx T wcpcpy T wcpncpy - T wcrtomb T wcscasecmp T wcscasecmp_l T wcscat @@ -227,7 +230,6 @@ T wcsxfrm T wcsxfrm_l T wctob - T wctomb T wctrans T wctrans_l T wctype diff --git a/tests/core/test_sscanf.in b/tests/core/test_sscanf.in index 55a310c5..470aaf37 100644 --- a/tests/core/test_sscanf.in +++ b/tests/core/test_sscanf.in @@ -66,7 +66,8 @@ int main() { char buf1[100], buf2[100], buf3[100], buf4[100]; memset(buf4, 0, 100); - int numItems = sscanf("level=4:ref=3", "%255[^:=]=%255[^:]:%255[^=]=%255c", + + int numItems = sscanf("level=4:ref=3", "%255[^:=]=%255[^:]:%255[^=]=%c", buf1, buf2, buf3, buf4); printf("%d, %s, %s, %s, %s\n", numItems, buf1, buf2, buf3, buf4); diff --git a/tests/test_core.py b/tests/test_core.py index 64cb1657..bcb03830 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -524,47 +524,25 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co def test_cube2hash(self): # extra testing for various codegen modes - for x86 in [0, 1] if self.run_name == 'asm2' else [0]: - print 'x86', x86 - try: - old_x86 = os.environ.get('EMCC_LLVM_TARGET') or '' - if x86: - os.environ['EMCC_LLVM_TARGET'] = "i386-pc-linux-gnu" - try: - old_fastcomp = os.environ.get('EMCC_FAST_COMPILER') or '' - if x86: - os.environ['EMCC_FAST_COMPILER'] = "0" - try: - old_chunk_size = os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or '' - - for chunk_size in ['1', old_chunk_size]: # test splitting out each function to a chunk in emscripten.py (21 functions here) - print ' chunks', chunk_size - os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = chunk_size - - # A good test of i64 math - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2 C-style memory aliasing') - self.do_run('', 'Usage: hashstring <seed>', - libraries=self.get_library('cube2hash', ['cube2hash.bc'], configure=None, cache_name_extra=str(x86)), - includes=[path_from_root('tests', 'cube2hash')]) - - for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'), - ('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'), - ('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]: - self.do_run('', 'hash value: ' + output, [text], no_build=True) - finally: - os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = old_chunk_size - finally: - if x86: - if old_fastcomp: - os.environ['EMCC_FAST_COMPILER'] = old_fastcomp - else: - del os.environ['EMCC_FAST_COMPILER'] - finally: - if x86: - if old_x86: - os.environ['EMCC_LLVM_TARGET'] = old_x86 - else: - del os.environ['EMCC_LLVM_TARGET'] + try: + old_chunk_size = os.environ.get('EMSCRIPT_MAX_CHUNK_SIZE') or '' + + for chunk_size in ['1', old_chunk_size]: # test splitting out each function to a chunk in emscripten.py (21 functions here) + print ' chunks', chunk_size + os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = chunk_size + + # A good test of i64 math + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2 C-style memory aliasing') + self.do_run('', 'Usage: hashstring <seed>', + libraries=self.get_library('cube2hash', ['cube2hash.bc'], configure=None), + includes=[path_from_root('tests', 'cube2hash')]) + + for text, output in [('fleefl', '892BDB6FD3F62E863D63DA55851700FDE3ACF30204798CE9'), + ('fleefl2', 'AA2CC5F96FC9D540CA24FDAF1F71E2942753DB83E8A81B61'), + ('64bitisslow', '64D8470573635EC354FEE7B7F87C566FCAF1EFB491041670')]: + self.do_run('', 'hash value: ' + output, [text], no_build=True) + finally: + os.environ['EMSCRIPT_MAX_CHUNK_SIZE'] = old_chunk_size def test_unaligned(self): if Settings.QUANTUM_SIZE == 1: return self.skip('No meaning to unaligned addresses in q1') @@ -872,6 +850,7 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co self.do_run_from_file(src, output) def test_frexp(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sprintf.') test_path = path_from_root('tests', 'core', 'test_frexp') src, output = (test_path + s for s in ('.in', '.out')) @@ -973,7 +952,7 @@ class T(RunnerCore): # Short name, to make it more fun to use manually on the co self.do_run_from_file(src, output) def test_strings(self): - if Settings.USE_TYPED_ARRAYS != 2: return self.skip('musl libc needs ta2') + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_strings') src, output = (test_path + s for s in ('.in', '.out')) @@ -2226,6 +2205,7 @@ def process(filename): self.do_run(src, '*4,3,4*\n*6,4,6*') def test_varargs(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sprintf.') if Settings.QUANTUM_SIZE == 1: return self.skip('FIXME: Add support for this') if not self.is_emscripten_abi(): return self.skip('we do not support all varargs stuff without asmjs-unknown-emscripten') @@ -3336,6 +3316,7 @@ def process(filename): self.do_run(src, 'success', force_c=True, post_build=self.dlfcn_post_build) def test_dlfcn_stacks(self): + if Settings.USE_TYPED_ARRAYS != 2: return self.skip('snprintf needs ta2 to be able to bitcast int<->float') if not self.can_dlfcn(): return self.prep_dlfcn_lib() @@ -3826,6 +3807,7 @@ int main() self.do_run_from_file(src, output) def test_printf_more(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sprintf.') test_path = path_from_root('tests', 'core', 'test_printf_more') src, output = (test_path + s for s in ('.in', '.out')) @@ -3924,18 +3906,21 @@ Pass: 0.000012 0.000012 Pass: 0.000012 0.000012''') def test_sscanf_n(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_sscanf_n') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_sscanf_whitespace(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_sscanf_whitespace') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_sscanf_other_whitespace(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') Settings.SAFE_HEAP = 0 # use i16s in printf test_path = path_from_root('tests', 'core', 'test_sscanf_other_whitespace') @@ -3944,6 +3929,7 @@ Pass: 0.000012 0.000012''') self.do_run_from_file(src, output) def test_sscanf_3(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') # i64 if not Settings.USE_TYPED_ARRAYS == 2: return self.skip('64-bit sscanf only supported in ta2') @@ -3953,23 +3939,27 @@ Pass: 0.000012 0.000012''') self.do_run_from_file(src, output) def test_sscanf_4(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_sscanf_4') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_sscanf_5(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_sscanf_5') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_sscanf_6(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_sscanf_6') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_sscanf_skip(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') if Settings.USE_TYPED_ARRAYS != 2: return self.skip("need ta2 for full i64") test_path = path_from_root('tests', 'core', 'test_sscanf_skip') @@ -3978,12 +3968,14 @@ Pass: 0.000012 0.000012''') self.do_run_from_file(src, output) def test_sscanf_caps(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_sscanf_caps') src, output = (test_path + s for s in ('.in', '.out')) self.do_run_from_file(src, output) def test_sscanf_hex(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2') test_path = path_from_root('tests', 'core', 'test_sscanf_hex') @@ -3992,6 +3984,7 @@ Pass: 0.000012 0.000012''') self.do_run_from_file(src, output) def test_sscanf_float(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') test_path = path_from_root('tests', 'core', 'test_sscanf_float') src, output = (test_path + s for s in ('.in', '.out')) @@ -4003,6 +3996,7 @@ Pass: 0.000012 0.000012''') self.do_run(src, expected, extra_emscripten_args=['-H', 'libc/langinfo.h']) def test_files(self): + if self.run_name.startswith('s_'): return self.skip('This test requires linking to musl lib for sscanf.') self.banned_js_engines = [SPIDERMONKEY_ENGINE] # closure can generate variables called 'gc', which pick up js shell stuff if self.emcc_args is not None and '-O2' in self.emcc_args: self.emcc_args += ['--closure', '1'] # Use closure here, to test we don't break FS stuff @@ -5302,8 +5296,10 @@ def process(filename): emcc_args = self.emcc_args + # The following tests link to libc, and must be run with EMCC_LEAVE_INPUTS_RAW = 0 + need_no_leave_inputs_raw = ['muli33_ta2', 'philoop_ta2'] + try: - os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' Settings.CHECK_OVERFLOWS = 0 for name in glob.glob(path_from_root('tests', 'cases', '*.ll')): @@ -5317,6 +5313,15 @@ def process(filename): 'atomicrmw_unaligned', # TODO XXX 'emptyasm_aue' # we don't support inline asm ]: continue + + if os.path.basename(shortname) in need_no_leave_inputs_raw: + if self.run_name.startswith('s_'): + print self.skip('case "%s" cannot be run in mode %s, since it would require EMCC_LEAVE_INPUTS_RAW=1' % (shortname, self.run_name)) + continue + if 'EMCC_LEAVE_INPUTS_RAW' in os.environ: del os.environ['EMCC_LEAVE_INPUTS_RAW'] + else: + os.environ['EMCC_LEAVE_INPUTS_RAW'] = '1' + if '_ta2' in shortname and not Settings.USE_TYPED_ARRAYS == 2: print self.skip('case "%s" only relevant for ta2' % shortname) continue @@ -5352,7 +5357,7 @@ def process(filename): exec(open(src_checker).read()) finally: - del os.environ['EMCC_LEAVE_INPUTS_RAW'] + if 'EMCC_LEAVE_INPUTS_RAW' in os.environ: del os.environ['EMCC_LEAVE_INPUTS_RAW'] self.emcc_args = emcc_args def test_fuzz(self): diff --git a/tools/system_libs.py b/tools/system_libs.py index bc81a351..78bf2d48 100644 --- a/tools/system_libs.py +++ b/tools/system_libs.py @@ -67,14 +67,27 @@ def calculate(temp_files, in_temp, stdout, stderr): 'shgetc.c', ]], ['math', [ + 'frexp.c', + 'frexpf.c', + 'frexpl.c', 'scalbn.c', 'scalbnl.c', ]], + ['multibyte', [ + 'wctomb.c', + 'wcrtomb.c', + ]], ['stdio', [ '__overflow.c', '__toread.c', '__towrite.c', '__uflow.c', + 'fwrite.c', + 'snprintf.c', + 'sprintf.c', + 'vfprintf.c', + 'vsnprintf.c', + 'vsprintf.c', ]], ['stdlib', [ 'atof.c', @@ -84,6 +97,7 @@ def calculate(temp_files, in_temp, stdout, stderr): 'strtol.c', ]], ['string', [ + 'memchr.c', 'memcmp.c', 'strcasecmp.c', 'strcmp.c', @@ -234,12 +248,10 @@ def calculate(temp_files, in_temp, stdout, stderr): 'mbsrtowcs.c', 'mbstowcs.c', 'mbtowc.c', - 'wcrtomb.c', 'wcsnrtombs.c', 'wcsrtombs.c', 'wcstombs.c', 'wctob.c', - 'wctomb.c', ]], ['regex', [ 'fnmatch.c', @@ -249,6 +261,8 @@ def calculate(temp_files, in_temp, stdout, stderr): 'tre-mem.c', ]], ['stdio', [ + '__string_read.c', + 'asprintf.c', 'fwprintf.c', 'swprintf.c', 'vfwprintf.c', @@ -257,6 +271,10 @@ def calculate(temp_files, in_temp, stdout, stderr): 'wprintf.c', 'fputwc.c', 'fputws.c', + 'sscanf.c', + 'vasprintf.c', + 'vfscanf.c', + 'vsscanf.c', ]], ['stdlib', [ 'atoll.c', @@ -276,7 +294,6 @@ def calculate(temp_files, in_temp, stdout, stderr): 'memccpy.c', 'memmem.c', 'mempcpy.c', - 'memchr.c', 'memrchr.c', 'rindex.c', 'stpcpy.c', |