// 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. LibraryManager.library = { // ========================================================================== // File system base. // ========================================================================== stdin: 0, stdout: 0, stderr: 0, _impure_ptr: 0, $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], $FS__postset: 'FS.init(); __ATEXIT__.push({ func: function() { FS.quit() } });', $FS: { // The path to the current folder. currentPath: '/', // The inode to assign to the next created object. nextInode: 2, // Currently opened file or directory streams. Padded with null so the zero // index is unused, as the indices are used as pointers. This is not split // into separate fileStreams and folderStreams lists because the pointers // must be interchangeable, e.g. when used in fdopen(). streams: [null], // Whether we are currently ignoring permissions. Useful when preparing the // filesystem and creating files inside read-only folders. ignorePermissions: true, // Converts any path to an absolute path. Resolves embedded "." and ".." // parts. absolutePath: function(relative, base) { if (typeof relative !== 'string') return null; if (base === undefined) base = FS.currentPath; if (relative && relative[0] == '/') base = ''; var full = base + '/' + relative; var parts = full.split('/').reverse(); var absolute = ['']; while (parts.length) { var part = parts.pop(); if (part == '' || part == '.') { // Nothing. } else if (part == '..') { if (absolute.length > 1) absolute.pop(); } else { absolute.push(part); } } return absolute.length == 1 ? '/' : absolute.join('/'); }, // Analyzes a relative or absolute path returning a description, containing: // isRoot: Whether the path points to the root. // exists: Whether the object at the path exists. // error: If !exists, this will contain the errno code of the cause. // name: The base name of the object (null if !parentExists). // path: The absolute path to the object, with all links resolved. // object: The filesystem record of the object referenced by the path. // parentExists: Whether the parent of the object exist and is a folder. // parentPath: The absolute path to the parent folder. // parentObject: The filesystem record of the parent folder. analyzePath: function(path, dontResolveLastLink, linksVisited) { var ret = { isRoot: false, exists: false, error: 0, name: null, path: null, object: null, parentExists: false, parentPath: null, parentObject: null }; path = FS.absolutePath(path); if (path == '/') { ret.isRoot = true; ret.exists = ret.parentExists = true; ret.name = '/'; ret.path = ret.parentPath = '/'; ret.object = ret.parentObject = FS.root; } else if (path !== null) { linksVisited = linksVisited || 0; path = path.slice(1).split('/'); var current = FS.root; var traversed = ['']; while (path.length) { if (path.length == 1 && current.isFolder) { ret.parentExists = true; ret.parentPath = traversed.length == 1 ? '/' : traversed.join('/'); ret.parentObject = current; ret.name = path[0]; } var target = path.shift(); if (!current.isFolder) { ret.error = ERRNO_CODES.ENOTDIR; break; } else if (!current.read) { ret.error = ERRNO_CODES.EACCES; break; } else if (!current.contents.hasOwnProperty(target)) { ret.error = ERRNO_CODES.ENOENT; break; } current = current.contents[target]; if (current.link && !(dontResolveLastLink && path.length == 0)) { if (linksVisited > 40) { // Usual Linux SYMLOOP_MAX. ret.error = ERRNO_CODES.ELOOP; break; } var link = FS.absolutePath(current.link, traversed.join('/')); return FS.analyzePath([link].concat(path).join('/'), dontResolveLastLink, linksVisited + 1); } traversed.push(target); if (path.length == 0) { ret.exists = true; ret.path = traversed.join('/'); ret.object = current; } } return ret; } return ret; }, // Finds the file system object at a given path. If dontResolveLastLink is // set to true and the object is a symbolic link, it will be returned as is // instead of being resolved. Links embedded in the path are still resolved. findObject: function(path, dontResolveLastLink) { FS.ensureRoot(); var ret = FS.analyzePath(path, dontResolveLastLink); if (ret.exists) { return ret.object; } else { ___setErrNo(ret.error); return null; } }, // Creates a file system record: file, link, device or folder. createObject: function(parent, name, properties, canRead, canWrite) { if (!parent) parent = '/'; if (typeof parent === 'string') parent = FS.findObject(parent); if (!parent) { ___setErrNo(ERRNO_CODES.EACCES); throw new Error('Parent path must exist.'); } if (!parent.isFolder) { ___setErrNo(ERRNO_CODES.ENOTDIR); throw new Error('Parent must be a folder.'); } if (!parent.write && !FS.ignorePermissions) { ___setErrNo(ERRNO_CODES.EACCES); throw new Error('Parent folder must be writeable.'); } if (!name || name == '.' || name == '..') { ___setErrNo(ERRNO_CODES.ENOENT); throw new Error('Name must not be empty.'); } if (parent.contents.hasOwnProperty(name)) { ___setErrNo(ERRNO_CODES.EEXIST); throw new Error("Can't overwrite object."); } parent.contents[name] = { read: canRead === undefined ? true : canRead, write: canWrite === undefined ? false : canWrite, timestamp: Date.now(), inodeNumber: FS.nextInode++ }; for (var key in properties) { if (properties.hasOwnProperty(key)) { parent.contents[name][key] = properties[key]; } } return parent.contents[name]; }, // Creates a folder. createFolder: function(parent, name, canRead, canWrite) { var properties = {isFolder: true, isDevice: false, contents: {}}; return FS.createObject(parent, name, properties, canRead, canWrite); }, // Creates a a folder and all its missing parents. createPath: function(parent, path, canRead, canWrite) { var current = FS.findObject(parent); if (current === null) throw new Error('Invalid parent.'); path = path.split('/').reverse(); while (path.length) { var part = path.pop(); if (!part) continue; if (!current.contents.hasOwnProperty(part)) { FS.createFolder(current, part, canRead, canWrite); } current = current.contents[part]; } return current; }, // Creates a file record, given specific properties. createFile: function(parent, name, properties, canRead, canWrite) { properties.isFolder = false; return FS.createObject(parent, name, properties, canRead, canWrite); }, // Creates a file record from existing data. createDataFile: function(parent, name, data, canRead, canWrite) { if (typeof data === 'string') { var dataArray = []; for (var i = 0; i < data.length; i++) dataArray.push(data.charCodeAt(i)); data = dataArray; } var properties = {isDevice: false, contents: data}; return FS.createFile(parent, name, properties, canRead, canWrite); }, // Creates a file record for lazy-loading from a URL. createLazyFile: function(parent, name, url, canRead, canWrite) { var properties = {isDevice: false, url: url}; return FS.createFile(parent, name, properties, canRead, canWrite); }, // Creates a link to a sepcific local path. createLink: function(parent, name, target, canRead, canWrite) { var properties = {isDevice: false, link: target}; return FS.createFile(parent, name, properties, canRead, canWrite); }, // Creates a character device with input and output callbacks: // input: Takes no parameters, returns a byte value or null if no data is // currently available. // output: Takes a byte value; doesn't return anything. Can also be passed // null to perform a flush of any cached data. createDevice: function(parent, name, input, output) { if (!(input || output)) { throw new Error('A device must have at least one callback defined.'); } var ops = {isDevice: true, input: input, output: output}; return FS.createFile(parent, name, ops, Boolean(input), Boolean(output)); }, // Makes sure a file's contents are loaded. Returns whether the file has // been loaded successfully. No-op for files that have been loaded already. forceLoadFile: function(obj) { if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; var success = true; if (typeof XMLHttpRequest !== 'undefined') { // Browser. // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. var xhr = new XMLHttpRequest(); xhr.open('GET', obj.url, false); // Some hints to the browser that we want binary data. if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; if (xhr.overrideMimeType) { xhr.overrideMimeType('text/plain; charset=x-user-defined'); } xhr.send(null); if (xhr.status != 200 && xhr.status != 0) success = false; if (xhr.response !== undefined) { obj.contents = new Uint8Array(xhr.response || []); } else { obj.contents = intArrayFromString(xhr.responseText || '', true); } } else if (typeof read !== 'undefined') { // Command-line. try { // WARNING: Can't read binary files in V8's d8 or tracemonkey's js, as // read() will try to parse UTF8. obj.contents = intArrayFromString(read(obj.url), true); } catch (e) { success = false; } } else { throw new Error('Cannot load without read() or XMLHttpRequest.'); } if (!success) ___setErrNo(ERRNO_CODES.EIO); return success; }, ensureRoot: function() { if (FS.root) return; // The main file system tree. All the contents are inside this. FS.root = { read: true, write: false, isFolder: true, isDevice: false, timestamp: Date.now(), inodeNumber: 1, contents: {} }; }, // Initializes the filesystems with stdin/stdout/stderr devices, given // optional handlers. init: function(input, output, error) { // Make sure we initialize only once. if (FS.init.initialized) return; FS.init.initialized = true; FS.ensureRoot(); // Default handlers. if (!input) input = function() { if (!input.cache || !input.cache.length) { var result; if (typeof window != 'undefined' && typeof window.prompt == 'function') { // Browser. result = window.prompt('Input: '); } else if (typeof readline == 'function') { // Command line. result = readline(); } if (!result) result = ''; input.cache = intArrayFromString(result + '\n', true); } return input.cache.shift(); }; if (!output) output = function(val) { if (val === null || val === '\n'.charCodeAt(0)) { output.printer(output.buffer.join('')); output.buffer = []; } else { output.buffer.push(String.fromCharCode(val)); } }; if (!output.printer) output.printer = print; if (!output.buffer) output.buffer = []; if (!error) error = output; // Create the temporary folder. FS.createFolder('/', 'tmp', true, true); // Create the I/O devices. var devFolder = FS.createFolder('/', 'dev', true, false); var stdin = FS.createDevice(devFolder, 'stdin', input); var stdout = FS.createDevice(devFolder, 'stdout', null, output); var stderr = FS.createDevice(devFolder, 'stderr', null, error); FS.createDevice(devFolder, 'tty', input, output); // Create default streams. FS.streams[1] = { path: '/dev/stdin', object: stdin, position: 0, isRead: true, isWrite: false, isAppend: false, error: false, eof: false, ungotten: [] }; FS.streams[2] = { path: '/dev/stdout', object: stdout, position: 0, isRead: false, isWrite: true, isAppend: false, error: false, eof: false, ungotten: [] }; FS.streams[3] = { path: '/dev/stderr', object: stderr, position: 0, isRead: false, isWrite: true, isAppend: false, error: false, eof: false, ungotten: [] }; _stdin = allocate([1], 'void*', ALLOC_STATIC); _stdout = allocate([2], 'void*', ALLOC_STATIC); _stderr = allocate([3], 'void*', ALLOC_STATIC); // Newlib initialization FS.streams[_stdin] = FS.streams[1]; FS.streams[_stdout] = FS.streams[2]; FS.streams[_stderr] = FS.streams[3]; __impure_ptr = allocate([ allocate( {{{ QUANTUM_SIZE === 4 ? '[0, 0, 0, 0, _stdin, 0, 0, 0, _stdout, 0, 0, 0, _stderr, 0, 0, 0]' : '[0, _stdin, _stdout, _stderr]' }}}, 'void*', ALLOC_STATIC) ], 'void*', ALLOC_STATIC); // Once initialized, permissions start having effect. FS.ignorePermissions = false; }, quit: function() { // Flush any partially-printed lines in stdout and stderr if (FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output('\n'.charCodeAt(0)); if (FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output('\n'.charCodeAt(0)); } }, // ========================================================================== // dirent.h // ========================================================================== __dirent_struct_layout: Runtime.generateStructInfo(null, '%struct.dirent'), opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], opendir: function(dirname) { // DIR *opendir(const char *dirname); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/opendir.html // NOTE: Calculating absolute path redundantly since we need to associate it // with the opened stream. var path = FS.absolutePath(Pointer_stringify(dirname)); if (path === null) { ___setErrNo(ERRNO_CODES.ENOENT); return 0; } var target = FS.findObject(path); if (target === null) return 0; if (!target.isFolder) { ___setErrNo(ERRNO_CODES.ENOTDIR); return 0; } else if (!target.read) { ___setErrNo(ERRNO_CODES.EACCES); return 0; } var id = FS.streams.length; var contents = []; for (var key in target.contents) contents.push(key); FS.streams[id] = { path: path, object: target, // An index into contents. Special values: -2 is ".", -1 is "..". position: -2, isRead: true, isWrite: false, isAppend: false, error: false, eof: false, ungotten: [], // Folder-specific properties: // Remember the contents at the time of opening in an array, so we can // seek between them relying on a single order. contents: contents, // Each stream has its own area for readdir() returns. currentEntry: _malloc(___dirent_struct_layout.__size__) }; return id; }, closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], closedir: function(dirp) { // int closedir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { return ___setErrNo(ERRNO_CODES.EBADF); } else { _free(FS.streams[dirp].currentEntry); delete FS.streams[dirp]; return 0; } }, telldir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], telldir: function(dirp) { // long int telldir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { return ___setErrNo(ERRNO_CODES.EBADF); } else { return FS.streams[dirp].position; } }, seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], seekdir: function(dirp, loc) { // void seekdir(DIR *dirp, long int loc); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/seekdir.html if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { ___setErrNo(ERRNO_CODES.EBADF); } else { var entries = 0; for (var key in FS.streams[dirp].contents) entries++; if (loc >= entries) { ___setErrNo(ERRNO_CODES.EINVAL); } else { FS.streams[dirp].position = loc; } } }, rewinddir__deps: ['seekdir'], rewinddir: function(dirp) { // void rewinddir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/rewinddir.html _seekdir(dirp, -2); }, readdir_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], readdir_r: function(dirp, entry, result) { // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { return ___setErrNo(ERRNO_CODES.EBADF); } var stream = FS.streams[dirp]; var loc = stream.position; var entries = 0; for (var key in stream.contents) entries++; if (loc < -2 || loc >= entries) { {{{ makeSetValue('result', '0', '0', 'i8*') }}} } else { var name, inode; if (loc === -2) { name = '.'; inode = 1; // Really undefined. } else if (loc === -1) { name = '..'; inode = 1; // Really undefined. } else { name = stream.contents[loc]; inode = stream.object.contents[name].inodeNumber; } stream.position++; var offsets = ___dirent_struct_layout; {{{ makeSetValue('entry', 'offsets.d_ino', 'inode', 'i32') }}} {{{ makeSetValue('entry', 'offsets.d_off', 'stream.position', 'i32') }}} {{{ makeSetValue('entry', 'offsets.d_reclen', 'name.length + 1', 'i32') }}} for (var i = 0; i < name.length; i++) { {{{ makeSetValue('entry + offsets.d_name', 'i', 'name.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('entry + offsets.d_name', 'i', '0', 'i8') }}} var type = stream.object.isDevice ? 2 // DT_CHR, character device. : stream.object.isFolder ? 4 // DT_DIR, directory. : stream.object.link !== undefined ? 10 // DT_LNK, symbolic link. : 8; // DT_REG, regular file. {{{ makeSetValue('entry', 'offsets.d_type', 'type', 'i8') }}} {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} } return 0; }, readdir__deps: ['readdir_r', '__setErrNo', '$ERRNO_CODES'], readdir: function(dirp) { // struct dirent *readdir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { ___setErrNo(ERRNO_CODES.EBADF); return 0; } else { if (!_readdir.result) _readdir.result = _malloc(4); _readdir_r(dirp, FS.streams[dirp].currentEntry, _readdir.result); if ({{{ makeGetValue(0, '_readdir.result', 'i8*') }}} === 0) { return 0; } else { return FS.streams[dirp].currentEntry; } } }, __01readdir64_: 'readdir', // TODO: Check if we need to link any other aliases. // ========================================================================== // utime.h // ========================================================================== __utimbuf_struct_layout: Runtime.generateStructInfo(null, '%struct.utimbuf'), utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__utimbuf_struct_layout'], utime: function(path, times) { // 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 = ___utimbuf_struct_layout.modtime; time = {{{ makeGetValue('times', 'offset', 'i32') }}} time *= 1000; } else { time = Date.now(); } var file = FS.findObject(Pointer_stringify(path)); if (file === null) return -1; if (!file.write) { ___setErrNo(ERRNO_CODES.EPERM); return -1; } file.timestamp = time; return 0; }, // ========================================================================== // libgen.h // ========================================================================== __libgenSplitName: function(path) { if (path === 0 || {{{ makeGetValue('path', 0, 'i8') }}} === 0) { // Null or empty results in '.'. var me = ___libgenSplitName; if (!me.ret) { me.ret = allocate(['.'.charCodeAt(0), 0], 'i8', ALLOC_NORMAL); } return [me.ret, -1]; } else { var slash = '/'.charCodeAt(0); var allSlashes = true; var slashPositions = []; for (var i = 0; {{{ makeGetValue('path', 'i', 'i8') }}} !== 0; i++) { if ({{{ makeGetValue('path', 'i', 'i8') }}} === slash) { slashPositions.push(i); } else { allSlashes = false; } } var length = i; if (allSlashes) { // All slashes result in a single slash. {{{ makeSetValue('path', '1', '0', 'i8') }}} return [path, -1]; } else { // Strip trailing slashes. while (slashPositions.length && slashPositions[slashPositions.length - 1] == length - 1) { {{{ makeSetValue('path', 'slashPositions.pop(i)', '0', 'i8') }}} length--; } return [path, slashPositions.pop()]; } } }, basename__deps: ['__libgenSplitName'], basename: function(path) { // char *basename(char *path); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/basename.html var result = ___libgenSplitName(path); return result[0] + result[1] + 1; }, __xpg_basename: 'basename', dirname__deps: ['__libgenSplitName'], dirname: function(path) { // char *dirname(char *path); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/dirname.html var result = ___libgenSplitName(path); if (result[1] == 0) { {{{ makeSetValue('result[0]', 1, '0', 'i8') }}} } else if (result[1] !== -1) { {{{ makeSetValue('result[0]', 'result[1]', '0', 'i8') }}} } return result[0]; }, // ========================================================================== // sys/stat.h // ========================================================================== __stat_struct_layout: Runtime.generateStructInfo(null, '%struct.stat'), stat__deps: ['$FS', '__stat_struct_layout'], stat: function(path, buf, dontResolveLastLink) { // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html // int stat(const char *path, struct stat *buf); // NOTE: dontResolveLastLink is a shortcut for lstat(). It should never be // used in client code. var obj = FS.findObject(Pointer_stringify(path), dontResolveLastLink); if (obj === null || !FS.forceLoadFile(obj)) return -1; var offsets = ___stat_struct_layout; // Constants. {{{ makeSetValue('buf', 'offsets.st_nlink', '1', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_uid', '0', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_gid', '0', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_blksize', '4096', 'i32') }}} // Variables. {{{ makeSetValue('buf', 'offsets.st_ino', 'obj.inodeNumber', 'i32') }}} var time = Math.floor(obj.timestamp / 1000); if (offsets.st_atime === undefined) { offsets.st_atime = offsets.st_atim.tv_sec; offsets.st_mtime = offsets.st_mtim.tv_sec; offsets.st_ctime = offsets.st_ctim.tv_sec; var nanosec = (obj.timestamp % 1000) * 1000; {{{ makeSetValue('buf', 'offsets.st_atim.tv_nsec', 'nanosec', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_mtim.tv_nsec', 'nanosec', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_ctim.tv_nsec', 'nanosec', 'i32') }}} } {{{ makeSetValue('buf', 'offsets.st_atime', 'time', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_mtime', 'time', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_ctime', 'time', 'i32') }}} var mode = 0; var size = 0; var blocks = 0; var dev = 0; var rdev = 0; if (obj.isDevice) { // Device numbers reuse inode numbers. dev = rdev = obj.inodeNumber; size = blocks = 0; mode = 0x2000; // S_IFCHR. } else { dev = 1; rdev = 0; // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), // but this is not required by the standard. if (obj.isFolder) { size = 4096; blocks = 1; mode = 0x4000; // S_IFDIR. } else { var data = obj.contents || obj.link; size = data.length; blocks = Math.ceil(data.length / 4096); mode = obj.link === undefined ? 0x8000 : 0xA000; // S_IFREG, S_IFLNK. } } {{{ makeSetValue('buf', 'offsets.st_dev', 'dev', 'i64') }}} {{{ makeSetValue('buf', 'offsets.st_rdev', 'rdev', 'i64') }}} // NOTE: These two may be i64, depending on compilation options. {{{ makeSetValue('buf', 'offsets.st_size', 'size', 'i32') }}} {{{ makeSetValue('buf', 'offsets.st_blocks', 'blocks', 'i32') }}} if (obj.read) mode |= 0x16D; // S_IRUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH. if (obj.write) mode |= 0x92; // S_IWUSR | S_IWGRP | S_IWOTH. {{{ makeSetValue('buf', 'offsets.st_mode', 'mode', 'i32') }}} return 0; }, lstat__deps: ['stat'], lstat: function(path, buf) { // int lstat(const char *path, struct stat *buf); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/lstat.html return _stat(path, buf, true); }, fstat__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'stat'], fstat: function(fildes, buf) { // int fstat(int fildes, struct stat *buf); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fstat.html if (!FS.streams[fildes]) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else { var pathArray = intArrayFromString(FS.streams[fildes].path); return _stat(allocate(pathArray, 'i8', ALLOC_STACK), buf); } }, mknod__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], mknod: function(path, mode, dev) { // int mknod(const char *path, mode_t mode, dev_t dev); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mknod.html if (dev !== 0 || !(mode & 0xC000)) { // S_IFREG | S_IFDIR. // Can't create devices or pipes through mknod(). ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { var properties = {contents: [], isFolder: Boolean(mode & 0x4000)}; // S_IFDIR. path = FS.analyzePath(Pointer_stringify(path)); try { FS.createObject(path.parentObject, path.name, properties, mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. return 0; } catch (e) { return -1; } } }, mkdir__deps: ['mknod'], mkdir: function(path, mode) { // int mkdir(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html return _mknod(path, 0x4000 | (mode & 0x180), 0); // S_IFDIR, S_IRUSR | S_IWUSR. }, mkfifo__deps: ['__setErrNo', '$ERRNO_CODES'], mkfifo: function(path, mode) { // int mkfifo(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkfifo.html // NOTE: We support running only a single process, and named pipes require // blocking, which we can't provide. The error code is not very // accurate, but it's the closest among those allowed in the standard // and unlikely to result in retries. ___setErrNo(ERRNO_CODES.EROFS); return -1; }, chmod__deps: ['$FS'], chmod: function(path, mode) { // int chmod(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chmod.html var obj = FS.findObject(Pointer_stringify(path)); if (obj === null) return -1; obj.read = mode & 0x100; // S_IRUSR. obj.write = mode & 0x80; // S_IWUSR. obj.timestamp = Date.now(); return 0; }, fchmod__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chmod'], fchmod: function(fildes, mode) { // int fchmod(int fildes, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fchmod.html if (!FS.streams[fildes]) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else { var pathArray = intArrayFromString(FS.streams[fildes].path); return _chmod(allocate(pathArray, 'i8', ALLOC_STACK), mode); } }, umask__deps: ['$FS'], umask: function(newMask) { // mode_t umask(mode_t cmask); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/umask.html // NOTE: This value isn't actually used for anything. if (_umask.cmask === undefined) _umask.cmask = 0x1FF; // S_IRWXU | S_IRWXG | S_IRWXO. var oldMask = _umask.cmask; _umask.cmask = newMask; return oldMask; }, stat64: 'stat', fstat64: 'fstat', __01fstat64_: 'fstat', __01stat64_: 'stat', __01lstat64_: 'lstat', // TODO: Check if other aliases are needed. // ========================================================================== // sys/statvfs.h // ========================================================================== __statvfs_struct_layout: Runtime.generateStructInfo(null, '%struct.statvfs'), statvfs__deps: ['$FS', '__statvfs_struct_layout'], statvfs: function(path, buf) { // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html // int statvfs(const char *restrict path, struct statvfs *restrict buf); var offsets = ___statvfs_struct_layout; // NOTE: None of the constants here are true. We're just returning safe and // sane values. {{{ makeSetValue('buf', 'offsets.f_bsize', '4096', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_frsize', '4096', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_blocks', '1000000', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_bfree', '500000', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_bavail', '500000', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_files', 'FS.nextInode', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_ffree', '1000000', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_favail', '1000000', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_fsid', '42', 'i32') }}} {{{ makeSetValue('buf', 'offsets.f_flag', '2', 'i32') }}} // ST_NOSUID {{{ makeSetValue('buf', 'offsets.f_namemax', '255', 'i32') }}} return 0; }, fstatvfs__deps: ['statvfs'], fstatvfs: function(fildes, buf) { // int fstatvfs(int fildes, struct statvfs *buf); // http://pubs.opengroup.org/onlinepubs/009604499/functions/statvfs.html return _statvfs(0, buf); }, __01statvfs64_: 'statvfs', __01fstatvfs64_: 'fstatvfs', // ========================================================================== // fcntl.h // ========================================================================== __flock_struct_layout: Runtime.generateStructInfo(null, '%struct.flock'), open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], open: function(path, oflag, varargs) { // int open(const char *path, int oflag, ...); // http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html // NOTE: This implementation tries to mimic glibc rather that strictly // following the POSIX standard. var mode = {{{ makeGetValue('varargs', 0, 'i32') }}}; // Simplify flags. var accessMode = oflag & {{{ cDefine('O_ACCMODE') }}}; var isWrite = accessMode != {{{ cDefine('O_RDONLY') }}}; var isRead = accessMode != {{{ cDefine('O_WRONLY') }}}; var isCreate = Boolean(oflag & {{{ cDefine('O_CREAT') }}}); var isExistCheck = Boolean(oflag & {{{ cDefine('O_EXCL') }}}); var isTruncate = Boolean(oflag & {{{ cDefine('O_TRUNC') }}}); var isAppend = Boolean(oflag & {{{ cDefine('O_APPEND') }}}); // Verify path. var origPath = path; path = FS.analyzePath(Pointer_stringify(path)); if (!path.parentExists) { ___setErrNo(path.error); return -1; } var target = path.object || null; var finalPath; // Verify the file exists, create if needed and allowed. if (target) { if (isCreate && isExistCheck) { ___setErrNo(ERRNO_CODES.EEXIST); return -1; } if ((isWrite || isCreate || isTruncate) && target.isFolder) { ___setErrNo(ERRNO_CODES.EISDIR); return -1; } if (isRead && !target.read || isWrite && !target.write) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } if (isTruncate && !target.isDevice) { target.contents = []; } else { if (!FS.forceLoadFile(target)) { ___setErrNo(ERRNO_CODES.EIO); return -1; } } finalPath = path.path; } else { if (!isCreate) { ___setErrNo(ERRNO_CODES.ENOENT); return -1; } if (!path.parentObject.write) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } target = FS.createDataFile(path.parentObject, path.name, [], mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. finalPath = path.parentPath + '/' + path.name; } // Actually create an open stream. var id = FS.streams.length; if (target.isFolder) { var entryBuffer = 0; if (___dirent_struct_layout) { entryBuffer = _malloc(___dirent_struct_layout.__size__); } var contents = []; for (var key in target.contents) contents.push(key); FS.streams[id] = { path: finalPath, object: target, // An index into contents. Special values: -2 is ".", -1 is "..". position: -2, isRead: true, isWrite: false, isAppend: false, error: false, eof: false, ungotten: [], // Folder-specific properties: // Remember the contents at the time of opening in an array, so we can // seek between them relying on a single order. contents: contents, // Each stream has its own area for readdir() returns. currentEntry: entryBuffer }; } else { FS.streams[id] = { path: finalPath, object: target, position: 0, isRead: isRead, isWrite: isWrite, isAppend: isAppend, error: false, eof: false, ungotten: [] }; } return id; }, creat__deps: ['open'], creat: function(path, mode) { // int creat(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/009695399/functions/creat.html return _open(path, {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_TRUNC') }}}, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK)); }, fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__flock_struct_layout'], fcntl: function(fildes, cmd, varargs) { // int fcntl(int fildes, int cmd, ...); // http://pubs.opengroup.org/onlinepubs/009695399/functions/fcntl.html if (!(fildes in FS.streams)) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } var stream = FS.streams[fildes]; switch (cmd) { case {{{ cDefine('F_DUPFD') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; if (arg < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } var newStream = {}; for (var member in stream) { newStream[member] = stream[member]; } if (arg in FS.streams) arg = FS.streams.length; FS.streams[arg] = newStream; return arg; case {{{ cDefine('F_GETFD') }}}: case {{{ cDefine('F_SETFD') }}}: return 0; // FD_CLOEXEC makes no sense for a single process. case {{{ cDefine('F_GETFL') }}}: var flags = 0; if (stream.isRead && stream.isWrite) flags = {{{ cDefine('O_RDWR') }}}; else if (!stream.isRead && stream.isWrite) flags = {{{ cDefine('O_WRONLY') }}}; else if (stream.isRead && !stream.isWrite) flags = {{{ cDefine('O_RDONLY') }}}; if (stream.isAppend) flags |= {{{ cDefine('O_APPEND') }}}; // Synchronization and blocking flags are irrelevant to us. return flags; case {{{ cDefine('F_SETFL') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; stream.isAppend = Boolean(arg | {{{ cDefine('O_APPEND') }}}); // Synchronization and blocking flags are irrelevant to us. return 0; case {{{ cDefine('F_GETLK') }}}: case {{{ cDefine('F_GETLK64') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; var offset = ___flock_struct_layout.l_type; // We're always unlocked. {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}} return 0; case {{{ cDefine('F_SETLK') }}}: case {{{ cDefine('F_SETLKW') }}}: case {{{ cDefine('F_SETLK64') }}}: case {{{ cDefine('F_SETLKW64') }}}: // Pretend that the locking is successful. return 0; case {{{ cDefine('F_SETOWN') }}}: case {{{ cDefine('F_GETOWN') }}}: // These are for sockets. We don't have them implemented (yet?). ___setErrNo(ERRNO_CODES.EINVAL); return -1; default: ___setErrNo(ERRNO_CODES.EINVAL); return -1; } // Should never be reached. Only to silence strict warnings. return -1; }, posix_fadvise: function(fd, offset, len, advice) { // int posix_fadvise(int fd, off_t offset, off_t len, int advice); // http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fadvise.html // Advise as much as you wish. We don't care. return 0; }, posix_madvise: 'posix_fadvise', posix_fallocate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], posix_fallocate: function(fd, offset, len) { // int posix_fallocate(int fd, off_t offset, off_t len); // http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fallocate.html if (!(fd in FS.streams) || FS.streams[fd].link || FS.streams[fd].isFolder || FS.streams[fd].isDevice) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } var contents = FS.streams[fd].object.contents; var limit = offset + len; while (limit > contents.length) contents.push(0); return 0; }, // ========================================================================== // poll.h // ========================================================================== __pollfd_struct_layout: Runtime.generateStructInfo(null, '%struct.pollfd'), poll__deps: ['$FS', '__pollfd_struct_layout'], poll: function(fds, nfds, timeout) { // int poll(struct pollfd fds[], nfds_t nfds, int timeout); // http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html // NOTE: This is pretty much a no-op mimicing glibc. var offsets = ___pollfd_struct_layout; var nonzero = 0; for (var i = 0; i < nfds; i++) { var pollfd = fds + ___pollfd_struct_layout.__size__ * i; var fd = {{{ makeGetValue('pollfd', 'offsets.fd', 'i32') }}}; var events = {{{ makeGetValue('pollfd', 'offsets.events', 'i16') }}}; var revents = 0; if (fd in FS.streams) { var stream = FS.streams[fd]; if (events & {{{ cDefine('POLLIN') }}}) revents |= {{{ cDefine('POLLIN') }}}; if (events & {{{ cDefine('POLLOUT') }}}) revents |= {{{ cDefine('POLLOUT') }}}; } else { if (events & {{{ cDefine('POLLNVAL') }}}) revents |= {{{ cDefine('POLLNVAL') }}}; } if (revents) nonzero++; {{{ makeSetValue('pollfd', 'offsets.revents', 'revents', 'i16') }}} } return nonzero; }, // ========================================================================== // unistd.h // ========================================================================== access__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], access: function(path, amode) { // int access(const char *path, int amode); // http://pubs.opengroup.org/onlinepubs/000095399/functions/access.html path = Pointer_stringify(path); var target = FS.findObject(path); if (target === null) return -1; if ((amode & 2 && !target.write) || // W_OK. ((amode & 1 || amode & 4) && !target.read)) { // X_OK, R_OK. ___setErrNo(ERRNO_CODES.EACCES); return -1; } else { return 0; } }, chdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chdir: function(path) { // int chdir(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/chdir.html // NOTE: The path argument may be a string, to simplify fchdir(). if (typeof path !== 'string') path = Pointer_stringify(path); path = FS.analyzePath(path); if (!path.exists) { ___setErrNo(path.error); return -1; } else if (!path.object.isFolder) { ___setErrNo(ERRNO_CODES.ENOTDIR); return -1; } else { FS.currentPath = path.path; return 0; } }, chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chown: function(path, owner, group, dontResolveLastLink) { // int chown(const char *path, uid_t owner, gid_t group); // http://pubs.opengroup.org/onlinepubs/000095399/functions/chown.html // We don't support multiple users, so changing ownership makes no sense. // NOTE: The path argument may be a string, to simplify fchown(). // NOTE: dontResolveLastLink is a shortcut for lchown(). It should never be // used in client code. if (typeof path !== 'string') path = Pointer_stringify(path); var target = FS.findObject(path, dontResolveLastLink); if (target === null) return -1; target.timestamp = Date.now(); return 0; }, chroot__deps: ['__setErrNo', '$ERRNO_CODES'], chroot: function(path) { // int chroot(const char *path); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html ___setErrNo(ERRNO_CODES.EACCES); return -1; }, close__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], close: function(fildes) { // int close(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/close.html if (FS.streams[fildes]) { if (FS.streams[fildes].currentEntry) { _free(FS.streams[fildes].currentEntry); } delete FS.streams[fildes]; return 0; } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, dup__deps: ['fcntl'], dup: function(fildes) { // int dup(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/dup.html return _fcntl(fildes, 0, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); // F_DUPFD. }, dup2__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'fcntl', 'close'], dup2: function(fildes, fildes2) { // int dup2(int fildes, int fildes2); // http://pubs.opengroup.org/onlinepubs/000095399/functions/dup.html if (fildes2 < 0) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else if (fildes === fildes2 && FS.streams[fildes]) { return fildes; } else { _close(fildes2); return _fcntl(fildes, 0, allocate([fildes2, 0, 0, 0], 'i32', ALLOC_STACK)); // F_DUPFD. } }, fchown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chown'], fchown: function(fildes, owner, group) { // int fchown(int fildes, uid_t owner, gid_t group); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchown.html if (FS.streams[fildes]) { return _chown(FS.streams[fildes].path, owner, group); } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, fchdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chdir'], fchdir: function(fildes) { // int fchdir(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchdir.html if (FS.streams[fildes]) { return _chdir(FS.streams[fildes].path); } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, ctermid__deps: ['strcpy'], ctermid: function(s) { // char *ctermid(char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ctermid.html if (!_ctermid.ret) { var arr = intArrayFromString('/dev/tty'); _ctermid.ret = allocate(arr, 'i8', ALLOC_NORMAL); } return s ? _strcpy(s, _ctermid.ret) : _ctermid.ret; }, crypt: function(key, salt) { // char *(const char *, const char *); // http://pubs.opengroup.org/onlinepubs/000095399/functions/crypt.html // TODO: Implement (probably compile from C). ___setErrNo(ERRNO_CODES.ENOSYS); return 0; }, encrypt: function(block, edflag) { // void encrypt(char block[64], int edflag); // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html // TODO: Implement (probably compile from C). ___setErrNo(ERRNO_CODES.ENOSYS); }, fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'], fpathconf: function(fildes, name) { // 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 mimicing 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', fsync__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fsync: function(fildes) { // int fsync(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsync.html if (FS.streams[fildes]) { // We write directly to the file system, so there's nothing to do here. return 0; } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, fdatasync: 'fsync', truncate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], truncate: function(path, length) { // int truncate(const char *path, off_t length); // http://pubs.opengroup.org/onlinepubs/000095399/functions/truncate.html // NOTE: The path argument may be a string, to simplify ftruncate(). if (length < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { if (typeof path !== 'string') path = Pointer_stringify(path); var target = FS.findObject(path); if (target === null) return -1; if (target.isFolder) { ___setErrNo(ERRNO_CODES.EISDIR); return -1; } else if (target.isDevice) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else if (!target.write) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else { var contents = target.contents; if (length < contents.length) contents.length = length; else while (length > contents.length) contents.push(0); target.timestamp = Date.now(); return 0; } } }, ftruncate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'truncate'], ftruncate: function(fildes, length) { // int ftruncate(int fildes, off_t length); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftruncate.html if (FS.streams[fildes] && FS.streams[fildes].isWrite) { return _truncate(FS.streams[fildes].path, length); } else if (FS.streams[fildes]) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, getcwd__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], getcwd: function(buf, size) { // char *getcwd(char *buf, size_t size); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getcwd.html if (size == 0) { ___setErrNo(ERRNO_CODES.EINVAL); return 0; } else if (size < FS.currentPath.length + 1) { ___setErrNo(ERRNO_CODES.ERANGE); return 0; } else { for (var i = 0; i < FS.currentPath.length; i++) { {{{ makeSetValue('buf', 'i', 'FS.currentPath.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('buf', 'i', '0', 'i8') }}} return buf; } }, getwd__deps: ['getcwd'], getwd: function(path_name) { // char *getwd(char *path_name); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getwd.html return _getcwd(path_name, 4096); // PATH_MAX. }, isatty__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], isatty: function(fildes) { // int isatty(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/isatty.html // For now it's easier to pretend we have no terminals. ___setErrNo(FS.streams[fildes] ? ERRNO_CODES.ENOTTY : ERRNO_CODES.EBADF); return -1; }, lchown__deps: ['chown'], lchown: function(path, owner, group) { // int lchown(const char *path, uid_t owner, gid_t group); // http://pubs.opengroup.org/onlinepubs/000095399/functions/lchown.html return _chown(path, owner, group, true); }, link__deps: ['__setErrNo', '$ERRNO_CODES'], link: function(path1, path2) { // int link(const char *path1, const char *path2); // http://pubs.opengroup.org/onlinepubs/000095399/functions/link.html // We don't support hard links. ___setErrNo(ERRNO_CODES.EMLINK); return -1; }, lockf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], lockf: function(fildes, func, size) { // int lockf(int fildes, int function, off_t size); // http://pubs.opengroup.org/onlinepubs/000095399/functions/lockf.html if (FS.streams[fildes]) { // Pretend whatever locking or unlocking operation succeeded. Locking does // not make much sense, since we have a single process/thread. return 0; } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, lseek__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], lseek: function(fildes, offset, whence) { // off_t lseek(int fildes, off_t offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html if (FS.streams[fildes] && !FS.streams[fildes].isDevice) { var stream = FS.streams[fildes]; var position = offset; if (whence === 1) { // SEEK_CUR. position += stream.position; } else if (whence === 2) { // SEEK_END. position += stream.object.contents.length; } if (position < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { stream.ungotten = []; stream.position = position; return position; } } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, pipe__deps: ['__setErrNo', '$ERRNO_CODES'], pipe: function(fildes) { // int pipe(int fildes[2]); // http://pubs.opengroup.org/onlinepubs/000095399/functions/pipe.html // It is possible to implement this using two device streams, but pipes make // little sense in a single-threaded environment, so we do not support them. ___setErrNo(ERRNO_CODES.ENOSYS); return -1; }, pread__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], pread: function(fildes, buf, nbyte, offset) { // ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset); // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html var stream = FS.streams[fildes]; if (!stream || stream.object.isDevice) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else if (!stream.isRead) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else if (stream.object.isFolder) { ___setErrNo(ERRNO_CODES.EISDIR); return -1; } else if (nbyte < 0 || offset < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { var bytesRead = 0; while (stream.ungotten.length && nbyte > 0) { {{{ makeSetValue('buf++', '0', 'stream.ungotten.pop()', 'i8') }}} nbyte--; bytesRead++; } var contents = stream.object.contents; var size = Math.min(contents.length - offset, nbyte); for (var i = 0; i < size; i++) { {{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}} bytesRead++; } return bytesRead; } }, read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'pread'], read: function(fildes, buf, nbyte) { // ssize_t read(int fildes, void *buf, size_t nbyte); // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html var stream = FS.streams[fildes]; if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else if (!stream.isRead) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else if (nbyte < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { var bytesRead; if (stream.object.isDevice) { if (stream.object.input) { bytesRead = 0; while (stream.ungotten.length && nbyte > 0) { {{{ makeSetValue('buf++', '0', 'stream.ungotten.pop()', 'i8') }}} nbyte--; bytesRead++; } for (var i = 0; i < nbyte; i++) { try { var result = stream.object.input(); } catch (e) { ___setErrNo(ERRNO_CODES.EIO); return -1; } if (result === null || result === undefined) break; bytesRead++; {{{ makeSetValue('buf', 'i', 'result', 'i8') }}} } return bytesRead; } else { ___setErrNo(ERRNO_CODES.ENXIO); return -1; } } else { var ungotSize = stream.ungotten.length; bytesRead = _pread(fildes, buf, nbyte, stream.position); if (bytesRead != -1) { stream.position += (stream.ungotten.length - ungotSize) + bytesRead; } return bytesRead; } } }, sync: function() { // void sync(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/sync.html // All our writing is already synchronized. This is a no-op. }, rmdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], rmdir: function(path) { // int rmdir(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html path = FS.analyzePath(Pointer_stringify(path)); if (!path.parentExists || !path.exists) { ___setErrNo(path.error); return -1; } else if (!path.object.write || path.isRoot) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else if (!path.object.isFolder) { ___setErrNo(ERRNO_CODES.ENOTDIR); return -1; } else { for (var i in path.object.contents) { ___setErrNo(ERRNO_CODES.ENOTEMPTY); return -1; } if (path.path == FS.currentPath) { ___setErrNo(ERRNO_CODES.EBUSY); return -1; } else { delete path.parentObject.contents[path.name]; return 0; } } }, unlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], unlink: function(path) { // int unlink(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html path = FS.analyzePath(Pointer_stringify(path)); if (!path.parentExists || !path.exists) { ___setErrNo(path.error); return -1; } else if (path.object.isFolder) { ___setErrNo(ERRNO_CODES.EISDIR); return -1; } else if (!path.object.write) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else { delete path.parentObject.contents[path.name]; return 0; } }, ttyname__deps: ['ttyname_r'], ttyname: function(fildes) { // char *ttyname(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html if (!_ttyname.ret) _ttyname.ret = _malloc(256); return _ttyname_r(fildes, _ttyname.ret, 256) ? 0 : _ttyname.ret; }, ttyname_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], ttyname_r: function(fildes, name, namesize) { // int ttyname_r(int fildes, char *name, size_t namesize); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html var stream = FS.streams[fildes]; if (!stream) { return ___setErrNo(ERRNO_CODES.EBADF); } else { var object = stream.object; if (!object.isDevice || !object.input || !object.output) { return ___setErrNo(ERRNO_CODES.ENOTTY); } else { var ret = stream.path; if (namesize < ret.length + 1) { return ___setErrNo(ERRNO_CODES.ERANGE); } else { for (var i = 0; i < ret.length; i++) { {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('name', 'i', '0', 'i8') }}} return 0; } } } }, symlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], symlink: function(path1, path2) { // int symlink(const char *path1, const char *path2); // http://pubs.opengroup.org/onlinepubs/000095399/functions/symlink.html var path = FS.analyzePath(Pointer_stringify(path2), true); if (!path.parentExists) { ___setErrNo(path.error); return -1; } else if (path.exists) { ___setErrNo(ERRNO_CODES.EEXIST); return -1; } else { FS.createLink(path.parentPath, path.name, Pointer_stringify(path1), true, true); return 0; } }, readlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], readlink: function(path, buf, bufsize) { // ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize); // http://pubs.opengroup.org/onlinepubs/000095399/functions/readlink.html var target = FS.findObject(Pointer_stringify(path), true); if (target === null) return -1; if (target.link !== undefined) { var length = Math.min(bufsize - 1, target.link.length); for (var i = 0; i < length; i++) { {{{ makeSetValue('buf', 'i', 'target.link.charCodeAt(i)', 'i8') }}} } if (bufsize - 1 > length) {{{ makeSetValue('buf', 'i', '0', 'i8') }}} return i; } else { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } }, pwrite__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], pwrite: function(fildes, buf, nbyte, offset) { // ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset); // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html var stream = FS.streams[fildes]; if (!stream || stream.object.isDevice) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else if (!stream.isWrite) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else if (stream.object.isFolder) { ___setErrNo(ERRNO_CODES.EISDIR); return -1; } else if (nbyte < 0 || offset < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { var contents = stream.object.contents; while (contents.length < offset) contents.push(0); for (var i = 0; i < nbyte; i++) { contents[offset + i] = {{{ makeGetValue('buf', 'i', 'i8') }}}; } stream.object.timestamp = Date.now(); return i; } }, write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'pwrite'], write: function(fildes, buf, nbyte) { // ssize_t write(int fildes, const void *buf, size_t nbyte); // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html var stream = FS.streams[fildes]; if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } else if (!stream.isWrite) { ___setErrNo(ERRNO_CODES.EACCES); return -1; } else if (nbyte < 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { if (stream.object.isDevice) { if (stream.object.output) { for (var i = 0; i < nbyte; i++) { try { stream.object.output({{{ makeGetValue('buf', 'i', 'i8') }}}); } catch (e) { ___setErrNo(ERRNO_CODES.EIO); return -1; } } stream.object.timestamp = Date.now(); return i; } else { ___setErrNo(ERRNO_CODES.ENXIO); return -1; } } else { var bytesWritten = _pwrite(fildes, buf, nbyte, stream.position); if (bytesWritten != -1) stream.position += bytesWritten; return bytesWritten; } } }, alarm: function(seconds) { // unsigned alarm(unsigned seconds); // http://pubs.opengroup.org/onlinepubs/000095399/functions/alarm.html // We don't support signals, and there's no way to indicate failure, so just // fail silently. return 0; }, ualarm: 'alarm', confstr__deps: ['__setErrNo', '$ERRNO_CODES', '$ENV'], confstr: function(name, buf, len) { // 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') }}}: // Mimicing 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 __shutdownRuntime__(); ABORT = true; throw 'exit(' + status + ') called, at ' + new Error().stack; }, 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', getgid: function() { // gid_t getgid(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getgid.html // We have just one process/group/user, all with ID 0. return 0; }, getegid: 'getgid', getuid: 'getgid', geteuid: 'getgid', getpgrp: 'getgid', getpid: 'getgid', getppid: 'getgid', getresuid: function(ruid, euid, suid) { // int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); // http://linux.die.net/man/2/getresuid // We have just one process/group/user, all with ID 0. {{{ makeSetValue('ruid', '0', '0', 'i32') }}} {{{ makeSetValue('euid', '0', '0', 'i32') }}} {{{ makeSetValue('suid', '0', '0', 'i32') }}} return 0; }, getresgid: 'getresuid', getgroups__deps: ['__setErrNo', '$ERRNO_CODES'], getgroups: function(gidsetsize, grouplist) { // int getgroups(int gidsetsize, gid_t grouplist[]); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getgroups.html if (gidsetsize < 1) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else { {{{ makeSetValue('grouplist', '0', '0', 'i32') }}} return 1; } }, // TODO: Implement initgroups, setgroups (grp.h). gethostid: function() { // long gethostid(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/gethostid.html return 42; }, gethostname__deps: ['__setErrNo', '$ERRNO_CODES'], gethostname: function(name, namelen) { // int gethostname(char *name, size_t namelen); // http://pubs.opengroup.org/onlinepubs/000095399/functions/gethostname.html var host = 'emscripten'; if (typeof window !== 'undefined' && window.location.host) { host = window.location.host; } var length = Math.min(namelen, host.length); for (var i = 0; i < length; i++) { {{{ makeSetValue('name', 'i', 'host.charCodeAt(i)', 'i8') }}} } if (namelen > length) { {{{ makeSetValue('name', 'i', '0', 'i8') }}} return 0; } else { ___setErrNo(ERRNO_CODES.ENAMETOOLONG); return -1; } }, getlogin__deps: ['getlogin_r'], getlogin: function() { // char *getlogin(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getlogin.html if (!_getlogin.ret) _getlogin.ret = _malloc(8); return _getlogin_r(_getlogin.ret, 8) ? 0 : _getlogin.ret; }, getlogin_r__deps: ['__setErrNo', '$ERRNO_CODES'], getlogin_r: function(name, namesize) { // int getlogin_r(char *name, size_t namesize); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getlogin.html var ret = 'root'; if (namesize < ret.length + 1) { return ___setErrNo(ERRNO_CODES.ERANGE); } else { for (var i = 0; i < ret.length; i++) { {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('name', 'i', '0', 'i8') }}} return 0; } }, getopt: function(argc, argv, optstring) { // int getopt(int argc, char * const argv[], const char *optstring); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html // TODO: Implement (probably compile from C). return -1; }, getpgid: function(pid) { // pid_t getpgid(pid_t pid); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getpgid.html // There can only be one process, and its group ID is 0. return 0; }, getsid: 'getpgid', nice__deps: ['__setErrNo', '$ERRNO_CODES'], nice: function(incr) { // int nice(int incr); // http://pubs.opengroup.org/onlinepubs/000095399/functions/nice.html // Niceness makes no sense in a single-process environment. ___setErrNo(ERRNO_CODES.EPERM); return 0; }, pause__deps: ['__setErrNo', '$ERRNO_CODES'], pause: function() { // int pause(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/pause.html // We don't support signals, so we return immediately. ___setErrNo(ERRNO_CODES.EINTR); return -1; }, setgid__deps: ['__setErrNo', '$ERRNO_CODES'], setgid: function(gid) { // int setgid(gid_t gid); // http://pubs.opengroup.org/onlinepubs/000095399/functions/setgid.html // We have just one process/group/user, so it makes no sense to set IDs. ___setErrNo(ERRNO_CODES.EPERM); return -1; }, setegid: 'setgid', setuid: 'setgid', seteuid: 'setgid', setsid: 'setgid', setpgrp: 'setgid', setpgid__deps: ['__setErrNo', '$ERRNO_CODES'], setpgid: function(pid, pgid) { // int setpgid(pid_t pid, pid_t pgid); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getpgid.html // We have just one process/group/user, so it makes no sense to set IDs. ___setErrNo(ERRNO_CODES.EPERM); return -1; }, setregid: 'setpgid', setreuid: 'setpgid', // NOTE: These do not match the signatures, but they all use the same stub. setresuid: 'setpgid', setresgid: 'setpgid', sleep__deps: ['usleep'], sleep: function(seconds) { // unsigned sleep(unsigned seconds); // http://pubs.opengroup.org/onlinepubs/000095399/functions/sleep.html return _usleep(seconds * 1e6); }, usleep: function(useconds) { // int usleep(useconds_t useconds); // http://pubs.opengroup.org/onlinepubs/000095399/functions/usleep.html // We're single-threaded, so use a busy loop. Super-ugly. var msec = useconds / 1000; var start = Date.now(); while (Date.now() - start < msec) { // Do nothing. } return 0; }, swab: function(src, dest, nbytes) { // void swab(const void *restrict src, void *restrict dest, ssize_t nbytes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/swab.html if (nbytes < 0) return; nbytes -= nbytes % 2; for (var i = 0; i < nbytes; i += 2) { var first = {{{ makeGetValue('src', 'i', 'i8') }}}; var second = {{{ makeGetValue('src', 'i + 1', 'i8') }}}; {{{ makeSetValue('dest', 'i', 'second', 'i8') }}} {{{ makeSetValue('dest', 'i + 1', 'first', 'i8') }}} } }, tcgetpgrp: function(fildes) { // pid_t tcgetpgrp(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/tcgetpgrp.html // Our only process always runs with group ID 0. return 0; }, tcsetpgrp__deps: ['__setErrNo', '$ERRNO_CODES'], tcsetpgrp: function(fildes, pgid_id) { // int tcsetpgrp(int fildes, pid_t pgid_id); // http://pubs.opengroup.org/onlinepubs/000095399/functions/tcsetpgrp.html // We don't support multiple processes or groups with ID other than 0. ___setErrNo(ERRNO_CODES.EINVAL); return -1; }, sysconf__deps: ['__setErrNo', '$ERRNO_CODES'], sysconf: function(name) { // 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_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_PRIORITY_SCHEDULING') }}}: 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_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; } ___setErrNo(ERRNO_CODES.EINVAL); return -1; }, sbrk: function(bytes) { // 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 need to make sure no one else allocates unfreeable memory! // We must control this entirely. So we don't even need to do // unfreeable allocations - the HEAP is ours, from STATICTOP up. // TODO: We could in theory slice off the top of the HEAP when // sbrk gets a negative increment in |bytes|... var self = _sbrk; if (!self.STATICTOP) { STATICTOP = alignMemoryPage(STATICTOP); self.STATICTOP = STATICTOP; self.DATASIZE = 0; } else { assert(self.STATICTOP == STATICTOP, "No one should touch the heap!"); } var ret = STATICTOP + self.DATASIZE; self.DATASIZE += alignMemoryPage(bytes); return ret; // Previous break location. }, open64: 'open', lseek64: 'lseek', ftruncate64: 'ftruncate', __01open64_: 'open', __01lseek64_: 'lseek', __01truncate64_: 'truncate', __01ftruncate64_: 'ftruncate', // TODO: Check if any other aliases are needed. // ========================================================================== // stdio.h // ========================================================================== // TODO: Document. _scanString: function(format, get, unget, varargs) { // Supports %x, %4x, %d.%d, %s. // TODO: Support all format specifiers. format = Pointer_stringify(format); var formatIndex = 0; var argsi = 0; var fields = 0; var argIndex = 0; for (var formatIndex = 0; formatIndex < format.length; formatIndex++) { var next = get(); if (next <= 0) return fields; // End of input. if (format[formatIndex] === '%') { formatIndex++; var maxSpecifierStart = formatIndex; while (format[formatIndex].charCodeAt(0) >= '0'.charCodeAt(0) && format[formatIndex].charCodeAt(0) <= '9'.charCodeAt(0)) { formatIndex++; } var max_; if (formatIndex != maxSpecifierStart) { max_ = parseInt(format.slice(maxSpecifierStart, formatIndex), 10); } // TODO: Handle type size modifier. var type = format[formatIndex]; formatIndex++; var curr = 0; var buffer = []; while ((curr < max_ || isNaN(max_)) && next > 0) { if ((type === 'd' && next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) || (type === 'x' && (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0) || next >= 'a'.charCodeAt(0) && next <= 'f'.charCodeAt(0) || next >= 'A'.charCodeAt(0) && next <= 'F'.charCodeAt(0))) || (type === 's')) { buffer.push(String.fromCharCode(next)); next = get(); curr++; } else { break; } } if (buffer.length === 0) return 0; // Failure. var text = buffer.join(''); var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; argIndex += Runtime.getNativeFieldSize('void*'); switch (type) { case 'd': {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i32') }}} break; case 'x': {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}} break; case 's': var array = intArrayFromString(text); for (var j = 0; j < array.length; j++) { {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}} } break; } fields++; } else { // Not a specifier. if (format[formatIndex].charCodeAt(0) !== next) { unget(next); return fields; } } } return fields; }, // Performs prtinf-style formatting. // format: A pointer to the format string. // varargs: A pointer to the start of the arguments list. // Returns the resulting string string as a character array. _formatString: function(format, varargs) { var textIndex = format; var argIndex = 0; var getNextArg = function(type) { // NOTE: Explicitly ignoring type safety. Otherwise this fails: // int x = 4; printf("%c\n", (char)x); var ret; if (type === 'float' || type === 'double') { ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true) }}}; } else { ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}; } argIndex += Runtime.getNativeFieldSize(type); return Number(ret); }; var ret = []; var curr, next, currArg; while(1) { var startTextIndex = textIndex; curr = {{{ makeGetValue(0, 'textIndex', 'i8') }}}; if (curr === 0) break; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; if (curr == '%'.charCodeAt(0)) { // Handle flags. var flagAlwaysSigned = false; var flagLeftAlign = false; var flagAlternative = false; var flagZeroPad = false; flagsLoop: while (1) { switch (next) { case '+'.charCodeAt(0): flagAlwaysSigned = true; break; case '-'.charCodeAt(0): flagLeftAlign = true; break; case '#'.charCodeAt(0): flagAlternative = true; break; case '0'.charCodeAt(0): if (flagZeroPad) { break flagsLoop; } else { flagZeroPad = true; break; } default: break flagsLoop; } textIndex++; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; } // Handle width. var width = 0; if (next == '*'.charCodeAt(0)) { width = getNextArg('i32'); textIndex++; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; } else { while (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) { width = width * 10 + (next - '0'.charCodeAt(0)); textIndex++; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; } } // Handle precision. var precisionSet = false; if (next == '.'.charCodeAt(0)) { var precision = 0; precisionSet = true; textIndex++; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; if (next == '*'.charCodeAt(0)) { precision = getNextArg('i32'); textIndex++; } else { while(1) { var precisionChr = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; if (precisionChr < '0'.charCodeAt(0) || precisionChr > '9'.charCodeAt(0)) break; precision = precision * 10 + (precisionChr - '0'.charCodeAt(0)); textIndex++; } } next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; } else { var precision = 6; // Standard default. } // Handle integer sizes. WARNING: These assume a 32-bit architecture! var argSize; switch (String.fromCharCode(next)) { case 'h': var nextNext = {{{ makeGetValue(0, 'textIndex+2', 'i8') }}}; if (nextNext == 'h'.charCodeAt(0)) { textIndex++; argSize = 1; // char } else { argSize = 2; // short } break; case 'l': var nextNext = {{{ makeGetValue(0, 'textIndex+2', 'i8') }}}; if (nextNext == 'l'.charCodeAt(0)) { textIndex++; argSize = 8; // long long } else { argSize = 4; // long } break; case 'L': // long long case 'q': // int64_t case 'j': // intmax_t argSize = 8; break; case 'z': // size_t case 't': // ptrdiff_t case 'I': // signed ptrdiff_t or unsigned size_t argSize = 4; break; default: argSize = undefined; } if (argSize !== undefined) textIndex++; next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; // Handle type specifier. if (['d', 'i', 'u', 'o', 'x', 'X', 'p'].indexOf(String.fromCharCode(next)) != -1) { // Integer. var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0); argSize = argSize || 4; var currArg = getNextArg('i' + (argSize * 8)); // Truncate to requested size. if (argSize <= 4) { var limit = Math.pow(256, argSize) - 1; currArg = (signed ? reSign : unSign)(currArg & limit, argSize * 8); } // Format the number. var currAbsArg = Math.abs(currArg); var argText; var prefix = ''; if (next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0)) { argText = reSign(currArg, 8 * argSize, 1).toString(10); } else if (next == 'u'.charCodeAt(0)) { argText = unSign(currArg, 8 * argSize, 1).toString(10); currArg = Math.abs(currArg); } else if (next == 'o'.charCodeAt(0)) { argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8); } else if (next == 'x'.charCodeAt(0) || next == 'X'.charCodeAt(0)) { prefix = flagAlternative ? '0x' : ''; if (currArg < 0) { // Represent negative numbers in hex as 2's complement. currArg = -currArg; argText = (currAbsArg - 1).toString(16); var buffer = []; for (var i = 0; i < argText.length; i++) { buffer.push((0xF - parseInt(argText[i], 16)).toString(16)); } argText = buffer.join(''); while (argText.length < argSize * 2) argText = 'f' + argText; } else { argText = currAbsArg.toString(16); } if (next == 'X'.charCodeAt(0)) { prefix = prefix.toUpperCase(); argText = argText.toUpperCase(); } } else if (next == 'p'.charCodeAt(0)) { if (currAbsArg === 0) { argText = '(nil)'; } else { prefix = '0x'; argText = currAbsArg.toString(16); } } if (precisionSet) { while (argText.length < precision) { argText = '0' + argText; } } // Add sign if needed if (flagAlwaysSigned) { if (currArg < 0) { prefix = '-' + prefix; } else { prefix = '+' + prefix; } } // Add padding. while (prefix.length + argText.length < width) { if (flagLeftAlign) { argText += ' '; } else { if (flagZeroPad) { argText = '0' + argText; } else { prefix = ' ' + prefix; } } } // Insert the result into the buffer. argText = prefix + argText; argText.split('').forEach(function(chr) { ret.push(chr.charCodeAt(0)); }); } else if (['f', 'F', 'e', 'E', 'g', 'G'].indexOf(String.fromCharCode(next)) != -1) { // Float. var currArg = getNextArg(argSize === 4 ? 'float' : 'double'); var argText; if (isNaN(currArg)) { argText = 'nan'; flagZeroPad = false; } else if (!isFinite(currArg)) { argText = (currArg < 0 ? '-' : '') + 'inf'; flagZeroPad = false; } else { var isGeneral = false; var effectivePrecision = Math.min(precision, 20); // Convert g/G to f/F or e/E, as per: // http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html if (next == 'g'.charCodeAt(0) || next == 'G'.charCodeAt(0)) { isGeneral = true; precision = precision || 1; var exponent = parseInt(currArg.toExponential(effectivePrecision).split('e')[1], 10); if (precision > exponent && exponent >= -4) { next = ((next == 'g'.charCodeAt(0)) ? 'f' : 'F').charCodeAt(0); precision -= exponent + 1; } else { next = ((next == 'g'.charCodeAt(0)) ? 'e' : 'E').charCodeAt(0); precision--; } effectivePrecision = Math.min(precision, 20); } if (next == 'e'.charCodeAt(0) || next == 'E'.charCodeAt(0)) { argText = currArg.toExponential(effectivePrecision); // Make sure the exponent has at least 2 digits. if (/[eE][-+]\d$/.test(argText)) { argText = argText.slice(0, -1) + '0' + argText.slice(-1); } } else if (next == 'f'.charCodeAt(0) || next == 'F'.charCodeAt(0)) { argText = currArg.toFixed(effectivePrecision); } var parts = argText.split('e'); if (isGeneral && !flagAlternative) { // Discard trailing zeros and periods. while (parts[0].length > 1 && parts[0].indexOf('.') != -1 && (parts[0].slice(-1) == '0' || parts[0].slice(-1) == '.')) { parts[0] = parts[0].slice(0, -1); } } else { // Make sure we have a period in alternative mode. if (flagAlternative && argText.indexOf('.') == -1) parts[0] += '.'; // Zero pad until required precision. while (precision > effectivePrecision++) parts[0] += '0'; } argText = parts[0] + (parts.length > 1 ? 'e' + parts[1] : ''); // Capitalize 'E' if needed. if (next == 'E'.charCodeAt(0)) argText = argText.toUpperCase(); // Add sign. if (flagAlwaysSigned && currArg >= 0) { argText = '+' + argText; } } // Add padding. while (argText.length < width) { if (flagLeftAlign) { argText += ' '; } else { if (flagZeroPad && (argText[0] == '-' || argText[0] == '+')) { argText = argText[0] + '0' + argText.slice(1); } else { argText = (flagZeroPad ? '0' : ' ') + argText; } } } // Adjust case. if (next < 'a'.charCodeAt(0)) argText = argText.toUpperCase(); // Insert the result into the buffer. argText.split('').forEach(function(chr) { ret.push(chr.charCodeAt(0)); }); } else if (next == 's'.charCodeAt(0)) { // String. var arg = getNextArg('i8*'); var copiedString; if (arg) { copiedString = String_copy(arg); if (precisionSet && copiedString.length > precision) { copiedString = copiedString.slice(0, precision); } } else { copiedString = intArrayFromString('(null)', true); } if (!flagLeftAlign) { while (copiedString.length < width--) { ret.push(' '.charCodeAt(0)); } } ret = ret.concat(copiedString); if (flagLeftAlign) { while (copiedString.length < width--) { ret.push(' '.charCodeAt(0)); } } } else if (next == 'c'.charCodeAt(0)) { // Character. if (flagLeftAlign) ret.push(getNextArg('i8')); while (--width > 0) { ret.push(' '.charCodeAt(0)); } if (!flagLeftAlign) ret.push(getNextArg('i8')); } else if (next == 'n'.charCodeAt(0)) { // Write the length written so far to the next parameter. var ptr = getNextArg('i32*'); {{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}} } else if (next == '%'.charCodeAt(0)) { // Literal percent sign. ret.push(curr); } else { // Unknown specifiers remain untouched. for (var i = startTextIndex; i < textIndex + 2; i++) { ret.push({{{ makeGetValue(0, 'i', 'i8') }}}); } } textIndex += 2; // TODO: Support a/A (hex float) and m (last error) specifiers. // TODO: Support %1${specifier} for arg selection. } else { ret.push(curr); textIndex += 1; } } return ret; }, // NOTE: Invalid stream pointers passed to these functions would cause a crash // in native code. We, on the other hand, just ignore them, since it's // easier. clearerr__deps: ['$FS'], clearerr: function(stream) { // void clearerr(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html if (stream in FS.streams) FS.streams[stream].error = false; }, fclose__deps: ['close', 'fsync'], fclose: function(stream) { // int fclose(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fclose.html _fsync(stream); return _close(stream); }, fdopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fdopen: function(fildes, mode) { // FILE *fdopen(int fildes, const char *mode); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fdopen.html if (fildes in FS.streams) { var stream = FS.streams[fildes]; mode = Pointer_stringify(mode); if ((mode.indexOf('w') != -1 && !stream.isWrite) || (mode.indexOf('r') != -1 && !stream.isRead) || (mode.indexOf('a') != -1 && !stream.isAppend) || (mode.indexOf('+') != -1 && (!stream.isRead || !stream.isWrite))) { ___setErrNo(ERRNO_CODES.EINVAL); return 0; } else { stream.error = false; stream.eof = false; return fildes; } } else { ___setErrNo(ERRNO_CODES.EBADF); return 0; } }, feof__deps: ['$FS'], feof: function(stream) { // int feof(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html return Number(stream in FS.streams && FS.streams[stream].eof); }, ferror__deps: ['$FS'], ferror: function(stream) { // int ferror(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ferror.html return Number(stream in FS.streams && FS.streams[stream].error); }, fflush__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fflush: function(stream) { // int fflush(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fflush.html var flush = function(filedes) { // Right now we write all data directly, except for output devices. if (filedes in FS.streams && FS.streams[filedes].object.output) { FS.streams[filedes].object.output(null); } }; try { if (stream === 0) { for (var i in FS.streams) flush(i); } else { flush(stream); } return 0; } catch (e) { ___setErrNo(ERRNO_CODES.EIO); return -1; } }, fgetc__deps: ['$FS', 'read'], fgetc__postset: '_fgetc.ret = allocate([0], "i8", ALLOC_STATIC);', fgetc: function(stream) { // int fgetc(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetc.html if (!(stream in FS.streams)) return -1; var streamObj = FS.streams[stream]; if (streamObj.eof || streamObj.error) return -1; var ret = _read(stream, _fgetc.ret, 1); if (ret == 0) { streamObj.eof = true; return -1; } else if (ret == -1) { streamObj.error = true; return -1; } else { return {{{ makeGetValue('_fgetc.ret', '0', 'i8') }}}; } }, getc: 'fgetc', getc_unlocked: 'fgetc', getchar__deps: ['fgetc', 'stdin'], getchar: function() { // int getchar(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/getchar.html return _fgetc({{{ makeGetValue('_stdin', '0', 'void*') }}}); }, fgetpos__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], fgetpos: function(stream, pos) { // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetpos.html if (stream in FS.streams) { stream = FS.streams[stream]; if (stream.object.isDevice) { ___setErrNo(ERRNO_CODES.ESPIPE); return -1; } else { {{{ makeSetValue('pos', '0', 'stream.position', 'i32') }}} var state = (stream.eof ? 1 : 0) + (stream.error ? 2 : 0); {{{ makeSetValue('pos', Runtime.getNativeTypeSize('i32'), 'state', 'i32') }}} return 0; } } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, fgets__deps: ['fgetc'], fgets: function(s, n, stream) { // char *fgets(char *restrict s, int n, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgets.html if (!(stream in FS.streams)) return 0; var streamObj = FS.streams[stream]; if (streamObj.error || streamObj.eof) return 0; var byte_; for (var i = 0; i < n - 1 && byte_ != '\n'.charCodeAt(0); i++) { byte_ = _fgetc(stream); if (byte_ == -1) { if (streamObj.error) return 0; else if (streamObj.eof) break; } {{{ makeSetValue('s', 'i', 'byte_', 'i8') }}} } {{{ makeSetValue('s', 'i', '0', 'i8') }}} return s; }, gets__deps: ['fgets'], gets: function(s) { // char *gets(char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/gets.html return _fgets(s, 1e6, {{{ makeGetValue('_stdin', '0', 'void*') }}}); }, fileno: function(stream) { // int fileno(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fileno.html // We use file descriptor numbers and FILE* streams interchangeably. return stream; }, ftrylockfile: function() { // int ftrylockfile(FILE *file); // http://pubs.opengroup.org/onlinepubs/000095399/functions/flockfile.html // Locking is useless in a single-threaded environment. Pretend to succeed. return 0; }, flockfile: 'ftrylockfile', funlockfile: 'ftrylockfile', fopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'open'], fopen: function(filename, mode) { // FILE *fopen(const char *restrict filename, const char *restrict mode); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fopen.html var flags; mode = Pointer_stringify(mode); if (mode[0] == 'r') { if (mode.indexOf('+') != -1) { flags = {{{ cDefine('O_RDWR') }}}; } else { flags = {{{ cDefine('O_RDONLY') }}}; } } else if (mode[0] == 'w') { if (mode.indexOf('+') != -1) { flags = {{{ cDefine('O_RDWR') }}}; } else { flags = {{{ cDefine('O_WRONLY') }}}; } flags |= {{{ cDefine('O_CREAT') }}}; flags |= {{{ cDefine('O_TRUNC') }}}; } else if (mode[0] == 'a') { if (mode.indexOf('+') != -1) { flags = {{{ cDefine('O_RDWR') }}}; } else { flags = {{{ cDefine('O_WRONLY') }}}; } flags |= {{{ cDefine('O_CREAT') }}}; flags |= {{{ cDefine('O_APPEND') }}}; } else { ___setErrNo(ERRNO_CODES.EINVAL); return 0; } var ret = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions. return (ret == -1) ? 0 : ret; }, fputc__deps: ['$FS', 'write'], fputc__postset: '_fputc.ret = allocate([0], "i8", ALLOC_STATIC);', fputc: function(c, stream) { // int fputc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputc.html var chr = unSign(c & 0xFF); {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}} var ret = _write(stream, _fputc.ret, 1); if (ret == -1) { if (stream in FS.streams) FS.streams[stream].error = true; return -1; } else { return chr; } }, putc: 'fputc', putc_unlocked: 'fputc', putchar__deps: ['fputc', 'stdout'], putchar: function(c) { // int putchar(int c); // http://pubs.opengroup.org/onlinepubs/000095399/functions/putchar.html return _fputc(c, {{{ makeGetValue('_stdout', '0', 'void*') }}}); }, putchar_unlocked: 'putchar', fputs__deps: ['write', 'strlen'], fputs: function(s, stream) { // int fputs(const char *restrict s, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputs.html return _write(stream, s, _strlen(s)); }, puts__deps: ['fputs', 'fputc', 'stdout'], puts: function(s) { // int puts(const char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/puts.html // NOTE: puts() always writes an extra newline. var stdout = {{{ makeGetValue('_stdout', '0', 'void*') }}}; var ret = _fputs(s, stdout); if (ret < 0) { return ret; } else { var newlineRet = _fputc('\n'.charCodeAt(0), stdout); return (newlineRet < 0) ? -1 : ret + 1; } }, fread__deps: ['$FS', 'read'], fread: function(ptr, size, nitems, stream) { // size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html var bytesToRead = nitems * size; if (bytesToRead == 0) return 0; var bytesRead = _read(stream, ptr, bytesToRead); var streamObj = FS.streams[stream]; if (bytesRead == -1) { if (streamObj) streamObj.error = true; return -1; } else { if (bytesRead < bytesToRead) streamObj.eof = true; return Math.floor(bytesRead / size); } }, freopen__deps: ['$FS', 'fclose', 'fopen', '__setErrNo', '$ERRNO_CODES'], freopen: function(filename, mode, stream) { // FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/freopen.html if (!filename) { if (!(stream in FS.streams)) { ___setErrNo(ERRNO_CODES.EBADF); return 0; } if (_freopen.buffer) _free(_freopen.buffer); filename = intArrayFromString(FS.streams[stream].path); filename = allocate(filename, 'i8', ALLOC_NORMAL); } _fclose(stream); return _fopen(filename, mode); }, fseek__deps: ['$FS', 'lseek'], fseek: function(stream, offset, whence) { // int fseek(FILE *stream, long offset, int whence); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fseek.html var ret = _lseek(stream, offset, whence); if (ret == -1) { return -1; } else { FS.streams[stream].eof = false; return 0; } }, fseeko: 'fseek', fseeko64: 'fseek', fsetpos__deps: ['$FS', 'lseek', '__setErrNo', '$ERRNO_CODES'], fsetpos: function(stream, pos) { // int fsetpos(FILE *stream, const fpos_t *pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsetpos.html if (stream in FS.streams) { if (FS.streams[stream].object.isDevice) { ___setErrNo(ERRNO_CODES.EPIPE); return -1; } else { FS.streams[stream].position = {{{ makeGetValue('pos', '0', 'i32') }}}; var state = {{{ makeGetValue('pos', Runtime.getNativeTypeSize('i32'), 'i32') }}}; FS.streams[stream].eof = Boolean(state & 1); FS.streams[stream].error = Boolean(state & 2); return 0; } } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, ftell__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], ftell: function(stream) { // long ftell(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftell.html if (stream in FS.streams) { stream = FS.streams[stream]; if (stream.object.isDevice) { ___setErrNo(ERRNO_CODES.ESPIPE); return -1; } else { return stream.position; } } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; } }, ftello: 'ftell', ftello64: 'ftell', fwrite__deps: ['$FS', 'write'], fwrite: function(ptr, size, nitems, stream) { // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fwrite.html var bytesToWrite = nitems * size; if (bytesToWrite == 0) return 0; var bytesWritten = _write(stream, ptr, bytesToWrite); if (bytesWritten == -1) { if (FS.streams[stream]) FS.streams[stream].error = true; return -1; } else { return Math.floor(bytesWritten / size); } }, popen__deps: ['__setErrNo', '$ERRNO_CODES'], popen: function(command, mode) { // FILE *popen(const char *command, const char *mode); // http://pubs.opengroup.org/onlinepubs/000095399/functions/popen.html // We allow only one process, so no pipes. ___setErrNo(ERRNO_CODES.EMFILE); return 0; }, pclose__deps: ['__setErrNo', '$ERRNO_CODES'], pclose: function(stream) { // int pclose(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/pclose.html // We allow only one process, so no pipes. ___setErrNo(ERRNO_CODES.ECHILD); return -1; }, perror__deps: ['puts', 'putc', 'strerror', '__errno_location'], perror: function(s) { // void perror(const char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/perror.html if (s) { _puts(s); _putc(':'.charCodeAt(0)); _putc(' '.charCodeAt(0)); } var errnum = {{{ makeGetValue('___errno_location()', '0', 'i32') }}}; _puts(_strerror(errnum)); }, remove__deps: ['unlink', 'rmdir'], remove: function(path) { // int remove(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/remove.html var ret = _unlink(path); if (ret == -1) ret = _rmdir(path); return ret; }, rename__deps: ['__setErrNo', '$ERRNO_CODES'], rename: function(old, new_) { // int rename(const char *old, const char *new); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rename.html var oldObj = FS.analyzePath(Pointer_stringify(old)); var newObj = FS.analyzePath(Pointer_stringify(new_)); if (newObj.path == oldObj.path) { return 0; } else if (!oldObj.exists) { ___setErrNo(oldObj.error); return -1; } else if (oldObj.isRoot || oldObj.path == FS.currentPath) { ___setErrNo(ERRNO_CODES.EBUSY); return -1; } else if (newObj.path && newObj.path.indexOf(oldObj.path) == 0) { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } else if (newObj.exists && newObj.object.isFolder) { ___setErrNo(ERRNO_CODES.EISDIR); return -1; } else { delete oldObj.parentObject.contents[oldObj.name]; newObj.parentObject.contents[newObj.name] = oldObj.object; return 0; } }, rewind__deps: ['$FS', 'fseek'], rewind: function(stream) { // void rewind(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rewind.html _fseek(stream, 0, 0); // SEEK_SET. if (stream in FS.streams) FS.streams[stream].error = false; }, setvbuf: function(stream, buf, type, size) { // int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size); // http://pubs.opengroup.org/onlinepubs/000095399/functions/setvbuf.html // TODO: Implement custom buffering. return 0; }, setbuf__deps: ['setvbuf'], setbuf: function(stream, buf) { // void setbuf(FILE *restrict stream, char *restrict buf); // http://pubs.opengroup.org/onlinepubs/000095399/functions/setbuf.html if (buf) _setvbuf(stream, buf, 0, 8192); // _IOFBF, BUFSIZ. else _setvbuf(stream, buf, 2, 8192); // _IONBF, BUFSIZ. }, tmpnam__deps: ['$FS'], tmpnam: function(s, dir, prefix) { // char *tmpnam(char *s); // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpnam.html // NOTE: The dir and prefix arguments are for internal use only. var folder = FS.findObject(dir || '/tmp'); if (!folder || !folder.isFolder) { dir = '/tmp'; folder = FS.findObject(dir); if (!folder || !folder.isFolder) return 0; } var name = prefix || 'file'; do { name += String.fromCharCode(65 + Math.floor(Math.random() * 25)); } while (name in folder.contents); var result = dir + '/' + name; if (!_tmpnam.buffer) _tmpnam.buffer = _malloc(256); if (!s) s = _tmpnam.buffer; for (var i = 0; i < result.length; i++) { {{{ makeSetValue('s', 'i', 'result.charCodeAt(i)', 'i8') }}}; } {{{ makeSetValue('s', 'i', '0', 'i8') }}}; return s; }, tempnam__deps: ['tmpnam'], tempnam: function(dir, pfx) { // char *tempnam(const char *dir, const char *pfx); // http://pubs.opengroup.org/onlinepubs/000095399/functions/tempnam.html return _tmpnam(0, Pointer_stringify(dir), Pointer_stringify(pfx)); }, tmpfile__deps: ['tmpnam', 'fopen'], tmpfile: function() { // FILE *tmpfile(void); // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpfile.html // TODO: Delete the created file on closing. if (_tmpfile.mode) { _tmpfile.mode = allocate(intArrayFromString('w+'), 'i8', ALLOC_NORMAL); } return _fopen(_tmpnam(0), _tmpfile.mode); }, ungetc__deps: ['$FS'], ungetc: function(c, stream) { // int ungetc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ungetc.html if (stream in FS.streams) { c = unSign(c & 0xFF); FS.streams[stream].ungotten.push(c); return c; } else { return -1; } }, 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; }, fscanf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '_scanString', 'getc', 'ungetc'], fscanf: function(stream, format, varargs) { // int fscanf(FILE *restrict stream, const char *restrict format, ... ); // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html if (stream in FS.streams) { var get = function() { return _fgetc(stream); }; var unget = function(c) { return _ungetc(c, stream); }; return __scanString(format, get, unget, varargs); } else { return -1; } }, scanf__deps: ['fscanf'], scanf: function(format, varargs) { // int scanf(const char *restrict format, ... ); // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html var stdin = {{{ makeGetValue('_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; var get = function() { return {{{ makeGetValue('s', 'index++', 'i8') }}}; }; var unget = function() { index--; }; return __scanString(format, get, unget, varargs); }, snprintf__deps: ['_formatString'], 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, n - 1); for (var i = 0; i < limit; i++) { {{{ makeSetValue('s', 'i', 'result[i]', 'i8') }}}; } {{{ 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, ...); // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html var result = __formatString(format, varargs); var stack = Runtime.stackSave(); var ret = _fwrite(allocate(result, 'i8', ALLOC_STACK), 1, result.length, stream); Runtime.stackRestore(stack); return ret; }, printf__deps: ['fprintf'], printf: function(format, varargs) { // int printf(const char *restrict format, ...); // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html var stdout = {{{ makeGetValue('_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); }, vfprintf: 'fprintf', vsnprintf: 'snprintf', vprintf: 'printf', vsprintf: 'sprintf', vscanf: 'scanf', vfscanf: 'fscanf', vsscanf: 'sscanf', fopen64: 'fopen', __01fopen64_: 'fopen', __01freopen64_: 'freopen', __01fseeko64_: 'fseek', __01ftello64_: 'ftell', __01tmpfile64_: 'tmpfile', __isoc99_fscanf: 'fscanf', // TODO: Check if any other aliases are needed. _IO_getc: 'getc', _IO_putc: 'putc', _ZNSo3putEc: 'putchar', _ZNSo5flushEv__deps: ['fflush', 'stdout'], _ZNSo5flushEv: function() { _fflush({{{ makeGetValue('_stdout', '0', 'void*') }}}); }, // ========================================================================== // sys/mman.h // ========================================================================== mmap__deps: ['$FS'], mmap: function(start, num, prot, flags, stream, offset) { // FIXME: Leaky and non-shared. var info = FS.streams[stream]; if (!info) return -1; return allocate(info.object.contents.slice(offset, offset+num), 'i8', ALLOC_NORMAL); }, __01mmap64_: 'mmap', munmap: function(start, num) { // FIXME: Not really correct at all. _free(start); }, // TODO: Implement mremap. // ========================================================================== // stdlib.h // ========================================================================== malloc: Runtime.staticAlloc, _Znwj: 'malloc', _Znaj: 'malloc', _Znam: 'malloc', _Znwm: 'malloc', free: function(){}, _ZdlPv: 'free', _ZdaPv: 'free', calloc__deps: ['malloc'], calloc: function(n, s) { var ret = _malloc(n*s); _memset(ret, 0, n*s); return ret; }, abs: 'Math.abs', // XXX should be integer? atoi: function(s) { return Math.floor(Number(Pointer_stringify(s))); }, exit__deps: ['_exit'], exit: function(status) { __exit(status); }, atexit: function(func, arg) { __ATEXIT__.push({ func: func, arg: arg }); }, __cxa_atexit: 'atexit', abort: function(code) { ABORT = true; throw 'ABORT: ' + code + ', at ' + (new Error().stack); }, realloc__deps: ['memcpy'], realloc: function(ptr, size) { // Very simple, inefficient implementation - if you use a real malloc, best to use // a real realloc with it if (!size) { if (ptr) _free(ptr); return 0; } var ret = _malloc(size); if (ptr) { _memcpy(ret, ptr, size); // might be some invalid reads _free(ptr); } return ret; }, strtod__deps: ['isspace', 'isdigit'], strtod: function(str, endptr) { // Skip space. while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; // Check for a plus/minus sign. var multiplier = 1; if ({{{ makeGetValue('str', 0, 'i8') }}} == '-'.charCodeAt(0)) { multiplier = -1; str++; } else if ({{{ makeGetValue('str', 0, 'i8') }}} == '+'.charCodeAt(0)) { str++; } var chr; var ret = 0; // Get whole part. while(1) { chr = {{{ makeGetValue('str', 0, 'i8') }}}; if (!_isdigit(chr)) break; ret = ret*10 + chr - '0'.charCodeAt(0); str++; } // Get fractional part. if ({{{ makeGetValue('str', 0, 'i8') }}} == '.'.charCodeAt(0)) { str++; var mul = 1/10; while(1) { chr = {{{ makeGetValue('str', 0, 'i8') }}}; if (!_isdigit(chr)) break; ret += mul*(chr - '0'.charCodeAt(0)); mul /= 10; str++; } } // Get exponent part. chr = {{{ makeGetValue('str', 0, 'i8') }}}; if (chr == 'e'.charCodeAt(0) || chr == 'E'.charCodeAt(0)) { str++; var exponent = 0; var expNegative = false; chr = {{{ makeGetValue('str', 0, 'i8') }}}; if (chr == '-'.charCodeAt(0)) { expNegative = true; str++; } else if (chr == '+'.charCodeAt(0)) { str++; } chr = {{{ makeGetValue('str', 0, 'i8') }}}; while(1) { if (!_isdigit(chr)) break; exponent = exponent*10 + chr - '0'.charCodeAt(0); str++; chr = {{{ makeGetValue('str', 0, 'i8') }}}; } if (expNegative) exponent = -exponent; ret *= Math.pow(10, exponent); } // Set end pointer. if (endptr) { {{{ makeSetValue('endptr', 0, 'str', '*') }}} } return ret * multiplier; }, _parseInt__deps: ['isspace', '__setErrNo', '$ERRNO_CODES'], _parseInt: function(str, endptr, base, min, max, unsignBits) { // Skip space. while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; // Check for a plus/minus sign. var multiplier = 1; if ({{{ makeGetValue('str', 0, 'i8') }}} == '-'.charCodeAt(0)) { multiplier = -1; str++; } else if ({{{ makeGetValue('str', 0, 'i8') }}} == '+'.charCodeAt(0)) { str++; } // Find base. var finalBase = base; if (!finalBase) { if ({{{ makeGetValue('str', 0, 'i8') }}} == '0'.charCodeAt(0)) { if ({{{ makeGetValue('str+1', 0, 'i8') }}} == 'x'.charCodeAt(0) || {{{ makeGetValue('str+1', 0, 'i8') }}} == 'X'.charCodeAt(0)) { finalBase = 16; str += 2; } else { finalBase = 8; str++; } } } if (!finalBase) finalBase = 10; // Get digits. var chr; var ret = 0; while ((chr = {{{ makeGetValue('str', 0, 'i8') }}}) != 0) { var digit = parseInt(String.fromCharCode(chr), finalBase); if (isNaN(digit)) { break; } else { ret = ret * finalBase + digit; str++; } } // Apply sign. ret *= multiplier; // Set end pointer. if (endptr) { {{{ makeSetValue('endptr', 0, 'str', '*') }}} } // Unsign if needed. if (unsignBits) { if (Math.abs(ret) > max) { ret = max; ___setErrNo(ERRNO_CODES.ERANGE); } else { ret = unSign(ret, unsignBits); } } // Validate range. if (ret > max || ret < min) { ret = ret > max ? max : min; ___setErrNo(ERRNO_CODES.ERANGE); } return ret; }, strtoll__deps: ['_parseInt'], strtoll: function(str, endptr, base) { return __parseInt(str, endptr, base, -9223372036854775808, 9223372036854775807); // LLONG_MIN, LLONG_MAX; imprecise. }, strtol__deps: ['_parseInt'], strtol: function(str, endptr, base) { return __parseInt(str, endptr, base, -2147483648, 2147483647); // LONG_MIN, LONG_MAX. }, strtoul__deps: ['_parseInt'], strtoul: function(str, endptr, base) { return __parseInt(str, endptr, base, 0, 4294967295, 32); // ULONG_MAX. }, strtoull__deps: ['_parseInt'], strtoull: function(str, endptr, base) { return __parseInt(str, endptr, base, 0, 18446744073709551615, 64); // ULONG_MAX; imprecise. }, qsort__deps: ['memcpy'], qsort: function(base, num, size, comparator) { if (num == 0 || size == 0) return; // forward calls to the JavaScript sort method // first, sort the items logically comparator = FUNCTION_TABLE[comparator]; var keys = []; for (var i = 0; i < num; i++) keys.push(i); keys.sort(function(a, b) { return comparator(base+a*size, base+b*size); }); // apply the sort var temp = _malloc(num*size); _memcpy(temp, base, num*size); for (var i = 0; i < num; i++) { if (keys[i] == i) continue; // already in place _memcpy(base+i*size, temp+keys[i]*size, size); } _free(temp); }, environ: null, __environ: null, __buildEnvironment__deps: ['environ', '__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 (_environ === null) { // Set default values. Use string keys for Closure Compiler compatibility. ENV['USER'] = 'root'; ENV['PATH'] = '/'; ENV['PWD'] = '/'; ENV['HOME'] = '/'; ENV['LANG'] = 'en_US.UTF-8'; ENV['_'] = './this.program'; // Allocate memory. poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC); envPtr = allocate(MAX_ENV_VALUES * {{{ QUANTUM_SIZE }}}, 'i8*', ALLOC_STATIC); {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}} _environ = allocate([envPtr], 'i8**', ALLOC_STATIC); // Set up global variable alias. ___environ = _environ; } else { envPtr = {{{ makeGetValue('_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]; for (var j = 0; j < line.length; j++) { {{{ makeSetValue('poolPtr', 'j', 'line.charCodeAt(j)', 'i8') }}} } {{{ makeSetValue('poolPtr', 'j', '0', 'i8') }}} {{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}} poolPtr += line.length + 1; } {{{ makeSetValue('envPtr', 'strings.length * ptrSize', '0', 'i8*') }}} }, $ENV__deps: ['__buildEnvironment'], $ENV__postset: '___buildEnvironment(ENV);', $ENV: {}, getenv__deps: ['$ENV'], getenv: function(name) { // 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) { // 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) { // 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) { // 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) { // 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; }, // A glibc-like implementation of the C random number generation functions: // http://pubs.opengroup.org/onlinepubs/000095399/functions/rand.html __rand_state: 42, srand__deps: ['__rand_state'], srand: function(seed) { // void srand(unsigned seed); ___rand_state = seed; }, rand__deps: ['__rand_state'], rand: function() { // int rand(void); ___rand_state = (1103515245 * ___rand_state + 12345) % 0x100000000; return ___rand_state & 0x7FFFFFFF; }, rand_r: function(seed) { // int rand_r(unsigned *seed); var state = {{{ makeGetValue('seed', 0, 'i32') }}}; state = (1103515245 * state + 12345) % 0x100000000; {{{ makeSetValue('seed', 0, 'state', 'i32') }}} return state & 0x7FFFFFFF; }, realpath__deps: ['$FS', '__setErrNo'], realpath: function(file_name, resolved_name) { // char *realpath(const char *restrict file_name, char *restrict resolved_name); // http://pubs.opengroup.org/onlinepubs/009604499/functions/realpath.html var absolute = FS.analyzePath(Pointer_stringify(file_name)); if (absolute.error) { ___setErrNo(absolute.error); return 0; } else { var size = Math.min(4095, absolute.path.length); // PATH_MAX - 1. for (var i = 0; i < size; i++) { {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('resolved_name', 'size', '0', 'i8') }}} return resolved_name; } }, // ========================================================================== // string.h // ========================================================================== memcpy__inline: function (dest, src, num, idunno) { var ret = ''; #if ASSERTIONS ret += "assert(" + num + " % 1 === 0, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with QUANTUM_SIZE=1 corrections perhaps?');"; #endif ret += makeCopyValues(dest, src, num, 'null'); return ret; }, memcpy: function (dest, src, num, idunno) { #if ASSERTIONS assert(num % 1 === 0, 'memcpy given ' + num + ' bytes to copy. Problem with QUANTUM_SIZE=1 corrections perhaps?'); #endif {{{ makeCopyValues('dest', 'src', 'num', 'null') }}}; }, llvm_memcpy_i32: 'memcpy', llvm_memcpy_i64: 'memcpy', llvm_memcpy_p0i8_p0i8_i32: 'memcpy', llvm_memcpy_p0i8_p0i8_i64: 'memcpy', memmove__deps: ['memcpy'], memmove: function(dest, src, num, idunno) { // not optimized! if (num === 0) return; // will confuse malloc if 0 var tmp = _malloc(num); _memcpy(tmp, src, num); _memcpy(dest, tmp, num); _free(tmp); }, 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) { return makeSetValues(ptr, 0, value, 'null', num); }, memset: function(ptr, value, num) { {{{ makeSetValues('ptr', '0', 'value', 'null', 'num') }}} }, llvm_memset_i32: 'memset', llvm_memset_p0i8_i32: 'memset', llvm_memset_p0i8_i64: 'memset', strlen: function(ptr) { return String_len(ptr); }, // TODO: Implement when we have real unicode support. mblen: function() { return 1; }, strspn: function(pstr, pset) { var str = String_copy(pstr, true); var set = String_copy(pset); var i = 0; while (set.indexOf(str[i]) != -1) i++; // Must halt, as 0 is in str but not set return i; }, strcspn: function(pstr, pset) { var str = String_copy(pstr, true); var set = String_copy(pset, true); var i = 0; while (set.indexOf(str[i]) == -1) i++; // Must halt, as 0 is in both return i; }, strcpy: function(pdest, psrc) { var i = 0; do { {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8') }}} i ++; } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0); return pdest; }, stpcpy: function(pdest, psrc) { var i = 0; do { {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8') }}} i ++; } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0); return pdest + i - 1; }, strncpy: function(pdest, psrc, num) { var padding = false, curr; for (var i = 0; i < num; i++) { curr = padding ? 0 : {{{ makeGetValue('psrc', 'i', 'i8') }}}; {{{ makeSetValue('pdest', 'i', 'curr', 'i8') }}} padding = padding || {{{ makeGetValue('psrc', 'i', 'i8') }}} == 0; } return pdest; }, strcat__deps: ['strlen'], strcat: function(pdest, psrc) { var len = _strlen(pdest); var i = 0; do { {{{ makeCopyValues('pdest+len+i', 'psrc+i', 1, 'i8') }}} i ++; } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0); return pdest; }, strncat__deps: ['strlen'], strncat: function(pdest, psrc, num) { var len = _strlen(pdest); var i = 0; while(1) { {{{ makeCopyValues('pdest+len+i', 'psrc+i', 1, 'i8') }}} if ({{{ makeGetValue('pdest', 'len+i', 'i8') }}} == 0) break; i ++; if (i == num) { {{{ makeSetValue('pdest', 'len+i', 0, 'i8') }}} break; } } return pdest; }, strcmp__deps: ['strncmp'], strcmp: function(px, py) { return _strncmp(px, py, TOTAL_MEMORY); }, // We always assume ASCII locale. strcoll: 'strcmp', strcasecmp__deps: ['strncasecmp'], strcasecmp: function(px, py) { return _strncasecmp(px, py, TOTAL_MEMORY); }, strncmp: function(px, py, n) { var i = 0; while (i < n) { var x = {{{ makeGetValue('px', 'i', 'i8') }}}; var y = {{{ makeGetValue('py', 'i', 'i8') }}}; if (x == y && x == 0) return 0; if (x == 0) return -1; if (y == 0) return 1; if (x == y) { i ++; continue; } else { return x > y ? 1 : -1; } } return 0; }, strncasecmp__deps: ['tolower'], strncasecmp: function(px, py, n) { var i = 0; while (i < n) { var x = _tolower({{{ makeGetValue('px', 'i', 'i8') }}}); var y = _tolower({{{ makeGetValue('py', 'i', 'i8') }}}); if (x == y && x == 0) return 0; if (x == 0) return -1; if (y == 0) return 1; if (x == y) { i ++; continue; } else { return x > y ? 1 : -1; } } return 0; }, memcmp: function(p1, p2, num) { for (var i = 0; i < num; i++) { var v1 = {{{ makeGetValue('p1', 'i', 'i8') }}}; var v2 = {{{ makeGetValue('p2', 'i', 'i8') }}}; if (v1 != v2) return v1 > v2 ? 1 : -1; } return 0; }, memchr: function(ptr, chr, num) { chr = unSign(chr); for (var i = 0; i < num; i++) { if ({{{ makeGetValue('ptr', 0, 'i8') }}} == chr) return ptr; ptr++; } return 0; }, strstr: function(ptr1, ptr2) { var str1 = Pointer_stringify(ptr1); var str2 = Pointer_stringify(ptr2); var ret = str1.search(str2); return ret >= 0 ? ptr1 + ret : 0; }, strchr: function(ptr, chr) { ptr--; do { ptr++; var val = {{{ makeGetValue('ptr', 0, 'i8') }}}; if (val == chr) return ptr; } while (val); return 0; }, strrchr__deps: ['strlen'], strrchr: function(ptr, chr) { var ptr2 = ptr + _strlen(ptr); do { if ({{{ makeGetValue('ptr2', 0, 'i8') }}} == chr) return ptr2; ptr2--; } while (ptr2 >= ptr); return 0; }, strdup: function(ptr) { var len = String_len(ptr); var newStr = _malloc(len + 1); {{{ makeCopyValues('newStr', 'ptr', 'len', 'null') }}}; {{{ makeSetValue('newStr', 'len', '0', 'i8') }}}; return newStr; }, strpbrk: function(ptr1, ptr2) { var searchSet = Runtime.set.apply(null, String_copy(ptr2)); while ({{{ makeGetValue('ptr1', 0, 'i8') }}}) { if ({{{ makeGetValue('ptr1', 0, 'i8') }}} in searchSet) return ptr1; ptr1++; } return 0; }, // Compiled from newlib; for the original source and licensing, see library_strtok_r.c XXX will not work with typed arrays strtok_r: function(b,j,f){var a;a=null;var c,e;b=b;var i=b!=0;a:do if(i)a=0;else{b=HEAP[f];if(b!=0){a=0;break a}c=0;a=3;break a}while(0);if(a==0){a:for(;;){e=HEAP[b];b+=1;a=j;var g=e;i=a;a=2;b:for(;;){d=a==5?d:0;a=HEAP[i+d];if(a!=0==0){a=9;break a}var d=d+1;if(g==a)break b;else a=5}a=2}if(a==9)if(g==0)c=HEAP[f]=0;else{c=b+-1;a:for(;;){e=HEAP[b];b+=1;a=j;g=e;d=a;a=10;b:for(;;){h=a==13?h:0;a=HEAP[d+h];if(a==g!=0)break a;var h=h+1;if(a!=0)a=13;else break b}}if(e==0)b=0;else HEAP[b+-1]=0; HEAP[f]=b;c=c}else if(a==7){HEAP[f]=b;HEAP[b+-1]=0;c=b+-1}}return c}, // TODO: Compile strtok() from source. strerror_r__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo'], strerror_r: function(errnum, strerrbuf, buflen) { if (errnum in ERRNO_MESSAGES) { if (ERRNO_MESSAGES[errnum].length > buflen - 1) { return ___setErrNo(ERRNO_CODES.ERANGE); } else { var msg = ERRNO_MESSAGES[errnum]; for (var i = 0; i < msg.length; i++) { {{{ makeSetValue('strerrbuf', 'i', 'msg.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('strerrbuf', 'i', 0, 'i8') }}} return 0; } } else { return ___setErrNo(ERRNO_CODES.EINVAL); } }, strerror__deps: ['strerror_r'], strerror: function(errnum) { if (!_strerror.buffer) _strerror.buffer = _malloc(256); _strerror_r(errnum, _strerror.buffer, 256); return _strerror.buffer; }, // ========================================================================== // ctype.h // ========================================================================== isascii: function(chr) { return chr >= 0 && (chr & 0x80) == 0; }, toascii: function(chr) { return chr & 0x7F; }, toupper: function(chr) { if (chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0)) { return chr - 'a'.charCodeAt(0) + 'A'.charCodeAt(0); } else { return chr; } }, _toupper: 'toupper', tolower: function(chr) { if (chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0)) { return chr - 'A'.charCodeAt(0) + 'a'.charCodeAt(0); } else { return chr; } }, _tolower: 'tolower', // The following functions are defined as macros in glibc. islower: function(chr) { return chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0); }, isupper: function(chr) { return chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0); }, isalpha: function(chr) { return (chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0)) || (chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0)); }, isdigit: function(chr) { return chr >= '0'.charCodeAt(0) && chr <= '9'.charCodeAt(0); }, isxdigit: function(chr) { return (chr >= '0'.charCodeAt(0) && chr <= '9'.charCodeAt(0)) || (chr >= 'a'.charCodeAt(0) && chr <= 'f'.charCodeAt(0)) || (chr >= 'A'.charCodeAt(0) && chr <= 'F'.charCodeAt(0)); }, isalnum: function(chr) { return (chr >= '0'.charCodeAt(0) && chr <= '9'.charCodeAt(0)) || (chr >= 'a'.charCodeAt(0) && chr <= 'z'.charCodeAt(0)) || (chr >= 'A'.charCodeAt(0) && chr <= 'Z'.charCodeAt(0)); }, ispunct: function(chr) { return (chr >= '!'.charCodeAt(0) && chr <= '/'.charCodeAt(0)) || (chr >= ':'.charCodeAt(0) && chr <= '@'.charCodeAt(0)) || (chr >= '['.charCodeAt(0) && chr <= '`'.charCodeAt(0)) || (chr >= '{'.charCodeAt(0) && chr <= '~'.charCodeAt(0)); }, isspace: function(chr) { return chr in { 32: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0 }; }, isblank: function(chr) { return chr == ' '.charCodeAt(0) || chr == '\t'.charCodeAt(0); }, iscntrl: function(chr) { return (0 <= chr && chr <= 0x1F) || chr === 0x7F; }, isprint: function(chr) { return 0x1F < chr && chr < 0x7F; }, isgraph: 'isprint', // Lookup tables for glibc ctype implementation. __ctype_b_loc: function() { // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html var me = ___ctype_b_loc; if (!me.ret) { var values = [ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,8195,8194,8194,8194,8194,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,24577,49156,49156,49156, 49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,55304,55304,55304,55304,55304,55304,55304,55304, 55304,55304,49156,49156,49156,49156,49156,49156,49156,54536,54536,54536,54536,54536,54536,50440,50440,50440,50440,50440, 50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,49156,49156,49156,49156,49156, 49156,54792,54792,54792,54792,54792,54792,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696, 50696,50696,50696,50696,50696,50696,50696,49156,49156,49156,49156,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 ]; var i16size = {{{ Runtime.getNativeTypeSize('i16') }}}; var arr = _malloc(values.length * i16size); for (var i = 0; i < values.length; i++) { {{{ makeSetValue('arr', 'i * i16size', 'values[i]', 'i16') }}} } me.ret = allocate([arr + 128 * i16size], 'i16*', ALLOC_NORMAL); } return me.ret; }, __ctype_tolower_loc: function() { // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-tolower-loc.html var me = ___ctype_tolower_loc; if (!me.ret) { var values = [ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, 158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187, 188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217, 218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, 248,249,250,251,252,253,254,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103, 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95,96,97,98,99,100,101,102,103, 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163, 164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193, 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, 254,255 ]; var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; var arr = _malloc(values.length * i32size); for (var i = 0; i < values.length; i++) { {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} } me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); } return me.ret; }, __ctype_toupper_loc: function() { // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-toupper-loc.html var me = ___ctype_toupper_loc; if (!me.ret) { var values = [ 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, 158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187, 188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217, 218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, 248,249,250,251,252,253,254,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72, 73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, 81,82,83,84,85,86,87,88,89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144, 145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, 175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204, 205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234, 235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 ]; var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; var arr = _malloc(values.length * i32size); for (var i = 0; i < values.length; i++) { {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} } me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); } return me.ret; }, // ========================================================================== // LLVM specifics // ========================================================================== llvm_va_start__inline: function(ptr) { // varargs - we received a pointer to the varargs as a final 'extra' parameter var data = 'arguments[' + Framework.currItem.funcData.ident + '.length]'; return makeSetValue(ptr, 0, data, 'void*'); }, llvm_va_end: function() {}, llvm_va_copy: function(ppdest, ppsrc) { {{{ makeCopyValues('ppdest', 'ppsrc', QUANTUM_SIZE, 'null') }}} /* Alternate implementation that copies the actual DATA; it assumes the va_list is prefixed by its size var psrc = IHEAP[ppsrc]-1; var num = IHEAP[psrc]; // right before the data, is the number of (flattened) values var pdest = _malloc(num+1); _memcpy(pdest, psrc, num+1); IHEAP[ppdest] = pdest+1; */ }, llvm_bswap_i32: function(x) { x = unSign(x, 32); var bytes = []; for (var i = 0; i < 4; i++) { bytes[i] = x & 255; x >>= 8; } var ret = 0; for (i = 0; i < 4; i++) { ret <<= 8; ret += bytes[i]; } return ret; }, llvm_ctlz_i32: function(x) { for (var i=0; i<32; i++) { if ( (x & (1 << (31-i))) != 0 ) { return i; } } return 32; }, __assert_fail: function(condition, file, line) { ABORT = true; throw 'Assertion failed: ' + Pointer_stringify(condition);//JSON.stringify(arguments)//condition; }, __assert_func: function(filename, line, func, condition) { throw 'Assertion failed: ' + Pointer_stringify(condition) + ', at: ' + [Pointer_stringify(filename), line, Pointer_stringify(func)]; }, __cxa_guard_acquire: function() { return 1; }, __cxa_guard_release: function() { return 1; }, // Exceptions __cxa_allocate_exception: function(size) { return _malloc(size); }, __cxa_free_exception: function(ptr) { return _free(ptr); }, __cxa_throw__deps: ['llvm_eh_exception'], __cxa_throw: function(ptr, type, destructor) { #if EXCEPTION_DEBUG print('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack); #endif {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}} {{{ makeSetValue('_llvm_eh_exception.buf', '4', 'type', 'void*') }}} {{{ makeSetValue('_llvm_eh_exception.buf', '8', 'destructor', 'void*') }}} throw ptr; }, __cxa_rethrow__deps: ['llvm_eh_exception', '__cxa_end_catch'], __cxa_rethrow: function() { ___cxa_end_catch.rethrown = true; throw {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; }, llvm_eh_exception__postset: '_llvm_eh_exception.buf = allocate(12, "void*", ALLOC_STATIC);', llvm_eh_exception: function() { return {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; }, llvm_eh_selector__jsargs: true, llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { var type = {{{ makeGetValue('_llvm_eh_exception.buf', '4', 'void*') }}} for (var i = 2; i < arguments.length; i++) { if (arguments[i] == type) return type; } return 0; }, llvm_eh_typeid_for: function(type) { return type; }, _Unwind_Resume_or_Rethrow: function(ptr) { throw ptr; }, __cxa_begin_catch: function(ptr) { return ptr; }, __cxa_end_catch__deps: ['llvm_eh_exception', '__cxa_free_exception'], __cxa_end_catch: function() { if (___cxa_end_catch.rethrown) { ___cxa_end_catch.rethrown = false; return; } // Clear state flag. __THREW__ = false; // Clear type. {{{ makeSetValue('_llvm_eh_exception.buf', '4', '0', 'void*') }}} // Call destructor if one is registered then clear it. var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', '8', 'void*') }}}; if (destructor) { FUNCTION_TABLE[destructor](ptr); {{{ makeSetValue('_llvm_eh_exception.buf', '8', '0', 'i32') }}} } // Free ptr if it isn't null. if (ptr) { ___cxa_free_exception(ptr); {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}} } }, __cxa_get_exception_ptr__deps: ['llvm_eh_exception'], __cxa_get_exception_ptr: function(ptr) { return ptr; }, __cxa_call_unexpected: function(exception) { ABORT = true; throw exception; }, terminate: '__cxa_call_unexpected', __gxx_personality_v0: function() { }, // RTTI hacks for exception handling, defining type_infos for common types. // The values are dummies. We simply use the addresses of these statically // allocated variables as unique identifiers. // type_info for int. _ZTIi: [0], // type_info for long. _ZTIl: [0], // type_info for long long. _ZTIx: [0], // type_info for float. _ZTIf: [0], // type_info for double. _ZTId: [0], // type_info for char. _ZTIc: [0], // type_info for void. _ZTIv: [0], llvm_umul_with_overflow_i32: function(x, y) { return { f0: x*y, f1: 0 // We never overflow... for now }; }, 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(x, y) { return '((' + x + ')==(' + y + '))'; }, llvm_lifetime_start: function() {}, llvm_lifetime_end: function() {}, // ========================================================================== // iostream.h // ========================================================================== // libc++ $libcxx__postset: 'try { __ZNSt3__14coutE = 1 } catch(e){}; try { __ZNSt3__14cerrE = 2 } catch(e){};', $libcxx: {}, _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv__deps: ['fputs', '$libcxx'], _ZNSt3__113basic_ostreamIcNS_11char_traitsIcEEElsEPKv: function(stream, str) { _fputs(str, _stdout); // XXX stderr etc. }, // glibc _ZNSt8ios_base4InitC1Ev: function() { // need valid 'file descriptors' for glibc //__ZSt4cout = 1; //__ZSt4cerr = 2; }, _ZNSt8ios_base4InitD1Ev: '_ZNSt8ios_base4InitC1Ev', _ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_: 0, // endl _ZNSolsEd__deps: ['putchar'], _ZNSolsEd: function(undefined_stream, data) { _putchar('\n'.charCodeAt(0)); }, _ZNSolsEPFRSoS_E: '_ZNSolsEd', _ZNSolsEi__deps: ['putchar'], _ZNSolsEi: function(undefined_stream, data) { var str = String(data); for (var i = 0; i < str.length; i++) { _putchar(str.charCodeAt(i)); } }, _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc__deps: ['fputs', 'stdout'], _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc: function(undefined_stream, data) { _fputs(data, {{{ makeGetValue('_stdout', '0', 'void*') }}}); }, _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i: '_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc', // ========================================================================== // math.h // ========================================================================== cos: 'Math.cos', cosf: 'Math.cos', sin: 'Math.sin', sinf: 'Math.sin', tan: 'Math.tan', tanf: 'Math.tan', acos: 'Math.acos', acosf: 'Math.acos', asin: 'Math.asin', asinf: 'Math.asin', atan: 'Math.atan', atanf: 'Math.atan', atan2: 'Math.atan2', atan2f: 'Math.atan2', exp: 'Math.exp', expf: 'Math.exp', log: 'Math.log', logf: 'Math.log', sqrt: 'Math.sqrt', sqrtf: 'Math.sqrt', fabs: 'Math.abs', fabsf: 'Math.abs', ceil: 'Math.ceil', ceilf: 'Math.ceil', floor: 'Math.floor', floorf: 'Math.floor', pow: 'Math.pow', powf: 'Math.powf', 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', ldexp: function(x, exp_) { return x * Math.pow(2, exp_); }, ldexpf: 'ldexp', scalb: 'ldexp', scalbn: 'ldexp', scalbnf: 'ldexp', scalbln: 'ldexp', scalblnf: 'ldexp', cbrt: function(x) { return Math.pow(x, 1/3); }, cbrtf: 'cbrt', cbrtl: 'cbrt', modf: function(x, intpart) { {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'double') }}} return x - {{{ makeGetValue('intpart', 0, 'double') }}}; }, modff: function(x, intpart) { {{{ 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 raw_exp = Math.log(x)/Math.log(2); exp_ = Math.ceil(raw_exp); if (exp_ === raw_exp) exp_ += 1; sig = x/Math.pow(2, exp_); } {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}} return sig; }, frexpf: 'frexp', finite: function(x) { return isFinite(x); }, __finite: 'finite', isinf: function(x) { return !isNaN(x) && !isFinite(x); }, __isinf: 'isinf', isnan: function(x) { return isNaN(x); }, __isnan: 'isnan', copysign: function(a, b) { if (a < 0 === b < 0) return a; return -a; }, copysignf: 'copysign', hypot: function(a, b) { return Math.sqrt(a*a + b*b); }, hypotf: 'hypot', sinh: function(x) { var p = Math.pow(Math.E, x); return (p - (1 / p)) / 2; }, sinhf: 'sinh', cosh: function(x) { var p = Math.pow(Math.E, x); return (p + (1 / p)) / 2; }, coshf: 'cosh', tanh__deps: ['sinh', 'cosh'], tanh: function(x) { return _sinh(x) / _cosh(x); }, tanhf: 'tanh', asinh: function(x) { return Math.log(x + Math.sqrt(x * x + 1)); }, asinhf: 'asinh', acosh: function(x) { return Math.log(x * 1 + Math.sqrt(x * x - 1)); }, acoshf: 'acosh', atanh: function(x) { return Math.log((1 + x) / (1 - x)) / 2; }, atanhf: 'atanh', exp2: function(x) { return Math.pow(2, x); }, exp2f: 'exp2', expm1: function(x) { return Math.exp(x) - 1; }, expm1f: 'expm1', round: function(x) { return (x < 0) ? -Math.round(-x) : Math.round(x); }, roundf: 'round', lround: 'round', lroundf: 'round', llround: 'round', llroundf: 'round', rint: function(x) { return (x > 0) ? -Math.round(-x) : Math.round(x); }, rintf: 'rint', lrint: 'rint', lrintf: 'rint', llrint: 'rint', llrintf: 'rint', nearbyint: 'rint', nearbyintf: 'rint', trunc: function(x) { return (x < 0) ? Math.ceil(x) : Math.floor(x); }, truncf: 'trunc', fdim: function(x, y) { return (x > y) ? x - y : 0; }, fdimf: 'fdim', fmax: function(x, y) { return isNaN(x) ? y : isNaN(y) ? x : Math.max(x, y); }, fmaxf: 'fmax', fmin: function(x, y) { return isNaN(x) ? y : isNaN(y) ? x : Math.max(x, y); }, fminf: 'fmin', fma: function(x, y, z) { return x * y + z; }, fmaf: 'fma', fmod: function(x, y) { return x % y; }, fmodf: 'fmod', remainder: 'fmod', remainderf: 'fmod', log10: function(x) { return Math.log(x) / Math.LN10; }, log10f: 'log10', log1p: function(x) { return Math.log(1 + x); }, log1pf: 'log1p', log2: function(x) { return Math.log(x) / Math.LN2; }, log2f: 'log2', nan: function(x) { return NaN; }, nanf: 'nan', __fpclassifyf: function(x) { if (isNaN(x)) return {{{ cDefine('FP_NAN') }}}; if (!isFinite(x)) return {{{ cDefine('FP_INFINITE') }}}; if (x == 0) return {{{ cDefine('FP_ZERO') }}}; // FP_SUBNORMAL..? return {{{ cDefine('FP_NORMAL') }}}; }, __fpclassifyd: '__fpclassifyf', // ========================================================================== // sys/utsname.h // ========================================================================== __utsname_struct_layout: Runtime.generateStructInfo(null, '%struct.utsname'), uname__deps: ['__utsname_struct_layout'], uname: function(name) { // int uname(struct utsname *name); // http://pubs.opengroup.org/onlinepubs/009695399/functions/uname.html function copyString(element, value) { var offset = ___utsname_struct_layout[element]; for (var i = 0; i < value.length; i++) { {{{ makeSetValue('name', 'offset + i', 'value.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('name', 'offset + i', '0', 'i8') }}} } if (name === 0) { return -1; } else { copyString('sysname', 'Emscripten'); copyString('nodename', 'emscripten'); copyString('release', '1.0'); copyString('version', '#1'); copyString('machine', 'x86-JS'); return 0; } }, // ========================================================================== // 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. // ========================================================================== // Data for dlfcn.h. $DLFCN_DATA: { error: null, errorMsg: null, loadedLibs: {}, // handle -> [refcount, name, lib_object] loadedLibNames: {}, // name -> handle }, // void* dlopen(const char* filename, int flag); dlopen__deps: ['$DLFCN_DATA', '$FS', '$ENV'], dlopen: function(filename, flag) { // void *dlopen(const char *file, int mode); // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html filename = (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); if (DLFCN_DATA.loadedLibNames[filename]) { // Already loaded; increment ref count and return. var handle = DLFCN_DATA.loadedLibNames[filename]; DLFCN_DATA.loadedLibs[handle].refcount++; return handle; } var target = FS.findObject(filename); if (!target || target.isFolder || target.isDevice) { DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename; return 0; } else { FS.forceLoadFile(target); var lib_data = intArrayToString(target.contents); } try { var lib_module = eval(lib_data)(FUNCTION_TABLE.length); } catch (e) { #if ASSERTIONS print('Error in loading dynamic library: ' + e); #endif DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename; return 0; } // Not all browsers support Object.keys(). var handle = 1; for (var key in DLFCN_DATA.loadedLibs) { if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++; } DLFCN_DATA.loadedLibs[handle] = { refcount: 1, name: filename, module: lib_module, cached_functions: {} }; DLFCN_DATA.loadedLibNames[filename] = 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]; } } } return handle; }, // int dlclose(void* handle); dlclose__deps: ['$DLFCN_DATA'], dlclose: function(handle) { // int dlclose(void *handle); // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html if (!DLFCN_DATA.loadedLibs[handle]) { DLFCN_DATA.errorMsg = 'Tried to dlclose() unopened handle: ' + handle; return 1; } else { var lib_record = DLFCN_DATA.loadedLibs[handle]; if (lib_record.refcount-- == 0) { delete DLFCN_DATA.loadedLibNames[lib_record.name]; delete DLFCN_DATA.loadedLibs[handle]; } return 0; } }, // void* dlsym(void* handle, const char* symbol); dlsym__deps: ['$DLFCN_DATA'], 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_DATA.loadedLibs[handle]) { DLFCN_DATA.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle; return 0; } else { var lib = DLFCN_DATA.loadedLibs[handle]; if (!lib.module.hasOwnProperty(symbol)) { DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol + '" in dynamic lib: ' + lib.name); return 0; } else { if (lib.cached_functions.hasOwnProperty(symbol)) { return lib.cached_functions[symbol]; } else { var result = lib.module[symbol]; if (typeof result == 'function') { FUNCTION_TABLE.push(result); FUNCTION_TABLE.push(0); result = FUNCTION_TABLE.length - 2; lib.cached_functions = result; } return result; } } } }, // char* dlerror(void); dlerror__deps: ['$DLFCN_DATA'], dlerror: function() { // char *dlerror(void); // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html if (DLFCN_DATA.errorMsg === null) { return 0; } else { if (DLFCN_DATA.error) _free(DLFCN_DATA.error); var msgArr = intArrayFromString(DLFCN_DATA.errorMsg); DLFCN_DATA.error = allocate(msgArr, 'i8', ALLOC_NORMAL); DLFCN_DATA.errorMsg = null; return DLFCN_DATA.error; } }, // ========================================================================== // 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 Math.floor((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}}/1000)); }, time: function(ptr) { var ret = Math.floor(Date.now()/1000); if (ptr) { {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}} } return ret; }, difftime: function(time1, time0) { return time1 - time0; }, __tm_struct_layout: Runtime.generateStructInfo(null, '%struct.tm'), // Statically allocated time struct. __tm_current: 0, // Statically allocated timezone strings. __tm_timezones: {}, // Statically allocated time strings. __tm_formatted: 0, mktime__deps: ['__tm_struct_layout', 'tzset'], mktime: function(tmPtr) { _tzset(); var offsets = ___tm_struct_layout; var year = {{{ makeGetValue('tmPtr', 'offsets.tm_year', 'i32') }}}; var timestamp = new Date(year >= 1900 ? year : year + 1900, {{{ makeGetValue('tmPtr', 'offsets.tm_mon', 'i32') }}}, {{{ makeGetValue('tmPtr', 'offsets.tm_mday', 'i32') }}}, {{{ makeGetValue('tmPtr', 'offsets.tm_hour', 'i32') }}}, {{{ makeGetValue('tmPtr', 'offsets.tm_min', 'i32') }}}, {{{ makeGetValue('tmPtr', 'offsets.tm_sec', 'i32') }}}, 0).getTime() / 1000; {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'new Date(timestamp).getDay()', 'i32') }}} var yday = Math.round((timestamp - (new Date(year, 0, 1)).getTime()) / (1000 * 60 * 60 * 24)); {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} return timestamp; }, timelocal: 'mktime', gmtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'gmtime_r'], gmtime: function(time) { if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__); return _gmtime_r(time, ___tm_current); }, gmtime_r__deps: ['__tm_struct_layout', '__tm_timezones'], gmtime_r: function(time, tmPtr) { var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); var offsets = ___tm_struct_layout; {{{ makeSetValue('tmPtr', 'offsets.tm_sec', 'date.getUTCSeconds()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_min', 'date.getUTCMinutes()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_hour', 'date.getUTCHours()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_mday', 'date.getUTCDate()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_mon', 'date.getUTCMonth()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_year', 'date.getUTCFullYear()-1900', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'date.getUTCDay()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_gmtoff', '0', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', '0', 'i32') }}} var start = new Date(date.getFullYear(), 0, 1); var yday = Math.round((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} var timezone = "GMT"; if (!(timezone in ___tm_timezones)) { ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL); } {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}} return tmPtr; }, timegm__deps: ['mktime'], timegm: function(tmPtr) { _tzset(); var offset = {{{ makeGetValue('__timezone', 0, 'i32') }}}; var daylight = {{{ makeGetValue('__daylight', 0, 'i32') }}}; daylight = (daylight == 1) ? 60 * 60 : 0; var ret = _mktime(tmPtr) + offset - daylight; return ret; }, localtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'localtime_r'], localtime: function(time) { if (!___tm_current) ___tm_current = _malloc(___tm_struct_layout.__size__); return _localtime_r(time, ___tm_current); }, localtime_r__deps: ['__tm_struct_layout', '__tm_timezones', 'tzset'], localtime_r: function(time, tmPtr) { _tzset(); var offsets = ___tm_struct_layout; var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); {{{ makeSetValue('tmPtr', 'offsets.tm_sec', 'date.getSeconds()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_min', 'date.getMinutes()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_hour', 'date.getHours()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_mday', 'date.getDate()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_mon', 'date.getMonth()', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_year', 'date.getFullYear()-1900', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'date.getDay()', 'i32') }}} var start = new Date(date.getFullYear(), 0, 1); var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} {{{ makeSetValue('tmPtr', 'offsets.tm_gmtoff', 'start.getTimezoneOffset() * 60', 'i32') }}} var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset()); {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', 'dst', 'i32') }}} var timezone = date.toString().match(/\(([A-Z]+)\)/)[1]; if (!(timezone in ___tm_timezones)) { ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL); } {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}} return tmPtr; }, asctime__deps: ['malloc', '__tm_formatted', 'asctime_r'], asctime: function(tmPtr) { if (!___tm_formatted) ___tm_formatted = _malloc(26); return _asctime_r(tmPtr, ___tm_formatted); }, asctime_r__deps: ['__tm_formatted', 'mktime'], asctime_r: function(tmPtr, buf) { var date = new Date(_mktime(tmPtr)*1000); var formatted = date.toString(); var datePart = formatted.replace(/\d{4}.*/, '').replace(/ 0/, ' '); var timePart = formatted.match(/\d{2}:\d{2}:\d{2}/)[0]; formatted = datePart + timePart + ' ' + date.getFullYear() + '\n'; formatted.split('').forEach(function(chr, index) { {{{ makeSetValue('buf', 'index', 'chr.charCodeAt(0)', 'i8') }}} }); {{{ makeSetValue('buf', '25', '0', 'i8') }}} return buf; }, ctime__deps: ['localtime', 'asctime'], ctime: function(timer) { return _asctime(_localtime(timer)); }, ctime_r__deps: ['localtime', 'asctime'], ctime_r: function(timer, buf) { return _asctime_r(_localtime_r(timer, ___tm_current), buf); }, 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) _tzname: null, _daylight: null, _timezone: null, tzset__deps: ['_tzname', '_daylight', '_timezone'], tzset: function() { // TODO: Use (malleable) environment variables instead of system settings. if (__tzname) return; // glibc does not need the double __ __timezone = _malloc(QUANTUM_SIZE); {{{ makeSetValue('__timezone', '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} __daylight = _malloc(QUANTUM_SIZE); var winter = new Date(2000, 0, 1); var summer = new Date(2000, 6, 1); {{{ makeSetValue('__daylight', '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} var winterName = winter.toString().match(/\(([A-Z]+)\)/)[1]; var summerName = summer.toString().match(/\(([A-Z]+)\)/)[1]; var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); __tzname = _malloc(2 * QUANTUM_SIZE); // glibc does not need the double __ {{{ makeSetValue('__tzname', '0', 'winterNamePtr', 'i32') }}} {{{ makeSetValue('__tzname', QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} }, stime__deps: ['$ERRNO_CODES', '__setErrNo'], stime: function(when) { ___setErrNo(ERRNO_CODES.EPERM); return -1; }, strftime: function(s, maxsize, format, timeptr) { // 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 // TODO: Implement. return 0; }, 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 // TODO: Implement. return 0; }, getdate: function(string) { // struct tm *getdate(const char *string); // http://pubs.opengroup.org/onlinepubs/009695399/functions/getdate.html // TODO: Implement. return 0; }, // ========================================================================== // sys/time.h // ========================================================================== __timespec_struct_layout: Runtime.generateStructInfo(null, '%struct.timespec'), // TODO: Implement these for real. clock_gettime__deps: ['__timespec_struct_layout'], clock_gettime: function(clk_id, tp) { // int clock_gettime(clockid_t clk_id, struct timespec *tp); {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}} {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} return 0; }, clock_settime: function(clk_id, tp) { // int clock_settime(clockid_t clk_id, const struct timespec *tp); // Nothing. return 0; }, clock_getres__deps: ['__timespec_struct_layout'], clock_getres: function(clk_id, res) { // int clock_getres(clockid_t clk_id, struct timespec *res); {{{ makeSetValue('res', '___timespec_struct_layout.tv_sec', '1', 'i32') }}} {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} return 0; }, // TODO: Implement remaining functions. // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html gettimeofday: function(ptr) { // %struct.timeval = type { i32, i32 } var indexes = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); var now = Date.now(); {{{ makeSetValue('ptr', 'indexes[0]', 'Math.floor(now/1000)', 'i32') }}} // seconds {{{ makeSetValue('ptr', 'indexes[1]', 'Math.floor((now-1000*Math.floor(now/1000))*1000)', 'i32') }}} // microseconds return 0; }, // ========================================================================== // sys/times.h // ========================================================================== __tms_struct_layout: Runtime.generateStructInfo(null, '%struct.tms'), times__deps: ['__tms_struct_layout'], times: function(buffer) { // clock_t times(struct tms *buffer); // http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html // NOTE: This is fake, since we can't calculate real CPU time usage in JS. if (buffer !== 0) { {{{ makeSetValues('buffer', '0', '0', 'null', '___tms_struct_layout.__size__') }}} } return 0; }, // ========================================================================== // sys/types.h // ========================================================================== // NOTE: These are fake, since we don't support the C device creation API. // http://www.kernel.org/doc/man-pages/online/pages/man3/minor.3.html makedev: function(maj, min) { return 0; }, gnu_dev_makedev: 'makedev', major: function(dev) { return 0; }, gnu_dev_major: 'major', minor: function(dev) { return 0; }, gnu_dev_minor: 'minor', // ========================================================================== // setjmp.h // ========================================================================== setjmp: function(env) { // XXX print('WARNING: setjmp() not really implemented, will fail if longjmp() is actually called'); return 0; }, _setjmp: 'setjmp', longjmp: function(env, val) { // not really working... assert(0); }, _longjmp: 'longjmp', // ========================================================================== // signal.h // ========================================================================== signal: function(sig, func) { // TODO return 0; }, sigemptyset: function(set) { // int sigemptyset(sigset_t *set); // TODO: Implement for real; don't hardcode offsets. {{{ makeSetValue('set', '0', '0', 'i32') }}} {{{ makeSetValue('set', '4', '0', 'i32') }}} {{{ makeSetValue('set', '8', '0', 'i32') }}} {{{ makeSetValue('set', '12', '0', 'i32') }}} return 0; }, sigfillset: 'sigemptyset', sigdelset: 'sigemptyset', sigaction: function(set) { // int sigemptyset(sigset_t *set); // TODO: Implement for real. return 0; }, sigprocmask: 'sigaction', __libc_current_sigrtmin: function() { return 0; }, __libc_current_sigrtmax: function() { return 0; }, kill__deps: ['$ERRNO_CODES', '__setErrNo'], kill: function(pid, sig) { // int kill(pid_t pid, int sig); // http://pubs.opengroup.org/onlinepubs/000095399/functions/kill.html // Makes no sense in a single-process environment. ___setErrNo(ERRNO_CODES.EPERM); return -1; }, killpg: 'kill', // ========================================================================== // sys/wait.h // ========================================================================== wait__deps: ['$ERRNO_CODES', '__setErrNo'], wait: function(stat_loc) { // pid_t wait(int *stat_loc); // http://pubs.opengroup.org/onlinepubs/009695399/functions/wait.html // Makes no sense in a single-process environment. ___setErrNo(ERRNO_CODES.ECHILD); return -1; }, // NOTE: These aren't really the same, but we use the same stub for them all. waitid: 'wait', waitpid: 'wait', wait3: 'wait', wait4: 'wait', // ========================================================================== // locale.h // ========================================================================== setlocale: function(category, locale) { if (!_setlocale.ret) _setlocale.ret = allocate([0], 'i8', ALLOC_NORMAL); return _setlocale.ret; }, localeconv: function() { // %struct.timeval = type { char* decimal point, other stuff... } // var indexes = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); var me = _localeconv; if (!me.ret) { me.ret = allocate([allocate(intArrayFromString('.'), 'i8', ALLOC_NORMAL)], 'i8', ALLOC_NORMAL); // just decimal point, for now } return me.ret; }, // ========================================================================== // langinfo.h // ========================================================================== nl_langinfo: function(item) { // char *nl_langinfo(nl_item item); // http://pubs.opengroup.org/onlinepubs/000095399/functions/nl_langinfo.html var result; switch (item) { case {{{ cDefine('CODESET') }}}: result = 'ANSI_X3.4-1968'; break; case {{{ cDefine('D_T_FMT') }}}: result = '%a %b %e %H:%M:%S %Y'; break; case {{{ cDefine('D_FMT') }}}: result = '%m/%d/%y'; break; case {{{ cDefine('T_FMT') }}}: result = '%H:%M:%S'; break; case {{{ cDefine('T_FMT_AMPM') }}}: result = '%I:%M:%S %p'; break; case {{{ cDefine('AM_STR') }}}: result = 'AM'; break; case {{{ cDefine('PM_STR') }}}: result = 'PM'; break; case {{{ cDefine('DAY_1') }}}: result = 'Sunday'; break; case {{{ cDefine('DAY_2') }}}: result = 'Monday'; break; case {{{ cDefine('DAY_3') }}}: result = 'Tuesday'; break; case {{{ cDefine('DAY_4') }}}: result = 'Wednesday'; break; case {{{ cDefine('DAY_5') }}}: result = 'Thursday'; break; case {{{ cDefine('DAY_6') }}}: result = 'Friday'; break; case {{{ cDefine('DAY_7') }}}: result = 'Saturday'; break; case {{{ cDefine('ABDAY_1') }}}: result = 'Sun'; break; case {{{ cDefine('ABDAY_2') }}}: result = 'Mon'; break; case {{{ cDefine('ABDAY_3') }}}: result = 'Tue'; break; case {{{ cDefine('ABDAY_4') }}}: result = 'Wed'; break; case {{{ cDefine('ABDAY_5') }}}: result = 'Thu'; break; case {{{ cDefine('ABDAY_6') }}}: result = 'Fri'; break; case {{{ cDefine('ABDAY_7') }}}: result = 'Sat'; break; case {{{ cDefine('MON_1') }}}: result = 'January'; break; case {{{ cDefine('MON_2') }}}: result = 'February'; break; case {{{ cDefine('MON_3') }}}: result = 'March'; break; case {{{ cDefine('MON_4') }}}: result = 'April'; break; case {{{ cDefine('MON_5') }}}: result = 'May'; break; case {{{ cDefine('MON_6') }}}: result = 'June'; break; case {{{ cDefine('MON_7') }}}: result = 'July'; break; case {{{ cDefine('MON_8') }}}: result = 'August'; break; case {{{ cDefine('MON_9') }}}: result = 'September'; break; case {{{ cDefine('MON_10') }}}: result = 'October'; break; case {{{ cDefine('MON_11') }}}: result = 'November'; break; case {{{ cDefine('MON_12') }}}: result = 'December'; break; case {{{ cDefine('ABMON_1') }}}: result = 'Jan'; break; case {{{ cDefine('ABMON_2') }}}: result = 'Feb'; break; case {{{ cDefine('ABMON_3') }}}: result = 'Mar'; break; case {{{ cDefine('ABMON_4') }}}: result = 'Apr'; break; case {{{ cDefine('ABMON_5') }}}: result = 'May'; break; case {{{ cDefine('ABMON_6') }}}: result = 'Jun'; break; case {{{ cDefine('ABMON_7') }}}: result = 'Jul'; break; case {{{ cDefine('ABMON_8') }}}: result = 'Aug'; break; case {{{ cDefine('ABMON_9') }}}: result = 'Sep'; break; case {{{ cDefine('ABMON_10') }}}: result = 'Oct'; break; case {{{ cDefine('ABMON_11') }}}: result = 'Nov'; break; case {{{ cDefine('ABMON_12') }}}: result = 'Dec'; break; case {{{ cDefine('ALT_DIGITS') }}}: result = ''; break; case {{{ cDefine('RADIXCHAR') }}}: result = '.'; break; case {{{ cDefine('THOUSEP') }}}: result = ''; break; case {{{ cDefine('YESEXPR') }}}: result = '^[yY]'; break; case {{{ cDefine('NOEXPR') }}}: result = '^[nN]'; break; case {{{ cDefine('CRNCYSTR') }}}: result = '-'; break; case {{{ cDefine('ERA') }}}: case {{{ cDefine('ERA_D_FMT') }}}: case {{{ cDefine('ERA_D_T_FMT') }}}: case {{{ cDefine('ERA_T_FMT') }}}: default: result = ''; break; } var me = _nl_langinfo; if (!me.ret) me.ret = _malloc(32); for (var i = 0; i < result.length; i++) { {{{ makeSetValue('me.ret', 'i', 'result.charCodeAt(i)', 'i8') }}} } {{{ makeSetValue('me.ret', 'i', '0', 'i8') }}} return me.ret; }, // ========================================================================== // errno.h // ========================================================================== $ERRNO_CODES: { E2BIG: 7, EACCES: 13, EADDRINUSE: 98, EADDRNOTAVAIL: 99, EAFNOSUPPORT: 97, EAGAIN: 11, EALREADY: 114, EBADF: 9, EBADMSG: 74, EBUSY: 16, ECANCELED: 125, ECHILD: 10, ECONNABORTED: 103, ECONNREFUSED: 111, ECONNRESET: 104, EDEADLK: 35, EDESTADDRREQ: 89, EDOM: 33, EDQUOT: 122, EEXIST: 17, EFAULT: 14, EFBIG: 27, EHOSTUNREACH: 113, EIDRM: 43, EILSEQ: 84, EINPROGRESS: 115, EINTR: 4, EINVAL: 22, EIO: 5, EISCONN: 106, EISDIR: 21, ELOOP: 40, EMFILE: 24, EMLINK: 31, EMSGSIZE: 90, EMULTIHOP: 72, ENAMETOOLONG: 36, ENETDOWN: 100, ENETRESET: 102, ENETUNREACH: 101, ENFILE: 23, ENOBUFS: 105, ENODATA: 61, ENODEV: 19, ENOENT: 2, ENOEXEC: 8, ENOLCK: 37, ENOLINK: 67, ENOMEM: 12, ENOMSG: 42, ENOPROTOOPT: 92, ENOSPC: 28, ENOSR: 63, ENOSTR: 60, ENOSYS: 38, ENOTCONN: 107, ENOTDIR: 20, ENOTEMPTY: 39, ENOTRECOVERABLE: 131, ENOTSOCK: 88, ENOTSUP: 95, ENOTTY: 25, ENXIO: 6, EOVERFLOW: 75, EOWNERDEAD: 130, EPERM: 1, EPIPE: 32, EPROTO: 71, EPROTONOSUPPORT: 93, EPROTOTYPE: 91, ERANGE: 34, EROFS: 30, ESPIPE: 29, ESRCH: 3, ESTALE: 116, ETIME: 62, ETIMEDOUT: 110, ETXTBSY: 26, EWOULDBLOCK: 11, EXDEV: 18, }, $ERRNO_MESSAGES: { 2: 'No such file or directory', 13: 'Permission denied', 98: 'Address already in use', 99: 'Cannot assign requested address', 97: 'Address family not supported by protocol', 11: 'Resource temporarily unavailable', 114: 'Operation already in progress', 9: 'Bad file descriptor', 74: 'Bad message', 16: 'Device or resource busy', 125: 'Operation canceled', 10: 'No child processes', 103: 'Software caused connection abort', 111: 'Connection refused', 104: 'Connection reset by peer', 35: 'Resource deadlock avoided', 89: 'Destination address required', 33: 'Numerical argument out of domain', 122: 'Disk quota exceeded', 17: 'File exists', 14: 'Bad address', 27: 'File too large', 113: 'No route to host', 43: 'Identifier removed', 84: 'Invalid or incomplete multibyte or wide character', 115: 'Operation now in progress', 4: 'Interrupted system call', 22: 'Invalid argument', 5: 'Input/output error', 106: 'Transport endpoint is already connected', 21: 'Is a directory', 40: 'Too many levels of symbolic links', 24: 'Too many open files', 31: 'Too many links', 90: 'Message too long', 72: 'Multihop attempted', 36: 'File name too long', 100: 'Network is down', 102: 'Network dropped connection on reset', 101: 'Network is unreachable', 23: 'Too many open files in system', 105: 'No buffer space available', 61: 'No data available', 19: 'No such device', 8: 'Exec format error', 37: 'No locks available', 67: 'Link has been severed', 12: 'Cannot allocate memory', 42: 'No message of desired type', 92: 'Protocol not available', 28: 'No space left on device', 63: 'Out of streams resources', 60: 'Device not a stream', 38: 'Function not implemented', 107: 'Transport endpoint is not connected', 20: 'Not a directory', 39: 'Directory not empty', 131: 'State not recoverable', 88: 'Socket operation on non-socket', 95: 'Operation not supported', 25: 'Inappropriate ioctl for device', 6: 'No such device or address', 75: 'Value too large for defined data type', 130: 'Owner died', 1: 'Operation not permitted', 32: 'Broken pipe', 71: 'Protocol error', 93: 'Protocol not supported', 91: 'Protocol wrong type for socket', 34: 'Numerical result out of range', 30: 'Read-only file system', 29: 'Illegal seek', 3: 'No such process', 116: 'Stale NFS file handle', 62: 'Timer expired', 110: 'Connection timed out', 26: 'Text file busy', 18: 'Invalid cross-device link' }, __setErrNo__postset: '___setErrNo(0);', __setErrNo: function(value) { // For convenient setting and returning of errno. if (!___setErrNo.ret) ___setErrNo.ret = allocate([0], 'i32', ALLOC_STATIC); {{{ makeSetValue('___setErrNo.ret', '0', 'value', 'i32') }}} return value; }, __errno_location__deps: ['__setErrNo'], __errno_location: function() { return ___setErrNo.ret; }, __errno: '__errno_location', // ========================================================================== // sys/resource.h // ========================================================================== // TODO: Implement for real. __rlimit_struct_layout: Runtime.generateStructInfo(null, '%struct.rlimit'), getrlimit__deps: ['__rlimit_struct_layout'], getrlimit: function(resource, rlp) { // int getrlimit(int resource, struct rlimit *rlp); {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_cur', '-1', 'i32') }}} // RLIM_INFINITY {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_max', '-1', 'i32') }}} // RLIM_INFINITY return 0; }, setrlimit: function(resource, rlp) { // int setrlimit(int resource, const struct rlimit *rlp) return 0; }, __01getrlimit64_: 'getrlimit', // TODO: Implement for real. We just do time used, and no useful data __rusage_struct_layout: Runtime.generateStructInfo(null, '%struct.rusage'), getrusage__deps: ['__rusage_struct_layout'], getrusage: function(resource, rlp) { // %struct.timeval = type { i32, i32 } var timeval = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); // int getrusage(int resource, struct rusage *rlp); {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_utime+timeval[0]', '1', 'i32') }}} {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_utime+timeval[1]', '2', 'i32') }}} {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_stime+timeval[0]', '3', 'i32') }}} {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_stime+timeval[1]', '4', 'i32') }}} return 0; }, // ========================================================================== // pthread.h (stubs for mutexes only - no thread support yet!) // ========================================================================== pthread_mutex_init: function() {}, pthread_mutex_destroy: function() {}, pthread_mutexattr_init: function() {}, pthread_mutexattr_settype: function() {}, pthread_mutexattr_destroy: function() {}, pthread_mutex_lock: function() {}, pthread_mutex_unlock: function() {}, pthread_cond_broadcast: function() {}, // ========================================================================== // malloc.h // ========================================================================== memalign: function(boundary, size) { // leaks, and even returns an invalid pointer. Horrible hack... but then, this is a deprecated function... var ret = Runtime.staticAlloc(size + boundary); return ret + boundary - (ret % boundary); }, posix_memalign__deps: ['memalign'], posix_memalign: function(memptr, alignment, size) { var ptr = _memalign(alignment, size); {{{ makeSetValue('memptr', '0', 'ptr', 'i8*') }}} return 0; }, // ========================================================================== // emscripten.h // ========================================================================== emscripten_run_script: function(ptr) { eval(Pointer_stringify(ptr)); }, _Z21emscripten_run_scriptPKc: function(ptr) { eval(Pointer_stringify(ptr)); }, EMSCRIPTEN_COMMENT__inline: function(param) { param = stripCorrections(param); return '// ' + Variables.globals[param].value.text.replace('\\00', '') + ' '; }, $Profiling: { max_: 0, times: null, invalid: 0, dump: function() { if (Profiling.invalid) { print('Invalid # of calls to Profiling begin and end!'); return; } print('Profiling data:') for (var i = 0; i < Profiling.max_; i++) { print('Block ' + i + ': ' + Profiling.times[i]); } } }, EMSCRIPTEN_PROFILE_INIT__deps: ['$Profiling'], EMSCRIPTEN_PROFILE_INIT: function(max_) { Profiling.max_ = max_; Profiling.times = new Array(max_); for (var i = 0; i < max_; i++) Profiling.times[i] = 0; }, EMSCRIPTEN_PROFILE_BEGIN__inline: function(id) { return 'Profiling.times[' + id + '] -= Date.now();' + 'Profiling.invalid++;' }, EMSCRIPTEN_PROFILE_END__inline: function(id) { return 'Profiling.times[' + id + '] += Date.now();' + 'Profiling.invalid--;' } };