//"use strict";
// An implementation of a libc for the web. Basically, implementations of
// the various standard C libraries, that can be called from compiled code,
// and work using the actual JavaScript environment.
//
// We search the Library object when there is an external function. If the
// entry in the Library is a function, we insert it. If it is a string, we
// do another lookup in the library (a simple way to write a function once,
// if it can be called by different names). We also allow dependencies,
// using __deps. Initialization code to be run after allocating all
// global constants can be defined by __postset.
//
// Note that the full function name will be '_' + the name in the Library
// object. For convenience, the short name appears here. Note that if you add a
// new function with an '_', it will not be found.
// Memory allocated during startup, in postsets, should only be ALLOC_STATIC
LibraryManager.library = {
// keep this low in memory, because we flatten arrays with them in them
#if USE_PTHREADS
stdin: '; if (ENVIRONMENT_IS_PTHREAD) _stdin = PthreadWorkerInit._stdin; else PthreadWorkerInit._stdin = _stdin = allocate(1, "i32*", ALLOC_STATIC)',
stdout: '; if (ENVIRONMENT_IS_PTHREAD) _stdout = PthreadWorkerInit._stdout; else PthreadWorkerInit._stdout = _stdout = allocate(1, "i32*", ALLOC_STATIC)',
stderr: '; if (ENVIRONMENT_IS_PTHREAD) _stderr = PthreadWorkerInit._stderr; else PthreadWorkerInit._stderr = _stderr = allocate(1, "i32*", ALLOC_STATIC)',
_impure_ptr: '; if (ENVIRONMENT_IS_PTHREAD) __impure_ptr = PthreadWorkerInit.__impure_ptr; else PthreadWorkerInit.__impure_ptr __impure_ptr = allocate(1, "i32*", ALLOC_STATIC)',
__dso_handle: '; if (ENVIRONMENT_IS_PTHREAD) ___dso_handle = PthreadWorkerInit.___dso_handle; else PthreadWorkerInit.___dso_handle = ___dso_handle = allocate(1, "i32*", ALLOC_STATIC)',
#else
stdin: 'allocate(1, "i32*", ALLOC_STATIC)',
stdout: 'allocate(1, "i32*", ALLOC_STATIC)',
stderr: 'allocate(1, "i32*", ALLOC_STATIC)',
_impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)',
__dso_handle: 'allocate(1, "i32*", ALLOC_STATIC)',
#endif
$PROCINFO: {
// permissions
/*
uid: 0,
gid: 0,
euid: 0,
egid: 0,
suid: 0,
sgid: 0,
fsuid: 0,
fsgid: 0,
*/
// process identification
ppid: 1,
pid: 42,
sid: 42,
pgid: 42
},
// ==========================================================================
// utime.h
// ==========================================================================
utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
utime: function(path, times) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_UTIME') }}}, path, times);
#endif
// int utime(const char *path, const struct utimbuf *times);
// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/utime.h.html
var time;
if (times) {
// NOTE: We don't keep track of access timestamps.
var offset = {{{ C_STRUCTS.utimbuf.modtime }}};
time = {{{ makeGetValue('times', 'offset', 'i32') }}};
time *= 1000;
} else {
time = Date.now();
}
path = Pointer_stringify(path);
try {
FS.utime(path, time, time);
return 0;
} catch (e) {
FS.handleFSError(e);
return -1;
}
},
utimes__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
utimes: function(path, times) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_UTIMES') }}}, path, times);
#endif
var time;
if (times) {
var offset = {{{ C_STRUCTS.timeval.__size__ }}} + {{{ C_STRUCTS.timeval.tv_sec }}};
time = {{{ makeGetValue('times', 'offset', 'i32') }}} * 1000;
offset = {{{ C_STRUCTS.timeval.__size__ }}} + {{{ C_STRUCTS.timeval.tv_usec }}};
time += {{{ makeGetValue('times', 'offset', 'i32') }}} / 1000;
} else {
time = Date.now();
}
path = Pointer_stringify(path);
try {
FS.utime(path, time, time);
return 0;
} catch (e) {
FS.handleFSError(e);
return -1;
}
},
// ==========================================================================
// sys/file.h
// ==========================================================================
flock: function(fd, operation) {
// int flock(int fd, int operation);
// Pretend to succeed
return 0;
},
chroot__deps: ['__setErrNo', '$ERRNO_CODES'],
chroot: function(path) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CHROOT') }}}, path);
#endif
// int chroot(const char *path);
// http://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html
___setErrNo(ERRNO_CODES.EACCES);
return -1;
},
fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'],
fpathconf: function(fildes, name) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_FPATHCONF') }}}, fildes, name);
#endif
// long fpathconf(int fildes, int name);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html
// NOTE: The first parameter is ignored, so pathconf == fpathconf.
// The constants here aren't real values. Just mimicking glibc.
switch (name) {
case {{{ cDefine('_PC_LINK_MAX') }}}:
return 32000;
case {{{ cDefine('_PC_MAX_CANON') }}}:
case {{{ cDefine('_PC_MAX_INPUT') }}}:
case {{{ cDefine('_PC_NAME_MAX') }}}:
return 255;
case {{{ cDefine('_PC_PATH_MAX') }}}:
case {{{ cDefine('_PC_PIPE_BUF') }}}:
case {{{ cDefine('_PC_REC_MIN_XFER_SIZE') }}}:
case {{{ cDefine('_PC_REC_XFER_ALIGN') }}}:
case {{{ cDefine('_PC_ALLOC_SIZE_MIN') }}}:
return 4096;
case {{{ cDefine('_PC_CHOWN_RESTRICTED') }}}:
case {{{ cDefine('_PC_NO_TRUNC') }}}:
case {{{ cDefine('_PC_2_SYMLINKS') }}}:
return 1;
case {{{ cDefine('_PC_VDISABLE') }}}:
return 0;
case {{{ cDefine('_PC_SYNC_IO') }}}:
case {{{ cDefine('_PC_ASYNC_IO') }}}:
case {{{ cDefine('_PC_PRIO_IO') }}}:
case {{{ cDefine('_PC_SOCK_MAXBUF') }}}:
case {{{ cDefine('_PC_REC_INCR_XFER_SIZE') }}}:
case {{{ cDefine('_PC_REC_MAX_XFER_SIZE') }}}:
case {{{ cDefine('_PC_SYMLINK_MAX') }}}:
return -1;
case {{{ cDefine('_PC_FILESIZEBITS') }}}:
return 64;
}
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
},
pathconf: 'fpathconf',
confstr__deps: ['__setErrNo', '$ERRNO_CODES', '$ENV'],
confstr: function(name, buf, len) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_CONFSTR') }}}, name, buf, len);
#endif
// size_t confstr(int name, char *buf, size_t len);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/confstr.html
var value;
switch (name) {
case {{{ cDefine('_CS_PATH') }}}:
value = ENV['PATH'] || '/';
break;
case {{{ cDefine('_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS') }}}:
// Mimicking glibc.
value = 'POSIX_V6_ILP32_OFF32\nPOSIX_V6_ILP32_OFFBIG';
break;
case {{{ cDefine('_CS_GNU_LIBC_VERSION') }}}:
// This JS implementation was tested against this glibc version.
value = 'glibc 2.14';
break;
case {{{ cDefine('_CS_GNU_LIBPTHREAD_VERSION') }}}:
// We don't support pthreads.
value = '';
break;
case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LIBS') }}}:
case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LIBS') }}}:
case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_CFLAGS') }}}:
case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LDFLAGS') }}}:
case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LIBS') }}}:
case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS') }}}:
case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS') }}}:
case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LIBS') }}}:
value = '';
break;
case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_CFLAGS') }}}:
case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LDFLAGS') }}}:
case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS') }}}:
value = '-m32';
break;
case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS') }}}:
value = '-m32 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64';
break;
default:
___setErrNo(ERRNO_CODES.EINVAL);
return 0;
}
if (len == 0 || buf == 0) {
return value.length + 1;
} else {
var length = Math.min(len, value.length);
for (var i = 0; i < length; i++) {
{{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}};
}
if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}};
return i;
}
},
execl__deps: ['__setErrNo', '$ERRNO_CODES'],
execl: function(/* ... */) {
// int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
// We don't support executing external code.
___setErrNo(ERRNO_CODES.ENOEXEC);
return -1;
},
execle: 'execl',
execlp: 'execl',
execv: 'execl',
execve: 'execl',
execvp: 'execl',
_exit: function(status) {
// void _exit(int status);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html
Module['exit'](status);
},
fork__deps: ['__setErrNo', '$ERRNO_CODES'],
fork: function() {
// pid_t fork(void);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html
// We don't support multiple processes.
___setErrNo(ERRNO_CODES.EAGAIN);
return -1;
},
vfork: 'fork',
setgroups__deps: ['__setErrNo', '$ERRNO_CODES', 'sysconf'],
setgroups: function(ngroups, gidset) {
// int setgroups(int ngroups, const gid_t *gidset);
// https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/setgroups.2.html
if (ngroups < 1 || ngroups > _sysconf({{{ cDefine('_SC_NGROUPS_MAX') }}})) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
} else {
// We have just one process/user/group, so it makes no sense to set groups.
___setErrNo(ERRNO_CODES.EPERM);
return -1;
}
},
getpagesize: function() {
// int getpagesize(void);
return PAGE_SIZE;
},
sysconf__deps: ['__setErrNo', '$ERRNO_CODES'],
sysconf: function(name) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_SYSCONF') }}}, name);
#endif
// long sysconf(int name);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/sysconf.html
switch(name) {
case {{{ cDefine('_SC_PAGE_SIZE') }}}: return PAGE_SIZE;
case {{{ cDefine('_SC_PHYS_PAGES') }}}: return totalMemory / PAGE_SIZE;
case {{{ cDefine('_SC_ADVISORY_INFO') }}}:
case {{{ cDefine('_SC_BARRIERS') }}}:
case {{{ cDefine('_SC_ASYNCHRONOUS_IO') }}}:
case {{{ cDefine('_SC_CLOCK_SELECTION') }}}:
case {{{ cDefine('_SC_CPUTIME') }}}:
case {{{ cDefine('_SC_FSYNC') }}}:
case {{{ cDefine('_SC_IPV6') }}}:
case {{{ cDefine('_SC_MAPPED_FILES') }}}:
case {{{ cDefine('_SC_MEMLOCK') }}}:
case {{{ cDefine('_SC_MEMLOCK_RANGE') }}}:
case {{{ cDefine('_SC_MEMORY_PROTECTION') }}}:
case {{{ cDefine('_SC_MESSAGE_PASSING') }}}:
case {{{ cDefine('_SC_MONOTONIC_CLOCK') }}}:
case {{{ cDefine('_SC_PRIORITIZED_IO') }}}:
case {{{ cDefine('_SC_PRIORITY_SCHEDULING') }}}:
case {{{ cDefine('_SC_RAW_SOCKETS') }}}:
case {{{ cDefine('_SC_READER_WRITER_LOCKS') }}}:
case {{{ cDefine('_SC_REALTIME_SIGNALS') }}}:
case {{{ cDefine('_SC_SEMAPHORES') }}}:
case {{{ cDefine('_SC_SHARED_MEMORY_OBJECTS') }}}:
case {{{ cDefine('_SC_SPAWN') }}}:
case {{{ cDefine('_SC_SPIN_LOCKS') }}}:
case {{{ cDefine('_SC_SYNCHRONIZED_IO') }}}:
case {{{ cDefine('_SC_THREAD_ATTR_STACKADDR') }}}:
case {{{ cDefine('_SC_THREAD_ATTR_STACKSIZE') }}}:
case {{{ cDefine('_SC_THREAD_CPUTIME') }}}:
case {{{ cDefine('_SC_THREAD_PRIO_INHERIT') }}}:
case {{{ cDefine('_SC_THREAD_PRIO_PROTECT') }}}:
case {{{ cDefine('_SC_THREAD_PROCESS_SHARED') }}}:
case {{{ cDefine('_SC_THREAD_SAFE_FUNCTIONS') }}}:
case {{{ cDefine('_SC_THREADS') }}}:
case {{{ cDefine('_SC_TIMEOUTS') }}}:
case {{{ cDefine('_SC_TIMERS') }}}:
case {{{ cDefine('_SC_VERSION') }}}:
case {{{ cDefine('_SC_2_C_BIND') }}}:
case {{{ cDefine('_SC_2_C_DEV') }}}:
case {{{ cDefine('_SC_2_CHAR_TERM') }}}:
case {{{ cDefine('_SC_2_LOCALEDEF') }}}:
case {{{ cDefine('_SC_2_SW_DEV') }}}:
case {{{ cDefine('_SC_2_VERSION') }}}:
return 200809;
case {{{ cDefine('_SC_THREAD_PRIORITY_SCHEDULING') }}}:
return 0;
case {{{ cDefine('_SC_MQ_OPEN_MAX') }}}:
case {{{ cDefine('_SC_XOPEN_STREAMS') }}}:
case {{{ cDefine('_SC_XBS5_LP64_OFF64') }}}:
case {{{ cDefine('_SC_XBS5_LPBIG_OFFBIG') }}}:
case {{{ cDefine('_SC_AIO_LISTIO_MAX') }}}:
case {{{ cDefine('_SC_AIO_MAX') }}}:
case {{{ cDefine('_SC_SPORADIC_SERVER') }}}:
case {{{ cDefine('_SC_THREAD_SPORADIC_SERVER') }}}:
case {{{ cDefine('_SC_TRACE') }}}:
case {{{ cDefine('_SC_TRACE_EVENT_FILTER') }}}:
case {{{ cDefine('_SC_TRACE_EVENT_NAME_MAX') }}}:
case {{{ cDefine('_SC_TRACE_INHERIT') }}}:
case {{{ cDefine('_SC_TRACE_LOG') }}}:
case {{{ cDefine('_SC_TRACE_NAME_MAX') }}}:
case {{{ cDefine('_SC_TRACE_SYS_MAX') }}}:
case {{{ cDefine('_SC_TRACE_USER_EVENT_MAX') }}}:
case {{{ cDefine('_SC_TYPED_MEMORY_OBJECTS') }}}:
case {{{ cDefine('_SC_V6_LP64_OFF64') }}}:
case {{{ cDefine('_SC_V6_LPBIG_OFFBIG') }}}:
case {{{ cDefine('_SC_2_FORT_DEV') }}}:
case {{{ cDefine('_SC_2_FORT_RUN') }}}:
case {{{ cDefine('_SC_2_PBS') }}}:
case {{{ cDefine('_SC_2_PBS_ACCOUNTING') }}}:
case {{{ cDefine('_SC_2_PBS_CHECKPOINT') }}}:
case {{{ cDefine('_SC_2_PBS_LOCATE') }}}:
case {{{ cDefine('_SC_2_PBS_MESSAGE') }}}:
case {{{ cDefine('_SC_2_PBS_TRACK') }}}:
case {{{ cDefine('_SC_2_UPE') }}}:
case {{{ cDefine('_SC_THREAD_THREADS_MAX') }}}:
case {{{ cDefine('_SC_SEM_NSEMS_MAX') }}}:
case {{{ cDefine('_SC_SYMLOOP_MAX') }}}:
case {{{ cDefine('_SC_TIMER_MAX') }}}:
return -1;
case {{{ cDefine('_SC_V6_ILP32_OFF32') }}}:
case {{{ cDefine('_SC_V6_ILP32_OFFBIG') }}}:
case {{{ cDefine('_SC_JOB_CONTROL') }}}:
case {{{ cDefine('_SC_REGEXP') }}}:
case {{{ cDefine('_SC_SAVED_IDS') }}}:
case {{{ cDefine('_SC_SHELL') }}}:
case {{{ cDefine('_SC_XBS5_ILP32_OFF32') }}}:
case {{{ cDefine('_SC_XBS5_ILP32_OFFBIG') }}}:
case {{{ cDefine('_SC_XOPEN_CRYPT') }}}:
case {{{ cDefine('_SC_XOPEN_ENH_I18N') }}}:
case {{{ cDefine('_SC_XOPEN_LEGACY') }}}:
case {{{ cDefine('_SC_XOPEN_REALTIME') }}}:
case {{{ cDefine('_SC_XOPEN_REALTIME_THREADS') }}}:
case {{{ cDefine('_SC_XOPEN_SHM') }}}:
case {{{ cDefine('_SC_XOPEN_UNIX') }}}:
return 1;
case {{{ cDefine('_SC_THREAD_KEYS_MAX') }}}:
case {{{ cDefine('_SC_IOV_MAX') }}}:
case {{{ cDefine('_SC_GETGR_R_SIZE_MAX') }}}:
case {{{ cDefine('_SC_GETPW_R_SIZE_MAX') }}}:
case {{{ cDefine('_SC_OPEN_MAX') }}}:
return 1024;
case {{{ cDefine('_SC_RTSIG_MAX') }}}:
case {{{ cDefine('_SC_EXPR_NEST_MAX') }}}:
case {{{ cDefine('_SC_TTY_NAME_MAX') }}}:
return 32;
case {{{ cDefine('_SC_ATEXIT_MAX') }}}:
case {{{ cDefine('_SC_DELAYTIMER_MAX') }}}:
case {{{ cDefine('_SC_SEM_VALUE_MAX') }}}:
return 2147483647;
case {{{ cDefine('_SC_SIGQUEUE_MAX') }}}:
case {{{ cDefine('_SC_CHILD_MAX') }}}:
return 47839;
case {{{ cDefine('_SC_BC_SCALE_MAX') }}}:
case {{{ cDefine('_SC_BC_BASE_MAX') }}}:
return 99;
case {{{ cDefine('_SC_LINE_MAX') }}}:
case {{{ cDefine('_SC_BC_DIM_MAX') }}}:
return 2048;
case {{{ cDefine('_SC_ARG_MAX') }}}: return 2097152;
case {{{ cDefine('_SC_NGROUPS_MAX') }}}: return 65536;
case {{{ cDefine('_SC_MQ_PRIO_MAX') }}}: return 32768;
case {{{ cDefine('_SC_RE_DUP_MAX') }}}: return 32767;
case {{{ cDefine('_SC_THREAD_STACK_MIN') }}}: return 16384;
case {{{ cDefine('_SC_BC_STRING_MAX') }}}: return 1000;
case {{{ cDefine('_SC_XOPEN_VERSION') }}}: return 700;
case {{{ cDefine('_SC_LOGIN_NAME_MAX') }}}: return 256;
case {{{ cDefine('_SC_COLL_WEIGHTS_MAX') }}}: return 255;
case {{{ cDefine('_SC_CLK_TCK') }}}: return 100;
case {{{ cDefine('_SC_HOST_NAME_MAX') }}}: return 64;
case {{{ cDefine('_SC_AIO_PRIO_DELTA_MAX') }}}: return 20;
case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16;
case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6;
case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4;
case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: {
if (typeof navigator === 'object') return navigator['hardwareConcurrency'] || 1;
return 1;
}
}
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
},
sbrk: function(bytes) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_SBRK') }}}, bytes);
#endif
// Implement a Linux-like 'memory area' for our 'process'.
// Changes the size of the memory area by |bytes|; returns the
// address of the previous top ('break') of the memory area
// We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP
var self = _sbrk;
if (!self.called) {
DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned
self.called = true;
assert(Runtime.dynamicAlloc);
self.alloc = Runtime.dynamicAlloc;
Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') };
}
var ret = DYNAMICTOP;
if (bytes != 0) {
var success = self.alloc(bytes);
if (!success) return -1 >>> 0; // sbrk failure code
}
return ret; // Previous break location.
},
system__deps: ['__setErrNo', '$ERRNO_CODES'],
system: function(command) {
// int system(const char *command);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html
// Can't call external programs.
___setErrNo(ERRNO_CODES.EAGAIN);
return -1;
},
// ==========================================================================
// stdlib.h
// ==========================================================================
// tiny, fake malloc/free implementation. If the program actually uses malloc,
// a compiled version will be used; this will only be used if the runtime
// needs to allocate something, for which this is good enough if otherwise
// no malloc is needed.
malloc: function(bytes) {
/* Over-allocate to make sure it is byte-aligned by 8.
* This will leak memory, but this is only the dummy
* implementation (replaced by dlmalloc normally) so
* not an issue.
*/
#if ASSERTIONS == 2
Runtime.warnOnce('using stub malloc (reference it from C to have the real one included)');
#endif
var ptr = Runtime.dynamicAlloc(bytes + 8);
return (ptr+8) & 0xFFFFFFF8;
},
free: function() {
#if ASSERTIONS == 2
Runtime.warnOnce('using stub free (reference it from C to have the real one included)');
#endif
},
abs: 'Math_abs',
labs: 'Math_abs',
exit__deps: ['_exit'],
exit: function(status) {
__exit(status);
},
_ZSt9terminatev__deps: ['exit'],
_ZSt9terminatev: function() {
_exit(-1234);
},
atexit: function(func, arg) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_2({{{ cDefine('EM_PROXIED_ATEXIT') }}}, func, arg);
#endif
__ATEXIT__.unshift({ func: func, arg: arg });
},
__cxa_atexit: 'atexit',
abort: function() {
Module['abort']();
},
environ__deps: ['$ENV'],
#if USE_PTHREADS
environ: '; if (ENVIRONMENT_IS_PTHREAD) _environ = PthreadWorkerInit._environ; else PthreadWorkerInit._environ = _environ = allocate(1, "i32*", ALLOC_STATIC)',
#else
environ: 'allocate(1, "i32*", ALLOC_STATIC)',
#endif
__environ__deps: ['environ'],
__environ: 'environ',
__buildEnvironment__deps: ['__environ'],
__buildEnvironment: function(env) {
// WARNING: Arbitrary limit!
var MAX_ENV_VALUES = 64;
var TOTAL_ENV_SIZE = 1024;
// Statically allocate memory for the environment.
var poolPtr;
var envPtr;
if (!___buildEnvironment.called) {
___buildEnvironment.called = true;
// Set default values. Use string keys for Closure Compiler compatibility.
ENV['USER'] = ENV['LOGNAME'] = 'web_user';
ENV['PATH'] = '/';
ENV['PWD'] = '/';
ENV['HOME'] = '/home/web_user';
ENV['LANG'] = 'C';
ENV['_'] = Module['thisProgram'];
// Allocate memory.
poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC);
envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}},
'i8*', ALLOC_STATIC);
{{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}};
{{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}};
} else {
envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}};
poolPtr = {{{ makeGetValue('envPtr', '0', 'i8*') }}};
}
// Collect key=value lines.
var strings = [];
var totalSize = 0;
for (var key in env) {
if (typeof env[key] === 'string') {
var line = key + '=' + env[key];
strings.push(line);
totalSize += line.length;
}
}
if (totalSize > TOTAL_ENV_SIZE) {
throw new Error('Environment size exceeded TOTAL_ENV_SIZE!');
}
// Make new.
var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}};
for (var i = 0; i < strings.length; i++) {
var line = strings[i];
writeAsciiToMemory(line, poolPtr);
{{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}};
poolPtr += line.length + 1;
}
{{{ makeSetValue('envPtr', 'strings.length * ptrSize', '0', 'i8*') }}};
},
$ENV__deps: ['__buildEnvironment'],
#if USE_PTHREADS
$ENV__postset: 'if (!ENVIRONMENT_IS_PTHREAD) ___buildEnvironment(ENV);',
#else
$ENV__postset: '___buildEnvironment(ENV);',
#endif
$ENV: {},
getenv__deps: ['$ENV'],
getenv: function(name) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_GETENV') }}}, name);
#endif
// char *getenv(const char *name);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/getenv.html
if (name === 0) return 0;
name = Pointer_stringify(name);
if (!ENV.hasOwnProperty(name)) return 0;
if (_getenv.ret) _free(_getenv.ret);
_getenv.ret = allocate(intArrayFromString(ENV[name]), 'i8', ALLOC_NORMAL);
return _getenv.ret;
},
clearenv__deps: ['$ENV', '__buildEnvironment'],
clearenv: function(name) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_CLEARENV') }}}, name);
#endif
// int clearenv (void);
// http://www.gnu.org/s/hello/manual/libc/Environment-Access.html#index-clearenv-3107
ENV = {};
___buildEnvironment(ENV);
return 0;
},
setenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'],
setenv: function(envname, envval, overwrite) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_3({{{ cDefine('EM_PROXIED_SETENV') }}}, envname, envval, overwrite);
#endif
// int setenv(const char *envname, const char *envval, int overwrite);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/setenv.html
if (envname === 0) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
}
var name = Pointer_stringify(envname);
var val = Pointer_stringify(envval);
if (name === '' || name.indexOf('=') !== -1) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
}
if (ENV.hasOwnProperty(name) && !overwrite) return 0;
ENV[name] = val;
___buildEnvironment(ENV);
return 0;
},
unsetenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'],
unsetenv: function(name) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_UNSETENV') }}}, name);
#endif
// int unsetenv(const char *name);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/unsetenv.html
if (name === 0) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
}
name = Pointer_stringify(name);
if (name === '' || name.indexOf('=') !== -1) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
}
if (ENV.hasOwnProperty(name)) {
delete ENV[name];
___buildEnvironment(ENV);
}
return 0;
},
putenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'],
putenv: function(string) {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_1({{{ cDefine('EM_PROXIED_PUTENV') }}}, string);
#endif
// int putenv(char *string);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html
// WARNING: According to the standard (and the glibc implementation), the
// string is taken by reference so future changes are reflected.
// We copy it instead, possibly breaking some uses.
if (string === 0) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
}
string = Pointer_stringify(string);
var splitPoint = string.indexOf('=')
if (string === '' || string.indexOf('=') === -1) {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
}
var name = string.slice(0, splitPoint);
var value = string.slice(splitPoint + 1);
if (!(name in ENV) || ENV[name] !== value) {
ENV[name] = value;
___buildEnvironment(ENV);
}
return 0;
},
getloadavg: function(loadavg, nelem) {
// int getloadavg(double loadavg[], int nelem);
// http://linux.die.net/man/3/getloadavg
var limit = Math.min(nelem, 3);
var doubleSize = {{{ Runtime.getNativeTypeSize('double') }}};
for (var i = 0; i < limit; i++) {
{{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}};
}
return limit;
},
// For compatibility, call to rand() when code requests arc4random(), although this is *not* at all
// as strong as rc4 is. See https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man3/arc4random.3.html
arc4random: 'rand',
// ==========================================================================
// string.h
// ==========================================================================
memcpy__inline: function(dest, src, num, align) {
var ret = '';
ret += makeCopyValues(dest, src, num, 'null', null, align);
return ret;
},
emscripten_memcpy_big: function(dest, src, num) {
HEAPU8.set(HEAPU8.subarray(src, src+num), dest);
return dest;
},
memcpy__asm: true,
memcpy__sig: 'iiii',
memcpy__deps: ['emscripten_memcpy_big'],
memcpy: function(dest, src, num) {
dest = dest|0; src = src|0; num = num|0;
var ret = 0;
if ((num|0) >= 4096) return _emscripten_memcpy_big(dest|0, src|0, num|0)|0;
ret = dest|0;
if ((dest&3) == (src&3)) {
while (dest & 3) {
if ((num|0) == 0) return ret|0;
{{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}};
dest = (dest+1)|0;
src = (src+1)|0;
num = (num-1)|0;
}
while ((num|0) >= 4) {
{{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i32'), 'i32') }}};
dest = (dest+4)|0;
src = (src+4)|0;
num = (num-4)|0;
}
}
while ((num|0) > 0) {
{{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}};
dest = (dest+1)|0;
src = (src+1)|0;
num = (num-1)|0;
}
return ret|0;
},
llvm_memcpy_i32: 'memcpy',
llvm_memcpy_i64: 'memcpy',
llvm_memcpy_p0i8_p0i8_i32: 'memcpy',
llvm_memcpy_p0i8_p0i8_i64: 'memcpy',
memmove__sig: 'iiii',
memmove__asm: true,
memmove__deps: ['memcpy'],
memmove: function(dest, src, num) {
dest = dest|0; src = src|0; num = num|0;
var ret = 0;
if (((src|0) < (dest|0)) & ((dest|0) < ((src + num)|0))) {
// Unlikely case: Copy backwards in a safe manner
ret = dest;
src = (src + num)|0;
dest = (dest + num)|0;
while ((num|0) > 0) {
dest = (dest - 1)|0;
src = (src - 1)|0;
num = (num - 1)|0;
{{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}};
}
dest = ret;
} else {
_memcpy(dest, src, num) | 0;
}
return dest | 0;
},
llvm_memmove_i32: 'memmove',
llvm_memmove_i64: 'memmove',
llvm_memmove_p0i8_p0i8_i32: 'memmove',
llvm_memmove_p0i8_p0i8_i64: 'memmove',
memset__inline: function(ptr, value, num, align) {
return makeSetValues(ptr, 0, value, 'null', num, align);
},
memset__sig: 'iiii',
memset__asm: true,
memset: function(ptr, value, num) {
ptr = ptr|0; value = value|0; num = num|0;
var stop = 0, value4 = 0, stop4 = 0, unaligned = 0;
stop = (ptr + num)|0;
if ((num|0) >= {{{ Math.round(2.5*UNROLL_LOOP_MAX) }}}) {
// This is unaligned, but quite large, so work hard to get to aligned settings
value = value & 0xff;
unaligned = ptr & 3;
value4 = value | (value << 8) | (value << 16) | (value << 24);
stop4 = stop & ~3;
if (unaligned) {
unaligned = (ptr + 4 - unaligned)|0;
while ((ptr|0) < (unaligned|0)) { // no need to check for stop, since we have large num
{{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}};
ptr = (ptr+1)|0;
}
}
while ((ptr|0) < (stop4|0)) {
{{{ makeSetValueAsm('ptr', 0, 'value4', 'i32') }}};
ptr = (ptr+4)|0;
}
}
while ((ptr|0) < (stop|0)) {
{{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}};
ptr = (ptr+1)|0;
}
return (ptr-num)|0;
},
llvm_memset_i32: 'memset',
llvm_memset_p0i8_i32: 'memset',
llvm_memset_p0i8_i64: 'memset',
// ==========================================================================
// GCC/LLVM specifics
// ==========================================================================
__builtin_prefetch: function(){},
// ==========================================================================
// LLVM specifics
// ==========================================================================
llvm_va_start__inline: function(ptr) {
// varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp'
// 2-word structure: struct { void* start; void* currentOffset; }
return makeSetValue(ptr, 0, 'varrp', 'void*') + ';' + makeSetValue(ptr, Runtime.QUANTUM_SIZE, 0, 'void*');
},
llvm_va_end: function() {},
llvm_va_copy: function(ppdest, ppsrc) {
// copy the list start
{{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}};
// copy the list's current offset (will be advanced with each call to va_arg)
{{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}};
},
llvm_bswap_i16__asm: true,
llvm_bswap_i16__sig: 'ii',
llvm_bswap_i16: function(x) {
x = x|0;
return (((x&0xff)<<8) | ((x>>8)&0xff))|0;
},
llvm_bswap_i32__asm: true,
llvm_bswap_i32__sig: 'ii',
llvm_bswap_i32: function(x) {
x = x|0;
return (((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24))|0;
},
llvm_bswap_i64__deps: ['llvm_bswap_i32'],
llvm_bswap_i64: function(l, h) {
var retl = _llvm_bswap_i32(h)>>>0;
var reth = _llvm_bswap_i32(l)>>>0;
{{{ makeStructuralReturn(['retl', 'reth']) }}};
},
llvm_ctlz_i64__asm: true,
llvm_ctlz_i64__sig: 'iii',
llvm_ctlz_i64: function(l, h, isZeroUndef) {
l = l | 0;
h = h | 0;
isZeroUndef = isZeroUndef | 0;
var ret = 0;
ret = Math_clz32(h) | 0;
if ((ret | 0) == 32) ret = ret + (Math_clz32(l) | 0) | 0;
{{{ makeSetTempRet0('0') }}};
return ret | 0;
},
llvm_cttz_i32__deps: [function() {
function cttz(x) {
for (var i = 0; i < 8; i++) {
if (x & (1 << i)) {
return i;
}
}
return 8;
}
if (SIDE_MODULE) return ''; // uses it from the parent
#if USE_PTHREADS
return 'var cttz_i8; if (ENVIRONMENT_IS_PTHREAD) cttz_i8 = PthreadWorkerInit.cttz_i8; else PthreadWorkerInit.cttz_i8 = cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);';
#else
return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);';
#endif
}],
llvm_cttz_i32__asm: true,
llvm_cttz_i32__sig: 'ii',
llvm_cttz_i32: function(x) {
x = x|0;
var ret = 0;
ret = {{{ makeGetValueAsm('cttz_i8', 'x & 0xff', 'i8') }}};
if ((ret|0) < 8) return ret|0;
ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 8)&0xff', 'i8') }}};
if ((ret|0) < 8) return (ret + 8)|0;
ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 16)&0xff', 'i8') }}};
if ((ret|0) < 8) return (ret + 16)|0;
return ({{{ makeGetValueAsm('cttz_i8', 'x >>> 24', 'i8') }}} + 24)|0;
},
llvm_cttz_i64__deps: ['llvm_cttz_i32'],
llvm_cttz_i64: function(l, h) {
var ret = _llvm_cttz_i32(l);
if (ret == 32) ret += _llvm_cttz_i32(h);
{{{ makeStructuralReturn(['ret', '0']) }}};
},
llvm_ctpop_i32: function(x) {
var ret = 0;
while (x) {
if (x&1) ret++;
x >>>= 1;
}
return ret;
},
llvm_ctpop_i64__deps: ['llvm_ctpop_i32'],
llvm_ctpop_i64: function(l, h) {
return _llvm_ctpop_i32(l) + _llvm_ctpop_i32(h);
},
llvm_trap: function() {
abort('trap!');
},
llvm_prefetch: function(){},
__assert_fail: function(condition, filename, line, func) {
ABORT = true;
throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace();
},
__assert_func: function(filename, line, func, condition) {
throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + stackTrace();
},
__cxa_guard_acquire: function(variable) {
if (!{{{ makeGetValue(0, 'variable', 'i8', null, null, 1) }}}) { // ignore SAFE_HEAP stuff because llvm mixes i64 and i8 here
{{{ makeSetValue(0, 'variable', '1', 'i8') }}};
return 1;
}
return 0;
},
__cxa_guard_release: function() {},
__cxa_guard_abort: function() {},
$EXCEPTIONS: {
last: 0,
caught: [],
infos: {},
deAdjust: function(adjusted) {
if (!adjusted || EXCEPTIONS.infos[adjusted]) return adjusted;
for (var ptr in EXCEPTIONS.infos) {
var info = EXCEPTIONS.infos[ptr];
if (info.adjusted === adjusted) {
#if EXCEPTION_DEBUG
Module.printErr('de-adjusted exception ptr ' + adjusted + ' to ' + ptr);
#endif
return ptr;
}
}
#if EXCEPTION_DEBUG
Module.printErr('no de-adjustment for unknown exception ptr ' + adjusted);
#endif
return adjusted;
},
addRef: function(ptr) {
#if EXCEPTION_DEBUG
Module.printErr('addref ' + ptr);
#endif
if (!ptr) return;
var info = EXCEPTIONS.infos[ptr];
info.refcount++;
},
decRef: function(ptr) {
#if EXCEPTION_DEBUG
Module.printErr('decref ' + ptr);
#endif
if (!ptr) return;
var info = EXCEPTIONS.infos[ptr];
assert(info.refcount > 0);
info.refcount--;
if (info.refcount === 0) {
if (info.destructor) {
Runtime.dynCall('vi', info.destructor, [ptr]);
}
delete EXCEPTIONS.infos[ptr];
___cxa_free_exception(ptr);
#if EXCEPTION_DEBUG
Module.printErr('decref freeing exception ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
#endif
}
},
clearRef: function(ptr) {
if (!ptr) return;
var info = EXCEPTIONS.infos[ptr];
info.refcount = 0;
},
},
// Exceptions
__cxa_allocate_exception__deps: ['malloc'],
__cxa_allocate_exception: function(size) {
return _malloc(size);
},
__cxa_free_exception__deps: ['free'],
__cxa_free_exception: function(ptr) {
try {
return _free(ptr);
} catch(e) { // XXX FIXME
#if ASSERTIONS
Module.printErr('exception during cxa_free_exception: ' + e);
#endif
}
},
__cxa_increment_exception_refcount__deps: ['$EXCEPTIONS'],
__cxa_increment_exception_refcount: function(ptr) {
EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr));
},
__cxa_decrement_exception_refcount__deps: ['$EXCEPTIONS'],
__cxa_decrement_exception_refcount: function(ptr) {
EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr));
},
// Here, we throw an exception after recording a couple of values that we need to remember
// We also remember that it was the last exception thrown as we need to know that later.
__cxa_throw__sig: 'viii',
__cxa_throw__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch', '$EXCEPTIONS'],
__cxa_throw: function(ptr, type, destructor) {
#if EXCEPTION_DEBUG
Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor]);
#endif
EXCEPTIONS.infos[ptr] = {
ptr: ptr,
adjusted: ptr,
type: type,
destructor: destructor,
refcount: 0
};
EXCEPTIONS.last = ptr;
if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) {
__ZSt18uncaught_exceptionv.uncaught_exception = 1;
} else {
__ZSt18uncaught_exceptionv.uncaught_exception++;
}
{{{ makeThrow('ptr') }}}
},
// This exception will be caught twice, but while begin_catch runs twice,
// we early-exit from end_catch when the exception has been rethrown, so
// pop that here from the caught exceptions.
__cxa_rethrow__deps: ['__cxa_end_catch', '$EXCEPTIONS'],
__cxa_rethrow: function() {
___cxa_end_catch.rethrown = true;
var ptr = EXCEPTIONS.caught.pop();
#if EXCEPTION_DEBUG
Module.printErr('Compiled code RE-throwing an exception, popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
#endif
EXCEPTIONS.last = ptr;
{{{ makeThrow('ptr') }}}
},
llvm_eh_exception__deps: ['$EXCEPTIONS'],
llvm_eh_exception: function() {
return EXCEPTIONS.last;
},
llvm_eh_selector__jsargs: true,
llvm_eh_selector__deps: ['$EXCEPTIONS'],
llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) {
var type = EXCEPTIONS.last;
for (var i = 2; i < arguments.length; i++) {
if (arguments[i] == type) return type;
}
return 0;
},
llvm_eh_typeid_for: function(type) {
return type;
},
__cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv', '$EXCEPTIONS'],
__cxa_begin_catch: function(ptr) {
__ZSt18uncaught_exceptionv.uncaught_exception--;
EXCEPTIONS.caught.push(ptr);
#if EXCEPTION_DEBUG
Module.printErr('cxa_begin_catch ' + [ptr, 'stack', EXCEPTIONS.caught]);
#endif
EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ptr));
return ptr;
},
// We're done with a catch. Now, we can run the destructor if there is one
// and free the exception. Note that if the dynCall on the destructor fails
// due to calling apply on undefined, that means that the destructor is
// an invalid index into the FUNCTION_TABLE, so something has gone wrong.
__cxa_end_catch__deps: ['__cxa_free_exception', '$EXCEPTIONS'],
__cxa_end_catch: function() {
if (___cxa_end_catch.rethrown) {
___cxa_end_catch.rethrown = false;
return;
}
// Clear state flag.
asm['setThrew'](0);
// Call destructor if one is registered then clear it.
var ptr = EXCEPTIONS.caught.pop();
#if EXCEPTION_DEBUG
Module.printErr('cxa_end_catch popped ' + [ptr, EXCEPTIONS.last, 'stack', EXCEPTIONS.caught]);
#endif
if (ptr) {
EXCEPTIONS.decRef(EXCEPTIONS.deAdjust(ptr));
EXCEPTIONS.last = 0; // XXX in decRef?
}
},
__cxa_get_exception_ptr: function(ptr) {
#if EXCEPTION_DEBUG
Module.printErr('cxa_get_exception_ptr ' + ptr);
#endif
// TODO: use info.adjusted?
return ptr;
},
_ZSt18uncaught_exceptionv: function() { // std::uncaught_exception()
return !!__ZSt18uncaught_exceptionv.uncaught_exception;
},
__cxa_uncaught_exception__deps: ['_ZSt18uncaught_exceptionv'],
__cxa_uncaught_exception: function() {
return !!__ZSt18uncaught_exceptionv.uncaught_exception;
},
__cxa_call_unexpected: function(exception) {
Module.printErr('Unexpected exception thrown, this is not properly supported - aborting');
ABORT = true;
throw exception;
},
__cxa_current_primary_exception: function() {
var ret = EXCEPTIONS.caught[EXCEPTIONS.caught.length-1] || 0;
if (ret) EXCEPTIONS.addRef(EXCEPTIONS.deAdjust(ret));
return ret;
},
__cxa_rethrow_primary_exception__deps: ['__cxa_rethrow'],
__cxa_rethrow_primary_exception: function(ptr) {
if (!ptr) return;
EXCEPTIONS.caught.push(ptr);
___cxa_rethrow();
},
terminate: '__cxa_call_unexpected',
__gxx_personality_v0__deps: ['_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'],
__gxx_personality_v0: function() {
},
// Finds a suitable catch clause for when an exception is thrown.
// In normal compilers, this functionality is handled by the C++
// 'personality' routine. This is passed a fairly complex structure
// relating to the context of the exception and makes judgements
// about how to handle it. Some of it is about matching a suitable
// catch clause, and some of it is about unwinding. We already handle
// unwinding using 'if' blocks around each function, so the remaining
// functionality boils down to picking a suitable 'catch' block.
// We'll do that here, instead, to keep things simpler.
__cxa_find_matching_catch__deps: ['__resumeException', '$EXCEPTIONS'],
__cxa_find_matching_catch: function() {
var thrown = EXCEPTIONS.last;
if (!thrown) {
// just pass through the null ptr
{{{ makeStructuralReturn([0, 0]) }}};
}
var info = EXCEPTIONS.infos[thrown];
var throwntype = info.type;
if (!throwntype) {
// just pass through the thrown ptr
{{{ makeStructuralReturn(['thrown', 0]) }}};
}
var typeArray = Array.prototype.slice.call(arguments);
var pointer = Module['___cxa_is_pointer_type'](throwntype);
// can_catch receives a **, add indirection
if (!___cxa_find_matching_catch.buffer) ___cxa_find_matching_catch.buffer = _malloc(4);
#if EXCEPTION_DEBUG
Module.print("can_catch on " + [thrown]);
#endif
{{{ makeSetValue('___cxa_find_matching_catch.buffer', '0', 'thrown', '*') }}};
thrown = ___cxa_find_matching_catch.buffer;
// The different catch blocks are denoted by different types.
// Due to inheritance, those types may not precisely match the
// type of the thrown object. Find one which matches, and
// return the type of the catch block which should be called.
for (var i = 0; i < typeArray.length; i++) {
if (typeArray[i] && Module['___cxa_can_catch'](typeArray[i], throwntype, thrown)) {
thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection
info.adjusted = thrown;
#if EXCEPTION_DEBUG
Module.print(" can_catch found " + [thrown, typeArray[i]]);
#endif
{{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}};
}
}
// Shouldn't happen unless we have bogus data in typeArray
// or encounter a type for which emscripten doesn't have suitable
// typeinfo defined. Best-efforts match just in case.
thrown = {{{ makeGetValue('thrown', '0', '*') }}}; // undo indirection
{{{ makeStructuralReturn(['thrown', 'throwntype']) }}};
},
__resumeException__deps: ['$EXCEPTIONS', function() { Functions.libraryFunctions['___resumeException'] = 1 }], // will be called directly from compiled code
__resumeException: function(ptr) {
#if EXCEPTION_DEBUG
Module.print("Resuming exception " + [ptr, EXCEPTIONS.last]);
#endif
if (!EXCEPTIONS.last) { EXCEPTIONS.last = ptr; }
EXCEPTIONS.clearRef(EXCEPTIONS.deAdjust(ptr)); // exception refcount should be cleared, but don't free it
{{{ makeThrow('ptr') }}}
},
llvm_uadd_with_overflow_i8: function(x, y) {
x = x & 0xff;
y = y & 0xff;
{{{ makeStructuralReturn(['(x+y) & 0xff', 'x+y > 255']) }}};
},
llvm_umul_with_overflow_i8: function(x, y) {
x = x & 0xff;
y = y & 0xff;
{{{ makeStructuralReturn(['(x*y) & 0xff', 'x*y > 255']) }}};
},
llvm_uadd_with_overflow_i16: function(x, y) {
x = x & 0xffff;
y = y & 0xffff;
{{{ makeStructuralReturn(['(x+y) & 0xffff', 'x+y > 65535']) }}};
},
llvm_umul_with_overflow_i16: function(x, y) {
x = x & 0xffff;
y = y & 0xffff;
{{{ makeStructuralReturn(['(x*y) & 0xffff', 'x*y > 65535']) }}};
},
llvm_uadd_with_overflow_i32: function(x, y) {
x = x>>>0;
y = y>>>0;
{{{ makeStructuralReturn(['(x+y)>>>0', 'x+y > 4294967295']) }}};
},
llvm_umul_with_overflow_i32: function(x, y) {
x = x>>>0;
y = y>>>0;
{{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}};
},
llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }],
llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) {
#if ASSERTIONS
Runtime.warnOnce('no overflow support in llvm_umul_with_overflow_i64');
#endif
var low = ___muldi3(xl, xh, yl, yh);
{{{ makeStructuralReturn(['low', makeGetTempRet0(), '0']) }}};
},
llvm_stacksave: function() {
var self = _llvm_stacksave;
if (!self.LLVM_SAVEDSTACKS) {
self.LLVM_SAVEDSTACKS = [];
}
self.LLVM_SAVEDSTACKS.push(Runtime.stackSave());
return self.LLVM_SAVEDSTACKS.length-1;
},
llvm_stackrestore: function(p) {
var self = _llvm_stacksave;
var ret = self.LLVM_SAVEDSTACKS[p];
self.LLVM_SAVEDSTACKS.splice(p, 1);
Runtime.stackRestore(ret);
},
__cxa_pure_virtual: function() {
ABORT = true;
throw 'Pure virtual function called!';
},
llvm_flt_rounds: function() {
return -1; // 'indeterminable' for FLT_ROUNDS
},
llvm_memory_barrier: function(){},
llvm_atomic_load_add_i32_p0i32: function(ptr, delta) {
var ret = {{{ makeGetValue('ptr', '0', 'i32') }}};
{{{ makeSetValue('ptr', '0', 'ret+delta', 'i32') }}};
return ret;
},
llvm_expect_i32__inline: function(val, expected) {
return '(' + val + ')';
},
llvm_lifetime_start: function() {},
llvm_lifetime_end: function() {},
llvm_invariant_start: function() {},
llvm_invariant_end: function() {},
llvm_objectsize_i32: function() { return -1 }, // TODO: support this
llvm_dbg_declare__inline: function() { throw 'llvm_debug_declare' }, // avoid warning
// llvm-nacl
llvm_nacl_atomic_store_i32__inline: true,
llvm_nacl_atomic_cmpxchg_i8__inline: true,
llvm_nacl_atomic_cmpxchg_i16__inline: true,
llvm_nacl_atomic_cmpxchg_i32__inline: true,
// gnu atomics
__atomic_is_lock_free: function(size, ptr) {
return size <= 4 && (ptr&(size-1)) == 0;
},
__atomic_load_8: function(ptr, memmodel) {
{{{ makeStructuralReturn([makeGetValue('ptr', 0, 'i32'), makeGetValue('ptr', 4, 'i32')]) }}};
},
__atomic_store_8: function(ptr, vall, valh, memmodel) {
{{{ makeSetValue('ptr', 0, 'vall', 'i32') }}};
{{{ makeSetValue('ptr', 4, 'valh', 'i32') }}};
},
__atomic_exchange_8: function(ptr, vall, valh, memmodel) {
var l = {{{ makeGetValue('ptr', 0, 'i32') }}};
var h = {{{ makeGetValue('ptr', 4, 'i32') }}};
{{{ makeSetValue('ptr', 0, 'vall', 'i32') }}};
{{{ makeSetValue('ptr', 4, 'valh', 'i32') }}};
{{{ makeStructuralReturn(['l', 'h']) }}};
},
__atomic_compare_exchange_8: function(ptr, expected, desiredl, desiredh, weak, success_memmodel, failure_memmodel) {
var pl = {{{ makeGetValue('ptr', 0, 'i32') }}};
var ph = {{{ makeGetValue('ptr', 4, 'i32') }}};
var el = {{{ makeGetValue('expected', 0, 'i32') }}};
var eh = {{{ makeGetValue('expected', 4, 'i32') }}};
if (pl === el && ph === eh) {
{{{ makeSetValue('ptr', 0, 'desiredl', 'i32') }}};
{{{ makeSetValue('ptr', 4, 'desiredh', 'i32') }}};
return 1;
} else {
{{{ makeSetValue('expected', 0, 'pl', 'i32') }}};
{{{ makeSetValue('expected', 4, 'ph', 'i32') }}};
return 0;
}
},
__atomic_fetch_add_8__deps: ['llvm_uadd_with_overflow_i64'],
__atomic_fetch_add_8: function(ptr, vall, valh, memmodel) {
var l = {{{ makeGetValue('ptr', 0, 'i32') }}};
var h = {{{ makeGetValue('ptr', 4, 'i32') }}};
{{{ makeSetValue('ptr', 0, '_llvm_uadd_with_overflow_i64(l, h, vall, valh)', 'i32') }}};
{{{ makeSetValue('ptr', 4, makeGetTempRet0(), 'i32') }}};
{{{ makeStructuralReturn(['l', 'h']) }}};
},
__atomic_fetch_sub_8__deps: ['i64Subtract'],
__atomic_fetch_sub_8: function(ptr, vall, valh, memmodel) {
var l = {{{ makeGetValue('ptr', 0, 'i32') }}};
var h = {{{ makeGetValue('ptr', 4, 'i32') }}};
{{{ makeSetValue('ptr', 0, '_i64Subtract(l, h, vall, valh)', 'i32') }}};
{{{ makeSetValue('ptr', 4, makeGetTempRet0(), 'i32') }}};
{{{ makeStructuralReturn(['l', 'h']) }}};
},
__atomic_fetch_and_8__deps: ['i64Subtract'],
__atomic_fetch_and_8: function(ptr, vall, valh, memmodel) {
var l = {{{ makeGetValue('ptr', 0, 'i32') }}};
var h = {{{ makeGetValue('ptr', 4, 'i32') }}};
{{{ makeSetValue('ptr', 0, 'l&vall', 'i32') }}};
{{{ makeSetValue('ptr', 4, 'h&valh', 'i32') }}};
{{{ makeStructuralReturn(['l', 'h']) }}};
},
__atomic_fetch_or_8: function(ptr, vall, valh, memmodel) {
var l = {{{ makeGetValue('ptr', 0, 'i32') }}};
var h = {{{ makeGetValue('ptr', 4, 'i32') }}};
{{{ makeSetValue('ptr', 0, 'l|vall', 'i32') }}};
{{{ makeSetValue('ptr', 4, 'h|valh', 'i32') }}};
{{{ makeStructuralReturn(['l', 'h']) }}};
},
__atomic_fetch_xor_8: function(ptr, vall, valh, memmodel) {
var l = {{{ makeGetValue('ptr', 0, 'i32') }}};
var h = {{{ makeGetValue('ptr', 4, 'i32') }}};
{{{ makeSetValue('ptr', 0, 'l^vall', 'i32') }}};
{{{ makeSetValue('ptr', 4, 'h^valh', 'i32') }}};
{{{ makeStructuralReturn(['l', 'h']) }}};
},
// ==========================================================================
// llvm-mono integration
// ==========================================================================
llvm_mono_load_i8_p0i8: function(ptr) {
return {{{ makeGetValue('ptr', 0, 'i8') }}};
},
llvm_mono_store_i8_p0i8: function(value, ptr) {
{{{ makeSetValue('ptr', 0, 'value', 'i8') }}};
},
llvm_mono_load_i16_p0i16: function(ptr) {
return {{{ makeGetValue('ptr', 0, 'i16') }}};
},
llvm_mono_store_i16_p0i16: function(value, ptr) {
{{{ makeSetValue('ptr', 0, 'value', 'i16') }}};
},
llvm_mono_load_i32_p0i32: function(ptr) {
return {{{ makeGetValue('ptr', 0, 'i32') }}};
},
llvm_mono_store_i32_p0i32: function(value, ptr) {
{{{ makeSetValue('ptr', 0, 'value', 'i32') }}};
},
// ==========================================================================
// math.h
// ==========================================================================
cos: 'Math_cos',
cosf: 'Math_cos',
cosl: 'Math_cos',
sin: 'Math_sin',
sinf: 'Math_sin',
sinl: 'Math_sin',
tan: 'Math_tan',
tanf: 'Math_tan',
tanl: 'Math_tan',
acos: 'Math_acos',
acosf: 'Math_acos',
acosl: 'Math_acos',
asin: 'Math_asin',
asinf: 'Math_asin',
asinl: 'Math_asin',
atan: 'Math_atan',
atanf: 'Math_atan',
atanl: 'Math_atan',
atan2: 'Math_atan2',
atan2f: 'Math_atan2',
atan2l: 'Math_atan2',
exp: 'Math_exp',
expf: 'Math_exp',
expl: 'Math_exp',
log: 'Math_log',
logf: 'Math_log',
logl: 'Math_log',
sqrt: 'Math_sqrt',
sqrtf: 'Math_sqrt',
sqrtl: 'Math_sqrt',
fabs: 'Math_abs',
fabsf: 'Math_abs',
fabsl: 'Math_abs',
llvm_fabs_f32: 'Math_abs',
llvm_fabs_f64: 'Math_abs',
ceil: 'Math_ceil',
ceilf: 'Math_ceil',
ceill: 'Math_ceil',
floor: 'Math_floor',
floorf: 'Math_floor',
floorl: 'Math_floor',
pow: 'Math_pow',
powf: 'Math_pow',
powl: 'Math_pow',
llvm_sqrt_f32: 'Math_sqrt',
llvm_sqrt_f64: 'Math_sqrt',
llvm_pow_f32: 'Math_pow',
llvm_pow_f64: 'Math_pow',
llvm_log_f32: 'Math_log',
llvm_log_f64: 'Math_log',
llvm_exp_f32: 'Math_exp',
llvm_exp_f64: 'Math_exp',
_reallyNegative: function(x) {
return x < 0 || (x === 0 && (1/x) === -Infinity);
},
// ==========================================================================
// dlfcn.h - Dynamic library loading
//
// Some limitations:
//
// * Minification on each file separately may not work, as they will
// have different shortened names. You can in theory combine them, then
// minify, then split... perhaps.
//
// * LLVM optimizations may fail. If the child wants to access a function
// in the parent, LLVM opts may remove it from the parent when it is
// being compiled. Not sure how to tell LLVM to not do so.
// ==========================================================================
$DLFCN: {
error: null,
errorMsg: null,
loadedLibs: {}, // handle -> [refcount, name, lib_object]
loadedLibNames: {}, // name -> handle
},
// void* dlopen(const char* filename, int flag);
dlopen__deps: ['$DLFCN', '$FS', '$ENV'],
dlopen: function(filename, flag) {
// void *dlopen(const char *file, int mode);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html
filename = filename === 0 ? '__self__' : (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename);
if (DLFCN.loadedLibNames[filename]) {
// Already loaded; increment ref count and return.
var handle = DLFCN.loadedLibNames[filename];
DLFCN.loadedLibs[handle].refcount++;
return handle;
}
if (filename === '__self__') {
var handle = -1;
var lib_module = Module;
var cached_functions = {};
} else {
var target = FS.findObject(filename);
if (!target || target.isFolder || target.isDevice) {
DLFCN.errorMsg = 'Could not find dynamic lib: ' + filename;
return 0;
} else {
FS.forceLoadFile(target);
var lib_data = FS.readFile(filename, { encoding: 'utf8' });
}
try {
var lib_module = eval(lib_data)(
Runtime.alignFunctionTables(),
Module
);
} catch (e) {
#if ASSERTIONS
Module.printErr('Error in loading dynamic library: ' + e);
#endif
DLFCN.errorMsg = 'Could not evaluate dynamic lib: ' + filename;
return 0;
}
// Not all browsers support Object.keys().
var handle = 1;
for (var key in DLFCN.loadedLibs) {
if (DLFCN.loadedLibs.hasOwnProperty(key)) handle++;
}
// We don't care about RTLD_NOW and RTLD_LAZY.
if (flag & 256) { // RTLD_GLOBAL
for (var ident in lib_module) {
if (lib_module.hasOwnProperty(ident)) {
Module[ident] = lib_module[ident];
}
}
}
var cached_functions = {};
}
DLFCN.loadedLibs[handle] = {
refcount: 1,
name: filename,
module: lib_module,
cached_functions: cached_functions
};
DLFCN.loadedLibNames[filename] = handle;
return handle;
},
// int dlclose(void* handle);
dlclose__deps: ['$DLFCN'],
dlclose: function(handle) {
// int dlclose(void *handle);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html
if (!DLFCN.loadedLibs[handle]) {
DLFCN.errorMsg = 'Tried to dlclose() unopened handle: ' + handle;
return 1;
} else {
var lib_record = DLFCN.loadedLibs[handle];
if (--lib_record.refcount == 0) {
if (lib_record.module.cleanups) {
lib_record.module.cleanups.forEach(function(cleanup) { cleanup() });
}
delete DLFCN.loadedLibNames[lib_record.name];
delete DLFCN.loadedLibs[handle];
}
return 0;
}
},
// void* dlsym(void* handle, const char* symbol);
dlsym__deps: ['$DLFCN'],
dlsym: function(handle, symbol) {
// void *dlsym(void *restrict handle, const char *restrict name);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html
symbol = Pointer_stringify(symbol);
if (!DLFCN.loadedLibs[handle]) {
DLFCN.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle;
return 0;
} else {
var lib = DLFCN.loadedLibs[handle];
symbol = '_' + symbol;
if (lib.cached_functions.hasOwnProperty(symbol)) {
return lib.cached_functions[symbol];
}
if (!lib.module.hasOwnProperty(symbol)) {
DLFCN.errorMsg = ('Tried to lookup unknown symbol "' + symbol +
'" in dynamic lib: ' + lib.name);
return 0;
} else {
var result = lib.module[symbol];
if (typeof result == 'function') {
result = Runtime.addFunction(result);
lib.cached_functions = result;
}
return result;
}
}
},
// char* dlerror(void);
dlerror__deps: ['$DLFCN'],
dlerror: function() {
// char *dlerror(void);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html
if (DLFCN.errorMsg === null) {
return 0;
} else {
if (DLFCN.error) _free(DLFCN.error);
var msgArr = intArrayFromString(DLFCN.errorMsg);
DLFCN.error = allocate(msgArr, 'i8', ALLOC_NORMAL);
DLFCN.errorMsg = null;
return DLFCN.error;
}
},
dladdr: function(addr, info) {
// report all function pointers as coming from this program itself XXX not really correct in any way
var fname = allocate(intArrayFromString(Module['thisProgram'] || './this.program'), 'i8', ALLOC_NORMAL); // XXX leak
{{{ makeSetValue('addr', 0, 'fname', 'i32') }}};
{{{ makeSetValue('addr', QUANTUM_SIZE, '0', 'i32') }}};
{{{ makeSetValue('addr', QUANTUM_SIZE*2, '0', 'i32') }}};
{{{ makeSetValue('addr', QUANTUM_SIZE*3, '0', 'i32') }}};
return 1;
},
// ==========================================================================
// pwd.h
// ==========================================================================
// TODO: Implement.
// http://pubs.opengroup.org/onlinepubs/009695399/basedefs/pwd.h.html
getpwuid: function(uid) {
return 0; // NULL
},
// ==========================================================================
// time.h
// ==========================================================================
clock: function() {
if (_clock.start === undefined) _clock.start = Date.now();
return ((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}} / 1000))|0;
},
time: function(ptr) {
var ret = (Date.now()/1000)|0;
if (ptr) {
{{{ makeSetValue('ptr', 0, 'ret', 'i32') }}};
}
return ret;
},
difftime: function(time1, time0) {
return time1 - time0;
},
// Statically allocated time struct.
#if USE_PTHREADS
__tm_current: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_current = PthreadWorkerInit.___tm_current; else PthreadWorkerInit.___tm_current = ___tm_current = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)',
__tm_timezone: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_timezone = PthreadWorkerInit.___tm_timezone; else PthreadWorkerInit.___tm_timezone = ___tm_timezone = allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)',
__tm_formatted: '; if (ENVIRONMENT_IS_PTHREAD) ___tm_formatted = PthreadWorkerInit.___tm_formatted; else PthreadWorkerInit.___tm_formatted = ___tm_formatted = allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)',
#else
__tm_current: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)',
// Statically allocated copy of the string "GMT" for gmtime() to point to
__tm_timezone: 'allocate(intArrayFromString("GMT"), "i8", ALLOC_STATIC)',
// Statically allocated time strings.
__tm_formatted: 'allocate({{{ C_STRUCTS.tm.__size__ }}}, "i8", ALLOC_STATIC)',
#endif
mktime__deps: ['tzset'],
mktime: function(tmPtr) {
_tzset();
var date = new Date({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900,
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}},
0);
// There's an ambiguous hour when the time goes back; the tm_isdst field is
// used to disambiguate it. Date() basically guesses, so we fix it up if it
// guessed wrong, or fill in tm_isdst with the guess if it's -1.
var dst = {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'i32') }}};
var guessedOffset = date.getTimezoneOffset();
var start = new Date(date.getFullYear(), 0, 1);
var summerOffset = new Date(2000, 6, 1).getTimezoneOffset();
var winterOffset = start.getTimezoneOffset();
var dstOffset = Math.min(winterOffset, summerOffset); // DST is in December in South
if (dst < 0) {
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'Number(winterOffset != guessedOffset)', 'i32') }}};
} else if ((dst > 0) != (winterOffset != guessedOffset)) {
var summerOffset = new Date(date.getFullYear(), 6, 1).getTimezoneOffset();
var trueOffset = dst > 0 ? summerOffset : winterOffset;
// Don't try setMinutes(date.getMinutes() + ...) -- it's messed up.
date.setTime(date.getTime() + (trueOffset - guessedOffset)*60000);
}
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}};
var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0;
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}};
return (date.getTime() / 1000)|0;
},
timelocal: 'mktime',
gmtime__deps: ['__tm_current', 'gmtime_r'],
gmtime: function(time) {
return _gmtime_r(time, ___tm_current);
},
gmtime_r__deps: ['__tm_timezone'],
gmtime_r: function(time, tmPtr) {
var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000);
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getUTCSeconds()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getUTCMinutes()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getUTCHours()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getUTCDate()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getUTCMonth()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getUTCFullYear()-1900', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '0', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, '0', 'i32') }}};
var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0);
var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0;
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, '___tm_timezone', 'i32') }}};
return tmPtr;
},
timegm__deps: ['tzset'],
timegm: function(tmPtr) {
_tzset();
var time = Date.UTC({{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}} + 1900,
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}},
{{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}},
0);
var date = new Date(time);
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getUTCDay()', 'i32') }}};
var start = Date.UTC(date.getUTCFullYear(), 0, 1, 0, 0, 0, 0);
var yday = ((date.getTime() - start) / (1000 * 60 * 60 * 24))|0;
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}};
return (date.getTime() / 1000)|0;
},
localtime__deps: ['__tm_current', 'localtime_r'],
localtime: function(time) {
return _localtime_r(time, ___tm_current);
},
localtime_r__deps: ['__tm_timezone', 'tzset'],
localtime_r: function(time, tmPtr) {
_tzset();
var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000);
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'date.getSeconds()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_min, 'date.getMinutes()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'date.getHours()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'date.getDate()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'date.getMonth()', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_year, 'date.getFullYear()-1900', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'date.getDay()', 'i32') }}};
var start = new Date(date.getFullYear(), 0, 1);
var yday = ((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))|0;
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_yday, 'yday', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_gmtoff, '-(date.getTimezoneOffset() * 60)', 'i32') }}};
// DST is in December in South
var summerOffset = new Date(2000, 6, 1).getTimezoneOffset();
var winterOffset = start.getTimezoneOffset();
var dst = (date.getTimezoneOffset() == Math.min(winterOffset, summerOffset))|0;
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_isdst, 'dst', 'i32') }}};
var zonePtr = {{{ makeGetValue(makeGlobalUse('_tzname'), 'dst ? Runtime.QUANTUM_SIZE : 0', 'i32') }}};
{{{ makeSetValue('tmPtr', C_STRUCTS.tm.tm_zone, 'zonePtr', 'i32') }}};
return tmPtr;
},
asctime__deps: ['__tm_formatted', 'asctime_r'],
asctime: function(tmPtr) {
return _asctime_r(tmPtr, ___tm_formatted);
},
asctime_r__deps: ['__tm_formatted', 'mktime'],
asctime_r: function(tmPtr, buf) {
var date = {
tm_sec: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_sec, 'i32') }}},
tm_min: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_min, 'i32') }}},
tm_hour: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_hour, 'i32') }}},
tm_mday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mday, 'i32') }}},
tm_mon: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_mon, 'i32') }}},
tm_year: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_year, 'i32') }}},
tm_wday: {{{ makeGetValue('tmPtr', C_STRUCTS.tm.tm_wday, 'i32') }}}
};
var days = [ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" ];
var months = [ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" ];
var s = days[date.tm_wday] + ' ' + months[date.tm_mon] +
(date.tm_mday < 10 ? ' ' : ' ') + date.tm_mday +
(date.tm_hour < 10 ? ' 0' : ' ') + date.tm_hour +
(date.tm_min < 10 ? ':0' : ':') + date.tm_min +
(date.tm_sec < 10 ? ':0' : ':') + date.tm_sec +
' ' + (1900 + date.tm_year) + "\n";
writeStringToMemory(s, buf);
return buf;
},
ctime__deps: ['__tm_current', 'ctime_r'],
ctime: function(timer) {
return _ctime_r(timer, ___tm_current);
},
ctime_r__deps: ['localtime_r', 'asctime_r'],
ctime_r: function(time, buf) {
var stack = Runtime.stackSave();
var rv = _asctime_r(_localtime_r(time, Runtime.stackAlloc({{{ C_STRUCTS.tm.__size__ }}})), buf);
Runtime.stackRestore(stack);
return rv;
},
dysize: function(year) {
var leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0)));
return leap ? 366 : 365;
},
// TODO: Initialize these to defaults on startup from system settings.
// Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm)
#if USE_PTHREADS
tzname: '; if (ENVIRONMENT_IS_PTHREAD) _tzname = PthreadWorkerInit._tzname; else PthreadWorkerInit._tzname = _tzname = allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)',
daylight: '; if (ENVIRONMENT_IS_PTHREAD) _daylight = PthreadWorkerInit._daylight; else PthreadWorkerInit._daylight = _daylight = allocate(1, "i32*", ALLOC_STATIC)',
timezone: '; if (ENVIRONMENT_IS_PTHREAD) _timezone = PthreadWorkerInit._timezone; else PthreadWorkerInit._timezone = _timezone = allocate(1, "i32*", ALLOC_STATIC)',
#else
tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)',
daylight: 'allocate(1, "i32*", ALLOC_STATIC)',
timezone: 'allocate(1, "i32*", ALLOC_STATIC)',
#endif
tzset__deps: ['tzname', 'daylight', 'timezone'],
tzset: function() {
#if USE_PTHREADS
if (ENVIRONMENT_IS_PTHREAD) return _emscripten_sync_run_in_main_thread_0({{{ cDefine('EM_PROXIED_TZSET') }}});
#endif
// TODO: Use (malleable) environment variables instead of system settings.
if (_tzset.called) return;
_tzset.called = true;
{{{ makeSetValue(makeGlobalUse('_timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}};
var winter = new Date(2000, 0, 1);
var summer = new Date(2000, 6, 1);
{{{ makeSetValue(makeGlobalUse('_daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}};
function extractZone(date) {
var match = date.toTimeString().match(/\(([A-Za-z ]+)\)$/);
return match ? match[1] : "GMT";
};
var winterName = extractZone(winter);
var summerName = extractZone(summer);
var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL);
var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL);
if (summer.getTimezoneOffset() < winter.getTimezoneOffset()) {
// Northern hemisphere
{{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'winterNamePtr', 'i32') }}};
{{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}};
} else {
{{{ makeSetValue(makeGlobalUse('_tzname'), '0', 'summerNamePtr', 'i32') }}};
{{{ makeSetValue(makeGlobalUse('_tzname'), Runtime.QUANTUM_SIZE, 'winterNamePtr', 'i32') }}};
}
},
stime__deps: ['$ERRNO_CODES', '__setErrNo'],
stime: function(when) {
___setErrNo(ERRNO_CODES.EPERM);
return -1;
},
_MONTH_DAYS_REGULAR: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
_MONTH_DAYS_LEAP: [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
_isLeapYear: function(year) {
return year%4 === 0 && (year%100 !== 0 || year%400 === 0);
},
_arraySum: function(array, index) {
var sum = 0;
for (var i = 0; i <= index; sum += array[i++]);
return sum;
},
_addDays__deps: ['_isLeapYear', '_MONTH_DAYS_LEAP', '_MONTH_DAYS_REGULAR'],
_addDays: function(date, days) {
var newDate = new Date(date.getTime());
while(days > 0) {
var leap = __isLeapYear(newDate.getFullYear());
var currentMonth = newDate.getMonth();
var daysInCurrentMonth = (leap ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR)[currentMonth];
if (days > daysInCurrentMonth-newDate.getDate()) {
// we spill over to next month
days -= (daysInCurrentMonth-newDate.getDate()+1);
newDate.setDate(1);
if (currentMonth < 11) {
newDate.setMonth(currentMonth+1)
} else {
newDate.setMonth(0);
newDate.setFullYear(newDate.getFullYear()+1);
}
} else {
// we stay in current month
newDate.setDate(newDate.getDate()+days);
return newDate;
}
}
return newDate;
},
strftime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'],
strftime: function(s, maxsize, format, tm) {
// size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html
var tm_zone = {{{ makeGetValue('tm', C_STRUCTS.tm.tm_zone, 'i32') }}};
var date = {
tm_sec: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_sec, 'i32') }}},
tm_min: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_min, 'i32') }}},
tm_hour: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_hour, 'i32') }}},
tm_mday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mday, 'i32') }}},
tm_mon: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_mon, 'i32') }}},
tm_year: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_year, 'i32') }}},
tm_wday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_wday, 'i32') }}},
tm_yday: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_yday, 'i32') }}},
tm_isdst: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_isdst, 'i32') }}},
tm_gmtoff: {{{ makeGetValue('tm', C_STRUCTS.tm.tm_gmtoff, 'i32') }}},
tm_zone: tm_zone ? Pointer_stringify(tm_zone) : ''
};
var pattern = Pointer_stringify(format);
// expand format
var EXPANSION_RULES_1 = {
'%c': '%a %b %d %H:%M:%S %Y', // Replaced by the locale's appropriate date and time representation - e.g., Mon Aug 3 14:02:01 2013
'%D': '%m/%d/%y', // Equivalent to %m / %d / %y
'%F': '%Y-%m-%d', // Equivalent to %Y - %m - %d
'%h': '%b', // Equivalent to %b
'%r': '%I:%M:%S %p', // Replaced by the time in a.m. and p.m. notation
'%R': '%H:%M', // Replaced by the time in 24-hour notation
'%T': '%H:%M:%S', // Replaced by the time
'%x': '%m/%d/%y', // Replaced by the locale's appropriate date representation
'%X': '%H:%M:%S' // Replaced by the locale's appropriate date representation
};
for (var rule in EXPANSION_RULES_1) {
pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_1[rule]);
}
var WEEKDAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
var MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
function leadingSomething(value, digits, character) {
var str = typeof value === 'number' ? value.toString() : (value || '');
while (str.length < digits) {
str = character[0]+str;
}
return str;
};
function leadingNulls(value, digits) {
return leadingSomething(value, digits, '0');
};
function compareByDay(date1, date2) {
function sgn(value) {
return value < 0 ? -1 : (value > 0 ? 1 : 0);
};
var compare;
if ((compare = sgn(date1.getFullYear()-date2.getFullYear())) === 0) {
if ((compare = sgn(date1.getMonth()-date2.getMonth())) === 0) {
compare = sgn(date1.getDate()-date2.getDate());
}
}
return compare;
};
function getFirstWeekStartDate(janFourth) {
switch (janFourth.getDay()) {
case 0: // Sunday
return new Date(janFourth.getFullYear()-1, 11, 29);
case 1: // Monday
return janFourth;
case 2: // Tuesday
return new Date(janFourth.getFullYear(), 0, 3);
case 3: // Wednesday
return new Date(janFourth.getFullYear(), 0, 2);
case 4: // Thursday
return new Date(janFourth.getFullYear(), 0, 1);
case 5: // Friday
return new Date(janFourth.getFullYear()-1, 11, 31);
case 6: // Saturday
return new Date(janFourth.getFullYear()-1, 11, 30);
}
};
function getWeekBasedYear(date) {
var thisDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday);
var janFourthThisYear = new Date(thisDate.getFullYear(), 0, 4);
var janFourthNextYear = new Date(thisDate.getFullYear()+1, 0, 4);
var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear);
var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear);
if (compareByDay(firstWeekStartThisYear, thisDate) <= 0) {
// this date is after the start of the first week of this year
if (compareByDay(firstWeekStartNextYear, thisDate) <= 0) {
return thisDate.getFullYear()+1;
} else {
return thisDate.getFullYear();
}
} else {
return thisDate.getFullYear()-1;
}
};
var EXPANSION_RULES_2 = {
'%a': function(date) {
return WEEKDAYS[date.tm_wday].substring(0,3);
},
'%A': function(date) {
return WEEKDAYS[date.tm_wday];
},
'%b': function(date) {
return MONTHS[date.tm_mon].substring(0,3);
},
'%B': function(date) {
return MONTHS[date.tm_mon];
},
'%C': function(date) {
var year = date.tm_year+1900;
return leadingNulls((year/100)|0,2);
},
'%d': function(date) {
return leadingNulls(date.tm_mday, 2);
},
'%e': function(date) {
return leadingSomething(date.tm_mday, 2, ' ');
},
'%g': function(date) {
// %g, %G, and %V give values according to the ISO 8601:2000 standard week-based year.
// In this system, weeks begin on a Monday and week 1 of the year is the week that includes
// January 4th, which is also the week that includes the first Thursday of the year, and
// is also the first week that contains at least four days in the year.
// If the first Monday of January is the 2nd, 3rd, or 4th, the preceding days are part of
// the last week of the preceding year; thus, for Saturday 2nd January 1999,
// %G is replaced by 1998 and %V is replaced by 53. If December 29th, 30th,
// or 31st is a Monday, it and any following days are part of week 1 of the following year.
// Thus, for Tuesday 30th December 1997, %G is replaced by 1998 and %V is replaced by 01.
return getWeekBasedYear(date).toString().substring(2);
},
'%G': function(date) {
return getWeekBasedYear(date);
},
'%H': function(date) {
return leadingNulls(date.tm_hour, 2);
},
'%I': function(date) {
return leadingNulls(date.tm_hour < 13 ? date.tm_hour : date.tm_hour-12, 2);
},
'%j': function(date) {
// Day of the year (001-366)
return leadingNulls(date.tm_mday+__arraySum(__isLeapYear(date.tm_year+1900) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, date.tm_mon-1), 3);
},
'%m': function(date) {
return leadingNulls(date.tm_mon+1, 2);
},
'%M': function(date) {
return leadingNulls(date.tm_min, 2);
},
'%n': function() {
return '\n';
},
'%p': function(date) {
if (date.tm_hour > 0 && date.tm_hour < 13) {
return 'AM';
} else {
return 'PM';
}
},
'%S': function(date) {
return leadingNulls(date.tm_sec, 2);
},
'%t': function() {
return '\t';
},
'%u': function(date) {
var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0);
return day.getDay() || 7;
},
'%U': function(date) {
// Replaced by the week number of the year as a decimal number [00,53].
// The first Sunday of January is the first day of week 1;
// days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
var janFirst = new Date(date.tm_year+1900, 0, 1);
var firstSunday = janFirst.getDay() === 0 ? janFirst : __addDays(janFirst, 7-janFirst.getDay());
var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday);
// is target date after the first Sunday?
if (compareByDay(firstSunday, endDate) < 0) {
// calculate difference in days between first Sunday and endDate
var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31;
var firstSundayUntilEndJanuary = 31-firstSunday.getDate();
var days = firstSundayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();
return leadingNulls(Math.ceil(days/7), 2);
}
return compareByDay(firstSunday, janFirst) === 0 ? '01': '00';
},
'%V': function(date) {
// Replaced by the week number of the year (Monday as the first day of the week)
// as a decimal number [01,53]. If the week containing 1 January has four
// or more days in the new year, then it is considered week 1.
// Otherwise, it is the last week of the previous year, and the next week is week 1.
// Both January 4th and the first Thursday of January are always in week 1. [ tm_year, tm_wday, tm_yday]
var janFourthThisYear = new Date(date.tm_year+1900, 0, 4);
var janFourthNextYear = new Date(date.tm_year+1901, 0, 4);
var firstWeekStartThisYear = getFirstWeekStartDate(janFourthThisYear);
var firstWeekStartNextYear = getFirstWeekStartDate(janFourthNextYear);
var endDate = __addDays(new Date(date.tm_year+1900, 0, 1), date.tm_yday);
if (compareByDay(endDate, firstWeekStartThisYear) < 0) {
// if given date is before this years first week, then it belongs to the 53rd week of last year
return '53';
}
if (compareByDay(firstWeekStartNextYear, endDate) <= 0) {
// if given date is after next years first week, then it belongs to the 01th week of next year
return '01';
}
// given date is in between CW 01..53 of this calendar year
var daysDifference;
if (firstWeekStartThisYear.getFullYear() < date.tm_year+1900) {
// first CW of this year starts last year
daysDifference = date.tm_yday+32-firstWeekStartThisYear.getDate()
} else {
// first CW of this year starts this year
daysDifference = date.tm_yday+1-firstWeekStartThisYear.getDate();
}
return leadingNulls(Math.ceil(daysDifference/7), 2);
},
'%w': function(date) {
var day = new Date(date.tm_year+1900, date.tm_mon+1, date.tm_mday, 0, 0, 0, 0);
return day.getDay();
},
'%W': function(date) {
// Replaced by the week number of the year as a decimal number [00,53].
// The first Monday of January is the first day of week 1;
// days in the new year before this are in week 0. [ tm_year, tm_wday, tm_yday]
var janFirst = new Date(date.tm_year, 0, 1);
var firstMonday = janFirst.getDay() === 1 ? janFirst : __addDays(janFirst, janFirst.getDay() === 0 ? 1 : 7-janFirst.getDay()+1);
var endDate = new Date(date.tm_year+1900, date.tm_mon, date.tm_mday);
// is target date after the first Monday?
if (compareByDay(firstMonday, endDate) < 0) {
var februaryFirstUntilEndMonth = __arraySum(__isLeapYear(endDate.getFullYear()) ? __MONTH_DAYS_LEAP : __MONTH_DAYS_REGULAR, endDate.getMonth()-1)-31;
var firstMondayUntilEndJanuary = 31-firstMonday.getDate();
var days = firstMondayUntilEndJanuary+februaryFirstUntilEndMonth+endDate.getDate();
return leadingNulls(Math.ceil(days/7), 2);
}
return compareByDay(firstMonday, janFirst) === 0 ? '01': '00';
},
'%y': function(date) {
// Replaced by the last two digits of the year as a decimal number [00,99]. [ tm_year]
return (date.tm_year+1900).toString().substring(2);
},
'%Y': function(date) {
// Replaced by the year as a decimal number (for example, 1997). [ tm_year]
return date.tm_year+1900;
},
'%z': function(date) {
// Replaced by the offset from UTC in the ISO 8601:2000 standard format ( +hhmm or -hhmm ).
// For example, "-0430" means 4 hours 30 minutes behind UTC (west of Greenwich).
var off = date.tm_gmtoff;
var ahead = off >= 0;
off = Math.abs(off) / 60;
// convert from minutes into hhmm format (which means 60 minutes = 100 units)
off = (off / 60)*100 + (off % 60);
return (ahead ? '+' : '-') + String("0000" + off).slice(-4);
},
'%Z': function(date) {
return date.tm_zone;
},
'%%': function() {
return '%';
}
};
for (var rule in EXPANSION_RULES_2) {
if (pattern.indexOf(rule) >= 0) {
pattern = pattern.replace(new RegExp(rule, 'g'), EXPANSION_RULES_2[rule](date));
}
}
var bytes = intArrayFromString(pattern, false);
if (bytes.length > maxsize) {
return 0;
}
writeArrayToMemory(bytes, s);
return bytes.length-1;
},
strftime_l__deps: ['strftime'],
strftime_l: function(s, maxsize, format, tm) {
return _strftime(s, maxsize, format, tm); // no locale support yet
},
strptime__deps: ['_isLeapYear', '_arraySum', '_addDays', '_MONTH_DAYS_REGULAR', '_MONTH_DAYS_LEAP'],
strptime: function(buf, format, tm) {
// char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html
var pattern = Pointer_stringify(format);
// escape special characters
// TODO: not sure we really need to escape all of these in JS regexps
var SPECIAL_CHARS = '\\!@#$^&*()+=-[]/{}|:<>?,.';
for (var i=0, ii=SPECIAL_CHARS.length; i