diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/analyzer.js | 2 | ||||
-rw-r--r-- | src/library.js | 3464 | ||||
-rw-r--r-- | src/library_gl.js | 7 | ||||
-rw-r--r-- | src/library_jansson.js | 2 | ||||
-rw-r--r-- | src/library_openal.js | 596 | ||||
-rw-r--r-- | src/library_path.js | 134 | ||||
-rw-r--r-- | src/library_sdl.js | 168 | ||||
-rw-r--r-- | src/modules.js | 2 | ||||
-rw-r--r-- | src/postamble.js | 94 | ||||
-rw-r--r-- | src/preamble.js | 73 | ||||
-rw-r--r-- | src/settings.js | 1684 | ||||
-rw-r--r-- | src/utility.js | 6 |
12 files changed, 3914 insertions, 2318 deletions
diff --git a/src/analyzer.js b/src/analyzer.js index b1f0b585..1a752305 100644 --- a/src/analyzer.js +++ b/src/analyzer.js @@ -338,7 +338,7 @@ function analyzer(data, sidePass) { if (subItem != item && (!(subItem.intertype in UNUNFOLDABLE) || (subItem.intertype == 'value' && isNumber(subItem.ident) && isIllegalType(subItem.type)))) { if (item.intertype == 'phi') { - assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue', 'We can only unfold illegal constants in phis'); + assert(subItem.intertype == 'value' || subItem.intertype == 'structvalue' || subItem.intertype in PARSABLE_LLVM_FUNCTIONS, 'We can only unfold some expressions in phis'); // we must handle this in the phi itself, if we unfold normally it will not be pushed back with the phi } else { var tempIdent = '$$etemp$' + (tempId++); diff --git a/src/library.js b/src/library.js index 61237d07..78222b9a 100644 --- a/src/library.js +++ b/src/library.js @@ -28,8 +28,9 @@ LibraryManager.library = { stderr: 'allocate(1, "i32*", ALLOC_STATIC)', _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)', - $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], - $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + + $FS__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo', '$VFS', '$PATH', '$TTY', '$MEMFS', 'stdin', 'stdout', 'stderr', 'fflush'], + $FS__postset: 'FS.staticInit();' + + '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + '__ATEXIT__.push({ func: function() { FS.quit() } });' + // export some names through closure @@ -41,288 +42,685 @@ LibraryManager.library = { 'Module["FS_createLink"] = FS.createLink;' + 'Module["FS_createDevice"] = FS.createDevice;', $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 is kept as a dense array. It may contain |null| to fill in - // holes, however. + root: null, + nodes: [null], + devices: [null], streams: [null], -#if ASSERTIONS - checkStreams: function() { - for (var i in FS.streams) if (FS.streams.hasOwnProperty(i)) assert(i >= 0 && i < FS.streams.length); // no keys not in dense span - for (var i = 0; i < FS.streams.length; i++) assert(typeof FS.streams[i] == 'object'); // no non-null holes in dense span - }, -#endif + nextInode: 1, + name_table: new Array(4096), + currentPath: '/', + initialized: false, // Whether we are currently ignoring permissions. Useful when preparing the // filesystem and creating files inside read-only folders. // This is set to false when the runtime is initialized, allowing you // to modify the filesystem freely before run() is called. ignorePermissions: true, - createFileHandle: function(stream, fd) { - if (typeof stream === 'undefined') { - stream = null; - } - if (!fd) { - if (stream && stream.socket) { - for (var i = 1; i < 64; i++) { - if (!FS.streams[i]) { - fd = i; - break; - } - } - assert(fd, 'ran out of low fds for sockets'); - } else { - fd = Math.max(FS.streams.length, 64); - for (var i = FS.streams.length; i < fd; i++) { - FS.streams[i] = null; // Keep dense - } + + ErrnoError: function (errno) { + this.errno = errno; + for (var key in ERRNO_CODES) { + if (ERRNO_CODES[key] === errno) { + this.code = key; + break; } } - // Close WebSocket first if we are about to replace the fd (i.e. dup2) - if (FS.streams[fd] && FS.streams[fd].socket && FS.streams[fd].socket.close) { - FS.streams[fd].socket.close(); + this.message = ERRNO_MESSAGES[errno]; + }, + + handleFSError: function(e) { + if (!(e instanceof FS.ErrnoError)) throw e + ' : ' + new Error().stack; + return ___setErrNo(e.errno); + }, + + // + // nodes + // + hashName: function (parentid, name) { + var hash = 0; + for (var i = 0; i < name.length; i++) { + hash = ((hash << 5) - hash + name.charCodeAt(i)) | 0; } - FS.streams[fd] = stream; - return fd; + return (parentid + hash) % FS.name_table.length; }, - removeFileHandle: function(fd) { - FS.streams[fd] = null; + hashAddNode: function (node) { + var hash = FS.hashName(node.parent.id, node.name); + node.name_next = FS.name_table[hash]; + FS.name_table[hash] = node; }, - joinPath: function(parts, forceRelative) { - var ret = parts[0]; - for (var i = 1; i < parts.length; i++) { - if (ret[ret.length-1] != '/') ret += '/'; - ret += parts[i]; + hashRemoveNode: function (node) { + var hash = FS.hashName(node.parent.id, node.name); + if (FS.name_table[hash] === node) { + FS.name_table[hash] = node.name_next; + } else { + var current = FS.name_table[hash]; + while (current) { + if (current.name_next === node) { + current.name_next = node.name_next; + break; + } + current = current.name_next; + } } - if (forceRelative && ret[0] == '/') ret = ret.substr(1); - return ret; }, - // 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); + lookupNode: function (parent, name) { + var err = FS.mayLookup(parent); + if (err) { + throw new FS.ErrnoError(err); + } + var hash = FS.hashName(parent.id, name); + for (var node = FS.name_table[hash]; node; node = node.name_next) { + if (node.parent.id === parent.id && node.name === name) { + return node; } } - return absolute.length == 1 ? '/' : absolute.join('/'); + // if we failed to find it in the cache, call into the VFS + return VFS.lookup(parent, name); }, - // 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 + createNode: function (parent, name, mode, rdev) { + var node = { + id: FS.nextInode++, + name: name, + mode: mode, + node_ops: {}, + stream_ops: {}, + rdev: rdev, + parent: null, + mount: null }; -#if FS_LOG - var inputPath = path; - function log() { - Module['print']('FS.analyzePath("' + inputPath + '", ' + - dontResolveLastLink + ', ' + - linksVisited + ') => {' + - 'isRoot: ' + ret.isRoot + ', ' + - 'exists: ' + ret.exists + ', ' + - 'error: ' + ret.error + ', ' + - 'name: "' + ret.name + '", ' + - 'path: "' + ret.path + '", ' + - 'object: ' + ret.object + ', ' + - 'parentExists: ' + ret.parentExists + ', ' + - 'parentPath: "' + ret.parentPath + '", ' + - 'parentObject: ' + ret.parentObject + '}'); + if (!parent) { + parent = node; // root node sets parent to itself + } + node.parent = parent; + node.mount = parent.mount; + // compatibility + var readMode = {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; + var writeMode = {{{ cDefine('S_IWUGO') }}}; + Object.defineProperty(node, 'read', { + get: function () { return (node.mode & readMode) === readMode; }, + set: function (val) { val ? node.mode |= readMode : node.mode &= ~readMode; } + }); + Object.defineProperty(node, 'write', { + get: function () { return (node.mode & writeMode) === writeMode; }, + set: function (val) { val ? node.mode |= writeMode : node.mode &= ~writeMode; } + }); + // TODO add: + // isFolder + // isDevice + FS.hashAddNode(node); + return node; + }, + destroyNode: function (node) { + FS.hashRemoveNode(node); + }, + isRoot: function (node) { + return node === node.parent; + }, + isMountpoint: function (node) { + return node.mounted; + }, + isFile: function (mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFREG') }}}; + }, + isDir: function (mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFDIR') }}}; + }, + isLink: function (mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFLNK') }}}; + }, + isChrdev: function (mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFCHR') }}}; + }, + isBlkdev: function (mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFBLK') }}}; + }, + isFIFO: function (mode) { + return (mode & {{{ cDefine('S_IFMT') }}}) === {{{ cDefine('S_IFIFO') }}}; + }, + + // + // paths + // + cwd: function() { + return FS.currentPath; + }, + lookupPath: function (path, opts) { + path = PATH.resolve(FS.currentPath, path); + opts = opts || { recurse_count: 0 }; + + if (opts.recurse_count > 8) { // max recursive lookup of 8 + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); } -#endif - 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; + + // split the path + var parts = PATH.normalizeArray(path.split('/').filter(function (p) { + return !!p; + }), false); + + // start at the root + var current = FS.root; + var current_path = '/'; + + for (var i = 0; i < parts.length; i++) { + var islast = (i === parts.length-1); + if (islast && opts.parent) { + // stop resolving + break; + } + + current = FS.lookupNode(current, parts[i]); + current_path = PATH.join(current_path, parts[i]); + + // jump to the mount's root node if this is a mountpoint + if (FS.isMountpoint(current)) { + current = current.mount.root; + } + + // follow symlinks + // by default, lookupPath will not follow a symlink if it is the final path component. + // setting opts.follow = true will override this behavior. + if (!islast || opts.follow) { + var count = 0; + while (FS.isLink(current.mode)) { + var link = VFS.readlink(current_path); + current_path = PATH.resolve(PATH.dirname(current_path), link); + + var lookup = FS.lookupPath(current_path, { recurse_count: opts.recurse_count }); + current = lookup.node; + + if (count++ > 40) { // limit max consecutive symlinks to 40 (SYMLOOP_MAX). + throw new FS.ErrnoError(ERRNO_CODES.ELOOP); } - var link = FS.absolutePath(current.link, traversed.join('/')); - ret = FS.analyzePath([link].concat(path).join('/'), - dontResolveLastLink, linksVisited + 1); -#if FS_LOG - log(); -#endif - return ret; - } - traversed.push(target); - if (path.length == 0) { - ret.exists = true; - ret.path = traversed.join('/'); - ret.object = current; } } } -#if FS_LOG - log(); -#endif - return ret; + + return { path: current_path, node: current }; }, - // 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; + getPath: function (node) { + var path; + while (true) { + if (FS.isRoot(node)) { + return path ? PATH.join(node.mount.mountpoint, path) : node.mount.mountpoint; + } + path = path ? PATH.join(node.name, path) : node.name; + node = node.parent; + } + }, + + // + // permissions + // + flagModes: { + '"r"': {{{ cDefine('O_RDONLY') }}}, + '"rs"': {{{ cDefine('O_RDONLY') }}} | {{{ cDefine('O_SYNC') }}}, + '"r+"': {{{ cDefine('O_RDWR') }}}, + '"w"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, + '"wx"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"xw"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"w+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, + '"wx+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"xw+"': {{{ cDefine('O_TRUNC') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"a"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}}, + '"ax"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"xa"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_EXCL') }}}, + '"a+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}}, + '"ax+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}}, + '"xa+"': {{{ cDefine('O_APPEND') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_RDWR') }}} | {{{ cDefine('O_EXCL') }}} + }, + // convert the 'r', 'r+', etc. to it's corresponding set of O_* flags + modeStringToFlags: function (str) { + var flags = FS.flagModes[str]; + if (typeof flags === 'undefined') { + throw new Error('Unknown file open mode: ' + str); + } + return flags; + }, + // convert O_* bitmask to a string for nodePermissions + flagsToPermissionString: function (flag) { + var accmode = flag & {{{ cDefine('O_ACCMODE') }}}; + var perms = ['r', 'w', 'rw'][accmode]; + if ((flag & {{{ cDefine('O_TRUNC') }}})) { + perms += 'w'; + } + return perms; + }, + nodePermissions: function (node, perms) { + if (FS.ignorePermissions) { + return 0; + } + // return 0 if any user, group or owner bits are set. + if (perms.indexOf('r') !== -1 && !(node.mode & {{{ cDefine('S_IRUGO') }}})) { + return ERRNO_CODES.EACCES; + } else if (perms.indexOf('w') !== -1 && !(node.mode & {{{ cDefine('S_IWUGO') }}})) { + return ERRNO_CODES.EACCES; + } else if (perms.indexOf('x') !== -1 && !(node.mode & {{{ cDefine('S_IXUGO') }}})) { + return ERRNO_CODES.EACCES; + } + return 0; + }, + mayLookup: function (dir) { + return FS.nodePermissions(dir, 'x'); + }, + mayMknod: function (mode) { + switch (mode & {{{ cDefine('S_IFMT') }}}) { + case {{{ cDefine('S_IFREG') }}}: + case {{{ cDefine('S_IFCHR') }}}: + case {{{ cDefine('S_IFBLK') }}}: + case {{{ cDefine('S_IFIFO') }}}: + case {{{ cDefine('S_IFSOCK') }}}: + return 0; + default: + return ERRNO_CODES.EINVAL; + } + }, + mayCreate: function (dir, name) { + try { + var node = FS.lookupNode(dir, name); + return ERRNO_CODES.EEXIST; + } catch (e) { + } + return FS.nodePermissions(dir, 'wx'); + }, + mayDelete: function (dir, name, isdir) { + var node; + try { + node = FS.lookupNode(dir, name); + } catch (e) { + return e.errno; + } + var err = FS.nodePermissions(dir, 'wx'); + if (err) { + return err; + } + if (isdir) { + if (!FS.isDir(node.mode)) { + return ERRNO_CODES.ENOTDIR; + } + if (FS.isRoot(node) || FS.getPath(node) === FS.currentPath) { + return ERRNO_CODES.EBUSY; + } } else { - ___setErrNo(ret.error); - return null; + if (FS.isDir(node.mode)) { + return ERRNO_CODES.EISDIR; + } } + return 0; + }, + mayOpen: function (node, flags) { + if (!node) { + return ERRNO_CODES.ENOENT; + } + if (FS.isLink(node.mode)) { + return ERRNO_CODES.ELOOP; + } else if (FS.isDir(node.mode)) { + if ((flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY')}}} || // opening for write + (flags & {{{ cDefine('O_TRUNC') }}})) { + return ERRNO_CODES.EISDIR; + } + } + return FS.nodePermissions(node, FS.flagsToPermissionString(flags)); }, - // Creates a file system record: file, link, device or folder. - createObject: function(parent, name, properties, canRead, canWrite) { -#if FS_LOG - Module['print']('FS.createObject("' + parent + '", ' + - '"' + name + '", ' + - JSON.stringify(properties) + ', ' + - canRead + ', ' + - canWrite + ')'); -#endif - if (!parent) parent = '/'; - if (typeof parent === 'string') parent = FS.findObject(parent); - if (!parent) { - ___setErrNo(ERRNO_CODES.EACCES); - throw new Error('Parent path must exist.'); + // + // devices + // + // each character device consists of a device id + stream operations. + // when a character device node is created (e.g. /dev/stdin) it is + // assigned a device id that lets us map back to the actual device. + // by default, each character device stream (e.g. _stdin) uses chrdev_stream_ops. + // however, once opened, the stream's operations are overridden with + // the operations of the device its underlying node maps back to. + chrdev_stream_ops: { + open: function (stream) { + var device = FS.getDevice(stream.node.rdev); + // override node's stream ops with the device's + stream.stream_ops = device.stream_ops; + // forward the open call + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); + } + }, + llseek: function () { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); } - if (!parent.isFolder) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - throw new Error('Parent must be a folder.'); + }, + major: function (dev) { + return ((dev) >> 8); + }, + minor: function (dev) { + return ((dev) & 0xff); + }, + makedev: function (ma, mi) { + return ((ma) << 8 | (mi)); + }, + registerDevice: function (dev, ops) { + FS.devices[dev] = { stream_ops: ops }; + }, + getDevice: function (dev) { + return FS.devices[dev]; + }, + + // + // streams + // + MAX_OPEN_FDS: 4096, + nextfd: function (fd_start, fd_end) { + fd_start = fd_start || 1; + fd_end = fd_end || FS.MAX_OPEN_FDS; + for (var fd = fd_start; fd <= fd_end; fd++) { + if (!FS.streams[fd]) { + return fd; + } } - if (!parent.write && !FS.ignorePermissions) { - ___setErrNo(ERRNO_CODES.EACCES); - throw new Error('Parent folder must be writeable.'); + throw new FS.ErrnoError(ERRNO_CODES.EMFILE); + }, + getStream: function (fd) { + return FS.streams[fd]; + }, + createStream: function (stream, fd_start, fd_end) { + var fd = FS.nextfd(fd_start, fd_end); + stream.fd = fd; + // compatibility + Object.defineProperty(stream, 'object', { + get: function () { return stream.node; }, + set: function (val) { stream.node = val; } + }); + Object.defineProperty(stream, 'isRead', { + get: function () { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_WRONLY') }}}; } + }); + Object.defineProperty(stream, 'isWrite', { + get: function () { return (stream.flags & {{{ cDefine('O_ACCMODE') }}}) !== {{{ cDefine('O_RDONLY') }}}; } + }); + Object.defineProperty(stream, 'isAppend', { + get: function () { return (stream.flags & {{{ cDefine('O_APPEND') }}}); } + }); + FS.streams[fd] = stream; + return stream; + }, + closeStream: function (fd) { + FS.streams[fd] = null; + }, + + // + // general + // + createDefaultDirectories: function () { + VFS.mkdir('/tmp', 0777); + }, + createDefaultDevices: function () { + // create /dev + VFS.mkdir('/dev', 0777); + // setup /dev/null + FS.registerDevice(FS.makedev(1, 3), { + read: function () { return 0; }, + write: function () { return 0; } + }); + VFS.mkdev('/dev/null', 0666, FS.makedev(1, 3)); + // setup /dev/tty and /dev/tty1 + // stderr needs to print output using Module['printErr'] + // so we register a second tty just for it. + TTY.register(FS.makedev(5, 0), TTY.default_tty_ops); + TTY.register(FS.makedev(6, 0), TTY.default_tty1_ops); + VFS.mkdev('/dev/tty', 0666, FS.makedev(5, 0)); + VFS.mkdev('/dev/tty1', 0666, FS.makedev(6, 0)); + // we're not going to emulate the actual shm device, + // just create the tmp dirs that reside in it commonly + VFS.mkdir('/dev/shm', 0777); + VFS.mkdir('/dev/shm/tmp', 0777); + }, + createStandardStreams: function () { + // TODO deprecate the old functionality of a single + // input / output callback and that utilizes FS.createDevice + // and instead require a unique set of stream ops + + // by default, we symlink the standard streams to the + // default tty devices. however, if the standard streams + // have been overwritten we create a unique device for + // them instead. + if (Module['stdin']) { + FS.createDevice('/dev', 'stdin', Module['stdin']); + } else { + VFS.symlink('/dev/tty', '/dev/stdin'); } - if (!name || name == '.' || name == '..') { - ___setErrNo(ERRNO_CODES.ENOENT); - throw new Error('Name must not be empty.'); + if (Module['stdout']) { + FS.createDevice('/dev', 'stdout', null, Module['stdout']); + } else { + VFS.symlink('/dev/tty', '/dev/stdout'); } - if (parent.contents.hasOwnProperty(name)) { - ___setErrNo(ERRNO_CODES.EEXIST); - throw new Error("Can't overwrite object."); + if (Module['stderr']) { + FS.createDevice('/dev', 'stderr', null, Module['stderr']); + } else { + VFS.symlink('/dev/tty1', '/dev/stderr'); } - 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]; + // open default streams for the stdin, stdout and stderr devices + var stdin = VFS.open('/dev/stdin', 'r'); + {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 'stdin.fd', 'void*') }}}; + assert(stdin.fd === 1, 'invalid handle for stdin (' + stdin.fd + ')'); + + var stdout = VFS.open('/dev/stdout', 'w'); + {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 'stdout.fd', 'void*') }}}; + assert(stdout.fd === 2, 'invalid handle for stdout (' + stdout.fd + ')'); + + var stderr = VFS.open('/dev/stderr', 'w'); + {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 'stderr.fd', 'void*') }}}; + assert(stderr.fd === 3, 'invalid handle for stderr (' + stderr.fd + ')'); + }, + staticInit: function () { + FS.root = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + VFS.mount(MEMFS, {}, '/'); + + FS.createDefaultDirectories(); + FS.createDefaultDevices(); + }, + init: function (input, output, error) { + assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); + FS.init.initialized = true; + + // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here + Module['stdin'] = input || Module['stdin']; + Module['stdout'] = output || Module['stdout']; + Module['stderr'] = error || Module['stderr']; + + FS.createStandardStreams(); + }, + quit: function () { + FS.init.initialized = false; + for (var i = 0; i < FS.streams.length; i++) { + var stream = FS.streams[i]; + if (!stream) { + continue; } + VFS.close(stream); } + }, - return parent.contents[name]; + // + // compatibility + // + getMode: function (canRead, canWrite) { + var mode = 0; + if (canRead) mode |= {{{ cDefine('S_IRUGO') }}} | {{{ cDefine('S_IXUGO') }}}; + if (canWrite) mode |= {{{ cDefine('S_IWUGO') }}}; + return mode; + }, + joinPath: function (parts, forceRelative) { + var path = PATH.join.apply(null, parts); + if (forceRelative && path[0] == '/') path = path.substr(1); + return path; + }, + absolutePath: function (relative, base) { + return PATH.resolve(base, relative); }, - // Creates a folder. - createFolder: function(parent, name, canRead, canWrite) { - var properties = {isFolder: true, isDevice: false, contents: {}}; - return FS.createObject(parent, name, properties, canRead, canWrite); + standardizePath: function (path) { + return PATH.normalize(path); }, - // Creates 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(); + findObject: function (path, dontResolveLastLink) { + var ret = FS.analyzePath(path, dontResolveLastLink); + if (ret.exists) { + return ret.object; + } else { + ___setErrNo(ret.error); + return null; + } + }, + analyzePath: function (path, dontResolveLastLink) { + // operate from within the context of the symlink's target + try { + var lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + path = lookup.path; + } catch (e) { + } + var ret = { + isRoot: false, exists: false, error: 0, name: null, path: null, object: null, + parentExists: false, parentPath: null, parentObject: null + }; + try { + var lookup = FS.lookupPath(path, { parent: true }); + ret.parentExists = true; + ret.parentPath = lookup.path; + ret.parentObject = lookup.node; + ret.name = PATH.basename(path); + lookup = FS.lookupPath(path, { follow: !dontResolveLastLink }); + ret.exists = true; + ret.path = lookup.path; + ret.object = lookup.node; + ret.name = lookup.node.name; + ret.isRoot = lookup.path === '/'; + } catch (e) { + ret.error = e.errno; + }; + return ret; + }, + createFolder: function (parent, name, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + return VFS.mkdir(path, mode); + }, + createPath: function (parent, path, canRead, canWrite) { + parent = typeof parent === 'string' ? parent : FS.getPath(parent); + var parts = path.split('/').reverse(); + while (parts.length) { + var part = parts.pop(); if (!part) continue; - if (!current.contents.hasOwnProperty(part)) { - FS.createFolder(current, part, canRead, canWrite); + var current = PATH.join(parent, part); + try { + VFS.mkdir(current, 0777); + } catch (e) { + // ignore EEXIST } - current = current.contents[part]; + parent = current; } 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); + createFile: function (parent, name, properties, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + return VFS.create(path, mode); }, - // Creates a file record from existing data. - createDataFile: function(parent, name, data, canRead, canWrite) { - if (typeof data === 'string') { - var dataArray = new Array(data.length); - for (var i = 0, len = data.length; i < len; ++i) dataArray[i] = data.charCodeAt(i); - data = dataArray; - } - var properties = { - isDevice: false, - contents: data.subarray ? data.subarray(0) : data // as an optimization, create a new array wrapper (not buffer) here, to help JS engines understand this object - }; - return FS.createFile(parent, name, properties, canRead, canWrite); + createDataFile: function (parent, name, data, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = FS.getMode(canRead, canWrite); + var node = VFS.create(path, mode); + if (data) { + if (typeof data === 'string') { + var arr = new Array(data.length); + for (var i = 0, len = data.length; i < len; ++i) arr[i] = data.charCodeAt(i); + data = arr; + } + // make sure we can write to the file + VFS.chmod(path, mode | {{{ cDefine('S_IWUGO') }}}); + var stream = VFS.open(path, 'w'); + VFS.write(stream, data, 0, data.length, 0); + VFS.close(stream); + VFS.chmod(path, mode); + } + return node; + }, + createDevice: function (parent, name, input, output) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + var mode = input && output ? 0777 : (input ? 0333 : 0555); + if (!FS.createDevice.major) FS.createDevice.major = 64; + var dev = FS.makedev(FS.createDevice.major++, 0); + // Create a fake device that a set of stream ops to emulate + // the old behavior. + FS.registerDevice(dev, { + open: function (stream) { + stream.seekable = false; + }, + close: function (stream) { + // flush any pending line data + if (output.buffer && output.buffer.length) { + output({{{ charCode('\n') }}}); + } + }, + read: function (stream, buffer, offset, length, pos /* ignored */) { + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = input(); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function (stream, buffer, offset, length, pos) { + for (var i = 0; i < length; i++) { + try { + output(buffer[offset+i]); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }); + return VFS.mkdev(path, mode, dev); + }, + createLink: function (parent, name, target, canRead, canWrite) { + var path = PATH.join(typeof parent === 'string' ? parent : FS.getPath(parent), name); + return VFS.symlink(target, path); + }, + // 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') { + throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); + } else if (Module['read']) { + // 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(Module['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; }, // Creates a file record for lazy-loading from a URL. XXX This requires a synchronous // XHR, which is not possible in browsers except in a web worker! Use preloading, // either --preload-file in emcc or FS.createPreloadedFile createLazyFile: function(parent, name, url, canRead, canWrite) { - if (typeof XMLHttpRequest !== 'undefined') { if (!ENVIRONMENT_IS_WORKER) throw 'Cannot do synchronous binary XHRs outside webworkers in modern browsers. Use --embed-file or --preload-file in emcc'; // Lazy chunked Uint8Array (implements get and length from Uint8Array). Actual getting is abstracted away for eventual reuse. @@ -341,7 +739,6 @@ LibraryManager.library = { LazyUint8Array.prototype.setDataGetter = function(getter) { this.getter = getter; } - LazyUint8Array.prototype.cacheLength = function() { // Find length var xhr = new XMLHttpRequest(); @@ -423,7 +820,45 @@ LibraryManager.library = { var properties = { isDevice: false, url: url }; } - return FS.createFile(parent, name, properties, canRead, canWrite); + var node = FS.createFile(parent, name, properties, canRead, canWrite); + // This is a total hack, but I want to get this lazy file code out of the + // core of MEMFS. If we want to keep this lazy file concept I feel it should + // be its own thin LAZYFS proxying calls to MEMFS. + if (properties.contents) { + node.contents = properties.contents; + } else if (properties.url) { + node.contents = null; + node.url = properties.url; + } + // override each stream op with one that tries to force load the lazy file first + var stream_ops = {}; + var keys = Object.keys(node.stream_ops); + keys.forEach(function (key) { + var fn = node.stream_ops[key]; + stream_ops[key] = function () { + if (!FS.forceLoadFile(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + return fn.apply(null, arguments); + }; + }); + // use a custom read function + stream_ops.read = function (stream, buffer, offset, length, position) { + var contents = stream.node.contents; + var size = Math.min(contents.length - position, length); + if (contents.slice) { // normal array + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } else { + for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR + buffer[offset + i] = contents.get(position + i); + } + } + return size; + }; + node.stream_ops = stream_ops; + return node; }, // Preloads a file asynchronously. You can call this before run, for example in // preRun. run will be delayed until this file arrives and is set up. @@ -469,216 +904,839 @@ LibraryManager.library = { } else { processData(url); } + } + }, + + $VFS__deps: ['$FS'], + $VFS: { + mount: function (type, opts, mountpoint) { + var mount = { + type: type, + opts: opts, + mountpoint: mountpoint, + root: null + }; + var lookup; + if (mountpoint) { + lookup = FS.lookupPath(mountpoint, { follow: false }); + } + // create a root node for the fs + var root = type.mount(mount); + root.mount = mount; + mount.root = root; + // assign the mount info to the mountpoint's node + if (lookup) { + lookup.node.mount = mount; + lookup.node.mounted = true; + // compatibility update FS.root if we mount to / + if (mountpoint === '/') { + FS.root = mount.root; + } + } + return root; }, - // Creates a link to a specific local path. - createLink: function(parent, name, target, canRead, canWrite) { - var properties = {isDevice: false, link: target}; - return FS.createFile(parent, name, properties, canRead, canWrite); + lookup: function (parent, name) { + return parent.node_ops.lookup(parent, name); }, - // 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)); + // generic function for all node creation + mknod: function (path, mode, dev) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var err = FS.mayCreate(parent, name); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.mknod) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return parent.node_ops.mknod(parent, name, mode, dev); }, - // 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') { - throw new Error("Lazy loading should have been performed (contents set) in createLazyFile, but it was not. Lazy loading only works in web workers. Use --embed-file or --preload-file in emcc on the main thread."); - } else if (Module['read']) { - // 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(Module['read'](obj.url), true); - } catch (e) { - success = false; + // helpers to create specific types of nodes + create: function (path, mode) { + mode &= {{{ cDefine('S_IALLUGO') }}}; + mode |= {{{ cDefine('S_IFREG') }}}; + return VFS.mknod(path, mode, 0); + }, + mkdir: function (path, mode) { + mode &= {{{ cDefine('S_IRWXUGO') }}} | {{{ cDefine('S_ISVTX') }}}; + mode |= {{{ cDefine('S_IFDIR') }}}; + return VFS.mknod(path, mode, 0); + }, + mkdev: function (path, mode, dev) { + mode |= {{{ cDefine('S_IFCHR') }}}; + return VFS.mknod(path, mode, dev); + }, + symlink: function (oldpath, newpath) { + var lookup = FS.lookupPath(newpath, { parent: true }); + var parent = lookup.node; + var newname = PATH.basename(newpath); + var err = FS.mayCreate(parent, newname); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.symlink) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return parent.node_ops.symlink(parent, newname, oldpath); + }, + rename: function (old_path, new_path) { + var old_dirname = PATH.dirname(old_path); + var new_dirname = PATH.dirname(new_path); + var old_name = PATH.basename(old_path); + var new_name = PATH.basename(new_path); + // parents must exist + var lookup, old_dir, new_dir; + try { + lookup = FS.lookupPath(old_path, { parent: true }); + old_dir = lookup.node; + lookup = FS.lookupPath(new_path, { parent: true }); + new_dir = lookup.node; + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + // need to be part of the same mount + if (old_dir.mount !== new_dir.mount) { + throw new FS.ErrnoError(ERRNO_CODES.EXDEV); + } + // source must exist + var old_node = FS.lookupNode(old_dir, old_name); + // old path should not be an ancestor of the new path + var relative = PATH.relative(old_path, new_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + // new path should not be an ancestor of the old path + relative = PATH.relative(new_path, old_dirname); + if (relative.charAt(0) !== '.') { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + // see if the new path alreay exists + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + // not fatal + } + // early out if nothing needs to changews + if (old_node === new_node) { + return; + } + // we'll need to delete the old entry + var isdir = FS.isDir(old_node.mode); + var err = FS.mayDelete(old_dir, old_name, isdir); + if (err) { + throw new FS.ErrnoError(err); + } + // need delete permissions if we'll be overwriting. + // need create permissions if new doesn't already exist. + err = new_node ? + FS.mayDelete(new_dir, new_name, isdir) : + FS.mayCreate(new_dir, new_name); + if (err) { + throw new FS.ErrnoError(err); + } + if (!old_dir.node_ops.rename) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(old_node) || (new_node && FS.isMountpoint(new_node))) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + // if we are going to change the parent, check write permissions + if (new_dir !== old_dir) { + err = FS.nodePermissions(old_dir, 'w'); + if (err) { + throw new FS.ErrnoError(err); } + } + // remove the node from the lookup hash + FS.hashRemoveNode(old_node); + // do the underlying fs rename + try { + old_node.node_ops.rename(old_node, new_dir, new_name); + } catch (e) { + throw e; + } finally { + // add the node back to the hash (in case node_ops.rename + // changed its name) + FS.hashAddNode(old_node); + } + }, + rmdir: function (path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var err = FS.mayDelete(parent, name, true); + if (err) { + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.rmdir) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + parent.node_ops.rmdir(parent, name); + FS.destroyNode(node); + }, + unlink: function (path) { + var lookup = FS.lookupPath(path, { parent: true }); + var parent = lookup.node; + var name = PATH.basename(path); + var node = FS.lookupNode(parent, name); + var err = FS.mayDelete(parent, name, false); + if (err) { + // POSIX says unlink should set EPERM, not EISDIR + if (err === ERRNO_CODES.EISDIR) err = ERRNO_CODES.EPERM; + throw new FS.ErrnoError(err); + } + if (!parent.node_ops.unlink) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isMountpoint(node)) { + throw new FS.ErrnoError(ERRNO_CODES.EBUSY); + } + parent.node_ops.unlink(parent, name); + FS.destroyNode(node); + }, + readlink: function (path) { + var lookup = FS.lookupPath(path, { follow: false }); + var link = lookup.node; + if (!link.node_ops.readlink) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return link.node_ops.readlink(link); + }, + stat: function (path, dontFollow) { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + var node = lookup.node; + if (!node.node_ops.getattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + return node.node_ops.getattr(node); + }, + lstat: function (path) { + return VFS.stat(path, true); + }, + chmod: function (path, mode, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; } else { - throw new Error('Cannot load without read() or XMLHttpRequest.'); + node = path; } - if (!success) ___setErrNo(ERRNO_CODES.EIO); - return success; + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + node.node_ops.setattr(node, { + mode: (mode & {{{ cDefine('S_IALLUGO') }}}) | (node.mode & ~{{{ cDefine('S_IALLUGO') }}}), + timestamp: Date.now() + }); }, - ensureRoot: function() { - if (FS.root) return; - // The main file system tree. All the contents are inside this. - FS.root = { - read: true, - write: true, - isFolder: true, - isDevice: false, - timestamp: Date.now(), - inodeNumber: 1, - contents: {} - }; + lchmod: function (path, mode) { + VFS.chmod(path, mode, true); }, - // Initializes the filesystems with stdin/stdout/stderr devices, given - // optional handlers. - init: function(input, output, error) { - // Make sure we initialize only once. - assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); - FS.init.initialized = true; - - FS.ensureRoot(); - - // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here - input = input || Module['stdin']; - output = output || Module['stdout']; - error = error || Module['stderr']; - - // Default handlers. - var stdinOverridden = true, stdoutOverridden = true, stderrOverridden = true; - if (!input) { - stdinOverridden = false; - input = function() { - if (!input.cache || !input.cache.length) { - var result; - if (typeof window != 'undefined' && - typeof window.prompt == 'function') { - // Browser. - result = window.prompt('Input: '); - if (result === null) result = String.fromCharCode(0); // cancel ==> EOF - } else if (typeof readline == 'function') { - // Command line. - result = readline(); - } - if (!result) result = ''; - input.cache = intArrayFromString(result + '\n', true); - } - return input.cache.shift(); - }; + fchmod: function (fd, mode) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); } - var utf8 = new Runtime.UTF8Processor(); - function createSimpleOutput() { - var fn = function (val) { - if (val === null || val === {{{ charCode('\n') }}}) { - fn.printer(fn.buffer.join('')); - fn.buffer = []; - } else { - fn.buffer.push(utf8.processCChar(val)); - } - }; - return fn; + VFS.chmod(stream.node, mode); + }, + chown: function (path, uid, gid, dontFollow) { + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: !dontFollow }); + node = lookup.node; + } else { + node = path; } - if (!output) { - stdoutOverridden = false; - output = createSimpleOutput(); + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); } - if (!output.printer) output.printer = Module['print']; - if (!output.buffer) output.buffer = []; - if (!error) { - stderrOverridden = false; - error = createSimpleOutput(); + node.node_ops.setattr(node, { + timestamp: Date.now() + // we ignore the uid / gid for now + }); + }, + lchown: function (path, uid, gid) { + VFS.chown(path, uid, gid, true); + }, + fchown: function (fd, uid, gid) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); } - if (!error.printer) error.printer = Module['printErr']; - if (!error.buffer) error.buffer = []; - - // Create the temporary folder, if not already created + VFS.chown(stream.node, uid, gid); + }, + truncate: function (path, len) { + if (len < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var node; + if (typeof path === 'string') { + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + } else { + node = path; + } + if (!node.node_ops.setattr) { + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + if (FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!FS.isFile(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var err = FS.nodePermissions(node, 'w'); + if (err) { + throw new FS.ErrnoError(err); + } + node.node_ops.setattr(node, { + size: len, + timestamp: Date.now() + }); + }, + ftruncate: function (fd, len) { + var stream = FS.getStream(fd); + if (!stream) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + VFS.truncate(stream.node, len); + }, + utime: function (path, atime, mtime) { + var lookup = FS.lookupPath(path, { follow: true }); + var node = lookup.node; + node.node_ops.setattr(node, { + timestamp: Math.max(atime, mtime) + }); + }, + open: function (path, flags, mode, fd_start, fd_end) { + path = PATH.normalize(path); + flags = typeof flags === 'string' ? FS.modeStringToFlags(flags) : flags; + if ((flags & {{{ cDefine('O_CREAT') }}})) { + mode = (mode & {{{ cDefine('S_IALLUGO') }}}) | {{{ cDefine('S_IFREG') }}}; + } else { + mode = 0; + } + var node; try { - FS.createFolder('/', 'tmp', true, true); - } catch(e) {} - - // Create the I/O devices. - var devFolder = FS.createFolder('/', 'dev', true, true); - var stdin = FS.createDevice(devFolder, 'stdin', input); - stdin.isTerminal = !stdinOverridden; - var stdout = FS.createDevice(devFolder, 'stdout', null, output); - stdout.isTerminal = !stdoutOverridden; - var stderr = FS.createDevice(devFolder, 'stderr', null, error); - stderr.isTerminal = !stderrOverridden; - FS.createDevice(devFolder, 'tty', input, output); - FS.createDevice(devFolder, 'null', function(){}, function(){}); - - // 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, + var lookup = FS.lookupPath(path, { + follow: !(flags & {{{ cDefine('O_NOFOLLOW') }}}) + }); + node = lookup.node; + path = lookup.path; + } catch (e) { + // ignore + } + // perhaps we need to create the node + if ((flags & {{{ cDefine('O_CREAT') }}})) { + if (node) { + // if O_CREAT and O_EXCL are set, error out if the node already exists + if ((flags & {{{ cDefine('O_EXCL') }}})) { + throw new FS.ErrnoError(ERRNO_CODES.EEXIST); + } + } else { + // node doesn't exist, try to create it + node = VFS.mknod(path, mode, 0); + } + } + if (!node) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + } + // can't truncate a device + if (FS.isChrdev(node.mode)) { + flags &= ~{{{ cDefine('O_TRUNC') }}}; + } + // check permissions + var err = FS.mayOpen(node, flags); + if (err) { + throw new FS.ErrnoError(err); + } + // do truncation if necessary + if ((flags & {{{ cDefine('O_TRUNC')}}})) { + VFS.truncate(node, 0); + } + // register the stream with the filesystem + var stream = FS.createStream({ + path: path, + node: node, + flags: flags, + seekable: true, position: 0, - isRead: false, - isWrite: true, - isAppend: false, - error: false, - eof: false, - ungotten: [] - }; - // TODO: put these low in memory like we used to assert on: assert(Math.max(_stdin, _stdout, _stderr) < 15000); // make sure these are low, we flatten arrays with these - {{{ makeSetValue(makeGlobalUse('_stdin'), 0, 1, 'void*') }}}; - {{{ makeSetValue(makeGlobalUse('_stdout'), 0, 2, 'void*') }}}; - {{{ makeSetValue(makeGlobalUse('_stderr'), 0, 3, 'void*') }}}; - - // Other system paths - FS.createPath('/', 'dev/shm/tmp', true, true); // temp files - - // Newlib initialization - for (var i = FS.streams.length; i < Math.max(_stdin, _stdout, _stderr) + {{{ QUANTUM_SIZE }}}; i++) { - FS.streams[i] = null; // Make sure to keep FS.streams dense + stream_ops: node.stream_ops, + // used by the file family libc calls (fopen, fwrite, ferror, etc.) + ungotten: [], + error: false + }, fd_start, fd_end); + // call the new stream's open function + if (stream.stream_ops.open) { + stream.stream_ops.open(stream); } - FS.streams[_stdin] = FS.streams[1]; - FS.streams[_stdout] = FS.streams[2]; - FS.streams[_stderr] = FS.streams[3]; -#if ASSERTIONS - FS.checkStreams(); - // see previous TODO on stdin etc.: assert(FS.streams.length < 1024); // at this early stage, we should not have a large set of file descriptors - just a few -#endif - allocate([ allocate( - {{{ Runtime.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_NORMAL) ], 'void*', ALLOC_NONE, {{{ makeGlobalUse('__impure_ptr') }}}); + return stream; }, - - quit: function() { - if (!FS.init.initialized) return; - // Flush any partially-printed lines in stdout and stderr. Careful, they may have been closed - if (FS.streams[2] && FS.streams[2].object.output.buffer.length > 0) FS.streams[2].object.output({{{ charCode('\n') }}}); - if (FS.streams[3] && FS.streams[3].object.output.buffer.length > 0) FS.streams[3].object.output({{{ charCode('\n') }}}); + close: function (stream) { + try { + if (stream.stream_ops.close) { + stream.stream_ops.close(stream); + } + } catch (e) { + throw e; + } finally { + FS.closeStream(stream.fd); + } + }, + llseek: function (stream, offset, whence) { + if (!stream.seekable || !stream.stream_ops.llseek) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + return stream.stream_ops.llseek(stream, offset, whence); + }, + readdir: function (stream) { + if (!stream.stream_ops.readdir) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTDIR); + } + return stream.stream_ops.readdir(stream); + }, + read: function (stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_WRONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!stream.stream_ops.read) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var seeking = true; + if (typeof position === 'undefined') { + position = stream.position; + seeking = false; + } else if (!stream.seekable) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + var bytesRead = stream.stream_ops.read(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesRead; + return bytesRead; + }, + write: function (stream, buffer, offset, length, position) { + if (length < 0 || position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (FS.isDir(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EISDIR); + } + if (!stream.stream_ops.write) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + var seeking = true; + if (typeof position === 'undefined') { + position = stream.position; + seeking = false; + } else if (!stream.seekable) { + throw new FS.ErrnoError(ERRNO_CODES.ESPIPE); + } + if (stream.flags & {{{ cDefine('O_APPEND') }}}) { + // seek to the end before writing in append mode + VFS.llseek(stream, 0, {{{ cDefine('SEEK_END') }}}); + } + var bytesWritten = stream.stream_ops.write(stream, buffer, offset, length, position); + if (!seeking) stream.position += bytesWritten; + return bytesWritten; }, + allocate: function (stream, offset, length) { + if (offset < 0 || length <= 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_RDONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EBADF); + } + if (!FS.isFile(stream.node.mode) && !FS.isDir(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + if (!stream.stream_ops.allocate) { + throw new FS.ErrnoError(ERRNO_CODES.EOPNOTSUPP); + } + stream.stream_ops.allocate(stream, offset, length); + }, + mmap: function (stream, buffer, offset, length, position, prot, flags) { + // TODO if PROT is PROT_WRITE, make sure we have write acccess + if ((stream.flags & {{{ cDefine('O_ACCMODE') }}}) === {{{ cDefine('O_WRONLY')}}}) { + throw new FS.ErrnoError(ERRNO_CODES.EACCES); + } + if (!stream.stream_ops.mmap) { + throw new FS.errnoError(ERRNO_CODES.ENODEV); + } + return stream.stream_ops.mmap(stream, buffer, offset, length, position, prot, flags); + } + }, - // Standardizes a path. Useful for making comparisons of pathnames work in a consistent manner. - // For example, ./file and file are really the same, so this function will remove ./ - standardizePath: function(path) { - if (path.substr(0, 2) == './') path = path.substr(2); - return path; + $MEMFS__deps: ['$FS'], + $MEMFS: { + mount: function (mount) { + return MEMFS.create_node(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + }, + create_node: function (parent, name, mode, dev) { + if (FS.isBlkdev(mode) || FS.isFIFO(mode)) { + // no supported + throw new FS.ErrnoError(ERRNO_CODES.EPERM); + } + var node = FS.createNode(parent, name, mode, dev); + node.node_ops = MEMFS.node_ops; + if (FS.isDir(node.mode)) { + node.stream_ops = MEMFS.stream_ops; + node.contents = {}; + } else if (FS.isFile(node.mode)) { + node.stream_ops = MEMFS.stream_ops; + node.contents = []; + } else if (FS.isLink(node.mode)) { + node.stream_ops = MEMFS.stream_ops; + } else if (FS.isChrdev(node.mode)) { + node.stream_ops = FS.chrdev_stream_ops; + } + node.timestamp = Date.now(); + // add the new node to the parent + if (parent) { + parent.contents[name] = node; + } + return node; }, + node_ops: { + getattr: function (node) { + var attr = {}; + // device numbers reuse inode numbers. + attr.dev = FS.isChrdev(node.mode) ? node.id : 1; + attr.ino = node.id; + attr.mode = node.mode; + attr.nlink = 1; + attr.uid = 0; + attr.gid = 0; + attr.rdev = node.rdev; + if (FS.isDir(node.mode)) { + attr.size = 4096; + } else if (FS.isFile(node.mode)) { + attr.size = node.contents.length; + } else if (FS.isLink(node.mode)) { + attr.size = node.link.length; + } else { + attr.size = 0; + } + attr.atime = new Date(node.timestamp); + attr.mtime = new Date(node.timestamp); + attr.ctime = new Date(node.timestamp); + // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), + // but this is not required by the standard. + attr.blksize = 4096; + attr.blocks = Math.ceil(attr.size / attr.blksize); + return attr; + }, + setattr: function (node, attr) { + if (attr.mode !== undefined) { + node.mode = attr.mode; + } + if (attr.timestamp !== undefined) { + node.timestamp = attr.timestamp; + } + if (attr.size !== undefined) { + var contents = node.contents; + if (attr.size < contents.length) contents.length = attr.size; + else while (attr.size > contents.length) contents.push(0); + } + }, + lookup: function (parent, name) { + throw new FS.ErrnoError(ERRNO_CODES.ENOENT); + }, + mknod: function (parent, name, mode, dev) { + return MEMFS.create_node(parent, name, mode, dev); + }, + rename: function (old_node, new_dir, new_name) { + // if we're overwriting a directory at new_name, make sure it's empty. + if (FS.isDir(old_node.mode)) { + var new_node; + try { + new_node = FS.lookupNode(new_dir, new_name); + } catch (e) { + } + if (new_node) { + for (var i in new_node.contents) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + } + } + // do the internal rewiring + delete old_node.parent.contents[old_node.name]; + old_node.name = new_name; + new_dir.contents[new_name] = old_node; + }, + unlink: function (parent, name) { + delete parent.contents[name]; + }, + rmdir: function (parent, name) { + var node = FS.lookupNode(parent, name); + for (var i in node.contents) { + throw new FS.ErrnoError(ERRNO_CODES.ENOTEMPTY); + } + delete parent.contents[name]; + }, + symlink: function (parent, newname, oldpath) { + var node = MEMFS.create_node(parent, newname, 0777 | {{{ cDefine('S_IFLNK') }}}, 0); + node.link = oldpath; + return node; + }, + readlink: function (node) { + if (!FS.isLink(node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + return node.link; + }, + }, + stream_ops: { + open: function (stream) { + if (FS.isDir(stream.node.mode)) { + // cache off the directory entries when open'd + var entries = ['.', '..'] + for (var key in stream.node.contents) { + if (!stream.node.contents.hasOwnProperty(key)) { + continue; + } + entries.push(key); + } + stream.entries = entries; + } + }, + read: function (stream, buffer, offset, length, position) { + var contents = stream.node.contents; + var size = Math.min(contents.length - position, length); +#if USE_TYPED_ARRAYS == 2 + if (contents.subarray) { // typed array + buffer.set(contents.subarray(position, position + size), offset); + } else +#endif + { + for (var i = 0; i < size; i++) { + buffer[offset + i] = contents[position + i]; + } + } + return size; + }, + write: function (stream, buffer, offset, length, position) { + var contents = stream.node.contents; + while (contents.length < position) contents.push(0); + for (var i = 0; i < length; i++) { + contents[position + i] = buffer[offset + i]; + } + stream.node.timestamp = Date.now(); + return length; + }, + llseek: function (stream, offset, whence) { + var position = offset; + if (whence === 1) { // SEEK_CUR. + position += stream.position; + } else if (whence === 2) { // SEEK_END. + if (FS.isFile(stream.node.mode)) { + position += stream.node.contents.length; + } + } + if (position < 0) { + throw new FS.ErrnoError(ERRNO_CODES.EINVAL); + } + stream.ungotten = []; + stream.position = position; + return position; + }, + readdir: function (stream) { + return stream.entries; + }, + allocate: function (stream, offset, length) { + var contents = stream.node.contents; + var limit = offset + length; + while (limit > contents.length) contents.push(0); + }, + mmap: function (stream, buffer, offset, length, position, prot, flags) { + if (!FS.isFile(stream.node.mode)) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + var ptr; + var allocated; + var contents = stream.node.contents; + // Only make a new copy when MAP_PRIVATE is specified. + if (!(flags & {{{ cDefine('MAP_PRIVATE') }}})) { + // We can't emulate MAP_SHARED when the file is not backed by the buffer + // we're mapping to (e.g. the HEAP buffer). + assert(contents.buffer === buffer || contents.buffer === buffer.buffer); + allocated = false; + ptr = contents.byteOffset; + } else { + // Try to avoid unnecessary slices. + if (position > 0 || position + length < contents.length) { + if (contents.subarray) { + contents = contents.subarray(position, position + length); + } else { + contents = Array.prototype.slice.call(contents, position, position + length); + } + } + allocated = true; + ptr = _malloc(length); + if (!ptr) { + throw new FS.ErrnoError(ERRNO_CODES.ENOMEM); + } + buffer.set(contents, ptr); + } + return { ptr: ptr, allocated: allocated }; + }, + } + }, + + $SOCKFS__postset: '__ATINIT__.push({ func: function() { SOCKFS.root = VFS.mount(SOCKFS, {}, null); } });', + $SOCKFS__deps: ['$FS'], + $SOCKFS: { + mount: function (mount) { + var node = FS.createNode(null, '/', {{{ cDefine('S_IFDIR') }}} | 0777, 0); + node.node_ops = SOCKFS.node_ops; + node.stream_ops = SOCKFS.stream_ops; + return node; + }, + node_ops: { + }, + stream_ops: { + }, + websocket_sock_ops: { + } + }, - deleteFile: function(path) { - path = FS.analyzePath(path); - if (!path.parentExists || !path.exists) { - throw 'Invalid path ' + path; + $TTY__deps: ['$FS'], + $TTY: { + ttys: [], + register: function (dev, ops) { + TTY.ttys[dev] = { input: [], output: [], ops: ops }; + FS.registerDevice(dev, TTY.stream_ops); + }, + stream_ops: { + open: function (stream) { + // this wouldn't be required if the library wasn't eval'd at first... + if (!TTY.utf8) { + TTY.utf8 = new Runtime.UTF8Processor(); + } + var tty = TTY.ttys[stream.node.rdev]; + if (!tty) { + throw new FS.ErrnoError(ERRNO_CODES.ENODEV); + } + stream.tty = tty; + stream.seekable = false; + }, + close: function (stream) { + // flush any pending line data + if (stream.tty.output.length) { + stream.tty.ops.put_char(stream.tty, {{{ charCode('\n') }}}); + } + }, + read: function (stream, buffer, offset, length, pos /* ignored */) { + if (!stream.tty || !stream.tty.ops.get_char) { + throw new FS.ErrnoError(ERRNO_CODES.ENXIO); + } + var bytesRead = 0; + for (var i = 0; i < length; i++) { + var result; + try { + result = stream.tty.ops.get_char(stream.tty); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + if (result === undefined && bytesRead === 0) { + throw new FS.ErrnoError(ERRNO_CODES.EAGAIN); + } + if (result === null || result === undefined) break; + bytesRead++; + buffer[offset+i] = result; + } + if (bytesRead) { + stream.node.timestamp = Date.now(); + } + return bytesRead; + }, + write: function (stream, buffer, offset, length, pos) { + if (!stream.tty || !stream.tty.ops.put_char) { + throw new FS.ErrnoError(ERRNO_CODES.ENXIO); + } + for (var i = 0; i < length; i++) { + try { + stream.tty.ops.put_char(stream.tty, buffer[offset+i]); + } catch (e) { + throw new FS.ErrnoError(ERRNO_CODES.EIO); + } + } + if (length) { + stream.node.timestamp = Date.now(); + } + return i; + } + }, + // NOTE: This is weird to support stdout and stderr + // overrides in addition to print and printErr orverrides. + default_tty_ops: { + get_char: function (tty) { + if (!tty.input.length) { + var result = null; + if (ENVIRONMENT_IS_NODE) { + if (process.stdin.destroyed) { + return undefined; + } + result = process.stdin.read(); + } else if (typeof window != 'undefined' && + typeof window.prompt == 'function') { + // Browser. + result = window.prompt('Input: '); // returns null on cancel + if (result !== null) { + result += '\n'; + } + } else if (typeof readline == 'function') { + // Command line. + result = readline(); + if (result !== null) { + result += '\n'; + } + } + if (!result) { + return null; + } + tty.input = intArrayFromString(result, true); + } + return tty.input.shift(); + }, + put_char: function (tty, val) { + if (val === null || val === {{{ charCode('\n') }}}) { + Module['print'](tty.output.join('')); + tty.output = []; + } else { + tty.output.push(TTY.utf8.processCChar(val)); + } + } + }, + default_tty1_ops: { + put_char: function (tty, val) { + if (val === null || val === {{{ charCode('\n') }}}) { + Module['printErr'](tty.output.join('')); + tty.output = []; + } else { + tty.output.push(TTY.utf8.processCChar(val)); + } } - delete path.parentObject.contents[path.name]; } }, +#endif + // ========================================================================== // dirent.h @@ -690,160 +1748,125 @@ LibraryManager.library = { ['i32', 'd_off'], ['i32', 'd_reclen'], ['i32', 'd_type']]), - opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], + opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout', 'open'], 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) { + var path = Pointer_stringify(dirname); + if (!path) { ___setErrNo(ERRNO_CODES.ENOENT); return 0; } - var target = FS.findObject(path); - if (target === null) return 0; - if (!target.isFolder) { - ___setErrNo(ERRNO_CODES.ENOTDIR); + var node; + try { + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + } catch (e) { + FS.handleFSError(e); return 0; - } else if (!target.read) { - ___setErrNo(ERRNO_CODES.EACCES); + } + if (!FS.isDir(node.mode)) { + ___setErrNo(ERRNO_CODES.ENOTDIR); return 0; } - var contents = []; - for (var key in target.contents) contents.push(key); - var id = FS.createFileHandle({ - 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__) - }); -#if ASSERTIONS - FS.checkStreams(); -#endif - return id; + var err = _open(dirname, {{{ cDefine('O_RDONLY') }}}, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); + // open returns 0 on failure, not -1 + return err === -1 ? 0 : err; }, - closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], + closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'close'], 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) { - ___setErrNo(ERRNO_CODES.EBADF); - return -1; - } else { - _free(FS.streams[dirp].currentEntry); - FS.streams[dirp] = null; - return 0; - } + return _close(dirp); }, 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) { + var stream = FS.getStream(dirp); + if (!stream || !FS.isDir(stream.node.mode)) { ___setErrNo(ERRNO_CODES.EBADF); return -1; - } else { - return FS.streams[dirp].position; } + return stream.position; }, - seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], + seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'lseek'], 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; - } - } + _lseek(dirp, loc, {{{ cDefine('SEEK_SET') }}}); }, rewinddir__deps: ['seekdir'], rewinddir: function(dirp) { // void rewinddir(DIR *dirp); // http://pubs.opengroup.org/onlinepubs/007908799/xsh/rewinddir.html - _seekdir(dirp, -2); + _seekdir(dirp, 0); }, 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) { + var stream = FS.getStream(dirp); + if (!stream) { 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) { + var entries; + try { + entries = VFS.readdir(stream); + } catch (e) { + return FS.handleFSError(e); + } + if (stream.position < 0 || stream.position >= entries.length) { {{{ makeSetValue('result', '0', '0', 'i8*') }}} - } else { - var name, inode, type; - if (loc === -2) { - name = '.'; - inode = 1; // Really undefined. - type = 4; //DT_DIR - } else if (loc === -1) { - name = '..'; - inode = 1; // Really undefined. - type = 4; //DT_DIR - } else { - var object; - name = stream.contents[loc]; - object = stream.object.contents[name]; - inode = object.inodeNumber; - type = object.isDevice ? 2 // DT_CHR, character device. - : object.isFolder ? 4 // DT_DIR, directory. - : object.link !== undefined ? 10 // DT_LNK, symbolic link. - : 8; // DT_REG, regular file. - } - 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') }}} - {{{ makeSetValue('entry', 'offsets.d_type', 'type', 'i8') }}} - {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} + return; } + var id; + var type; + var name = entries[stream.position]; + var offset = stream.position + 1; + if (!name.indexOf('.')) { + id = 1; + type = 4; + } else { + var child = FS.lookupNode(stream.node, name); + id = child.id; + type = FS.isChrdev(child.mode) ? 2 : // DT_CHR, character device. + FS.isDir(child.mode) ? 4 : // DT_DIR, directory. + FS.isLink(child.mode) ? 10 : // DT_LNK, symbolic link. + 8; // DT_REG, regular file. + } + {{{ makeSetValue('entry', '___dirent_struct_layout.d_ino', 'id', 'i32') }}} + {{{ makeSetValue('entry', '___dirent_struct_layout.d_off', 'offset', 'i32') }}} + {{{ makeSetValue('entry', '___dirent_struct_layout.d_reclen', 'name.length + 1', 'i32') }}} + for (var i = 0; i < name.length; i++) { + {{{ makeSetValue('entry + ___dirent_struct_layout.d_name', 'i', 'name.charCodeAt(i)', 'i8') }}} + } + {{{ makeSetValue('entry + ___dirent_struct_layout.d_name', 'i', '0', 'i8') }}} + {{{ makeSetValue('entry', '___dirent_struct_layout.d_type', 'type', 'i8') }}} + {{{ makeSetValue('result', '0', 'entry', 'i8*') }}} + stream.position++; 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) { + var stream = FS.getStream(dirp); + if (!stream) { ___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; - } } + // TODO Is it supposed to be safe to execute multiple readdirs? + if (!_readdir.entry) _readdir.entry = _malloc(___dirent_struct_layout.__size__); + if (!_readdir.result) _readdir.result = _malloc(4); + var err = _readdir_r(dirp, _readdir.entry, _readdir.result); + if (err) { + ___setErrNo(err); + return 0; + } + return {{{ makeGetValue(0, '_readdir.result', 'i8*') }}}; }, __01readdir64_: 'readdir', // TODO: Check if we need to link any other aliases. @@ -868,10 +1891,14 @@ LibraryManager.library = { } else { time = Date.now(); } - var file = FS.findObject(Pointer_stringify(path)); - if (file === null) return -1; - file.timestamp = time; - return 0; + path = Pointer_stringify(path); + try { + VFS.utime(path, time, time); + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; + } }, utimes: function() { throw 'utimes not implemented' }, @@ -964,67 +1991,27 @@ LibraryManager.library = { // 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. - } + path = typeof path !== 'string' ? Pointer_stringify(path) : path; + try { + var stat = dontResolveLastLink ? VFS.lstat(path) : VFS.stat(path); + {{{ makeSetValue('buf', '___stat_struct_layout.st_dev', 'stat.dev', 'i32') }}}; + {{{ makeSetValue('buf', '___stat_struct_layout.st_ino', 'stat.ino', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_mode', 'stat.mode', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_nlink', 'stat.nlink', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_uid', 'stat.uid', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_gid', 'stat.gid', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_rdev', 'stat.rdev', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_size', 'stat.size', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_atime', 'Math.floor(stat.atime.getTime() / 1000)', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_mtime', 'Math.floor(stat.mtime.getTime() / 1000)', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_ctime', 'Math.floor(stat.ctime.getTime() / 1000)', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_blksize', '4096', 'i32') }}} + {{{ makeSetValue('buf', '___stat_struct_layout.st_blocks', 'stat.blocks', 'i32') }}} + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; } - {{{ makeSetValue('buf', 'offsets.st_dev', 'dev', 'i32') }}}; - {{{ makeSetValue('buf', 'offsets.st_rdev', 'rdev', 'i32') }}}; - {{{ 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) { @@ -1036,40 +2023,30 @@ LibraryManager.library = { fstat: function(fildes, buf) { // int fstat(int fildes, struct stat *buf); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fstat.html - if (!FS.streams[fildes]) { + var stream = FS.getStream(fildes); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; - } else { - var pathArray = intArrayFromString(FS.streams[fildes].path); - return _stat(allocate(pathArray, 'i8', ALLOC_STACK), buf); } + return _stat(stream.path, 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 path = Pointer_stringify(path); - var fmt = (mode & {{{ cDefine('S_IFMT') }}}); - if (fmt !== {{{ cDefine('S_IFREG') }}} && fmt !== {{{ cDefine('S_IFCHR') }}} && - fmt !== {{{ cDefine('S_IFBLK') }}} && fmt !== {{{ cDefine('S_IFIFO') }}} && - fmt !== {{{ cDefine('S_IFSOCK') }}}) { - // not valid formats for mknod - ___setErrNo(ERRNO_CODES.EINVAL); + // we don't want this in the JS API as the JS API + // uses mknod to create all nodes. + var err = FS.mayMknod(mode); + if (err) { + ___setErrNo(err); return -1; } - if (fmt === {{{ cDefine('S_IFCHR') }}} || fmt === {{{ cDefine('S_IFBLK') }}} || - fmt === {{{ cDefine('S_IFIFO') }}} || fmt === {{{ cDefine('S_IFSOCK') }}}) { - // not supported currently - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - } - path = FS.analyzePath(path); - var properties = { contents: [], isFolder: false }; // S_IFDIR. try { - FS.createObject(path.parentObject, path.name, properties, - mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. + VFS.mknod(path, mode, dev); return 0; } catch (e) { + FS.handleFSError(e); return -1; } }, @@ -1078,13 +2055,11 @@ LibraryManager.library = { // int mkdir(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html path = Pointer_stringify(path); - path = FS.analyzePath(path); - var properties = { contents: [], isFolder: true }; try { - FS.createObject(path.parentObject, path.name, properties, - mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. + VFS.mkdir(path, mode, 0); return 0; } catch (e) { + FS.handleFSError(e); return -1; } }, @@ -1099,33 +2074,43 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EROFS); return -1; }, - chmod__deps: ['$FS'], + chmod__deps: ['$FS', '__setErrNo'], chmod: function(path, mode, dontResolveLastLink) { // int chmod(const char *path, mode_t mode); // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chmod.html // NOTE: dontResolveLastLink is a shortcut for lchmod(). It should never be // used in client code. path = typeof path !== 'string' ? Pointer_stringify(path) : path; - var obj = FS.findObject(path, dontResolveLastLink); - if (obj === null) return -1; - obj.read = mode & 0x100; // S_IRUSR. - obj.write = mode & 0x80; // S_IWUSR. - obj.timestamp = Date.now(); - return 0; + try { + VFS.chmod(path, mode); + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; + } }, 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 - var stream = FS.streams[fildes]; - if (!stream) { - ___setErrNo(ERRNO_CODES.EBADF); + try { + VFS.fchmod(fildes, mode); + return 0; + } catch (e) { + FS.handleFSError(e); return -1; } - return _chmod(stream.path, mode); }, - lchmod: function(path, mode) { - return _chmod(path, mode, true); + lchmod__deps: ['chmod'], + lchmod: function (path, mode) { + path = Pointer_stringify(path); + try { + VFS.lchmod(path, mode); + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; + } }, umask__deps: ['$FS'], @@ -1165,7 +2150,7 @@ LibraryManager.library = { ['i32', 'f_namemax']]), statvfs__deps: ['$FS', '__statvfs_struct_layout'], statvfs: function(path, buf) { - // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html + // http://pubs.opengroup.org/onlinepubs/009695399/functions/statvfs.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 @@ -1209,108 +2194,15 @@ LibraryManager.library = { // http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html // NOTE: This implementation tries to mimic glibc rather than 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); + path = Pointer_stringify(path); + try { + var stream = VFS.open(path, oflag, mode); + return stream.fd; + } catch (e) { + FS.handleFSError(e); 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 || 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; - 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); - id = FS.createFileHandle({ - 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 { - id = FS.createFileHandle({ - path: finalPath, - object: target, - position: 0, - isRead: isRead, - isWrite: isWrite, - isAppend: isAppend, - error: false, - eof: false, - ungotten: [] - }); - } -#if ASSERTIONS - FS.checkStreams(); -#endif - return id; }, creat__deps: ['open'], creat: function(path, mode) { @@ -1331,11 +2223,11 @@ LibraryManager.library = { fcntl: function(fildes, cmd, varargs, dup2) { // int fcntl(int fildes, int cmd, ...); // http://pubs.opengroup.org/onlinepubs/009695399/functions/fcntl.html - if (!FS.streams[fildes]) { + var stream = FS.getStream(fildes); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } - var stream = FS.streams[fildes]; switch (cmd) { case {{{ cDefine('F_DUPFD') }}}: var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; @@ -1343,31 +2235,22 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EINVAL); return -1; } - var newStream = {}; - for (var member in stream) { - newStream[member] = stream[member]; + var newStream; + try { + newStream = VFS.open(stream.path, stream.flags, 0, arg); + } catch (e) { + FS.handleFSError(e); + return -1; } - arg = dup2 ? arg : Math.max(arg, FS.streams.length); // dup2 wants exactly arg; fcntl wants a free descriptor >= arg - FS.createFileHandle(newStream, arg); -#if ASSERTIONS - FS.checkStreams(); -#endif - return arg; + return newStream.fd; 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; + return stream.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. + stream.flags |= arg; return 0; case {{{ cDefine('F_GETLK') }}}: case {{{ cDefine('F_GETLK64') }}}: @@ -1405,14 +2288,27 @@ LibraryManager.library = { 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 (!FS.streams[fd] || !FS.streams[fd].isWrite || FS.streams[fd].link || - FS.streams[fd].isFolder || FS.streams[fd].isDevice) { + var stream = FS.getStream(fd); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } - var contents = FS.streams[fd].object.contents; - var limit = offset + len; - while (limit > contents.length) contents.push(0); + try { + VFS.allocate(stream, offset, len); + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; + } + }, + + // ========================================================================== + // sys/file.h + // ========================================================================== + + flock: function(fd, operation) { + // int flock(int fd, int operation); + // Pretend to succeed return 0; }, @@ -1436,8 +2332,8 @@ LibraryManager.library = { var fd = {{{ makeGetValue('pollfd', 'offsets.fd', 'i32') }}}; var events = {{{ makeGetValue('pollfd', 'offsets.events', 'i16') }}}; var revents = 0; - if (FS.streams[fd]) { - var stream = FS.streams[fd]; + var stream = FS.getStream(fd); + if (stream) { if (events & {{{ cDefine('POLLIN') }}}) revents |= {{{ cDefine('POLLIN') }}}; if (events & {{{ cDefine('POLLOUT') }}}) revents |= {{{ cDefine('POLLOUT') }}}; } else { @@ -1458,15 +2354,28 @@ LibraryManager.library = { // 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. + if (amode & ~{{{ cDefine('S_IRWXO') }}}) { + // need a valid mode + ___setErrNo(ERRNO_CODES.EINVAL); + return -1; + } + var node; + try { + var lookup = FS.lookupPath(path, { follow: true }); + node = lookup.node; + } catch (e) { + FS.handleFSError(e); + return -1; + } + var perms = ''; + if (amode & {{{ cDefine('R_OK') }}}) perms += 'r'; + if (amode & {{{ cDefine('W_OK') }}}) perms += 'w'; + if (amode & {{{ cDefine('X_OK') }}}) perms += 'x'; + if (perms /* otherwise, they've just passed F_OK */ && FS.nodePermissions(node, perms)) { ___setErrNo(ERRNO_CODES.EACCES); return -1; - } else { - return 0; } + return 0; }, chdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chdir: function(path) { @@ -1474,17 +2383,24 @@ LibraryManager.library = { // 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); + var lookup; + try { + lookup = FS.lookupPath(path, { follow: true }); + } catch (e) { + FS.handleFSError(e); return -1; - } else if (!path.object.isFolder) { + } + if (!FS.isDir(lookup.node.mode)) { ___setErrNo(ERRNO_CODES.ENOTDIR); return -1; - } else { - FS.currentPath = path.path; - return 0; } + var err = FS.nodePermissions(lookup.node, 'x'); + if (err) { + ___setErrNo(err); + return -1; + } + FS.currentPath = lookup.path; + return 0; }, chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], chown: function(path, owner, group, dontResolveLastLink) { @@ -1495,10 +2411,13 @@ LibraryManager.library = { // 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; + try { + VFS.chown(path, owner, group); + return 0; + } catch (e) { + FS.handleFSError(e); + return -1; + } }, chroot__deps: ['__setErrNo', '$ERRNO_CODES'], chroot: function(path) { @@ -1511,16 +2430,18 @@ LibraryManager.library = { 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); - } - FS.streams[fildes] = null; - return 0; - } else { + var stream = FS.getStream(fildes); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } + try { + VFS.close(stream); + return 0; + } catch (e) { + FS.handleFSError(e);; + return -1; + } }, dup__deps: ['fcntl'], dup: function(fildes) { @@ -1532,24 +2453,32 @@ LibraryManager.library = { dup2: function(fildes, fildes2) { // int dup2(int fildes, int fildes2); // http://pubs.opengroup.org/onlinepubs/000095399/functions/dup.html + var stream = FS.getStream(fildes); if (fildes2 < 0) { ___setErrNo(ERRNO_CODES.EBADF); return -1; - } else if (fildes === fildes2 && FS.streams[fildes]) { + } else if (fildes === fildes2 && stream) { return fildes; } else { _close(fildes2); - return _fcntl(fildes, 0, allocate([fildes2, 0, 0, 0], 'i32', ALLOC_STACK), true); // F_DUPFD. + try { + var stream2 = VFS.open(stream.path, stream.flags, 0, fildes2, fildes2); + return stream2.fd; + } catch (e) { + FS.handleFSError(e); + return -1; + } } }, 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); + try { + VFS.fchown(fildes, owner, group); + return 0; + } catch (e) { + FS.handleFSError(e); return -1; } }, @@ -1557,8 +2486,9 @@ LibraryManager.library = { 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); + var stream = FS.getStream(fildes); + if (stream) { + return _chdir(stream.path); } else { ___setErrNo(ERRNO_CODES.EBADF); return -1; @@ -1631,7 +2561,8 @@ LibraryManager.library = { fsync: function(fildes) { // int fsync(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsync.html - if (FS.streams[fildes]) { + var stream = FS.getStream(fildes); + if (stream) { // We write directly to the file system, so there's nothing to do here. return 0; } else { @@ -1645,42 +2576,24 @@ LibraryManager.library = { // 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); + if (typeof path !== 'string') path = Pointer_stringify(path); + try { + VFS.truncate(path, length); + return 0; + } catch (e) { + FS.handleFSError(e); 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); + try { + VFS.ftruncate(fildes, length); + return 0; + } catch (e) { + FS.handleFSError(e); return -1; } }, @@ -1712,12 +2625,13 @@ LibraryManager.library = { isatty: function(fildes) { // int isatty(int fildes); // http://pubs.opengroup.org/onlinepubs/000095399/functions/isatty.html - var stream = FS.streams[fildes]; + var stream = FS.getStream(fildes); if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return 0; } - if (!stream.object.isTerminal) { + // HACK - implement tcgetattr + if (!stream.tty) { ___setErrNo(ERRNO_CODES.ENOTTY); return 0; } @@ -1741,7 +2655,8 @@ LibraryManager.library = { 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]) { + var stream = FS.getStream(fildes); + if (stream) { // Pretend whatever locking or unlocking operation succeeded. Locking does // not make much sense, since we have a single process/thread. return 0; @@ -1754,26 +2669,17 @@ LibraryManager.library = { 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].object.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 { + var stream = FS.getStream(fildes); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } + try { + return VFS.llseek(stream, offset, whence); + } catch (e) { + FS.handleFSError(e); + return -1; + } }, pipe__deps: ['__setErrNo', '$ERRNO_CODES'], pipe: function(fildes) { @@ -1788,94 +2694,39 @@ LibraryManager.library = { 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) { + var stream = FS.getStream(fildes); + if (!stream) { ___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); + } + try { + var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; + return VFS.read(stream, slab, buf, nbyte, offset); + } catch (e) { + FS.handleFSError(e); return -1; - } else if (offset >= stream.object.contents.length) { - return 0; - } else { - var bytesRead = 0; - var contents = stream.object.contents; - var size = Math.min(contents.length - offset, nbyte); - assert(size >= 0); - -#if USE_TYPED_ARRAYS == 2 - if (contents.subarray) { // typed array - HEAPU8.set(contents.subarray(offset, offset+size), buf); - } else -#endif - if (contents.slice) { // normal array - for (var i = 0; i < size; i++) { - {{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}} - } - } else { - for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR - {{{ makeSetValue('buf', 'i', 'contents.get(offset + i)', 'i8') }}} - } - } - bytesRead += size; - return bytesRead; } }, read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'recv', '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 && ('socket' in stream)) { - return _recv(fildes, buf, nbyte, 0); - } else if (!stream) { + var stream = FS.getStream(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); + } + + if (stream && ('socket' in stream)) { + return _recv(fildes, buf, nbyte, 0); + } + + try { + var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; + return VFS.read(stream, slab, buf, nbyte); + } catch (e) { + FS.handleFSError(e); return -1; - } else { - var bytesRead; - if (stream.object.isDevice) { - if (stream.object.input) { - bytesRead = 0; - for (var i = 0; i < nbyte; i++) { - try { - var result = stream.object.input(); - } catch (e) { - ___setErrNo(ERRNO_CODES.EIO); - return -1; - } - if (result === undefined && bytesRead === 0) { - ___setErrNo(ERRNO_CODES.EAGAIN); - return -1; - } - if (result === null || result === undefined) break; - bytesRead++; - {{{ makeSetValue('buf', 'i', 'result', 'i8') }}} - } - return bytesRead; - } else { - ___setErrNo(ERRNO_CODES.ENXIO); - return -1; - } - } else { - bytesRead = _pread(fildes, buf, nbyte, stream.position); - assert(bytesRead >= -1); - if (bytesRead != -1) { - stream.position += bytesRead; - } - return bytesRead; - } } }, sync: function() { @@ -1888,26 +2739,12 @@ LibraryManager.library = { // int rmdir(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html path = Pointer_stringify(path); - path = FS.analyzePath(path, true); - if (!path.parentExists || !path.exists) { - ___setErrNo(path.error); - return -1; - } else if (!path.parentObject.write) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else if (!path.object.isFolder) { - ___setErrNo(ERRNO_CODES.ENOTDIR); - return -1; - } else if (path.isRoot || path.path == FS.currentPath) { - ___setErrNo(ERRNO_CODES.EBUSY); - return -1; - } else { - for (var i in path.object.contents) { - ___setErrNo(ERRNO_CODES.ENOTEMPTY); - return -1; - } - delete path.parentObject.contents[path.name]; + try { + VFS.rmdir(path); return 0; + } catch (e) { + FS.handleFSError(e); + return -1; } }, unlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], @@ -1915,19 +2752,12 @@ LibraryManager.library = { // int unlink(const char *path); // http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html path = Pointer_stringify(path); - path = FS.analyzePath(path, true); - if (!path.parentExists || !path.exists) { - ___setErrNo(path.error); - return -1; - } else if (path.object.isFolder) { - ___setErrNo(ERRNO_CODES.EPERM); - return -1; - } else if (!path.parentObject.write) { - ___setErrNo(ERRNO_CODES.EACCES); - return -1; - } else { - delete path.parentObject.contents[path.name]; + try { + VFS.unlink(path); return 0; + } catch (e) { + FS.handleFSError(e); + return -1; } }, ttyname__deps: ['ttyname_r'], @@ -1941,7 +2771,7 @@ LibraryManager.library = { 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]; + var stream = FS.getStream(fildes); var ttyname = '/dev/tty'; if (!stream) { return ___setErrNo(ERRNO_CODES.EBADF); @@ -1953,106 +2783,73 @@ LibraryManager.library = { writeStringToMemory(ttyname, name); return 0; }, - symlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], + symlink__deps: ['$FS', '$PATH', '__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); + path1 = Pointer_stringify(path1); + path2 = Pointer_stringify(path2); + try { + VFS.symlink(path1, path2); return 0; + } catch (e) { + FS.handleFSError(e); + return -1; } }, 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); + path = Pointer_stringify(path); + var str; + try { + str = VFS.readlink(path); + } catch (e) { + FS.handleFSError(e); return -1; } + str = str.slice(0, Math.max(0, bufsize - 1)); + writeStringToMemory(str, buf, true); + return str.length; }, 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) { + var stream = FS.getStream(fildes); + if (!stream) { ___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); + } + try { + var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; + return VFS.write(stream, slab, buf, nbyte, offset); + } catch (e) { + FS.handleFSError(e); 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', undefined, 1) }}}; - } - stream.object.timestamp = Date.now(); - return i; } }, write__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'send', '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 && ('socket' in stream)) { - return _send(fildes, buf, nbyte, 0); - } else if (!stream) { + var stream = FS.getStream(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); + } + + if (stream && ('socket' in stream)) { + return _send(fildes, buf, nbyte, 0); + } + + try { + var slab = {{{ makeGetSlabs('buf', 'i8', true) }}}; + return VFS.write(stream, slab, buf, nbyte); + } catch (e) { + FS.handleFSError(e); 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) { @@ -2133,20 +2930,7 @@ LibraryManager.library = { _exit: function(status) { // void _exit(int status); // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html - - function ExitStatus() { - this.name = "ExitStatus"; - this.message = "Program terminated with exit(" + status + ")"; - this.status = status; - Module.print('Exit Status: ' + status); - }; - ExitStatus.prototype = new Error(); - ExitStatus.prototype.constructor = ExitStatus; - - exitRuntime(); - ABORT = true; - - throw new ExitStatus(); + Module['exit'](status); }, fork__deps: ['__setErrNo', '$ERRNO_CODES'], fork: function() { @@ -3164,7 +3948,8 @@ LibraryManager.library = { clearerr: function(stream) { // void clearerr(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html - if (FS.streams[stream]) FS.streams[stream].error = false; + stream = FS.getStream(stream); + if (stream) stream.error = false; }, fclose__deps: ['close', 'fsync'], fclose: function(stream) { @@ -3177,68 +3962,51 @@ LibraryManager.library = { fdopen: function(fildes, mode) { // FILE *fdopen(int fildes, const char *mode); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fdopen.html - if (FS.streams[fildes]) { - 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 { + mode = Pointer_stringify(mode); + var stream = FS.getStream(fildes); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return 0; } + 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; + } }, feof__deps: ['$FS'], feof: function(stream) { // int feof(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html - return Number(FS.streams[stream] && FS.streams[stream].eof); + stream = FS.getStream(stream); + return Number(stream && stream.eof); }, ferror__deps: ['$FS'], ferror: function(stream) { // int ferror(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ferror.html - return Number(FS.streams[stream] && FS.streams[stream].error); + stream = FS.getStream(stream); + return Number(stream && 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 (FS.streams[filedes] && FS.streams[filedes].object.output) { - if (!FS.streams[filedes].object.isTerminal) { // don't flush terminals, it would cause a \n to also appear - FS.streams[filedes].object.output(null); - } - } - }; - try { - if (stream === 0) { - for (var i = 0; i < FS.streams.length; i++) if (FS.streams[i]) flush(i); - } else { - flush(stream); - } - return 0; - } catch (e) { - ___setErrNo(ERRNO_CODES.EIO); - return -1; - } + // we don't currently perform any user-space buffering of data }, fgetc__deps: ['$FS', 'fread'], 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 (!FS.streams[stream]) return -1; - var streamObj = FS.streams[stream]; + var streamObj = FS.getStream(stream); + if (!streamObj) return -1; if (streamObj.eof || streamObj.error) return -1; var ret = _fread(_fgetc.ret, 1, 1, stream); if (ret == 0) { @@ -3263,28 +4031,26 @@ LibraryManager.library = { fgetpos: function(stream, pos) { // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetpos.html - if (FS.streams[stream]) { - 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 { + stream = FS.getStream(stream); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } + if (FS.isChrdev(stream.node.mode)) { + ___setErrNo(ERRNO_CODES.ESPIPE); + return -1; + } + {{{ 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; }, 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 (!FS.streams[stream]) return 0; - var streamObj = FS.streams[stream]; + var streamObj = FS.getStream(stream); + if (!streamObj) return 0; if (streamObj.error || streamObj.eof) return 0; var byte_; for (var i = 0; i < n - 1 && byte_ != {{{ charCode('\n') }}}; i++) { @@ -3362,7 +4128,8 @@ LibraryManager.library = { {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}} var ret = _write(stream, _fputc.ret, 1); if (ret == -1) { - if (FS.streams[stream]) FS.streams[stream].error = true; + var streamObj = FS.getStream(stream); + if (streamObj) streamObj.error = true; return -1; } else { return chr; @@ -3406,7 +4173,7 @@ LibraryManager.library = { return 0; } var bytesRead = 0; - var streamObj = FS.streams[stream]; + var streamObj = FS.getStream(stream); while (streamObj.ungotten.length && bytesToRead > 0) { {{{ makeSetValue('ptr++', '0', 'streamObj.ungotten.pop()', 'i8') }}} bytesToRead--; @@ -3426,12 +4193,13 @@ LibraryManager.library = { // 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 (!FS.streams[stream]) { + var streamObj = FS.getStream(stream); + if (!streamObj) { ___setErrNo(ERRNO_CODES.EBADF); return 0; } if (_freopen.buffer) _free(_freopen.buffer); - filename = intArrayFromString(FS.streams[stream].path); + filename = intArrayFromString(streamObj.path); filename = allocate(filename, 'i8', ALLOC_NORMAL); } _fclose(stream); @@ -3444,10 +4212,10 @@ LibraryManager.library = { var ret = _lseek(stream, offset, whence); if (ret == -1) { return -1; - } else { - FS.streams[stream].eof = false; - return 0; } + stream = FS.getStream(stream); + stream.eof = false; + return 0; }, fseeko: 'fseek', fseeko64: 'fseek', @@ -3455,38 +4223,36 @@ LibraryManager.library = { fsetpos: function(stream, pos) { // int fsetpos(FILE *stream, const fpos_t *pos); // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsetpos.html - if (FS.streams[stream]) { - 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 { + stream = FS.getStream(stream); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } + if (FS.isChrdev(stream.node.mode)) { + ___setErrNo(ERRNO_CODES.EPIPE); + return -1; + } + stream.position = {{{ makeGetValue('pos', '0', 'i32') }}}; + var state = {{{ makeGetValue('pos', Runtime.getNativeTypeSize('i32'), 'i32') }}}; + stream.eof = Boolean(state & 1); + stream.error = Boolean(state & 2); + return 0; }, ftell__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], ftell: function(stream) { // long ftell(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftell.html - if (FS.streams[stream]) { - stream = FS.streams[stream]; - if (stream.object.isDevice) { - ___setErrNo(ERRNO_CODES.ESPIPE); - return -1; - } else { - return stream.position; - } - } else { + stream = FS.getStream(stream); + if (!stream) { ___setErrNo(ERRNO_CODES.EBADF); return -1; } + if (FS.isChrdev(stream.node.mode)) { + ___setErrNo(ERRNO_CODES.ESPIPE); + return -1; + } else { + return stream.position; + } }, ftello: 'ftell', ftello64: 'ftell', @@ -3498,7 +4264,8 @@ LibraryManager.library = { if (bytesToWrite == 0) return 0; var bytesWritten = _write(stream, ptr, bytesToWrite); if (bytesWritten == -1) { - if (FS.streams[stream]) FS.streams[stream].error = true; + var streamObj = FS.getStream(stream); + if (streamObj) streamObj.error = true; return 0; } else { return Math.floor(bytesWritten / size); @@ -3542,30 +4309,17 @@ LibraryManager.library = { return ret; }, rename__deps: ['__setErrNo', '$ERRNO_CODES'], - rename: function(old, new_) { + rename: function(old_path, new_path) { // 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) { + old_path = Pointer_stringify(old_path); + new_path = Pointer_stringify(new_path); + try { + VFS.rename(old_path, new_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.parentPath && - newObj.parentPath.indexOf(oldObj.path) == 0) { - ___setErrNo(ERRNO_CODES.EINVAL); - return -1; - } else if (newObj.exists && newObj.object.isFolder) { - ___setErrNo(ERRNO_CODES.EISDIR); + } catch (e) { + FS.handleFSError(e); return -1; - } else { - delete oldObj.parentObject.contents[oldObj.name]; - newObj.parentObject.contents[newObj.name] = oldObj.object; - return 0; } }, rewind__deps: ['$FS', 'fseek'], @@ -3573,7 +4327,8 @@ LibraryManager.library = { // void rewind(FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/rewind.html _fseek(stream, 0, 0); // SEEK_SET. - if (FS.streams[stream]) FS.streams[stream].error = false; + var streamObj = FS.getStream(stream); + if (streamObj) streamObj.error = false; }, setvbuf: function(stream, buf, type, size) { // int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size); @@ -3632,7 +4387,7 @@ LibraryManager.library = { ungetc: function(c, stream) { // int ungetc(int c, FILE *stream); // http://pubs.opengroup.org/onlinepubs/000095399/functions/ungetc.html - stream = FS.streams[stream]; + stream = FS.getStream(stream); if (!stream) { return -1; } @@ -3653,34 +4408,24 @@ LibraryManager.library = { ___setErrNo(ERRNO_CODES.EAGAIN); return -1; }, - fscanf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', - '_scanString', 'fgetc', 'fseek', 'ftell'], + fscanf__deps: ['$FS', '_scanString', 'fgetc', '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 (FS.streams[stream]) { - var i = _ftell(stream), SEEK_SET = 0; - // if the stream does not support seeking backwards (e.g. stdin), buffer it here - var buffer = [], bufferIndex = 0; - var get = function() { - if (bufferIndex < buffer.length) { - return buffer[bufferIndex++]; - } - i++; - bufferIndex++; - var c = _fgetc(stream); - buffer.push(c); - return c; - }; - var unget = function() { - if (_fseek(stream, --i, SEEK_SET) !== 0) { - bufferIndex--; - } - }; - return __scanString(format, get, unget, varargs); - } else { + var streamObj = FS.getStream(stream); + if (!streamObj) { return -1; } + var buffer = []; + var get = function() { + var c = _fgetc(stream); + buffer.push(c); + return c; + }; + var unget = function() { + _ungetc(buffer.pop(), stream); + }; + return __scanString(format, get, unget, varargs); }, scanf__deps: ['fscanf'], scanf: function(format, varargs) { @@ -3820,38 +4565,26 @@ LibraryManager.library = { * mmap. */ var MAP_PRIVATE = 2; + var ptr; var allocated = false; if (!_mmap.mappings) _mmap.mappings = {}; if (stream == -1) { - var ptr = _malloc(num); + ptr = _malloc(num); if (!ptr) return -1; _memset(ptr, 0, num); allocated = true; } else { - var info = FS.streams[stream]; + var info = FS.getStream(stream); if (!info) return -1; - var contents = info.object.contents; - // Only make a new copy when MAP_PRIVATE is specified. - if (flags & MAP_PRIVATE == 0) { - // We can't emulate MAP_SHARED when the file is not backed by HEAP. - assert(contents.buffer === HEAPU8.buffer); - ptr = contents.byteOffset; - allocated = false; - } else { - // Try to avoid unnecessary slices. - if (offset > 0 || offset + num < contents.length) { - if (contents.subarray) { - contents = contents.subarray(offset, offset+num); - } else { - contents = Array.prototype.slice.call(contents, offset, offset+num); - } - } - ptr = _malloc(num); - if (!ptr) return -1; - HEAPU8.set(contents, ptr); - allocated = true; + try { + var res = VFS.mmap(info, HEAPU8, start, num, offset, prot, flags); + ptr = res.ptr; + allocated = res.allocated; + } catch (e) { + FS.handleFSError(e); + return -1; } } @@ -3876,6 +4609,20 @@ LibraryManager.library = { // TODO: Implement mremap. + mprotect: function(addr, len, prot) { + // int mprotect(void *addr, size_t len, int prot); + // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mprotect.html + // Pretend to succeed + return 0; + }, + + msync: function(addr, len, flags) { + // int msync(void *addr, size_t len, int flags); + // http://pubs.opengroup.org/onlinepubs/009696799/functions/msync.html + // Pretend to succeed + return 0; + }, + // ========================================================================== // stdlib.h // ========================================================================== @@ -3939,13 +4686,16 @@ LibraryManager.library = { __cxa_atexit: 'atexit', abort: function() { - ABORT = true; - throw 'abort() at ' + (new Error().stack); + Module['abort'](); }, bsearch: function(key, base, num, size, compar) { var cmp = function(x, y) { - return Runtime.dynCall('iii', compar, [x, y]) +#if ASM_JS + return Module['dynCall_iii'](compar, x, y); +#else + return FUNCTION_TABLE[compar](x, y); +#endif }; var left = 0; var right = num; @@ -3955,7 +4705,6 @@ LibraryManager.library = { mid = (left + right) >>> 1; addr = base + (mid * size); test = cmp(key, addr); - if (test < 0) { right = mid; } else if (test > 0) { @@ -4175,13 +4924,14 @@ LibraryManager.library = { if (num == 0 || size == 0) return; // forward calls to the JavaScript sort method // first, sort the items logically - var comparator = function(x, y) { - return Runtime.dynCall('iii', cmp, [x, y]); - } 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); +#if ASM_JS + return Module['dynCall_iii'](cmp, base+a*size, base+b*size); +#else + return FUNCTION_TABLE[cmp](base+a*size, base+b*size); +#endif }); // apply the sort var temp = _malloc(num*size); @@ -4459,6 +5209,13 @@ LibraryManager.library = { llvm_memmove_p0i8_p0i8_i32: 'memmove', llvm_memmove_p0i8_p0i8_i64: 'memmove', + bcopy__deps: ['memmove'], + bcopy: function(src, dest, num) { + // void bcopy(const void *s1, void *s2, size_t n); + // http://pubs.opengroup.org/onlinepubs/009695399/functions/bcopy.html + _memmove(dest, src, num); + }, + memset__inline: function(ptr, value, num, align) { return makeSetValues(ptr, 0, value, 'null', num, align); }, @@ -6161,9 +6918,14 @@ LibraryManager.library = { {{{ 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)); + var start = new Date(date); // define date using UTC, start from Jan 01 00:00:00 UTC + start.setUTCDate(1); + start.setUTCMonth(0); + start.setUTCHours(0); + start.setUTCMinutes(0); + start.setUTCSeconds(0); + start.setUTCMilliseconds(0); + var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} var timezone = "GMT"; @@ -6174,7 +6936,6 @@ LibraryManager.library = { return tmPtr; }, - timegm__deps: ['mktime'], timegm: function(tmPtr) { _tzset(); @@ -6966,8 +7727,6 @@ LibraryManager.library = { // ========================================================================== // 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 ((maj) << 8 | (min)); @@ -7814,42 +8573,53 @@ LibraryManager.library = { ntohl: 'htonl', ntohs: 'htons', - inet_addr: function(ptr) { - var b = Pointer_stringify(ptr).split("."); - if (b.length !== 4) return -1; // we return -1 for error, and otherwise a uint32. this helps inet_pton differentiate - return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0; + // http://pubs.opengroup.org/onlinepubs/9699919799/functions/inet_ntop.html + inet_ntop__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_ntop4', 'inet_ntop6'], + inet_ntop: function(af, src, dst, size) { + switch (af) { + case {{{ cDefine('AF_INET') }}}: + return _inet_ntop4(src, dst, size); + case {{{ cDefine('AF_INET6') }}}: + return _inet_ntop6(src, dst, size); + default: + ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + return 0; + } }, - - inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_addr'], + inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_pton4', 'inet_pton6'], inet_pton: function(af, src, dst) { - // int af, const char *src, void *dst - if ((af ^ {{{ cDefine("AF_INET") }}}) !== 0) { ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); return -1; } - var ret = _inet_addr(src); - if (ret == -1 || isNaN(ret)) return 0; - setValue(dst, ret, 'i32'); - return 1; + switch (af) { + case {{{ cDefine('AF_INET') }}}: + return _inet_pton4(src, dst); + case {{{ cDefine('AF_INET6') }}}: + return _inet_pton6(src, dst); + default: + ___setErrNo(ERRNO_CODES.EAFNOSUPPORT); + return -1; + } }, - - _inet_ntop_raw: function(addr) { - return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) + inet_addr: function(ptr) { + var b = Pointer_stringify(ptr).split("."); + if (b.length !== 4) return -1; // we return -1 for error, and otherwise a uint32. this helps inet_pton differentiate + return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0; }, - - inet_ntop__deps: ['_inet_ntop_raw'], - inet_ntop: function(af, src, dst, size) { - var addr = getValue(src, 'i32'); - var str = __inet_ntop_raw(addr); - writeStringToMemory(str.substr(0, size), dst); - return dst; + _inet_aton_raw: function(str) { + var b = str.split("."); + return (Number(b[0]) | (Number(b[1]) << 8) | (Number(b[2]) << 16) | (Number(b[3]) << 24)) >>> 0; }, - - inet_ntoa__deps: ['inet_ntop'], + _inet_ntoa_raw: function(addr) { + return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) + }, + inet_ntoa__deps: ['_inet_ntoa_raw'], inet_ntoa: function(in_addr) { if (!_inet_ntoa.buffer) { _inet_ntoa.buffer = _malloc(1024); } - return _inet_ntop(0, in_addr, _inet_ntoa.buffer, 1024); + var addr = getValue(in_addr, 'i32'); + var str = __inet_ntoa_raw(addr); + writeStringToMemory(str.substr(0, 1024), _inet_ntoa.buffer); + return _inet_ntoa.buffer; }, - inet_aton__deps: ['inet_addr'], inet_aton: function(cp, inp) { var addr = _inet_addr(cp); @@ -7858,6 +8628,182 @@ LibraryManager.library = { return 1; }, + inet_ntop4__deps: ['__setErrNo', '$ERRNO_CODES', '_inet_ntoa_raw'], + inet_ntop4: function(src, dst, size) { + var str = __inet_ntoa_raw(getValue(src, 'i32')); + if (str.length+1 > size) { + ___setErrNo(ERRNO_CODES.ENOSPC); + return 0; + } + writeStringToMemory(str, dst); + return dst; + }, + + inet_ntop6__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_ntop6_raw'], + inet_ntop6: function(src, dst, size) { + var str = _inet_ntop6_raw(src); + if (str.length+1 > size) { + ___setErrNo(ERRNO_CODES.ENOSPC); + return 0; + } + writeStringToMemory(str, dst); + return dst; + }, + inet_ntop6_raw__deps: ['ntohs'], + inet_ntop6_raw: function(src) { + + // ref: http://www.ietf.org/rfc/rfc2373.txt - section 2.5.4 + // Format for IPv4 compatible and mapped 128-bit IPv6 Addresses + // 128-bits are split into eight 16-bit words + // stored in network byte order (big-endian) + // | 80 bits | 16 | 32 bits | + // +-----------------------------------------------------------------+ + // | 10 bytes | 2 | 4 bytes | + // +--------------------------------------+--------------------------+ + // + 5 words | 1 | 2 words | + // +--------------------------------------+--------------------------+ + // |0000..............................0000|0000| IPv4 ADDRESS | (compatible) + // +--------------------------------------+----+---------------------+ + // |0000..............................0000|FFFF| IPv4 ADDRESS | (mapped) + // +--------------------------------------+----+---------------------+ + + var str = ""; + var word = 0; + var longest = 0; + var lastzero = 0; + var zstart = 0; + var len = 0; + var i = 0; + + // Handle IPv4-compatible, IPv4-mapped, loopback and any/unspecified addresses + + var hasipv4 = true; + var v4part = ""; + // check if the 10 high-order bytes are all zeros (first 5 words) + for (i = 0; i < 10; i++) { + if ({{{ makeGetValue('src', 'i', 'i8') }}} !== 0) { hasipv4 = false; break; } + } + + if (hasipv4) { + // low-order 32-bits store an IPv4 address (bytes 13 to 16) (last 2 words) + v4part = __inet_ntoa_raw({{{ makeGetValue('src', '12', 'i32') }}}); + // IPv4-mapped IPv6 address if 16-bit value (bytes 11 and 12) == 0xFFFF (6th word) + if ({{{ makeGetValue('src', '10', 'i16') }}} === -1) { + str = "::ffff:"; + str += v4part; + return str; + } + // IPv4-compatible IPv6 address if 16-bit value (bytes 11 and 12) == 0x0000 (6th word) + if ({{{ makeGetValue('src', '10', 'i16') }}} === 0) { + str = "::"; + //special case IPv6 addresses + if(v4part === "0.0.0.0") v4part = ""; // any/unspecified address + if(v4part === "0.0.0.1") v4part = "1";// loopback address + str += v4part; + return str; + } + } + + // Handle all other IPv6 addresses + + // first run to find the longest contiguous zero words + for (word = 0; word < 8; word++) { + if ({{{ makeGetValue('src', 'word*2', 'i16') }}} === 0) { + if (word - lastzero > 1) { + len = 0; + } + lastzero = word; + len++; + } + if (len > longest) { + longest = len; + zstart = word - longest + 1; + } + } + + for (word = 0; word < 8; word++) { + if (longest > 1) { + // compress contiguous zeros - to produce "::" + if ({{{ makeGetValue('src', 'word*2', 'i16') }}} === 0 && word >= zstart && word < (zstart + longest) ) { + if (word === zstart) { + str += ":"; + if (zstart === 0) str += ":"; //leading zeros case + } + continue; + } + } + // converts 16-bit words from big-endian to little-endian before converting to hex string + str += Number(_ntohs({{{ makeGetValue('src', 'word*2', 'i16') }}} & 0xffff)).toString(16); + str += word < 7 ? ":" : ""; + } + return str; + }, + + inet_pton4__deps: ['inet_addr'], + inet_pton4: function(src, dst) { + var ret = _inet_addr(src); + if (ret === -1 || isNaN(ret)) return 0; + setValue(dst, ret, 'i32'); + return 1; + }, + + inet_pton6__deps: ['inet_pton6_raw'], + inet_pton6: function(src, dst) { + return _inet_pton6_raw(Pointer_stringify(src), dst); + }, + + inet_pton6_raw__deps: ['htons'], + inet_pton6_raw: function(addr, dst) { + var words; + var w, offset, z, i; + /* http://home.deds.nl/~aeron/regex/ */ + var valid6regx = /^((?=.*::)(?!.*::.+::)(::)?([\dA-F]{1,4}:(:|\b)|){5}|([\dA-F]{1,4}:){6})((([\dA-F]{1,4}((?!\3)::|:\b|$))|(?!\2\3)){2}|(((2[0-4]|1\d|[1-9])?\d|25[0-5])\.?\b){4})$/i + if (!valid6regx.test(addr)) { + return 0; + } + if (addr === "::") { + for (i=0; i < 4; i++) {{{ makeSetValue('dst', 'i*4', '0', 'i32') }}}; + return 1; + } + // Z placeholder to keep track of zeros when splitting the string on ":" + if (addr.indexOf("::") === 0) { + addr = addr.replace("::", "Z:"); // leading zeros case + } else { + addr = addr.replace("::", ":Z:"); + } + + if (addr.indexOf(".") > 0) { + // parse IPv4 embedded address + addr = addr.replace(new RegExp('[.]', 'g'), ":"); + words = addr.split(":"); + words[words.length-4] = parseInt(words[words.length-4]) + parseInt(words[words.length-3])*256; + words[words.length-3] = parseInt(words[words.length-2]) + parseInt(words[words.length-1])*256; + words = words.slice(0, words.length-2); + } else { + words = addr.split(":"); + } + + offset = 0; z = 0; + for (w=0; w < words.length; w++) { + if (typeof words[w] === 'string') { + if (words[w] === 'Z') { + // compressed zeros - write appropriate number of zero words + for (z = 0; z < (8 - words.length+1); z++) { + {{{ makeSetValue('dst', '(w+z)*2', '0', 'i16') }}}; + } + offset = z-1; + } else { + // parse hex to field to 16-bit value and write it in network byte-order + {{{ makeSetValue('dst', '(w+offset)*2', '_htons(parseInt(words[w],16))', 'i16') }}}; + } + } else { + // parsed IPv4 words + {{{ makeSetValue('dst', '(w+offset)*2', 'words[w]', 'i16') }}}; + } + } + return 1; + }, + // netinet/in.h _in6addr_any: @@ -7911,7 +8857,7 @@ LibraryManager.library = { var aliasesBuf = _malloc(4); setValue(aliasesBuf, 0, 'i8*'); setValue(ret+___hostent_struct_layout.h_aliases, aliasesBuf, 'i8**'); - setValue(ret+___hostent_struct_layout.h_addrtype, {{{ cDefine("AF_INET") }}}, 'i32'); + setValue(ret+___hostent_struct_layout.h_addrtype, {{{ cDefine('AF_INET') }}}, 'i32'); setValue(ret+___hostent_struct_layout.h_length, 4, 'i32'); var addrListBuf = _malloc(12); setValue(addrListBuf, addrListBuf+8, 'i32*'); @@ -7989,15 +8935,16 @@ LibraryManager.library = { socket__deps: ['$Sockets'], socket: function(family, type, protocol) { var INCOMING_QUEUE_LENGTH = 64; - var fd = FS.createFileHandle({ + var stream = FS.createStream({ addr: null, port: null, inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH), header: new Uint16Array(2), bound: false, - socket: true + socket: true, + stream_ops: {} }); - assert(fd < 64); // select() assumes socket fd values are in 0..63 + assert(stream.fd < 64); // select() assumes socket fd values are in 0..63 var stream = type == {{{ cDefine('SOCK_STREAM') }}}; if (protocol) { assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if stream, must be tcp @@ -8111,7 +9058,7 @@ LibraryManager.library = { }; }; - return fd; + return stream.fd; }, mkport__deps: ['$Sockets'], @@ -8130,9 +9077,9 @@ LibraryManager.library = { // Stub: connection-oriented sockets are not supported yet. }, - bind__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'mkport'], + bind__deps: ['$Sockets', '_inet_ntoa_raw', 'ntohs', 'mkport'], bind: function(fd, addr, addrlen) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; if (addr) { info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); @@ -8142,7 +9089,7 @@ LibraryManager.library = { info.port = _mkport(); } info.addr = Sockets.localAddr; // 10.0.0.254 - info.host = __inet_ntop_raw(info.addr); + info.host = __inet_ntoa_raw(info.addr); info.close = function() { Sockets.portmap[info.port] = undefined; } @@ -8151,9 +9098,9 @@ LibraryManager.library = { info.bound = true; }, - sendmsg__deps: ['$Sockets', 'bind', '_inet_ntop_raw', 'ntohs'], + sendmsg__deps: ['$Sockets', 'bind', '_inet_ntoa_raw', 'ntohs'], sendmsg: function(fd, msg, flags) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; // if we are not connected, use the address info in the message if (!info.bound) { @@ -8165,7 +9112,7 @@ LibraryManager.library = { var port = _ntohs(getValue(name + Sockets.sockaddr_in_layout.sin_port, 'i16')); var addr = getValue(name + Sockets.sockaddr_in_layout.sin_addr, 'i32'); var connection = Sockets.connections[addr]; - // var host = __inet_ntop_raw(addr); + // var host = __inet_ntoa_raw(addr); if (!(connection && connection.connected)) { ___setErrNo(ERRNO_CODES.EWOULDBLOCK); @@ -8209,7 +9156,7 @@ LibraryManager.library = { recvmsg__deps: ['$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'], recvmsg: function(fd, msg, flags) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; // if we are not connected, use the address info in the message if (!info.port) { @@ -8260,14 +9207,14 @@ LibraryManager.library = { }, shutdown: function(fd, how) { - var info = FS.streams[fd]; - if (!info) return -1; - info.close(); - FS.removeFileHandle(fd); + var stream = FS.getStream(fd); + if (!stream) return -1; + stream.close(); + FS.closeStream(stream); }, ioctl: function(fd, request, varargs) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; var bytes = 0; if (info.hasData()) { @@ -8287,7 +9234,7 @@ LibraryManager.library = { // TODO: webrtc queued incoming connections, etc. // For now, the model is that bind does a connect, and we "accept" that one connection, // which has host:port the same as ours. We also return the same socket fd. - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; if (addr) { setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32'); @@ -8328,7 +9275,7 @@ LibraryManager.library = { var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh; if (int_ & mask) { // index is in the set, check if it is ready for read - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (info && can(info)) { // set bit fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); @@ -8357,23 +9304,24 @@ LibraryManager.library = { if (protocol) { assert(stream == (protocol == {{{ cDefine('IPPROTO_TCP') }}})); // if SOCK_STREAM, must be tcp } - var fd = FS.createFileHandle({ + var stream = FS.createStream({ connected: false, stream: stream, - socket: true + socket: true, + stream_ops: {} }); - assert(fd < 64); // select() assumes socket fd values are in 0..63 - return fd; + assert(stream.fd < 64); // select() assumes socket fd values are in 0..63 + return stream.fd; }, - connect__deps: ['$FS', '$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'], + connect__deps: ['$FS', '$Sockets', '_inet_ntoa_raw', 'ntohs', 'gethostbyname'], connect: function(fd, addr, addrlen) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; info.connected = true; info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); info.port = _htons(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); - info.host = __inet_ntop_raw(info.addr); + info.host = __inet_ntoa_raw(info.addr); // Support 'fake' ips from gethostbyname var parts = info.host.split('.'); if (parts[0] == '172' && parts[1] == '29') { @@ -8486,7 +9434,7 @@ LibraryManager.library = { recv__deps: ['$FS'], recv: function(fd, buf, len, flags) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; if (!info.hasData()) { ___setErrNo(ERRNO_CODES.EAGAIN); // no data, and all sockets are nonblocking, so this is the right behavior @@ -8512,7 +9460,7 @@ LibraryManager.library = { send__deps: ['$FS'], send: function(fd, buf, len, flags) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; info.sender(HEAPU8.subarray(buf, buf+len)); return len; @@ -8520,7 +9468,7 @@ LibraryManager.library = { sendmsg__deps: ['$FS', '$Sockets', 'connect'], sendmsg: function(fd, msg, flags) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; // if we are not connected, use the address info in the message if (!info.connected) { @@ -8555,7 +9503,7 @@ LibraryManager.library = { recvmsg__deps: ['$FS', '$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons'], recvmsg: function(fd, msg, flags) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; // if we are not connected, use the address info in the message if (!info.connected) { @@ -8613,7 +9561,7 @@ LibraryManager.library = { recvfrom__deps: ['$FS', 'connect', 'recv'], recvfrom: function(fd, buf, len, flags, addr, addrlen) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; // if we are not connected, use the address info in the message if (!info.connected) { @@ -8624,14 +9572,14 @@ LibraryManager.library = { }, shutdown: function(fd, how) { - var info = FS.streams[fd]; - if (!info) return -1; - info.socket.close(); - FS.removeFileHandle(fd); + var stream = FS.getStream(fd); + if (!stream) return -1; + stream.socket.close(); + FS.closeStream(stream); }, ioctl: function(fd, request, varargs) { - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; var bytes = 0; if (info.hasData()) { @@ -8661,7 +9609,7 @@ LibraryManager.library = { // TODO: webrtc queued incoming connections, etc. // For now, the model is that bind does a connect, and we "accept" that one connection, // which has host:port the same as ours. We also return the same socket fd. - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (!info) return -1; if (addr) { setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32'); @@ -8717,7 +9665,7 @@ LibraryManager.library = { var mask = 1 << (fd % 32), int_ = fd < 32 ? srcLow : srcHigh; if (int_ & mask) { // index is in the set, check if it is ready for read - var info = FS.streams[fd]; + var info = FS.getStream(fd); if (info && can(info)) { // set bit fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); diff --git a/src/library_gl.js b/src/library_gl.js index d0f1a692..8c724245 100644 --- a/src/library_gl.js +++ b/src/library_gl.js @@ -636,6 +636,11 @@ var LibraryGL = { for (var i = 0; i < n; i++) { var id = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; var buffer = GL.buffers[id]; + + // From spec: "glDeleteBuffers silently ignores 0's and names that do not + // correspond to existing buffer objects." + if (!buffer) continue; + Module.ctx.deleteBuffer(buffer); buffer.name = 0; GL.buffers[id] = null; @@ -1318,7 +1323,7 @@ var LibraryGL = { GLEmulation.fogColor = new Float32Array(4); // Add some emulation workarounds - Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work'); + Module.printErr('WARNING: using emscripten GL emulation. This is a collection of limited workarounds, do not expect it to work. (If you do not want this, build with -s DISABLE_GL_EMULATION=1)'); #if GL_UNSAFE_OPTS == 0 Module.printErr('WARNING: using emscripten GL emulation unsafe opts. If weirdness happens, try -s GL_UNSAFE_OPTS=0'); #endif diff --git a/src/library_jansson.js b/src/library_jansson.js index 93f239fc..da8c5aa8 100644 --- a/src/library_jansson.js +++ b/src/library_jansson.js @@ -79,7 +79,7 @@ var LibraryJansson = { load: function(string, flags, error) { // This is potentially a security problem. // TODO: Make sure everything is properly escaped - var json_obj = eval('(' + string + ')'); + var json_obj = JSON.parse(string); if (json_obj != null) { // The context is an array storing all child nodes. diff --git a/src/library_openal.js b/src/library_openal.js index 6a97fce7..d2516559 100644 --- a/src/library_openal.js +++ b/src/library_openal.js @@ -5,6 +5,8 @@ var LibraryOpenAL = { $AL: { contexts: [], currentContext: null, + QUEUE_INTERVAL: 25, + QUEUE_LOOKAHEAD: 100 }, alcProcessContext: function(context) {}, @@ -39,6 +41,7 @@ var LibraryOpenAL = { alcDestroyContext: function(context) { // Stop playback, etc + clearInterval(context.interval); }, alcCloseDevice: function(device) { @@ -54,6 +57,7 @@ var LibraryOpenAL = { } }, + alcCreateContext__deps: ['updateSources'], alcCreateContext: function(device, attrList) { if (device != 1) { return 0; @@ -76,18 +80,152 @@ var LibraryOpenAL = { } if (ctx) { - AL.contexts.push({ctx: ctx, err: 0, src: [], buf: []}); + var context = { + ctx: ctx, + err: 0, + src: [], + buf: [], + interval: setInterval(function () { _updateSources(context); }, AL.QUEUE_INTERVAL) + }; + AL.contexts.push(context); return AL.contexts.length; } else { return 0; } }, + updateSources__deps: ['updateSource'], + updateSources: function (context) { + for (var i = 0; i < context.src.length; i++) { + _updateSource(context.src[i]); + } + }, + + updateSource__deps: ['setSourceState'], + updateSource: function (src) { +#if OPENAL_DEBUG + var idx = AL.currentContext.src.indexOf(src); +#endif + if (src.state !== 0x1012 /* AL_PLAYING */) { + return; + } + + var currentTime = AL.currentContext.ctx.currentTime; + var startTime = src.bufferPosition; + + for (var i = src.buffersPlayed; i < src.queue.length; i++) { + var entry = src.queue[i]; + + var startOffset = startTime - currentTime; + var endTime = startTime + entry.buffer.duration; + + // Clean up old buffers. + if (currentTime >= endTime) { + // Update our location in the queue. + src.bufferPosition = endTime; + src.buffersPlayed = i + 1; + + // Stop / restart the source when we hit the end. + if (src.buffersPlayed >= src.queue.length) { + if (src.loop) { + _setSourceState(src, 0x1012 /* AL_PLAYING */); + } else { + _setSourceState(src, 0x1014 /* AL_STOPPED */); + } + } + } + // Process all buffers that'll be played before the next tick. + else if (startOffset < (AL.QUEUE_LOOKAHEAD / 1000) && !entry.src) { + // If the start offset is negative, we need to offset the actual buffer. + var offset = Math.abs(Math.min(startOffset, 0)); + + entry.src = AL.currentContext.ctx.createBufferSource(); + entry.src.buffer = entry.buffer; + entry.src.connect(src.gain); + entry.src.start(startTime, offset); + +#if OPENAL_DEBUG + console.log('updateSource queuing buffer ' + i + ' for source ' + idx + ' at ' + startTime + ' (offset by ' + offset + ')'); +#endif + } + + startTime = endTime; + } + }, + + setSourceState__deps: ['updateSource', 'stopSourceQueue'], + setSourceState: function (src, state) { +#if OPENAL_DEBUG + var idx = AL.currentContext.src.indexOf(src); +#endif + if (state === 0x1012 /* AL_PLAYING */) { + if (src.state !== 0x1013 /* AL_PAUSED */) { + src.state = 0x1012 /* AL_PLAYING */; + // Reset our position. + src.bufferPosition = AL.currentContext.ctx.currentTime; + src.buffersPlayed = 0; +#if OPENAL_DEBUG + console.log('setSourceState resetting and playing source ' + idx); +#endif + } else { + src.state = 0x1012 /* AL_PLAYING */; + // Use the current offset from src.bufferPosition to resume at the correct point. + src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; +#if OPENAL_DEBUG + console.log('setSourceState resuming source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); +#endif + } + _stopSourceQueue(src); + _updateSource(src); + } else if (state === 0x1013 /* AL_PAUSED */) { + if (src.state === 0x1012 /* AL_PLAYING */) { + src.state = 0x1013 /* AL_PAUSED */; + // Store off the current offset to restore with on resume. + src.bufferPosition = AL.currentContext.ctx.currentTime - src.bufferPosition; + _stopSourceQueue(src); +#if OPENAL_DEBUG + console.log('setSourceState pausing source ' + idx + ' at ' + src.bufferPosition.toFixed(4)); +#endif + } + } else if (state === 0x1014 /* AL_STOPPED */) { + if (src.state !== 0x1011 /* AL_INITIAL */) { + src.state = 0x1014 /* AL_STOPPED */; + src.buffersPlayed = src.queue.length; + _stopSourceQueue(src); +#if OPENAL_DEBUG + console.log('setSourceState stopping source ' + idx); +#endif + } + } else if (state == 0x1011 /* AL_INITIAL */) { + if (src.state !== 0x1011 /* AL_INITIAL */) { + src.state = 0x1011 /* AL_INITIAL */; + src.bufferPosition = 0; + src.buffersPlayed = 0; +#if OPENAL_DEBUG + console.log('setSourceState initializing source ' + idx); +#endif + } + } + }, + + stopSourceQueue: function (src) { + for (var i = 0; i < src.queue.length; i++) { + var entry = src.queue[i]; + if (entry.src) { + entry.src.stop(0); + entry.src = null; + } + } + }, + alGetError: function() { if (!AL.currentContext) { return 0xA004 /* AL_INVALID_OPERATION */; } else { - return AL.currentContext.err; + // Reset error on get. + var err = AL.currentContext.err; + AL.currentContext.err = 0 /* AL_NO_ERROR */; + return err; } }, @@ -97,12 +235,12 @@ var LibraryOpenAL = { return _alGetError(); }, - alDeleteSources: function(count, sources) - { + alDeleteSources: function(count, sources) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alDeleteSources called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } for (var i = 0; i < count; ++i) { @@ -116,57 +254,126 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.error("alGenSources called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } for (var i = 0; i < count; ++i) { var gain = AL.currentContext.ctx.createGain(); - var panner = AL.currentContext.ctx.createPanner(); - panner.panningModel = "equalpower"; - panner.distanceModel = "linear"; - panner.rolloffFactor = 0.3; - gain.connect(panner); - panner.connect(AL.currentContext.ctx.destination); + gain.connect(AL.currentContext.ctx.destination); AL.currentContext.src.push({ + state: 0x1011 /* AL_INITIAL */, + queue: [], loop: false, - buffer: null, + get refDistance() { + return this._refDistance || 1; + }, + set refDistance(val) { + this._refDistance = val; + if (this.panner) this.panner.refDistance = val; + }, + get maxDistance() { + return this._maxDistance || 10000; + }, + set maxDistance(val) { + this._maxDistance = val; + if (this.panner) this.panner.maxDistance = val; + }, + get rolloffFactor() { + return this._rolloffFactor || 1; + }, + set rolloffFactor(val) { + this._rolloffFactor = val; + if (this.panner) this.panner.rolloffFactor = val; + }, + get position() { + return this._position || [0, 0, 0]; + }, + set position(val) { + this._position = val; + if (this.panner) this.panner.setPosition(val[0], val[1], val[2]); + }, + get velocity() { + return this._velocity || [0, 0, 0]; + }, + set velocity(val) { + this._velocity = val; + if (this.panner) this.panner.setVelocity(val[0], val[1], val[2]); + }, gain: gain, - panner: panner, - paused: false, - playTime: -1, - pausedTime: 0 + panner: null, + buffersPlayed: 0, + bufferPosition: 0 }); {{{ makeSetValue('sources', 'i*4', 'AL.currentContext.src.length', 'i32') }}}; } }, + alSourcei__deps: ['updateSource'], alSourcei: function(source, param, value) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alSourcei called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alSourcei called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } switch (param) { case 0x1007 /* AL_LOOPING */: - AL.currentContext.src[source - 1].loop = (value != 0 /* AL_FALSE */); + src.loop = (value === 1 /* AL_TRUE */); break; case 0x1009 /* AL_BUFFER */: + var buffer = AL.currentContext.buf[value - 1]; if (value == 0) { - AL.currentContext.src[source - 1].buffer = null; + src.queue = []; + } else { + src.queue = [{ buffer: buffer }]; + } + _updateSource(src); + break; + case 0x202 /* AL_SOURCE_RELATIVE */: + if (value === 1 /* AL_TRUE */) { + if (src.panner) { + src.panner = null; + + // Disconnect from the panner. + src.gain.disconnect(); + + src.gain.connect(AL.currentContext.ctx.destination); + } + } else if (value === 0 /* AL_FALSE */) { + if (!src.panner) { + var panner = src.panner = AL.currentContext.ctx.createPanner(); + panner.panningModel = "equalpower"; + panner.distanceModel = "linear"; + panner.refDistance = src.refDistance; + panner.maxDistance = src.maxDistance; + panner.rolloffFactor = src.rolloffFactor; + panner.setPosition(src.position[0], src.position[1], src.position[2]); + panner.setVelocity(src.velocity[0], src.velocity[1], src.velocity[2]); + panner.connect(AL.currentContext.ctx.destination); + + // Disconnect from the default source. + src.gain.disconnect(); + + src.gain.connect(panner); + } } else { - AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[value - 1].buf; + AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; } break; default: #if OPENAL_DEBUG console.log("alSourcei with param " + param + " not implemented yet"); #endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; } }, @@ -176,130 +383,168 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.error("alSourcef called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alSourcef called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } switch (param) { - case 0x100A /* AL_GAIN */: - if (AL.currentContext.src[source - 1]) { - AL.currentContext.src[source - 1].gain.gain.value = value; - } - break; case 0x1003 /* AL_PITCH */: #if OPENAL_DEBUG - console.log("alSourcef was called with AL_PITCH, but Web Audio does not support static pitch changes"); + console.log("alSourcef was called with 0x1003 /* AL_PITCH */, but Web Audio does not support static pitch changes"); #endif break; + case 0x100A /* AL_GAIN */: + src.gain.gain.value = value; + break; + // case 0x100D /* AL_MIN_GAIN */: + // break; + // case 0x100E /* AL_MAX_GAIN */: + // break; + case 0x1023 /* AL_MAX_DISTANCE */: + src.maxDistance = value; + break; + case 0x1021 /* AL_ROLLOFF_FACTOR */: + src.rolloffFactor = value; + break; + // case 0x1022 /* AL_CONE_OUTER_GAIN */: + // break; + // case 0x1001 /* AL_CONE_INNER_ANGLE */: + // break; + // case 0x1002 /* AL_CONE_OUTER_ANGLE */: + // break; + case 0x1020 /* AL_REFERENCE_DISTANCE */: + src.refDistance = value; + break; default: #if OPENAL_DEBUG console.log("alSourcef with param " + param + " not implemented yet"); #endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; } }, - alSourcefv: function(source, param, value) { + alSource3f: function(source, param, v1, v2, v3) { if (!AL.currentContext) { #if OPENAL_DEBUG - console.error("alSourcefv called without a valid context"); + console.error("alSource3f called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG - console.error("alSourcefv called with an invalid source"); + console.error("alSource3f called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } switch (param) { case 0x1004 /* AL_POSITION */: - AL.currentContext.src[source - 1].panner.setPosition( - {{{ makeGetValue('value', '0', 'float') }}}, - {{{ makeGetValue('value', '4', 'float') }}}, - {{{ makeGetValue('value', '8', 'float') }}} - ); + src.position = [v1, v2, v3]; break; case 0x1006 /* AL_VELOCITY */: - AL.currentContext.src[source - 1].panner.setVelocity( - {{{ makeGetValue('value', '0', 'float') }}}, - {{{ makeGetValue('value', '4', 'float') }}}, - {{{ makeGetValue('value', '8', 'float') }}} - ); + src.velocity = [v1, v2, v3]; break; default: #if OPENAL_DEBUG - console.log("alSourcefv with param " + param + " not implemented yet"); + console.log("alSource3f with param " + param + " not implemented yet"); #endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; } }, + alSourcefv__deps: ['alSource3f'], + alSourcefv: function(source, param, value) { + _alSource3f(source, param, + {{{ makeGetValue('value', '0', 'float') }}}, + {{{ makeGetValue('value', '4', 'float') }}}, + {{{ makeGetValue('value', '8', 'float') }}}); + }, + + alSourceQueueBuffers__deps: ["updateSource"], alSourceQueueBuffers: function(source, count, buffers) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alSourceQueueBuffers called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alSourceQueueBuffers called with an invalid source"); #endif - return; - } - if (count != 1) { -#if OPENAL_DEBUG - console.error("Queuing multiple buffers using alSourceQueueBuffers is not supported yet"); -#endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } for (var i = 0; i < count; ++i) { - var buffer = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; - if (buffer > AL.currentContext.buf.length) { + var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; + if (bufferIdx > AL.currentContext.buf.length) { #if OPENAL_DEBUG console.error("alSourceQueueBuffers called with an invalid buffer"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - AL.currentContext.src[source - 1].buffer = AL.currentContext.buf[buffer - 1].buf; } + + for (var i = 0; i < count; ++i) { + var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}}; + var buffer = AL.currentContext.buf[bufferIdx - 1]; + src.queue.push({ buffer: buffer, src: null }); + } + + _updateSource(src); }, - alSourceUnqueueBuffers: function(source, count, buffers) - { + alSourceUnqueueBuffers__deps: ["updateSource"], + alSourceUnqueueBuffers: function(source, count, buffers) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alSourceUnqueueBuffers called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alSourceUnqueueBuffers called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - if (count != 1) { -#if OPENAL_DEBUG - console.error("Queuing multiple buffers using alSourceUnqueueBuffers is not supported yet"); -#endif + + if (count > src.buffersPlayed) { + AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; return; } - for (var i = 0; i < count; ++i) { - var buffer = AL.currentContext.src[source - 1].buffer; - for (var j = 0; j < AL.currentContext.buf.length; ++j) { - if (buffer == AL.currentContext.buf[j].buf) { + + for (var i = 0; i < count; i++) { + var entry = src.queue.shift(); + // Write the buffers index out to the return list. + for (var j = 0; j < AL.currentContext.buf.length; j++) { + var b = AL.currentContext.buf[j]; + if (b && b == entry.buffer) { {{{ makeSetValue('buffers', 'i*4', 'j+1', 'i32') }}}; - AL.currentContext.src[source - 1].buffer = null; break; } } + src.buffersPlayed--; } + + _updateSource(src); }, alDeleteBuffers: function(count, buffers) @@ -308,21 +553,43 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.error("alDeleteBuffers called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; + return; + } + if (count > AL.currentContext.buf.length) { + AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; return; } + for (var i = 0; i < count; ++i) { var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}} - 1; - if (bufferIdx < AL.currentContext.buf.length && AL.currentContext.buf[bufferIdx]) { - var buffer = AL.currentContext.buf[bufferIdx].buf; - for (var j = 0; j < AL.currentContext.src.length; ++j) { - if (buffer == AL.currentContext.src[j].buffer) { - AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; + + // Make sure the buffer index is valid. + if (bufferIdx >= AL.currentContext.buf.length || !AL.currentContext.buf[bufferIdx]) { + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; + return; + } + + // Make sure the buffer is no longer in use. + var buffer = AL.currentContext.buf[bufferIdx]; + for (var j = 0; j < AL.currentContext.src.length; ++j) { + var src = AL.currentContext.src[j]; + if (!src) { + continue; + } + for (var k = 0; k < src.queue.length; k++) { + if (buffer === src.queue[k].buffer) { + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } } - delete AL.currentContext.buf[bufferIdx]; } } + + for (var i = 0; i < count; ++i) { + var bufferIdx = {{{ makeGetValue('buffers', 'i*4', 'i32') }}} - 1; + delete AL.currentContext.buf[bufferIdx]; + } }, alGenBuffers: function(count, buffers) { @@ -330,10 +597,11 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.error("alGenBuffers called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } for (var i = 0; i < count; ++i) { - AL.currentContext.buf.push({buf: null}); + AL.currentContext.buf.push(null); {{{ makeSetValue('buffers', 'i*4', 'AL.currentContext.buf.length', 'i32') }}}; } }, @@ -343,6 +611,7 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.error("alBufferData called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } if (buffer > AL.currentContext.buf.length) { @@ -375,16 +644,21 @@ var LibraryOpenAL = { #endif return; } - AL.currentContext.buf[buffer - 1].buf = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq); + try { + AL.currentContext.buf[buffer - 1] = AL.currentContext.ctx.createBuffer(channels, size / (bytes * channels), freq); + } catch (e) { + AL.currentContext.err = 0xA003 /* AL_INVALID_VALUE */; + return; + } var buf = new Array(channels); for (var i = 0; i < channels; ++i) { - buf[i] = AL.currentContext.buf[buffer - 1].buf.getChannelData(i); + buf[i] = AL.currentContext.buf[buffer - 1].getChannelData(i); } for (var i = 0; i < size / (bytes * channels); ++i) { for (var j = 0; j < channels; ++j) { switch (bytes) { case 1: - var val = {{{ makeGetValue('data', 'i*channels+j', 'i8') }}}; + var val = {{{ makeGetValue('data', 'i*channels+j', 'i8') }}} & 0xff; // unsigned buf[j][i] = -1.0 + val * (2/256); break; case 2: @@ -396,110 +670,105 @@ var LibraryOpenAL = { } }, - alSourcePlay__deps: ["alSourceStop"], + alSourcePlay__deps: ['setSourceState'], alSourcePlay: function(source) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alSourcePlay called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alSourcePlay called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - var offset = 0; - if ("src" in AL.currentContext.src[source - 1] && - AL.currentContext.src[source - 1]["src"].buffer == - AL.currentContext.src[source - 1].buffer) { - if (AL.currentContext.src[source - 1].paused) { - // So now we have to resume playback, remember the offset here. - offset = AL.currentContext.src[source - 1].pausedTime - - AL.currentContext.src[source - 1].playTime; - } else { - // If the source is already playing, we need to resume from beginning. - // We do that by stopping the current source and replaying it. - _alSourceStop(source); - } - } - var src = AL.currentContext.ctx.createBufferSource(); - src.loop = AL.currentContext.src[source - 1].loop; - src.buffer = AL.currentContext.src[source - 1].buffer; - src.connect(AL.currentContext.src[source - 1].gain); - src.start(0, offset); - AL.currentContext.src[source - 1].playTime = AL.currentContext.ctx.currentTime; - AL.currentContext.src[source - 1].paused = false; - AL.currentContext.src[source - 1]['src'] = src; + _setSourceState(src, 0x1012 /* AL_PLAYING */); }, + alSourceStop__deps: ['setSourceState'], alSourceStop: function(source) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alSourceStop called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alSourceStop called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - if (AL.currentContext.src[source - 1] && "src" in AL.currentContext.src[source - 1]) { - AL.currentContext.src[source - 1]["src"].stop(0); - delete AL.currentContext.src[source - 1]["src"]; - } + _setSourceState(src, 0x1014 /* AL_STOPPED */); }, + alSourcePause__deps: ['setSourceState'], alSourcePause: function(source) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alSourcePause called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alSourcePause called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } - if ("src" in AL.currentContext.src[source - 1] && - !AL.currentContext.src[source - 1].paused) { - AL.currentContext.src[source - 1].paused = true; - AL.currentContext.src[source - 1].pausedTime = AL.currentContext.ctx.currentTime; - AL.currentContext.src[source - 1]["src"].stop(0); - delete AL.currentContext.src[source - 1].src; - } + _setSourceState(src, 0x1013 /* AL_PAUSED */); }, + alGetSourcei__deps: ['updateSource'], alGetSourcei: function(source, param, value) { if (!AL.currentContext) { #if OPENAL_DEBUG console.error("alGetSourcei called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } - if (source > AL.currentContext.src.length) { + var src = AL.currentContext.src[source - 1]; + if (!src) { #if OPENAL_DEBUG console.error("alGetSourcei called with an invalid source"); #endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; return; } + + // Being that we have no way to receive end events from buffer nodes, + // we currently proccess and update a source's buffer queue every + // ~QUEUE_INTERVAL milliseconds. However, this interval is not precise, + // so we also forcefully update the source when alGetSourcei is queried + // to aid in the common scenario of application calling alGetSourcei(AL_BUFFERS_PROCESSED) + // to recycle buffers. + _updateSource(src); + switch (param) { case 0x202 /* AL_SOURCE_RELATIVE */: - // Always return 1 - {{{ makeSetValue('value', '0', '1', 'i32') }}}; + {{{ makeSetValue('value', '0', 'src.panner ? 1 : 0', 'i32') }}}; break; case 0x1009 /* AL_BUFFER */: - if (AL.currentContext.src[source - 1].buffer == null) { + if (!src.queue.length) { {{{ makeSetValue('value', '0', '0', 'i32') }}}; } else { - var buf = AL.currentContext.src[source - 1].buffer; + // Find the first unprocessed buffer. + var buffer = src.queue[src.buffersPlayed].buffer; + // Return its index. for (var i = 0; i < AL.currentContext.buf.length; ++i) { - if (buf == AL.currentContext.buf[i].buf) { + if (buffer == AL.currentContext.buf[i]) { {{{ makeSetValue('value', '0', 'i+1', 'i32') }}}; return; } @@ -508,32 +777,79 @@ var LibraryOpenAL = { } break; case 0x1010 /* AL_SOURCE_STATE */: - if ("src" in AL.currentContext.src[source - 1]) { - {{{ makeSetValue('value', '0', '0x1012', 'i32') }}} /* AL_PLAYING */; - } else if (AL.currentContext.src[source - 1].paused) { - {{{ makeSetValue('value', '0', '0x1013', 'i32') }}} /* AL_PAUSED */; - } else if (AL.currentContext.src[source - 1].playTime == -1) { - {{{ makeSetValue('value', '0', '0x1011', 'i32') }}} /* AL_INITIAL */; - } else { - {{{ makeSetValue('value', '0', '0x1014', 'i32') }}} /* AL_STOPPED */; - } + {{{ makeSetValue('value', '0', 'src.state', 'i32') }}}; break; case 0x1015 /* AL_BUFFERS_QUEUED */: - if (AL.currentContext.src[source - 1].buffer) { - {{{ makeSetValue('value', '0', '1', 'i32') }}} - } else { + {{{ makeSetValue('value', '0', 'src.queue.length', 'i32') }}} + break; + case 0x1016 /* AL_BUFFERS_PROCESSED */: + if (src.loop) { {{{ makeSetValue('value', '0', '0', 'i32') }}} + } else { + {{{ makeSetValue('value', '0', 'src.buffersPlayed', 'i32') }}} } break; - case 0x1016 /* AL_BUFFERS_PROCESSED */: - // Always return 1 - {{{ makeSetValue('value', '0', '1', 'i32') }}} + default: + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; + break; + } + }, + + alGetSourcef: function(source, param, value) { + if (!AL.currentContext) { +#if OPENAL_DEBUG + console.error("alGetSourcef called without a valid context"); +#endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; + return; + } + var src = AL.currentContext.src[source - 1]; + if (!src) { +#if OPENAL_DEBUG + console.error("alGetSourcef called with an invalid source"); +#endif + AL.currentContext.err = 0xA001 /* AL_INVALID_NAME */; + return; + } + switch (param) { + // case 0x1003 /* AL_PITCH */: + // break; + case 0x100A /* AL_GAIN */: + {{{ makeSetValue('value', '0', 'src.gain.gain.value', 'float') }}} + break; + // case 0x100D /* AL_MIN_GAIN */: + // break; + // case 0x100E /* AL_MAX_GAIN */: + // break; + case 0x1023 /* AL_MAX_DISTANCE */: + {{{ makeSetValue('value', '0', 'src.maxDistance', 'float') }}} + break; + case 0x1021 /* AL_ROLLOFF_FACTOR */: + {{{ makeSetValue('value', '0', 'src.rolloffFactor', 'float') }}} + break; + // case 0x1022 /* AL_CONE_OUTER_GAIN */: + // break; + // case 0x1001 /* AL_CONE_INNER_ANGLE */: + // break; + // case 0x1002 /* AL_CONE_OUTER_ANGLE */: + // break; + case 0x1020 /* AL_REFERENCE_DISTANCE */: + {{{ makeSetValue('value', '0', 'src.refDistance', 'float') }}} + break; + // case 0x1024 /* AL_SEC_OFFSET */: + // break; + // case 0x1025 /* AL_SAMPLE_OFFSET */: + // break; + // case 0x1026 /* AL_BYTE_OFFSET */: + // break; + default: + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; } }, alDistanceModel: function(model) { - if (model != 0 /* AL_NONE */) { + if (model !== 0 /* AL_NONE */) { #if OPENAL_DEBUG console.log("Only alDistanceModel(AL_NONE) is currently supported"); #endif @@ -545,6 +861,7 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.error("alListenerfv called without a valid context"); #endif + AL.currentContext.err = 0xA004 /* AL_INVALID_OPERATION */; return; } switch (param) { @@ -576,6 +893,7 @@ var LibraryOpenAL = { #if OPENAL_DEBUG console.log("alListenerfv with param " + param + " not implemented yet"); #endif + AL.currentContext.err = 0xA002 /* AL_INVALID_ENUM */; break; } }, @@ -588,13 +906,27 @@ var LibraryOpenAL = { return 0; }, + alGetString: function (param) { + return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + }, + alGetProcAddress: function(fname) { return 0; }, + alcGetString: function (param) { + return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL); + }, + alcGetProcAddress: function(device, fname) { return 0; }, + + alDopplerFactor: function(value) { + }, + + alDopplerVelocity: function(value) { + } }; autoAddDeps(LibraryOpenAL, '$AL'); diff --git a/src/library_path.js b/src/library_path.js new file mode 100644 index 00000000..feec1e68 --- /dev/null +++ b/src/library_path.js @@ -0,0 +1,134 @@ +mergeInto(LibraryManager.library, { + $PATH__deps: ['$FS'], + $PATH: { + // split a filename into [root, dir, basename, ext], unix version + // 'root' is just a slash, or nothing. + splitPath: function (filename) { + var splitPathRe = /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/; + return splitPathRe.exec(filename).slice(1); + }, + normalizeArray: function (parts, allowAboveRoot) { + // if the path tries to go above the root, `up` ends up > 0 + var up = 0; + for (var i = parts.length - 1; i >= 0; i--) { + var last = parts[i]; + if (last === '.') { + parts.splice(i, 1); + } else if (last === '..') { + parts.splice(i, 1); + up++; + } else if (up) { + parts.splice(i, 1); + up--; + } + } + // if the path is allowed to go above the root, restore leading ..s + if (allowAboveRoot) { + for (; up--; up) { + parts.unshift('..'); + } + } + return parts; + }, + normalize: function (path) { + var isAbsolute = path.charAt(0) === '/', + trailingSlash = path.substr(-1) === '/'; + // Normalize the path + path = PATH.normalizeArray(path.split('/').filter(function(p) { + return !!p; + }), !isAbsolute).join('/'); + if (!path && !isAbsolute) { + path = '.'; + } + if (path && trailingSlash) { + path += '/'; + } + return (isAbsolute ? '/' : '') + path; + }, + dirname: function (path) { + var result = PATH.splitPath(path), + root = result[0], + dir = result[1]; + if (!root && !dir) { + // No dirname whatsoever + return '.'; + } + if (dir) { + // It has a dirname, strip trailing slash + dir = dir.substr(0, dir.length - 1); + } + return root + dir; + }, + basename: function (path, ext) { + // EMSCRIPTEN return '/'' for '/', not an empty string + if (path === '/') return '/'; + var f = PATH.splitPath(path)[2]; + if (ext && f.substr(-1 * ext.length) === ext) { + f = f.substr(0, f.length - ext.length); + } + return f; + }, + join: function () { + var paths = Array.prototype.slice.call(arguments, 0); + return PATH.normalize(paths.filter(function(p, index) { + if (typeof p !== 'string') { + throw new TypeError('Arguments to path.join must be strings'); + } + return p; + }).join('/')); + }, + resolve: function () { + var resolvedPath = '', + resolvedAbsolute = false; + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) { + var path = (i >= 0) ? arguments[i] : FS.cwd(); + // Skip empty and invalid entries + if (typeof path !== 'string') { + throw new TypeError('Arguments to path.resolve must be strings'); + } else if (!path) { + continue; + } + resolvedPath = path + '/' + resolvedPath; + resolvedAbsolute = path.charAt(0) === '/'; + } + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + resolvedPath = PATH.normalizeArray(resolvedPath.split('/').filter(function(p) { + return !!p; + }), !resolvedAbsolute).join('/'); + return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; + }, + relative: function(from, to) { + from = PATH.resolve(from).substr(1); + to = PATH.resolve(to).substr(1); + function trim(arr) { + var start = 0; + for (; start < arr.length; start++) { + if (arr[start] !== '') break; + } + var end = arr.length - 1; + for (; end >= 0; end--) { + if (arr[end] !== '') break; + } + if (start > end) return []; + return arr.slice(start, end - start + 1); + } + var fromParts = trim(from.split('/')); + var toParts = trim(to.split('/')); + var length = Math.min(fromParts.length, toParts.length); + var samePartsLength = length; + for (var i = 0; i < length; i++) { + if (fromParts[i] !== toParts[i]) { + samePartsLength = i; + break; + } + } + var outputParts = []; + for (var i = samePartsLength; i < fromParts.length; i++) { + outputParts.push('..'); + } + outputParts = outputParts.concat(toParts.slice(samePartsLength)); + return outputParts.join('/'); + } + } +});
\ No newline at end of file diff --git a/src/library_sdl.js b/src/library_sdl.js index 4477e457..80c7ac07 100644 --- a/src/library_sdl.js +++ b/src/library_sdl.js @@ -1238,61 +1238,133 @@ var LibrarySDL = { return flags; // We support JPG, PNG, TIF because browsers do }, - IMG_Load_RW__deps: ['SDL_LockSurface'], - IMG_Load_RW: function(rwopsID, freesrc) { - var rwops = SDL.rwops[rwopsID]; + IMG_Load_RW__deps: ['SDL_LockSurface', 'SDL_FreeRW'], + IMG_Load_RW: function(rwopsID, freeSrc) { + try { + // stb_image integration support + var cleanup = function() { + if (rwops && freeSrc) _SDL_FreeRW(rwopsID); + }; + function addCleanup(func) { + var old = cleanup; + cleanup = function() { + old(); + func(); + } + } + function callStbImage(func, params) { + var x = Module['_malloc']({{{ QUANTUM_SIZE }}}); + var y = Module['_malloc']({{{ QUANTUM_SIZE }}}); + var comp = Module['_malloc']({{{ QUANTUM_SIZE }}}); + addCleanup(function() { + Module['_free'](x); + Module['_free'](y); + Module['_free'](comp); + if (data) Module['_stbi_image_free'](data); + }); + var data = Module['_' + func].apply(null, params.concat([x, y, comp, 0])); + if (!data) return null; + return { + rawData: true, + data: data, + width: {{{ makeGetValue('x', 0, 'i32') }}}, + height: {{{ makeGetValue('y', 0, 'i32') }}}, + size: {{{ makeGetValue('x', 0, 'i32') }}} * {{{ makeGetValue('y', 0, 'i32') }}} * {{{ makeGetValue('comp', 0, 'i32') }}}, + bpp: {{{ makeGetValue('comp', 0, 'i32') }}} + }; + } - if (rwops === undefined) { - return 0; - } + var rwops = SDL.rwops[rwopsID]; + if (rwops === undefined) { + return 0; + } - var filename = rwops.filename; - - if (filename === undefined) { - Runtime.warnOnce('Only file names that have been preloaded are supported for IMG_Load_RW.'); - // TODO. Support loading image data from embedded files, similarly to Mix_LoadWAV_RW - // TODO. Support loading image data from byte arrays, similarly to Mix_LoadWAV_RW - return 0; - } - - filename = FS.standardizePath(filename); - if (filename[0] == '/') { - // Convert the path to relative - filename = filename.substr(1); - } - var raw = Module["preloadedImages"][filename]; - if (!raw) { - if (raw === null) Module.printErr('Trying to reuse preloaded image, but freePreloadedMediaOnUse is set!'); - Runtime.warnOnce('Cannot find preloaded image ' + filename); - return 0; - } - if (Module['freePreloadedMediaOnUse']) { - Module["preloadedImages"][filename] = null; - } - var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename); - var surfData = SDL.surfaces[surf]; - surfData.ctx.globalCompositeOperation = "copy"; - surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height); - surfData.ctx.globalCompositeOperation = "source-over"; - // XXX SDL does not specify that loaded images must have available pixel data, in fact - // there are cases where you just want to blit them, so you just need the hardware - // accelerated version. However, code everywhere seems to assume that the pixels - // are in fact available, so we retrieve it here. This does add overhead though. - _SDL_LockSurface(surf); - surfData.locked--; // The surface is not actually locked in this hack - if (SDL.GL) { - // After getting the pixel data, we can free the canvas and context if we do not need to do 2D canvas blitting - surfData.canvas = surfData.ctx = null; + var filename = rwops.filename; + if (filename === undefined) { +#if STB_IMAGE + var raw = callStbImage('stbi_load_from_memory', [rwops.bytes, rwops.count]); + if (!raw) return 0; +#else + Runtime.warnOnce('Only file names that have been preloaded are supported for IMG_Load_RW. Consider using STB_IMAGE=1 if you want synchronous image decoding (see settings.js)'); + return 0; +#endif + } + + if (!raw) { + filename = FS.standardizePath(filename); + if (filename[0] == '/') { + // Convert the path to relative + filename = filename.substr(1); + } + var raw = Module["preloadedImages"][filename]; + if (!raw) { + if (raw === null) Module.printErr('Trying to reuse preloaded image, but freePreloadedMediaOnUse is set!'); +#if STB_IMAGE + var name = Module['_malloc'](filename.length+1); + writeStringToMemory(filename, name); + addCleanup(function() { + Module['_free'](name); + }); + var raw = callStbImage('stbi_load', [name]); + if (!raw) return 0; +#else + Runtime.warnOnce('Cannot find preloaded image ' + filename); + Runtime.warnOnce('Cannot find preloaded image ' + filename + '. Consider using STB_IMAGE=1 if you want synchronous image decoding (see settings.js)'); + return 0; +#endif + } else if (Module['freePreloadedMediaOnUse']) { + Module["preloadedImages"][filename] = null; + } + } + + var surf = SDL.makeSurface(raw.width, raw.height, 0, false, 'load:' + filename); + var surfData = SDL.surfaces[surf]; + surfData.ctx.globalCompositeOperation = "copy"; + if (!raw.rawData) { + surfData.ctx.drawImage(raw, 0, 0, raw.width, raw.height, 0, 0, raw.width, raw.height); + } else { + var imageData = surfData.ctx.getImageData(0, 0, surfData.width, surfData.height); + if (raw.bpp == 4) { + imageData.data.set({{{ makeHEAPView('U8', 'raw.data', 'raw.data+raw.size') }}}); + } else if (raw.bpp == 3) { + var pixels = raw.size/3; + var data = imageData.data; + var sourcePtr = raw.data; + var destPtr = 0; + for (var i = 0; i < pixels; i++) { + data[destPtr++] = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}}; + data[destPtr++] = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}}; + data[destPtr++] = {{{ makeGetValue('sourcePtr++', 0, 'i8', null, 1) }}}; + data[destPtr++] = 255; + } + } else { + Module.printErr('cannot handle bpp ' + raw.bpp); + return 0; + } + surfData.ctx.putImageData(imageData, 0, 0); + } + surfData.ctx.globalCompositeOperation = "source-over"; + // XXX SDL does not specify that loaded images must have available pixel data, in fact + // there are cases where you just want to blit them, so you just need the hardware + // accelerated version. However, code everywhere seems to assume that the pixels + // are in fact available, so we retrieve it here. This does add overhead though. + _SDL_LockSurface(surf); + surfData.locked--; // The surface is not actually locked in this hack + if (SDL.GL) { + // After getting the pixel data, we can free the canvas and context if we do not need to do 2D canvas blitting + surfData.canvas = surfData.ctx = null; + } + return surf; + } finally { + cleanup(); } - return surf; }, SDL_LoadBMP: 'IMG_Load', SDL_LoadBMP_RW: 'IMG_Load_RW', - IMG_Load__deps: ['IMG_Load_RW', 'SDL_RWFromFile', 'SDL_FreeRW'], + IMG_Load__deps: ['IMG_Load_RW', 'SDL_RWFromFile'], IMG_Load: function(filename){ var rwops = _SDL_RWFromFile(filename); - var result = _IMG_Load_RW(rwops); - _SDL_FreeRW(rwops); + var result = _IMG_Load_RW(rwops, 1); return result; }, @@ -2055,11 +2127,13 @@ var LibrarySDL = { // Misc SDL_InitSubSystem: function(flags) { return 0 }, + SDL_RWFromConstMem: function(mem, size) { var id = SDL.rwops.length; // TODO: recycle ids when they are null SDL.rwops.push({ bytes: mem, count: size }); return id; }, + SDL_RWFromMem: 'SDL_RWFromConstMem', SDL_RWFromFile: function(_name, mode) { var id = SDL.rwops.length; // TODO: recycle ids when they are null diff --git a/src/modules.js b/src/modules.js index 9f419234..f85e7d0e 100644 --- a/src/modules.js +++ b/src/modules.js @@ -422,7 +422,7 @@ var LibraryManager = { load: function() { if (this.library) return; - var libraries = ['library.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); + var libraries = ['library.js', 'library_path.js', 'library_browser.js', 'library_sdl.js', 'library_gl.js', 'library_glut.js', 'library_xlib.js', 'library_egl.js', 'library_gc.js', 'library_jansson.js', 'library_openal.js', 'library_glfw.js'].concat(additionalLibraries); for (var i = 0; i < libraries.length; i++) { eval(processMacros(preprocess(read(libraries[i])))); } diff --git a/src/postamble.js b/src/postamble.js index 49fd9b3e..25a50bfc 100644 --- a/src/postamble.js +++ b/src/postamble.js @@ -1,9 +1,12 @@ // === Auto-generated postamble setup entry stuff === -Module['callMain'] = function callMain(args) { +var initialStackTop; +var inMain; + +Module['callMain'] = Module.callMain = function callMain(args) { assert(runDependencies == 0, 'cannot call main when async dependencies remain! (listen on __ATMAIN__)'); - assert(!Module['preRun'] || Module['preRun'].length == 0, 'cannot call main when preRun functions remain to be called'); + assert(__ATPRERUN__.length == 0, 'cannot call main when preRun functions remain to be called'); args = args || []; @@ -28,29 +31,37 @@ Module['callMain'] = function callMain(args) { var start = Date.now(); #endif - var ret; + initialStackTop = STACKTOP; + inMain = true; - var initialStackTop = STACKTOP; + var ret; try { ret = Module['_main'](argc, argv, 0); } catch(e) { - if (e.name == 'ExitStatus') { - return e.status; + if (e && typeof e == 'object' && e.type == 'ExitStatus') { + // exit() throws this once it's done to make sure execution + // has been stopped completely + Module.print('Exit Status: ' + e.value); + return e.value; } else if (e == 'SimulateInfiniteLoop') { + // running an evented main loop, don't immediately exit Module['noExitRuntime'] = true; } else { throw e; } } finally { - STACKTOP = initialStackTop; + inMain = false; } #if BENCHMARK Module.realPrint('main() took ' + (Date.now() - start) + ' milliseconds'); #endif - return ret; + // if we're not running an evented main loop, it's time to exit + if (!Module['noExitRuntime']) { + exit(ret); + } } {{GLOBAL_VARS}} @@ -60,20 +71,14 @@ function run(args) { if (runDependencies > 0) { Module.printErr('run() called, but dependencies remain, so not running'); - return 0; + return; } - if (Module['preRun']) { - if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - var toRun = Module['preRun']; - Module['preRun'] = []; - for (var i = toRun.length-1; i >= 0; i--) { - toRun[i](); - } - if (runDependencies > 0) { - // a preRun added a dependency, run will be called later - return 0; - } + preRun(); + + if (runDependencies > 0) { + // a preRun added a dependency, run will be called later + return; } function doRun() { @@ -81,21 +86,12 @@ function run(args) { preMain(); - var ret = 0; calledRun = true; if (Module['_main'] && shouldRunNow) { - ret = Module['callMain'](args); - if (!Module['noExitRuntime']) { - exitRuntime(); - } - } - if (Module['postRun']) { - if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; - while (Module['postRun'].length > 0) { - Module['postRun'].pop()(); - } + Module['callMain'](args); } - return ret; + + postRun(); } if (Module['setStatus']) { @@ -106,13 +102,43 @@ function run(args) { }, 1); if (!ABORT) doRun(); }, 1); - return 0; } else { - return doRun(); + doRun(); } } Module['run'] = Module.run = run; +function exit(status) { + ABORT = true; + STACKTOP = initialStackTop; + + // TODO call externally added 'exit' callbacks with the status code. + // It'd be nice to provide the same interface for all Module events (e.g. + // prerun, premain, postmain). Perhaps an EventEmitter so we can do: + // Module.on('exit', function (status) {}); + + // exit the runtime + exitRuntime(); + + if (inMain) { + // if we're still inside the callMain's try/catch, we need to throw an + // exception in order to immediately terminate execution. + throw { type: 'ExitStatus', value: status }; + } +} +Module['exit'] = Module.exit = exit; + +function abort(text) { + if (text) { + Module.print(text); + } + + ABORT = true; + + throw 'abort() at ' + (new Error().stack); +} +Module['abort'] = Module.abort = abort; + // {{PRE_RUN_ADDITIONS}} if (Module['preInit']) { diff --git a/src/preamble.js b/src/preamble.js index 218e0388..2955c885 100644 --- a/src/preamble.js +++ b/src/preamble.js @@ -242,12 +242,6 @@ var tempI64, tempI64b; var tempRet0, tempRet1, tempRet2, tempRet3, tempRet4, tempRet5, tempRet6, tempRet7, tempRet8, tempRet9; #endif -function abort(text) { - Module.print(text + ':\n' + (new Error).stack); - ABORT = true; - throw "Assertion: " + text; -} - function assert(condition, text) { if (!condition) { abort('Assertion failed: ' + text); @@ -711,24 +705,75 @@ function callRuntimeCallbacks(callbacks) { } } -var __ATINIT__ = []; // functions called during startup -var __ATMAIN__ = []; // functions called when main() is to be run -var __ATEXIT__ = []; // functions called during shutdown +var __ATPRERUN__ = []; // functions called before the runtime is initialized +var __ATINIT__ = []; // functions called during startup +var __ATMAIN__ = []; // functions called when main() is to be run +var __ATEXIT__ = []; // functions called during shutdown +var __ATPOSTRUN__ = []; // functions called after the runtime has exited var runtimeInitialized = false; +function preRun() { + // compatibility - merge in anything from Module['preRun'] at this time + if (Module['preRun']) { + if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; + while (Module['preRun'].length) { + addOnPreRun(Module['preRun'].shift()); + } + } + callRuntimeCallbacks(__ATPRERUN__); +} + function ensureInitRuntime() { if (runtimeInitialized) return; runtimeInitialized = true; callRuntimeCallbacks(__ATINIT__); } + function preMain() { callRuntimeCallbacks(__ATMAIN__); } + function exitRuntime() { callRuntimeCallbacks(__ATEXIT__); } +function postRun() { + // compatibility - merge in anything from Module['postRun'] at this time + if (Module['postRun']) { + if (typeof Module['postRun'] == 'function') Module['postRun'] = [Module['postRun']]; + while (Module['postRun'].length) { + addOnPostRun(Module['postRun'].shift()); + } + } + callRuntimeCallbacks(__ATPOSTRUN__); +} + +function addOnPreRun(cb) { + __ATPRERUN__.unshift(cb); +} +Module['addOnPreRun'] = Module.addOnPreRun = addOnPreRun; + +function addOnInit(cb) { + __ATINIT__.unshift(cb); +} +Module['addOnInit'] = Module.addOnInit = addOnInit; + +function addOnPreMain(cb) { + __ATMAIN__.unshift(cb); +} +Module['addOnPreMain'] = Module.addOnPreMain = addOnPreMain; + +function addOnExit(cb) { + __ATEXIT__.unshift(cb); +} +Module['addOnExit'] = Module.addOnExit = addOnExit; + +function addOnPostRun(cb) { + __ATPOSTRUN__.unshift(cb); +} +Module['addOnPostRun'] = Module.addOnPostRun = addOnPostRun; + // Tools // This processes a JS string into a C-line array of numbers, 0-terminated. @@ -865,12 +910,6 @@ Module['removeRunDependency'] = removeRunDependency; Module["preloadedImages"] = {}; // maps url to image data Module["preloadedAudios"] = {}; // maps url to audio data -function addPreRun(func) { - if (!Module['preRun']) Module['preRun'] = []; - else if (typeof Module['preRun'] == 'function') Module['preRun'] = [Module['preRun']]; - Module['preRun'].push(func); -} - #if PGO var PGOMonitor = { called: {}, @@ -885,7 +924,7 @@ var PGOMonitor = { }; Module['PGOMonitor'] = PGOMonitor; __ATEXIT__.push({ func: function() { PGOMonitor.dump() } }); -addPreRun(function() { addRunDependency('pgo') }); +addOnPreRun(function() { addRunDependency('pgo') }); #endif function loadMemoryInitializer(filename) { @@ -898,7 +937,7 @@ function loadMemoryInitializer(filename) { } // always do this asynchronously, to keep shell and web as similar as possible - addPreRun(function() { + addOnPreRun(function() { if (ENVIRONMENT_IS_NODE || ENVIRONMENT_IS_SHELL) { applyData(Module['readBinary'](filename)); } else { diff --git a/src/settings.js b/src/settings.js index 52e4eeb0..19108f3b 100644 --- a/src/settings.js +++ b/src/settings.js @@ -194,6 +194,12 @@ var FORCE_GL_EMULATION = 0; // Forces inclusion of full GL emulation code. var DISABLE_GL_EMULATION = 0; // Disable inclusion of full GL emulation code. Useful when you don't want emulation // but do need INCLUDE_FULL_LIBRARY or MAIN_MODULE. +var STB_IMAGE = 0; // Enables building of stb-image, a tiny public-domain library for decoding images, allowing + // decoding of images without using the browser's built-in decoders. The benefit is that this + // can be done synchronously, however, it will not be as fast as the browser itself. + // When enabled, stb-image will be used automatically from IMG_Load and IMG_Load_RW. You + // can also call the stbi_* functions directly yourself. + var DISABLE_EXCEPTION_CATCHING = 0; // Disables generating code to actually catch exceptions. If the code you // are compiling does not actually rely on catching exceptions (but the // compiler generates code for it, maybe because of stdlibc++ stuff), @@ -243,8 +249,6 @@ var FS_LOG = 0; // Log all FS operations. This is especially helpful when you'r // a new project and want to see a list of file system operations happening // so that you can create a virtual file system with all of the required files. -var USE_OLD_FS = 1; // Switch to toggle the new / old FS code. Currently only used for testing purposes. - var USE_BSS = 1; // https://en.wikipedia.org/wiki/.bss // When enabled, 0-initialized globals are sorted to the end of the globals list, // enabling us to not explicitly store the initialization value for each 0 byte. @@ -416,880 +420,908 @@ var DEBUG_TAGS_SHOWING = []; // A cached set of defines, generated from the header files. This // lets the emscripten libc (library.js) see the right values. -// If you the headers or use different ones, you will need to override -// this. -var C_DEFINES = {'SI_MESGQ': '5', - 'M_SQRTPI': '1.77245385091', - '_NL_MESSAGES_CODESET': '86', - 'SIGRTMIN': '27', - 'math_errhandling': '1', - 'M_LOG10E': '0.434294481903', - '_S_IFMT': '0170000', - '_CS_V7_ENV': '20', - 'USHRT_MAX': '65535', - '_SC_XOPEN_LEGACY': '98', - 'HAVE_STDINT_H': '1', - '_SC_XOPEN_VERSION': '106', - 'F_UNLCK': '3', - '_SC_BC_DIM_MAX': '58', - 'SDL_LOADSO_DLOPEN': '1', - 'S_IFDIR': '0040000', - 'HAVE_ALLOCA': '1', - '_SC_BARRIERS': '56', - '_IFDIR': '0040000', - 'SDL_JOYSTICK_DINPUT': '1', - '_IFLNK': '0120000', - '__long_double_t': "<type 'long'>", - 'DEFFILEMODE': '0000400', - 'HAVE_SSCANF': '1', - '_FCREAT': '512', - 'HAVE_STRTOLL': '1', - 'SDL_VIDEO_OPENGL_ES': '1', - 'O_CREAT': '512', - 'SHRT_MAX': '32767', - 'SDL_JOYSTICK_ANDROID': '1', - '_SC_NPROCESSORS_CONF': '9', - '_XOPEN_ENH_I18N': '1', - 'F_DUPFD_CLOEXEC': '14', - '_CS_POSIX_V6_LP64_OFF64_LIBS': '11', - '_POSIX_SHARED_MEMORY_OBJECTS': '200112', - 'ABDAY_7': '20', - 'ABDAY_6': '19', - 'ABDAY_5': '18', - 'ABDAY_4': '17', - 'ABDAY_3': '16', - 'M_PI': '3.14159265359', +// If you modify the headers or use different ones, you will need +// to override this. +var C_DEFINES = { 'ABDAY_1': '14', - '_PC_REC_MIN_XFER_SIZE': '18', - '_SC_V6_ILP32_OFFBIG': '93', - 'SIGSTOP': '17', - '_M_LN2': '0.69314718056', - 'F_UNLKSYS': '4', - 'PTHREAD_CREATE_JOINABLE': '1', - 'SDL_VIDEO_OPENGL_GLX': '1', - 'M_PI_2': '1.57079632679', - '_SC_MEMLOCK': '24', - 'M_PI_4': '0.785398163397', + 'ABDAY_2': '15', + 'ABDAY_3': '16', + 'ABDAY_4': '17', + 'ABDAY_5': '18', + 'ABDAY_6': '19', + 'ABDAY_7': '20', + 'ABMON_1': '33', + 'ABMON_10': '42', + 'ABMON_11': '43', + 'ABMON_12': '44', + 'ABMON_2': '34', + 'ABMON_3': '35', + 'ABMON_4': '36', + 'ABMON_5': '37', + 'ABMON_6': '38', + 'ABMON_7': '39', + 'ABMON_8': '40', + 'ABMON_9': '41', + 'ACCESSPERMS': '0000400', + 'AF_INET': '2', + 'AF_INET6': '6', + 'ALLPERMS': '0004000', + 'ALT_DIGITS': '49', + 'AM_STR': '5', + 'ARG_MAX': '4096', + 'AT_EACCESS': '1', + 'AT_FDCWD': '-2', + 'AT_REMOVEDIR': '8', + 'AT_SYMLINK_FOLLOW': '4', + 'AT_SYMLINK_NOFOLLOW': '2', + 'CHAR_BIT': '8', + 'CHAR_MAX': '127', + 'CHAR_MIN': '-128', + 'CLK_TCK': '1000', + 'CLOCKS_PER_SEC': '1000', + 'CLOCK_ALLOWED': '1', + 'CLOCK_DISABLED': '0', + 'CLOCK_DISALLOWED': '0', + 'CLOCK_ENABLED': '1', + 'CODESET': '0', + 'CRNCYSTR': '56', + 'DAY_1': '7', + 'DAY_2': '8', + 'DAY_3': '9', + 'DAY_4': '10', + 'DAY_5': '11', + 'DAY_6': '12', + 'DAY_7': '13', + 'DEFFILEMODE': '0000400', + 'DOMAIN': '1', + 'D_FMT': '2', + 'D_MD_ORDER': '57', + 'D_T_FMT': '1', + 'EOF': '-1', + 'ERA': '45', + 'ERA_D_FMT': '46', + 'ERA_D_T_FMT': '47', + 'ERA_T_FMT': '48', + 'FAPPEND': '8', + 'FASYNC': '64', + 'FCREAT': '512', + 'FDEFER': '32', + 'FD_CLOEXEC': '1', + 'FD_SETSIZE': '64', + 'FEXCL': '2048', 'FEXLOCK': '256', - '_FNDELAY': '16384', - 'SIGEV_NONE': '1', - 'SIGWINCH': '28', - 'UTIME_NOW': '-2', - '_SC_THREADS': '42', - '__INT_MAX__': '2147483647', - '_XBS5_LP64_OFF64': '-1', - '_CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS': '6', - '___int_least32_t_defined': '1', - '_POSIX_MAPPED_FILES': '200112', - 'HAVE_FREE': '1', - 'M_E': '2.71828182846', - 'SIGTRAP': '5', - '_SC_SS_REPL_MAX': '78', - '_PC_SOCK_MAXBUF': '100', - '_SC_THREAD_KEYS_MAX': '38', - 'O_RDWR': '2', - '__LARGE64_FILES': '1', - '_POSIX_V6_LP64_OFF64': '-1', - 'HAVE_COSF': '1', - '_SC_2_PBS': '113', + 'FIONREAD': '1', + 'FLT_EVAL_METHOD': '0', + 'FMARK': '16', + 'FNBIO': '4096', + 'FNDELAY': '16384', 'FNOCTTY': '32768', - '_SC_TRACE_INHERIT': '86', - 'PTHREAD_PRIO_NONE': '0', - '_SC_REGEXP': '72', - '_CS_POSIX_V6_LP64_OFF64_CFLAGS': '9', - '_SC_DELAYTIMER_MAX': '37', - 'HAVE_SINF': '1', - '_POSIX_RAW_SOCKETS': '200112', - '___int64_t_defined': '1', - 'S_IFREG': '0100000', - 'SIGCLD': '20', + 'FNONBIO': '16384', + 'FOPEN': '-1', + 'FP_ILOGBNAN': '2147483647', + 'FP_INFINITE': '1', + 'FP_NAN': '0', + 'FP_NORMAL': '4', + 'FP_SUBNORMAL': '3', + 'FP_ZERO': '2', + 'FREAD': '1', + 'FSHLOCK': '128', + 'FSYNC': '8192', + 'FTRUNC': '1024', + 'FWRITE': '2', + 'F_CNVT': '12', + 'F_DUPFD': '0', + 'F_DUPFD_CLOEXEC': '14', + 'F_GETFD': '1', + 'F_GETFL': '3', + 'F_GETLK': '7', 'F_GETLK64': '20', - '_IFCHR': '0020000', - 'S_IRWXG': '0000040', - 'SDL_VIDEO_DRIVER_ANDROID': '1', - 'POLLHUP': '16', - 'S_IFMT': '0170000', - 'RADIXCHAR': '50', - 'HAVE_UNSETENV': '1', - '_S_IEXEC': '0000100', - '_SC_XOPEN_CRYPT': '96', - 'M_LN10': '2.30258509299', - 'S_IRWXU': '0000400', - 'OPTIONAL_ARG': '2', - '_PC_CHOWN_RESTRICTED': '6', - 'CRNCYSTR': '56', - 'SIZEOF_VOIDP': '4', - 'SCHAR_MAX': '127', - 'S_BLKSIZE': '1024', - 'SDL_JOYSTICK_NDS': '1', - '_SC_CLK_TCK': '2', - 'AM_STR': '5', - '__BUFSIZ__': '16', - 'ALT_DIGITS': '49', - 'HAVE_SIGNAL_H': '1', - 'HAVE_ATAN': '1', - '_NL_CTYPE_MB_CUR_MAX': '85', - '_REENT_SIGNAL_SIZE': '24', - 'SDL_AUDIO_DRIVER_DSOUND': '1', + 'F_GETOWN': '5', + 'F_LOCK': '1', + 'F_OK': '0', + 'F_RDLCK': '1', + 'F_RGETLK': '10', + 'F_RSETLK': '11', 'F_RSETLKW': '13', - 'HAVE_STRTOULL': '1', - '___int16_t_defined': '1', - 'SIGXCPU': '24', - '_SC_MQ_PRIO_MAX': '14', - '_FTRUNC': '1024', - '__MACOSX__': '1', - 'SDL_LOADSO_WINDOWS': '1', - 'SDL_VIDEO_DRIVER_X11_XINERAMA': '1', - 'MALLOC_ALIGNMENT': '16', - 'PTHREAD_CREATE_DETACHED': '0', - '_POSIX2_VERSION': '200112', - '_O_CREAT': '512', - 'PM_STR': '6', - '_PC_POSIX_SECURITY': '91', + 'F_SETFD': '2', + 'F_SETFL': '4', + 'F_SETLK': '8', + 'F_SETLK64': '21', + 'F_SETLKW': '9', + 'F_SETLKW64': '22', + 'F_SETOWN': '6', + 'F_TEST': '3', + 'F_TLOCK': '2', + 'F_ULOCK': '0', + 'F_UNLCK': '3', + 'F_UNLKSYS': '4', + 'F_WRLCK': '2', + 'H8300': '1', + 'HAVE_ABS': '1', + 'HAVE_ALLOCA': '1', + 'HAVE_ALLOCA_H': '1', + 'HAVE_ATAN': '1', + 'HAVE_ATAN2': '1', + 'HAVE_ATOF': '1', + 'HAVE_ATOI': '1', + 'HAVE_BCOPY': '1', + 'HAVE_CALLOC': '1', + 'HAVE_CEIL': '1', + 'HAVE_COPYSIGN': '1', + 'HAVE_COS': '1', + 'HAVE_COSF': '1', + 'HAVE_CTYPE_H': '1', + 'HAVE_FABS': '1', + 'HAVE_FLOOR': '1', + 'HAVE_FREE': '1', + 'HAVE_GCC_ATOMICS': '1', + 'HAVE_GCC_SYNC_LOCK_TEST_AND_SET': '1', + 'HAVE_GETENV': '1', + 'HAVE_INDEX': '1', 'HAVE_INTTYPES_H': '1', - '_SC_2_LOCALEDEF': '112', - '_SC_STREAM_MAX': '100', + 'HAVE_ITOA': '1', + 'HAVE_LOG': '1', + 'HAVE_MALLOC': '1', + 'HAVE_MATH_H': '1', 'HAVE_MEMCMP': '1', - '_CS_POSIX_V7_ILP32_OFF32_LIBS': '3', - '_POSIX2_C_BIND': '200112', - '_POSIX_VERSION': '200112', - 'S_IFIFO': '0010000', - 'SDL_VIDEO_DRIVER_X11_XSCRNSAVER': '1', - 'SCHED_FIFO': '1', - 'SDL_HAPTIC_DISABLED': '1', - 'M_LN2LO': '1.90821492927e-10', - 'MON_10': '30', - '_CS_XBS5_ILP32_OFF32_LIBS': '3', - 'O_SYNC': '8192', - '_CS_POSIX_V6_ILP32_OFFBIG_LIBS': '7', - 'YESEXPR': '52', - '_PC_PATH_MAX': '4', - '_SC_SPORADIC_SERVER': '77', - 'SDL_POWER_UIKIT': '1', - '_POSIX_SYNCHRONIZED_IO': '200112', - 'SIGXFSZ': '25', - '_SC_NPROCESSORS_ONLN': '10', - '_CS_POSIX_V6_LPBIG_OFFBIG_LIBS': '15', - '_PC_MAX_INPUT': '2', - 'F_TLOCK': '2', - 'REQUIRED_ARG': '1', - '_SC_VERSION': '7', + 'HAVE_MEMCPY': '1', + 'HAVE_MEMMOVE': '1', + 'HAVE_MEMSET': '1', + 'HAVE_M_PI': '1', + 'HAVE_NANOSLEEP': '1', + 'HAVE_POW': '1', + 'HAVE_PUTENV': '1', + 'HAVE_QSORT': '1', + 'HAVE_REALLOC': '1', + 'HAVE_RINDEX': '1', + 'HAVE_SCALBN': '1', + 'HAVE_SETENV': '1', + 'HAVE_SETJMP': '1', + 'HAVE_SIGACTION': '1', + 'HAVE_SIGNAL_H': '1', + 'HAVE_SIN': '1', + 'HAVE_SINF': '1', + 'HAVE_SNPRINTF': '1', + 'HAVE_SQRT': '1', + 'HAVE_SSCANF': '1', + 'HAVE_STDARG_H': '1', + 'HAVE_STDDEF_H': '1', + 'HAVE_STDINT_H': '1', + 'HAVE_STDIO_H': '1', + 'HAVE_STRCASECMP': '1', + 'HAVE_STRCHR': '1', + 'HAVE_STRCMP': '1', + 'HAVE_STRDUP': '1', + 'HAVE_STRICMP': '1', + 'HAVE_STRING_H': '1', + 'HAVE_STRLCAT': '1', + 'HAVE_STRLCPY': '1', + 'HAVE_STRLEN': '1', + 'HAVE_STRNCASECMP': '1', + 'HAVE_STRNCMP': '1', + 'HAVE_STRRCHR': '1', + 'HAVE_STRSTR': '1', + 'HAVE_STRTOD': '1', + 'HAVE_STRTOL': '1', + 'HAVE_STRTOLL': '1', + 'HAVE_STRTOUL': '1', + 'HAVE_STRTOULL': '1', + 'HAVE_SYSCONF': '1', + 'HAVE_SYSCTLBYNAME': '1', + 'HAVE_SYS_TYPES_H': '1', + 'HAVE_UNSETENV': '1', + 'HAVE_VSNPRINTF': '1', + 'HAVE__LTOA': '1', + 'HAVE__STRICMP': '1', + 'HAVE__STRLWR': '1', + 'HAVE__STRNICMP': '1', + 'HAVE__STRREV': '1', + 'HAVE__STRUPR': '1', + 'HAVE__ULTOA': '1', 'HUGE_VAL': 'inf', - 'AT_EACCESS': '1', - 'ABMON_3': '35', - 'ABMON_2': '34', - 'ABMON_1': '33', - '_SC_THREAD_ROBUST_PRIO_PROTECT': '123', - 'ABMON_7': '39', - 'ABMON_6': '38', - 'ABMON_5': '37', - 'ABMON_4': '36', - 'S_IWUSR': '0000200', - 'ABMON_9': '41', - 'ABMON_8': '40', - 'UNDERFLOW': '4', - '_SC_AIO_MAX': '35', - 'ERA': '45', - '_CS_XBS5_ILP32_OFFBIG_LIBS': '7', - 'S_IXUSR': '0000100', - '_SC_THREAD_PRIO_INHERIT': '46', - '__HPUX__': '1', - 'M_2_PI': '0.636619772368', - '_O_RDWR': '2', - '_PC_2_SYMLINKS': '13', - 'POSIX_FADV_DONTNEED': '135', - 'SIG_BLOCK': '1', - 'SDL_VIDEO_RENDER_NDS': '0', - '_O_WRONLY': '1', - '_CS_XBS5_LP64_OFF64_CFLAGS': '9', - '__OS2__': '1', + 'INT_MAX': '2147483647', + 'IPPROTO_TCP': '1', + 'IPPROTO_UDP': '2', + 'ITIMER_PROF': '2', + 'ITIMER_REAL': '0', + 'ITIMER_VIRTUAL': '1', + 'LACKS_SYS_MMAN_H': '1', + 'LONG_MAX': '2147483647', + 'MAC_OS_X_VERSION_10_4': '1040', + 'MAC_OS_X_VERSION_10_5': '1050', + 'MAC_OS_X_VERSION_10_6': '1060', + 'MALLOC_ALIGNMENT': '16', + 'MATH_ERREXCEPT': '2', + 'math_errhandling': '1', + 'MATH_ERRNO': '1', + 'MAXPATHLEN': '1024', + 'MB_LEN_MAX': '1', + 'MON_1': '21', + 'MON_10': '30', + 'MON_11': '31', + 'MON_12': '32', 'MON_2': '22', 'MON_3': '23', - '_POSIX_PRIORITY_SCHEDULING': '200112', - 'MON_1': '21', - 'MON_6': '26', - 'MON_7': '27', 'MON_4': '24', 'MON_5': '25', - '_SC_SPAWN': '75', + 'MON_6': '26', + 'MON_7': '27', 'MON_8': '28', 'MON_9': '29', - '_CS_POSIX_V6_ILP32_OFF32_LDFLAGS': '2', - '_SC_TRACE_EVENT_NAME_MAX': '85', - 'SA_SIGINFO': '2', - '_FBINARY': '65536', - 'PTHREAD_PRIO_PROTECT': '2', - 'POLLERR': '8', - 'SIGVTALRM': '26', - 'O_BINARY': '65536', - '_REENT_EMERGENCY_SIZE': '25', - 'S_IEXEC': '0000100', - '_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS': '13', - 'SIGEV_THREAD': '3', - 'ITIMER_VIRTUAL': '1', - 'HAVE_ATOI': '1', - '_SC_TRACE_SYS_MAX': '89', - '_POSIX_NO_TRUNC': '1', - 'HAVE_ATOF': '1', - '__RISCOS__': '1', - '_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS': '17', - 'HAVE_ATAN2': '1', - 'HAVE_PUTENV': '1', - 'SDL_AUDIO_DRIVER_ANDROID': '1', - 'F_SETFL': '4', - 'HAVE_GCC_ATOMICS': '1', - '_CS_POSIX_V7_THREADS_CFLAGS': '18', - '_SC_AIO_PRIO_DELTA_MAX': '36', - '_POSIX2_C_DEV': '200112', - '_SC_MONOTONIC_CLOCK': '69', - '_POSIX_THREAD_SPORADIC_SERVER': '1', - '_FNOINHERIT': '262144', - 'SDL_HAPTIC_NDS': '1', - '_SC_XOPEN_ENH_I18N': '97', - 'SIGPROF': '27', - 'F_SETLKW64': '22', - 'HAVE__STRREV': '1', - '_O_APPEND': '8', - '_FDEFER': '32', - 'SDL_VIDEO_DRIVER_X11_XINPUT': '1', - 'CLOCK_DISALLOWED': '0', - 'SDL_VIDEO_DRIVER_X11': '1', - '_SC_MEMORY_PROTECTION': '26', - 'HAVE_STDIO_H': '1', - 'LONG_MAX': '2147483647', - 'no_argument': '0', - '__NINTENDODS__': '1', - 'F_OK': '0', - 'SDL_ASSEMBLY_ROUTINES': '1', - 'FAPPEND': '8', - 'SA_RESETHAND': '8', - 'FREAD': '1', - '_SC_SPIN_LOCKS': '76', - 'SDL_VIDEO_DRIVER_COCOA': '1', + 'M_1_PI': '0.318309886184', + 'M_2_PI': '0.636619772368', + 'M_2_SQRTPI': '1.1283791671', + 'M_3PI_4': '2.35619449019', + 'M_E': '2.71828182846', + 'M_INVLN2': '1.44269504089', + 'M_IVLN10': '0.434294481903', + 'M_LN10': '2.30258509299', + 'M_LN2': '0.69314718056', 'M_LN2HI': '0.693147180369', - '_LIBC_LIMITS_H_': '1', - 'S_IFSOCK': '0140000', - 'SDL_AUDIO_DRIVER_COREAUDIO': '1', - 'HAVE_MALLOC': '1', - '_POSIX_DEVCTL_DIRECTION': '1', - 'ABDAY_2': '15', - 'HAVE_STRTOL': '1', - 'SDL_VIDEO_DRIVER_DUMMY': '1', - '_PC_LINK_MAX': '0', - '_POSIX_THREAD_PRIO_PROTECT': '1', - 'HAVE_STRTOD': '1', - '__DREAMCAST__': '1', + 'M_LN2LO': '1.90821492927e-10', + 'M_LOG10E': '0.434294481903', + 'M_LOG2E': '1.44269504089', + 'M_LOG2_E': '0.69314718056', + 'M_PI': '3.14159265359', + 'M_PI_2': '1.57079632679', + 'M_PI_4': '0.785398163397', + 'M_SQRT1_2': '0.707106781187', + 'M_SQRT2': '1.41421356237', + 'M_SQRT3': '1.73205080757', + 'M_SQRTPI': '1.77245385091', + 'M_TWOPI': '6.28318530718', + 'NBBY': '8', + 'NL_ARGMAX': '32', 'NOEXPR': '53', - 'FEXCL': '2048', - '_SC_FSYNC': '22', - '_SC_GETGR_R_SIZE_MAX': '50', - '_POSIX_THREAD_PROCESS_SHARED': '200112', - 'HAVE_QSORT': '1', - '_ATEXIT_SIZE': '32', - '_SC_TRACE_NAME_MAX': '88', - '_SC_BC_BASE_MAX': '57', - '__WIN32__': '1', - '_LIMITS_H': '1', - 'PTHREAD_STACK_MIN': '200', + 'NOSTR': '55', + 'NO_ARG': '0', + 'no_argument': '0', + 'NSIG': '32', + 'NULL': '0', + 'OPTIONAL_ARG': '2', + 'optional_argument': '2', + 'OPT_ARG': '2', + 'OVERFLOW': '3', + 'O_ACCMODE': '3', 'O_APPEND': '8', - '_CS_XBS5_LP64_OFF64_LINTFLAGS': '12', - '_SC_XOPEN_STREAMS': '104', - 'HAVE_CALLOC': '1', - 'HAVE_CTYPE_H': '1', - '_SC_GETPW_R_SIZE_MAX': '51', - '_POSIX_ASYNCHRONOUS_IO': '1', - 'UCHAR_MAX': '255', - '__BSDI__': '1', - '_SC_PAGE_SIZE': '8', - 'SDL_THREADS_DISABLED': '1', - '_SC_XBS5_ILP32_OFFBIG': '93', - 'S_IFBLK': '0060000', - '_S_IFIFO': '0010000', - 'T_FMT_AMPM': '4', - '_POSIX_SEMAPHORES': '200112', - 'HAVE_SCALBN': '1', - '__NETBSD__': '1', - 'NBBY': '8', - 'SDL_AUDIO_DRIVER_XAUDIO2': '1', - 'SIGEMT': '7', - '_POSIX_FSYNC': '200112', - 'F_SETLKW': '9', - '_MB_EXTENDED_CHARSETS_WINDOWS': '1', - 'SIGALRM': '14', - 'SDL_VIDEO_DRIVER_UIKIT': '1', - '___int32_t_defined': '1', + 'O_BINARY': '65536', + 'O_CLOEXEC': '262144', + 'O_CREAT': '512', + 'O_EXCL': '2048', + 'O_NOCTTY': '32768', + 'O_NOINHERIT': '262144', + 'O_NONBLOCK': '16384', + 'O_RDONLY': '0', + 'O_RDWR': '2', + 'O_SYNC': '8192', + 'O_TEXT': '131072', + 'O_TRUNC': '1024', + 'O_WRONLY': '1', + 'PATH_MAX': '4096', + 'PF_INET': '2', + 'PF_INET6': '6', + 'PLOSS': '6', + 'PM_STR': '6', + 'POLLERR': '8', + 'POLLHUP': '16', + 'POLLIN': '1', 'POLLNVAL': '4', - '_SC_OPEN_MAX': '4', - 'CHAR_BIT': '8', - '_N_LISTS': '30', - '_SC_2_FORT_RUN': '111', + 'POLLOUT': '2', + 'POSIX_FADV_DONTNEED': '135', + 'PTHREAD_CREATE_DETACHED': '0', + 'PTHREAD_CREATE_JOINABLE': '1', + 'PTHREAD_EXPLICIT_SCHED': '2', + 'PTHREAD_INHERIT_SCHED': '1', 'PTHREAD_MUTEX_DEFAULT': '3', - 'HAVE_STDARG_H': '1', - '_POSIX_REGEXP': '1', - '_SC_RE_DUP_MAX': '73', + 'PTHREAD_MUTEX_ERRORCHECK': '2', + 'PTHREAD_MUTEX_NORMAL': '0', + 'PTHREAD_MUTEX_RECURSIVE': '1', + 'PTHREAD_PRIO_INHERIT': '1', + 'PTHREAD_PRIO_NONE': '0', + 'PTHREAD_PRIO_PROTECT': '2', 'PTHREAD_PROCESS_PRIVATE': '0', - '_S_IFREG': '0100000', - '_SC_THREAD_THREADS_MAX': '40', - '_SC_THREAD_PRIO_PROTECT': '47', - 'SDL_THREAD_WINDOWS': '1', - '_SC_2_PBS_CHECKPOINT': '115', - 'M_1_PI': '0.318309886184', - '_PC_POSIX_PERMISSIONS': '90', - '_SC_TIMERS': '33', - 'MON_11': '31', - 'MON_12': '32', - 'CLOCK_DISABLED': '0', - '_SC_XBS5_LPBIG_OFFBIG': '95', - '_POSIX_SPIN_LOCKS': '200112', - '_FREAD': '1', - 'HAVE_SYSCONF': '1', - '_SC_SHARED_MEMORY_OBJECTS': '199', - 'F_RDLCK': '1', - 'F_GETFD': '1', - 'MAC_OS_X_VERSION_10_4': '1040', - 'AT_SYMLINK_NOFOLLOW': '2', - '_PC_ALLOC_SIZE_MIN': '15', - '_POSIX_C_SOURCE': '2', - '_SC_READER_WRITER_LOCKS': '71', - 'HAVE_STRING_H': '1', - 'SI_USER': '1', - '_SC_MEMLOCK_RANGE': '25', - '_SC_PRIORITY_SCHEDULING': '101', - 'optional_argument': '2', - 'T_FMT': '3', - 'LACKS_SYS_MMAN_H': '1', - 'MAC_OS_X_VERSION_10_5': '1050', - '_PC_VDISABLE': '8', - 'SDL_VIDEO_DRIVER_X11_XSHAPE': '1', - 'THOUSEP': '51', - 'O_NOINHERIT': '262144', 'PTHREAD_PROCESS_SHARED': '1', - '_SC_TRACE_EVENT_FILTER': '84', - 'ERA_T_FMT': '48', - '_SC_THREAD_ATTR_STACKADDR': '43', - '_SC_LOGIN_NAME_MAX': '52', - 'M_LOG2E': '1.44269504089', - 'ITIMER_PROF': '2', - 'HAVE_LOG': '1', - '_SC_2_C_BIND': '108', - 'FNONBIO': '16384', - '_PC_NO_TRUNC': '7', - 'F_RSETLK': '11', - '_SC_V7_ILP32_OFF32': '92', - '_FAPPEND': '8', - 'PTHREAD_EXPLICIT_SCHED': '2', - '_FNBIO': '4096', - 'HAVE_SYSCTLBYNAME': '1', - 'F_CNVT': '12', - '_SC_SHELL': '74', - '_SC_V6_LP64_OFF64': '94', + 'PTHREAD_SCOPE_PROCESS': '0', + 'PTHREAD_SCOPE_SYSTEM': '1', + 'PTHREAD_STACK_MIN': '200', + 'RADIXCHAR': '50', + 'REQUIRED_ARG': '1', + 'required_argument': '1', + 'REQ_ARG': '1', + 'R_OK': '4', + 'SA_NOCLDSTOP': '1', + 'SA_NODEFER': '4', + 'SA_RESETHAND': '8', + 'SA_SIGINFO': '2', + 'SCHAR_MAX': '127', + 'SCHAR_MIN': '-128', + 'SCHED_FIFO': '1', 'SCHED_OTHER': '0', - '_CS_GNU_LIBC_VERSION': '42', - '_SC_SEM_VALUE_MAX': '17', - 'S_ENFMT': '0002000', - '_SC_MQ_OPEN_MAX': '13', + 'SCHED_RR': '2', + 'SCHED_SPORADIC': '4', + 'SDL_ALTIVEC_BLITTERS': '1', + 'SDL_ASSEMBLY_ROUTINES': '1', + 'SDL_ASSERT_LEVEL': '1', + 'SDL_AUDIO_DRIVER_ANDROID': '1', + 'SDL_AUDIO_DRIVER_COREAUDIO': '1', + 'SDL_AUDIO_DRIVER_COREAUDIOIPHONE': '1', + 'SDL_AUDIO_DRIVER_DISK': '1', + 'SDL_AUDIO_DRIVER_DSOUND': '1', + 'SDL_AUDIO_DRIVER_DUMMY': '1', + 'SDL_AUDIO_DRIVER_NDS': '1', + 'SDL_AUDIO_DRIVER_WINMM': '1', + 'SDL_AUDIO_DRIVER_XAUDIO2': '1', + 'SDL_HAPTIC_DINPUT': '1', + 'SDL_HAPTIC_DISABLED': '1', + 'SDL_HAPTIC_DUMMY': '1', + 'SDL_HAPTIC_IOKIT': '1', + 'SDL_HAPTIC_NDS': '1', + 'SDL_IPHONE_KEYBOARD': '1', + 'SDL_IPHONE_MAX_GFORCE': '5.0', + 'SDL_JOYSTICK_ANDROID': '1', + 'SDL_JOYSTICK_DINPUT': '1', 'SDL_JOYSTICK_DISABLED': '1', - '_POSIX_ADVISORY_INFO': '200112', - 'SIGABRT': '6', - '_CS_POSIX_V7_ILP32_OFF32_CFLAGS': '1', - '_CS_XBS5_ILP32_OFF32_CFLAGS': '1', - '_MB_EXTENDED_CHARSETS_ISO': '1', - '_SC_HOST_NAME_MAX': '65', - '_SC_THREAD_STACK_MIN': '39', - '_SC_TIMEOUTS': '82', - 'POLLOUT': '2', - '_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS': '16', - '_SC_CHILD_MAX': '1', - '__RAND_MAX': '2147483647', + 'SDL_JOYSTICK_IOKIT': '1', + 'SDL_JOYSTICK_NDS': '1', + 'SDL_LOADSO_DISABLED': '1', + 'SDL_LOADSO_DLOPEN': '1', + 'SDL_LOADSO_WINDOWS': '1', + 'SDL_POWER_MACOSX': '1', + 'SDL_POWER_NINTENDODS': '1', + 'SDL_POWER_UIKIT': '1', + 'SDL_POWER_WINDOWS': '1', + 'SDL_THREADS_DISABLED': '1', + 'SDL_THREAD_PTHREAD': '1', + 'SDL_THREAD_PTHREAD_RECURSIVE_MUTEX': '1', + 'SDL_THREAD_WINDOWS': '1', + 'SDL_TIMERS_DISABLED': '1', + 'SDL_TIMER_NDS': '1', + 'SDL_TIMER_UNIX': '1', + 'SDL_TIMER_WINCE': '1', + 'SDL_TIMER_WINDOWS': '1', + 'SDL_VIDEO_DRIVER_ANDROID': '1', + 'SDL_VIDEO_DRIVER_COCOA': '1', + 'SDL_VIDEO_DRIVER_DUMMY': '1', 'SDL_VIDEO_DRIVER_NDS': '1', - '_POSIX_THREAD_ATTR_STACKADDR': '1', + 'SDL_VIDEO_DRIVER_UIKIT': '1', + 'SDL_VIDEO_DRIVER_WINDOWS': '1', + 'SDL_VIDEO_DRIVER_X11': '1', + 'SDL_VIDEO_DRIVER_X11_XINERAMA': '1', + 'SDL_VIDEO_DRIVER_X11_XINPUT': '1', + 'SDL_VIDEO_DRIVER_X11_XRANDR': '1', + 'SDL_VIDEO_DRIVER_X11_XSCRNSAVER': '1', + 'SDL_VIDEO_DRIVER_X11_XSHAPE': '1', + 'SDL_VIDEO_DRIVER_X11_XVIDMODE': '1', + 'SDL_VIDEO_OPENGL': '1', + 'SDL_VIDEO_OPENGL_CGL': '1', + 'SDL_VIDEO_OPENGL_ES': '1', + 'SDL_VIDEO_OPENGL_GLX': '1', + 'SDL_VIDEO_OPENGL_WGL': '1', + 'SDL_VIDEO_RENDER_D3D': '1', + 'SDL_VIDEO_RENDER_NDS': '0', + 'SDL_VIDEO_RENDER_OGL': '1', + 'SDL_VIDEO_RENDER_OGL_ES': '1', + 'SDL_VIDEO_RENDER_OGL_ES2': '1', + 'SEEK_CUR': '1', + 'SEEK_END': '2', + 'SEEK_SET': '0', + 'SHRT_MAX': '32767', + 'SHRT_MIN': '-32768', + 'SIGABRT': '6', + 'SIGALRM': '14', + 'SIGBUS': '10', + 'SIGCHLD': '20', + 'SIGCLD': '20', + 'SIGCONT': '19', + 'SIGEMT': '7', + 'SIGEV_NONE': '1', + 'SIGEV_SIGNAL': '2', + 'SIGEV_THREAD': '3', 'SIGFPE': '8', - 'NL_ARGMAX': '32', - '_SC_2_PBS_MESSAGE': '117', - 'TIMER_ABSTIME': '4', - '_NL_CTYPE_CODESET_NAME': '0', - '_SC_2_C_DEV': '109', - '_SC_TIMER_MAX': '19', - 'S_IXOTH': '0000001', - 'FP_ZERO': '2', - 'SING': '2', - 'M_INVLN2': '1.44269504089', - 'SDL_TIMERS_DISABLED': '1', - 'M_TWOPI': '6.28318530718', - '_PC_REC_XFER_ALIGN': '19', - '_NL_TIME_DATE_FMT': '84', - '_SC_REALTIME_SIGNALS': '29', - '_POSIX2_RE_DUP_MAX': '255', - 'CLOCKS_PER_SEC': '1000', - '_READ_WRITE_RETURN_TYPE': "<type 'int'>", - 'ERA_D_T_FMT': '47', + 'SIGHUP': '1', + 'SIGILL': '4', + 'SIGINT': '2', + 'SIGIO': '23', + 'SIGIOT': '6', + 'SIGKILL': '9', + 'SIGLOST': '29', + 'SIGPIPE': '13', + 'SIGPOLL': '23', + 'SIGPROF': '27', + 'SIGPWR': '19', + 'SIGQUIT': '3', + 'SIGRTMAX': '31', + 'SIGRTMIN': '27', + 'SIGSEGV': '11', + 'SIGSTOP': '17', + 'SIGSYS': '12', + 'SIGTERM': '15', + 'SIGTRAP': '5', + 'SIGTSTP': '18', + 'SIGTTIN': '21', + 'SIGTTOU': '22', + 'SIGURG': '16', + 'SIGUSR1': '30', + 'SIGUSR2': '31', + 'SIGVTALRM': '26', + 'SIGWINCH': '28', + 'SIGXCPU': '24', + 'SIGXFSZ': '25', + 'SIG_BLOCK': '1', + 'SIG_SETMASK': '0', 'SIG_UNBLOCK': '2', - '_CS_XBS5_ILP32_OFFBIG_LDFLAGS': '6', - '_FSHLOCK': '128', - 'CLK_TCK': '1000', - 'D_FMT': '2', - 'SDL_VIDEO_OPENGL_CGL': '1', - '_POSIX_SPAWN': '1', - '_XBS5_ILP32_OFF32': '-1', - '_SC_THREAD_PRIO_CEILING': '47', - 'SCHED_SPORADIC': '4', - '_PC_ASYNC_IO': '9', + 'SING': '2', + 'SIZEOF_VOIDP': '4', + 'SI_ASYNCIO': '4', + 'SI_MESGQ': '5', + 'SI_QUEUE': '2', 'SI_TIMER': '3', - 'DAY_2': '8', - 'DAY_3': '9', - 'DAY_1': '7', - 'DAY_6': '12', - 'DAY_7': '13', - 'DAY_4': '10', - 'DAY_5': '11', - 'F_GETFL': '3', - 'HAVE_STRNCMP': '1', - 'AT_REMOVEDIR': '8', - 'SDL_THREAD_PTHREAD_RECURSIVE_MUTEX': '1', - 'PATH_MAX': '4096', - '_POSIX_TIMEOUTS': '1', - '_SC_MAPPED_FILES': '23', - '__IRIX__': '1', - 'HAVE_INDEX': '1', - 'HAVE__LTOA': '1', - '_SC_NGROUPS_MAX': '3', - '__QNXNTO__': '1', - '_FSYNC': '8192', - 'MATH_ERRNO': '1', - '_POSIX_SAVED_IDS': '1', - 'SDL_POWER_MACOSX': '1', - '_SC_SEMAPHORES': '30', - '__FILENAME_MAX__': '255', - 'SIGTSTP': '18', - 'F_ULOCK': '0', - 'HAVE_COS': '1', - '__LONG_MAX__': '2147483647', - 'F_WRLCK': '2', - '_POSIX_JOB_CONTROL': '1', - 'FLT_EVAL_METHOD': '0', - '_XOPEN_SHM': '1', - '_POSIX_CHOWN_RESTRICTED': '1', - 'F_SETLK64': '21', - '_SC_TRACE_LOG': '87', - 'HAVE_ITOA': '1', - 'SIGILL': '4', - '_FNONBLOCK': '16384', - '__OPENBSD__': '1', - '_POSIX_TIMERS': '1', - 'FNDELAY': '16384', - 'FD_CLOEXEC': '1', - 'POLLIN': '1', + 'SI_USER': '1', + 'SOCK_DGRAM': '20', + 'SOCK_STREAM': '200', + 'STDC_HEADERS': '1', + 'STDERR_FILENO': '2', + 'STDIN_FILENO': '0', + 'STDOUT_FILENO': '1', + 'S_BLKSIZE': '1024', + 'S_ENFMT': '0002000', + 'S_IEXEC': '0000100', + 'S_IFBLK': '0060000', + 'S_IFCHR': '0020000', + 'S_IFDIR': '0040000', + 'S_IFIFO': '0010000', + 'S_IFLNK': '0120000', + 'S_IFMT': '0170000', + 'S_IFREG': '0100000', + 'S_IFSOCK': '0140000', + 'S_IREAD': '0000400', + 'S_IRGRP': '0000040', + 'S_IROTH': '0000004', + 'S_IRUSR': '0000400', + 'S_IRWXG': '0000070', + 'S_IRWXO': '0000007', + 'S_IRWXU': '0000700', + 'S_ISGID': '0002000', + 'S_ISUID': '0004000', + 'S_ISVTX': '0001000', + 'S_IWGRP': '0000020', + 'S_IWOTH': '0000002', + 'S_IWRITE': '0000200', + 'S_IWUSR': '0000200', + 'S_IXGRP': '0000010', + 'S_IXOTH': '0000001', + 'S_IXUSR': '0000100', + 'THOUSEP': '51', + 'TIMER_ABSTIME': '4', + 'TLOSS': '5', + 'T_FMT': '3', + 'T_FMT_AMPM': '4', + 'UCHAR_MAX': '255', + 'UINT_MAX': '2147483647', + 'ULONG_MAX': '2147483647', + 'UNDERFLOW': '4', + 'USHRT_MAX': '65535', + 'UTIME_NOW': '-2', + 'UTIME_OMIT': '-1', + 'W_OK': '2', + 'X_OK': '1', + 'YESEXPR': '52', + 'YESSTR': '54', + '_ATEXIT_SIZE': '32', + '_CLOCKS_PER_SEC_': '1000', + '_CS_GNU_LIBC_VERSION': '42', + '_CS_GNU_LIBPTHREAD_VERSION': '43', + '_CS_PATH': '0', + '_CS_POSIX_V6_ILP32_OFF32_CFLAGS': '1', + '_CS_POSIX_V6_ILP32_OFF32_LDFLAGS': '2', + '_CS_POSIX_V6_ILP32_OFF32_LIBS': '3', + '_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS': '5', + '_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS': '6', + '_CS_POSIX_V6_ILP32_OFFBIG_LIBS': '7', + '_CS_POSIX_V6_LP64_OFF64_CFLAGS': '9', + '_CS_POSIX_V6_LP64_OFF64_LDFLAGS': '10', + '_CS_POSIX_V6_LP64_OFF64_LIBS': '11', + '_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS': '13', '_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS': '14', - '_POSIX_THREAD_CPUTIME': '1', - 'F_LOCK': '1', - '_FLOAT_ARG': "<type 'float'>", - 'REQ_ARG': '1', + '_CS_POSIX_V6_LPBIG_OFFBIG_LIBS': '15', + '_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS': '17', + '_CS_POSIX_V7_ILP32_OFF32_CFLAGS': '1', + '_CS_POSIX_V7_ILP32_OFF32_LDFLAGS': '2', + '_CS_POSIX_V7_ILP32_OFF32_LIBS': '3', + '_CS_POSIX_V7_ILP32_OFFBIG_CFLAGS': '5', + '_CS_POSIX_V7_ILP32_OFFBIG_LDFLAGS': '6', + '_CS_POSIX_V7_ILP32_OFFBIG_LIBS': '7', + '_CS_POSIX_V7_LP64_OFF64_CFLAGS': '9', + '_CS_POSIX_V7_LP64_OFF64_LDFLAGS': '10', + '_CS_POSIX_V7_LP64_OFF64_LIBS': '11', + '_CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS': '13', + '_CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS': '14', + '_CS_POSIX_V7_LPBIG_OFFBIG_LIBS': '15', + '_CS_POSIX_V7_THREADS_CFLAGS': '18', + '_CS_POSIX_V7_THREADS_LDFLAGS': '19', + '_CS_POSIX_V7_WIDTH_RESTRICTED_ENVS': '17', + '_CS_V7_ENV': '20', + '_CS_XBS5_ILP32_OFF32_CFLAGS': '1', + '_CS_XBS5_ILP32_OFF32_LDFLAGS': '2', + '_CS_XBS5_ILP32_OFF32_LIBS': '3', '_CS_XBS5_ILP32_OFF32_LINTFLAGS': '4', - '_SC_THREAD_DESTRUCTOR_ITERATIONS': '53', - 'M_LN2': '0.69314718056', - 'UINT_MAX': '2147483647', - 'HAVE_STRDUP': '1', - 'SIG_SETMASK': '0', - '_SC_BC_STRING_MAX': '60', + '_CS_XBS5_ILP32_OFFBIG_CFLAGS': '5', + '_CS_XBS5_ILP32_OFFBIG_LDFLAGS': '6', + '_CS_XBS5_ILP32_OFFBIG_LIBS': '7', + '_CS_XBS5_ILP32_OFFBIG_LINTFLAGS': '8', + '_CS_XBS5_LP64_OFF64_CFLAGS': '9', + '_CS_XBS5_LP64_OFF64_LDFLAGS': '10', + '_CS_XBS5_LP64_OFF64_LIBS': '11', + '_CS_XBS5_LP64_OFF64_LINTFLAGS': '12', + '_CS_XBS5_LPBIG_OFFBIG_CFLAGS': '13', + '_CS_XBS5_LPBIG_OFFBIG_LDFLAGS': '14', '_CS_XBS5_LPBIG_OFFBIG_LIBS': '15', - '_SC_XOPEN_UUCP': '124', - '_SC_2_SW_DEV': '119', - 'FDEFER': '32', - 'FP_NAN': '0', - 'F_SETOWN': '6', - 'SDL_LOADSO_DISABLED': '1', - 'CHAR_MIN': '-128', - 'PTHREAD_SCOPE_SYSTEM': '1', - 'HAVE_SETENV': '1', - 'HAVE_STRNCASECMP': '1', - '_POSIX_V6_LPBIG_OFFBIG': '-1', - '_S_IWRITE': '0000200', - '_S_IFDIR': '0040000', - '_SC_ARG_MAX': '0', - '_SC_THREAD_PRIORITY_SCHEDULING': '45', - 'F_GETLK': '7', - 'SIGTTIN': '21', - '_CS_POSIX_V7_WIDTH_RESTRICTED_ENVS': '17', - '_POSIX_SPORADIC_SERVER': '1', - '_SC_THREAD_CPUTIME': '80', - '_POSIX_V6_ILP32_OFF32': '-1', - '_CS_POSIX_V7_ILP32_OFFBIG_LIBS': '7', - '_CS_POSIX_V6_ILP32_OFF32_LIBS': '3', - '_SC_SYNCHRONIZED_IO': '32', - '_UNIX98_THREAD_MUTEX_ATTRIBUTES': '1', - '_POSIX_REALTIME_SIGNALS': '200112', - '__SIGLASTNOTRT': '31', - 'ERA_D_FMT': '46', - 'HAVE_RINDEX': '1', - 'OPT_ARG': '2', - 'SDL_HAPTIC_IOKIT': '1', + '_CS_XBS5_LPBIG_OFFBIG_LINTFLAGS': '16', + '_DATE_FMT': '84', + '_FAPPEND': '8', '_FASYNC': '64', - '_CS_POSIX_V6_ILP32_OFF32_CFLAGS': '1', - 'NOSTR': '55', - '_POSIX_MONOTONIC_CLOCK': '200112', - 'SIGPOLL': '23', - 'S_ISGID': '0002000', - 'FP_INFINITE': '1', - 'ULONG_MAX': '2147483647', - '__SIGFIRSTNOTRT': '1', - 'AT_SYMLINK_FOLLOW': '4', - 'FSYNC': '8192', - '__USE_XOPEN2K': '1', - 'SDL_VIDEO_RENDER_OGL_ES': '1', - '_CS_XBS5_ILP32_OFFBIG_CFLAGS': '5', - '_IFSOCK': '0140000', + '_FBINARY': '65536', + '_FCREAT': '512', + '_FDEFER': '32', + '_FEXCL': '2048', + '_FEXLOCK': '256', + '_FLOAT_ARG': "<type 'float'>", + '_FMARK': '16', + '_FNBIO': '4096', + '_FNDELAY': '16384', + '_FNOCTTY': '32768', + '_FNOINHERIT': '262144', + '_FNONBLOCK': '16384', + '_FOPEN': '-1', + '_FREAD': '1', + '_FSHLOCK': '128', + '_FSYNC': '8192', + '_FTEXT': '131072', + '_FTRUNC': '1024', + '_FWRITE': '2', + '_IFBLK': '0060000', + '_IFCHR': '0020000', + '_IFDIR': '0040000', '_IFIFO': '0010000', - 'ARG_MAX': '4096', - 'SIGPIPE': '13', - 'HAVE__ULTOA': '1', + '_IFLNK': '0120000', + '_IFMT': '0170000', + '_IFREG': '0100000', + '_IFSOCK': '0140000', + '_LARGEFILE64_SOURCE': '1', + '_LIBC_LIMITS_H_': '1', + '_LIMITS_H': '1', + '_LONG_LONG_TYPE': "<type 'long'>", + '_MB_EXTENDED_CHARSETS_ISO': '1', + '_MB_EXTENDED_CHARSETS_WINDOWS': '1', + '_M_LN2': '0.69314718056', + '_NL_CTYPE_CODESET_NAME': '0', + '_NL_CTYPE_MB_CUR_MAX': '85', + '_NL_MESSAGES_CODESET': '86', + '_NL_TIME_DATE_FMT': '84', + '_NULL': '0', + '_N_LISTS': '30', + '_O_APPEND': '8', + '_O_BINARY': '65536', + '_O_CREAT': '512', '_O_EXCL': '2048', - 'O_TRUNC': '1024', - 'O_TEXT': '131072', - '_POSIX_THREAD_PRIO_INHERIT': '1', - '_XBS5_ILP32_OFFBIG': '1', - 'HAVE_MEMMOVE': '1', - 'STDERR_FILENO': '2', - '_CS_XBS5_LPBIG_OFFBIG_CFLAGS': '13', - '__LINUX__': '1', - 'PLOSS': '6', - 'S_IRWXO': '0000004', - '_SC_V7_LP64_OFF64': '94', '_O_NOINHERIT': '262144', - 'D_MD_ORDER': '57', - '_IFMT': '0170000', - '_SC_SYMLOOP_MAX': '79', - 'MB_LEN_MAX': '1', - 'SDL_TIMER_WINDOWS': '1', - '_SC_XOPEN_UNIX': '105', - 'M_IVLN10': '0.434294481903', - 'ALLPERMS': '0004000', - 'HAVE_STRSTR': '1', - '__BEOS__': '1', - 'HAVE_GCC_SYNC_LOCK_TEST_AND_SET': '1', - 'F_SETFD': '2', - 'SIGUSR1': '30', - 'HAVE_SIN': '1', - 'SDL_VIDEO_DRIVER_X11_XRANDR': '1', - 'MAC_OS_X_VERSION_10_6': '1060', - '___int8_t_defined': '1', - 'SIGKILL': '9', - '_CS_POSIX_V7_ILP32_OFFBIG_CFLAGS': '5', - 'PTHREAD_MUTEX_RECURSIVE': '1', - 'SIGSEGV': '11', - 'M_LOG2_E': '0.69314718056', - 'FWRITE': '2', - '_FEXCL': '2048', - 'SIGINT': '2', - 'HAVE_STRRCHR': '1', - 'H8300': '1', - '_POSIX_MEMORY_PROTECTION': '200112', - 'FP_ILOGBNAN': '2147483647', - '_SC_V7_LPBIG_OFFBIG': '95', - '_SC_CLOCK_SELECTION': '61', + '_O_RAW': '65536', + '_O_RDONLY': '0', + '_O_RDWR': '2', + '_O_TEXT': '131072', + '_O_TRUNC': '1024', + '_O_WRONLY': '1', + '_PC_2_SYMLINKS': '13', + '_PC_ALLOC_SIZE_MIN': '15', + '_PC_ASYNC_IO': '9', + '_PC_CHOWN_RESTRICTED': '6', + '_PC_FILESIZEBITS': '12', + '_PC_LINK_MAX': '0', + '_PC_MAX_CANON': '1', + '_PC_MAX_INPUT': '2', + '_PC_NAME_MAX': '3', + '_PC_NO_TRUNC': '7', + '_PC_PATH_MAX': '4', + '_PC_PIPE_BUF': '5', + '_PC_POSIX_PERMISSIONS': '90', + '_PC_POSIX_SECURITY': '91', '_PC_PRIO_IO': '10', - 'M_2_SQRTPI': '1.1283791671', - 'S_IROTH': '0000004', - '_SC_MESSAGE_PASSING': '27', - '_SC_V6_LPBIG_OFFBIG': '95', - 'SDL_AUDIO_DRIVER_DUMMY': '1', - 'HAVE_MEMCPY': '1', - '_SC_EXPR_NEST_MAX': '64', - 'SDL_TIMER_WINCE': '1', - 'STDC_HEADERS': '1', - '_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS': '5', - 'S_ISVTX': '0001000', - 'HAVE_STRLCPY': '1', - 'UTIME_OMIT': '-1', - '_CS_POSIX_V7_THREADS_LDFLAGS': '19', - 'SDL_POWER_WINDOWS': '1', - '_SC_PAGESIZE': '8', + '_PC_REC_INCR_XFER_SIZE': '16', '_PC_REC_MAX_XFER_SIZE': '17', - 'SIGIOT': '6', - 'FASYNC': '64', - '_SC_V7_ILP32_OFFBIG': '93', - '_RAND48_MULT_0': '58989', - '_RAND48_MULT_1': '57068', - '_RAND48_MULT_2': '5', - '_CS_POSIX_V6_LP64_OFF64_LDFLAGS': '10', - 'HAVE_STRTOUL': '1', - 'PTHREAD_MUTEX_NORMAL': '0', - '_O_TRUNC': '1024', - 'W_OK': '2', - 'O_NONBLOCK': '16384', - 'R_OK': '4', - '_IFBLK': '0060000', - 'FTRUNC': '1024', - '__OSF__': '1', - '_SC_XBS5_LP64_OFF64': '94', - 'STDIN_FILENO': '0', - 'HAVE_ABS': '1', - 'SDL_TIMER_NDS': '1', - '_CS_POSIX_V7_LPBIG_OFFBIG_LIBS': '15', - 'SDL_AUDIO_DRIVER_DISK': '1', - '_SC_SIGQUEUE_MAX': '18', - 'M_3PI_4': '2.35619449019', - 'HAVE_STRCMP': '1', + '_PC_REC_MIN_XFER_SIZE': '18', + '_PC_REC_XFER_ALIGN': '19', + '_PC_SOCK_MAXBUF': '100', + '_PC_SYMLINK_MAX': '14', + '_PC_SYNC_IO': '11', + '_PC_TIMESTAMP_RESOLUTION': '20', + '_PC_VDISABLE': '8', + '_POINTER_INT': "<type 'long'>", + '_POSIX2_CHAR_TERM': '200112', + '_POSIX2_C_BIND': '200112', + '_POSIX2_C_DEV': '200112', + '_POSIX2_RE_DUP_MAX': '255', + '_POSIX2_SW_DEV': '200112', + '_POSIX2_UPE': '200112', + '_POSIX2_VERSION': '200112', + '_POSIX_ADVISORY_INFO': '200112', + '_POSIX_ASYNCHRONOUS_IO': '1', + '_POSIX_BARRIERS': '200112', + '_POSIX_CHOWN_RESTRICTED': '1', + '_POSIX_CPUTIME': '1', + '_POSIX_C_SOURCE': '2', + '_POSIX_DEVCTL_DIRECTION': '1', + '_POSIX_DEVICE_CONTROL': '1', + '_POSIX_FSYNC': '200112', + '_POSIX_INTERRUPT_CONTROL': '1', + '_POSIX_IPV6': '200112', + '_POSIX_JOB_CONTROL': '1', + '_POSIX_MAPPED_FILES': '200112', + '_POSIX_MEMLOCK': '1', + '_POSIX_MEMLOCK_RANGE': '200112', + '_POSIX_MEMORY_PROTECTION': '200112', '_POSIX_MESSAGE_PASSING': '200112', - 'S_ISUID': '0004000', - 'SIGLOST': '29', + '_POSIX_MONOTONIC_CLOCK': '200112', + '_POSIX_NO_TRUNC': '1', + '_POSIX_PRIORITIZED_IO': '1', + '_POSIX_PRIORITY_SCHEDULING': '200112', + '_POSIX_RAW_SOCKETS': '200112', + '_POSIX_READER_WRITER_LOCKS': '200112', + '_POSIX_REALTIME_SIGNALS': '200112', + '_POSIX_REGEXP': '1', + '_POSIX_SAVED_IDS': '1', + '_POSIX_SEMAPHORES': '200112', + '_POSIX_SHARED_MEMORY_OBJECTS': '200112', + '_POSIX_SHELL': '1', + '_POSIX_SPAWN': '1', + '_POSIX_SPIN_LOCKS': '200112', + '_POSIX_SPORADIC_SERVER': '1', + '_POSIX_SYNCHRONIZED_IO': '200112', + '_POSIX_THREADS': '200112', + '_POSIX_THREAD_ATTR_STACKADDR': '1', + '_POSIX_THREAD_ATTR_STACKSIZE': '200112', + '_POSIX_THREAD_CPUTIME': '1', '_POSIX_THREAD_PRIORITY_SCHEDULING': '200112', - 'SDL_VIDEO_RENDER_OGL_ES2': '1', - '__FREEBSD__': '1', - '_SC_TZNAME_MAX': '20', - '_O_RAW': '65536', - '_CS_PATH': '0', - '_POSIX_BARRIERS': '200112', - 'SDL_ALTIVEC_BLITTERS': '1', - 'SEEK_SET': '0', + '_POSIX_THREAD_PRIO_INHERIT': '1', + '_POSIX_THREAD_PRIO_PROTECT': '1', + '_POSIX_THREAD_PROCESS_SHARED': '200112', '_POSIX_THREAD_SAFE_FUNCTIONS': '200112', - 'S_IREAD': '0000400', - '_LONG_LONG_TYPE': "<type 'long'>", - '___int_least8_t_defined': '1', - 'INT_MAX': '2147483647', + '_POSIX_THREAD_SPORADIC_SERVER': '1', + '_POSIX_TIMEOUTS': '1', + '_POSIX_TIMERS': '1', + '_POSIX_V6_ILP32_OFF32': '-1', '_POSIX_V6_ILP32_OFFBIG': '1', - '_SC_PHYS_PAGES': '11', - 'HAVE_BCOPY': '1', - '_PC_MAX_CANON': '1', - 'HAVE_STRICMP': '1', - '_CS_XBS5_LPBIG_OFFBIG_LDFLAGS': '14', - '_SC_THREAD_SAFE_FUNCTIONS': '49', - 'SIGRTMAX': '31', - 'S_IXGRP': '0000010', - 'HAVE_GETENV': '1', - '_XBS5_LPBIG_OFFBIG': '-1', - '_PC_NAME_MAX': '3', - 'O_EXCL': '2048', - '_SC_XOPEN_SHM': '103', - 'S_IWGRP': '0000020', - '_SC_TRACE_USER_EVENT_MAX': '90', - 'SDL_VIDEO_RENDER_D3D': '1', - 'HAVE__STRNICMP': '1', - 'M_SQRT1_2': '0.707106781187', - '_SC_AVPHYS_PAGES': '12', - '_SC_RAW_SOCKETS': '70', - 'O_RDONLY': '0', - '_DATE_FMT': '84', - 'HAVE_STRCASECMP': '1', - '__SOLARIS__': '1', - '_SC_RTSIG_MAX': '15', - '_POSIX_DEVICE_CONTROL': '1', - 'CLOCK_ENABLED': '1', - '_NULL': '0', - '_SC_PRIORITIZED_IO': '28', - '_O_TEXT': '131072', - 'SDL_VIDEO_DRIVER_X11_XVIDMODE': '1', - 'SIGBUS': '10', - 'CODESET': '0', - 'CHAR_MAX': '127', - 'SDL_POWER_NINTENDODS': '1', - 'SIGSYS': '12', - '_PC_REC_INCR_XFER_SIZE': '16', - 'S_IRUSR': '0000400', - '_PC_FILESIZEBITS': '12', - '_SC_XBS5_ILP32_OFF32': '92', - 'HAVE_MATH_H': '1', - 'HAVE_SQRT': '1', - 'SIGURG': '16', - '_POSIX_THREAD_ATTR_STACKSIZE': '200112', - '_CS_POSIX_V7_LP64_OFF64_LIBS': '11', - '_CS_GNU_LIBPTHREAD_VERSION': '43', - 'HAVE_M_PI': '1', + '_POSIX_V6_LP64_OFF64': '-1', + '_POSIX_V6_LPBIG_OFFBIG': '-1', + '_POSIX_VERSION': '200112', + '_RAND48_ADD': '11', + '_RAND48_MULT_0': '58989', + '_RAND48_MULT_1': '57068', + '_RAND48_MULT_2': '5', + '_RAND48_SEED_0': '13070', + '_RAND48_SEED_1': '43981', + '_RAND48_SEED_2': '4660', + '_READ_WRITE_RETURN_TYPE': "<type 'int'>", '_REENT_ASCTIME_SIZE': '26', + '_REENT_EMERGENCY_SIZE': '25', + '_REENT_SIGNAL_SIZE': '24', + '_SC_2_CHAR_TERM': '107', + '_SC_2_C_BIND': '108', + '_SC_2_C_DEV': '109', + '_SC_2_FORT_DEV': '110', + '_SC_2_FORT_RUN': '111', + '_SC_2_LOCALEDEF': '112', + '_SC_2_PBS': '113', + '_SC_2_PBS_ACCOUNTING': '114', + '_SC_2_PBS_CHECKPOINT': '115', '_SC_2_PBS_LOCATE': '116', - '_SC_V6_ILP32_OFF32': '92', - 'SIGCHLD': '20', - 'SHRT_MIN': '-32768', - '__HAIKU__': '1', - 'PTHREAD_MUTEX_ERRORCHECK': '2', - '_PC_SYNC_IO': '11', - 'SDL_VIDEO_OPENGL': '1', - 'FP_NORMAL': '4', + '_SC_2_PBS_MESSAGE': '117', + '_SC_2_PBS_TRACK': '118', + '_SC_2_SW_DEV': '119', '_SC_2_UPE': '120', - 'HAVE_POW': '1', - '_SC_SEM_NSEMS_MAX': '16', - '__ANDROID__': '1', + '_SC_2_VERSION': '121', + '_SC_ADVISORY_INFO': '54', + '_SC_AIO_LISTIO_MAX': '34', + '_SC_AIO_MAX': '35', + '_SC_AIO_PRIO_DELTA_MAX': '36', + '_SC_ARG_MAX': '0', + '_SC_ASYNCHRONOUS_IO': '21', + '_SC_ATEXIT_MAX': '55', + '_SC_AVPHYS_PAGES': '12', + '_SC_BARRIERS': '56', + '_SC_BC_BASE_MAX': '57', + '_SC_BC_DIM_MAX': '58', + '_SC_BC_SCALE_MAX': '59', + '_SC_BC_STRING_MAX': '60', + '_SC_CHILD_MAX': '1', + '_SC_CLK_TCK': '2', + '_SC_CLOCK_SELECTION': '61', + '_SC_COLL_WEIGHTS_MAX': '62', + '_SC_CPUTIME': '63', + '_SC_DELAYTIMER_MAX': '37', + '_SC_EXPR_NEST_MAX': '64', + '_SC_FSYNC': '22', + '_SC_GETGR_R_SIZE_MAX': '50', + '_SC_GETPW_R_SIZE_MAX': '51', + '_SC_HOST_NAME_MAX': '65', '_SC_IOV_MAX': '66', - 'S_IRGRP': '0000040', - 'YESSTR': '54', - 'HAVE_ALLOCA_H': '1', - 'S_IFCHR': '0020000', - '_POSIX_MEMLOCK': '1', - '_SC_TRACE': '83', - '_POSIX_INTERRUPT_CONTROL': '1', - '_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS': '6', - 'PTHREAD_SCOPE_PROCESS': '0', - '__GNUC_VA_LIST': '1', - 'HAVE_FABS': '1', - '_CS_POSIX_V7_LPBIG_OFFBIG_LDFLAGS': '14', - 'TLOSS': '5', - '_TIME_T_': "<type 'long'>", - 'DOMAIN': '1', - 'HAVE_REALLOC': '1', - 'HAVE_STRLEN': '1', - '__IPHONEOS__': '1', - '_POINTER_INT': "<type 'long'>", - 'O_NOCTTY': '32768', - 'PTHREAD_PRIO_INHERIT': '1', - '_SC_THREAD_SPORADIC_SERVER': '81', - 'O_ACCMODE': '3', + '_SC_IPV6': '67', + '_SC_JOB_CONTROL': '5', '_SC_LINE_MAX': '68', - 'D_T_FMT': '1', - '_RAND48_SEED_1': '43981', - '_RAND48_SEED_0': '13070', - '_RAND48_SEED_2': '4660', - 'HAVE_STRLCAT': '1', - 'HAVE_SETJMP': '1', - 'SDL_AUDIO_DRIVER_COREAUDIOIPHONE': '1', - 'FOPEN': '-1', - 'F_RGETLK': '10', - 'F_DUPFD': '0', - '_S_IFCHR': '0020000', - 'SDL_IPHONE_KEYBOARD': '1', - '_POSIX_IPV6': '200112', - '_XOPEN_VERSION': '600', - 'HAVE_FLOOR': '1', - '_CS_XBS5_LP64_OFF64_LDFLAGS': '10', - '_FNOCTTY': '32768', - '_IFREG': '0100000', - 'FP_SUBNORMAL': '3', - 'SA_NOCLDSTOP': '1', - 'HAVE_STDDEF_H': '1', - 'NSIG': '32', - 'HAVE__STRUPR': '1', - 'SDL_THREAD_PTHREAD': '1', - 'S_IWOTH': '0000002', - 'SEEK_END': '2', - 'SDL_ASSERT_LEVEL': '1', - 'SI_ASYNCIO': '4', - 'required_argument': '1', - '_FWRITE': '2', - 'SCHED_RR': '2', - '_SC_2_FORT_DEV': '110', - 'SA_NODEFER': '4', - '_POSIX2_CHAR_TERM': '200112', - 'F_SETLK': '8', - 'SIGQUIT': '3', - '_SC_ATEXIT_MAX': '55', - '_POSIX_MEMLOCK_RANGE': '200112', + '_SC_LOGIN_NAME_MAX': '52', + '_SC_MAPPED_FILES': '23', + '_SC_MEMLOCK': '24', + '_SC_MEMLOCK_RANGE': '25', + '_SC_MEMORY_PROTECTION': '26', + '_SC_MESSAGE_PASSING': '27', + '_SC_MONOTONIC_CLOCK': '69', + '_SC_MQ_OPEN_MAX': '13', + '_SC_MQ_PRIO_MAX': '14', + '_SC_NGROUPS_MAX': '3', + '_SC_NPROCESSORS_CONF': '9', + '_SC_NPROCESSORS_ONLN': '10', + '_SC_OPEN_MAX': '4', + '_SC_PAGESIZE': '8', + '_SC_PAGE_SIZE': '8', + '_SC_PHYS_PAGES': '11', + '_SC_PRIORITIZED_IO': '28', + '_SC_PRIORITY_SCHEDULING': '101', + '_SC_RAW_SOCKETS': '70', + '_SC_READER_WRITER_LOCKS': '71', + '_SC_REALTIME_SIGNALS': '29', + '_SC_REGEXP': '72', + '_SC_RE_DUP_MAX': '73', + '_SC_RTSIG_MAX': '15', '_SC_SAVED_IDS': '6', - 'PTHREAD_INHERIT_SCHED': '1', - 'SEEK_CUR': '1', - 'S_IFLNK': '0120000', - '_S_IREAD': '0000400', - '_FOPEN': '-1', - 'OVERFLOW': '3', - '_POSIX_SHELL': '1', - 'FMARK': '16', - '_POSIX2_UPE': '200112', - 'SDL_VIDEO_RENDER_OGL': '1', - '_SC_2_PBS_TRACK': '118', - '_POSIX_CPUTIME': '1', + '_SC_SEMAPHORES': '30', + '_SC_SEM_NSEMS_MAX': '16', + '_SC_SEM_VALUE_MAX': '17', + '_SC_SHARED_MEMORY_OBJECTS': '199', + '_SC_SHELL': '74', + '_SC_SIGQUEUE_MAX': '18', + '_SC_SPAWN': '75', + '_SC_SPIN_LOCKS': '76', + '_SC_SPORADIC_SERVER': '77', + '_SC_SS_REPL_MAX': '78', + '_SC_STREAM_MAX': '100', + '_SC_SYMLOOP_MAX': '79', + '_SC_SYNCHRONIZED_IO': '32', + '_SC_THREADS': '42', + '_SC_THREAD_ATTR_STACKADDR': '43', + '_SC_THREAD_ATTR_STACKSIZE': '44', + '_SC_THREAD_CPUTIME': '80', + '_SC_THREAD_DESTRUCTOR_ITERATIONS': '53', + '_SC_THREAD_KEYS_MAX': '38', + '_SC_THREAD_PRIORITY_SCHEDULING': '45', + '_SC_THREAD_PRIO_CEILING': '47', + '_SC_THREAD_PRIO_INHERIT': '46', + '_SC_THREAD_PRIO_PROTECT': '47', '_SC_THREAD_PROCESS_SHARED': '48', - '_SC_JOB_CONTROL': '5', - '_O_RDONLY': '0', - 'FNBIO': '4096', - 'O_CLOEXEC': '262144', - '_CS_XBS5_LP64_OFF64_LIBS': '11', - '_PC_TIMESTAMP_RESOLUTION': '20', - '_POSIX_READER_WRITER_LOCKS': '200112', - 'ACCESSPERMS': '0000400', - '_POSIX_PRIORITIZED_IO': '1', - '_SC_IPV6': '67', - 'SDL_VIDEO_OPENGL_WGL': '1', - '_CS_XBS5_ILP32_OFFBIG_LINTFLAGS': '8', - 'HAVE__STRICMP': '1', - '_SC_ADVISORY_INFO': '54', - 'SCHAR_MIN': '-128', - '_SC_XOPEN_REALTIME_THREADS': '102', - 'SIGEV_SIGNAL': '2', - 'HAVE_NANOSLEEP': '1', - 'O_WRONLY': '1', - '_PC_SYMLINK_MAX': '14', '_SC_THREAD_ROBUST_PRIO_INHERIT': '122', - 'X_OK': '1', - '_CS_XBS5_ILP32_OFF32_LDFLAGS': '2', - 'SIGTERM': '15', - '_SC_COLL_WEIGHTS_MAX': '62', - '_CS_POSIX_V7_ILP32_OFF32_LDFLAGS': '2', - 'NO_ARG': '0', - '_CS_POSIX_V7_LP64_OFF64_CFLAGS': '9', - 'S_IWRITE': '0000200', - '_FEXLOCK': '256', + '_SC_THREAD_ROBUST_PRIO_PROTECT': '123', + '_SC_THREAD_SAFE_FUNCTIONS': '49', + '_SC_THREAD_SPORADIC_SERVER': '81', + '_SC_THREAD_STACK_MIN': '39', + '_SC_THREAD_THREADS_MAX': '40', + '_SC_TIMEOUTS': '82', + '_SC_TIMERS': '33', + '_SC_TIMER_MAX': '19', + '_SC_TRACE': '83', + '_SC_TRACE_EVENT_FILTER': '84', + '_SC_TRACE_EVENT_NAME_MAX': '85', + '_SC_TRACE_INHERIT': '86', + '_SC_TRACE_LOG': '87', + '_SC_TRACE_NAME_MAX': '88', + '_SC_TRACE_SYS_MAX': '89', + '_SC_TRACE_USER_EVENT_MAX': '90', + '_SC_TTY_NAME_MAX': '41', + '_SC_TYPED_MEMORY_OBJECTS': '91', + '_SC_TZNAME_MAX': '20', + '_SC_V6_ILP32_OFF32': '92', + '_SC_V6_ILP32_OFFBIG': '93', + '_SC_V6_LP64_OFF64': '94', + '_SC_V6_LPBIG_OFFBIG': '95', + '_SC_V7_ILP32_OFF32': '92', + '_SC_V7_ILP32_OFFBIG': '93', + '_SC_V7_LP64_OFF64': '94', + '_SC_V7_LPBIG_OFFBIG': '95', + '_SC_VERSION': '7', + '_SC_XBS5_ILP32_OFF32': '92', + '_SC_XBS5_ILP32_OFFBIG': '93', + '_SC_XBS5_LP64_OFF64': '94', + '_SC_XBS5_LPBIG_OFFBIG': '95', + '_SC_XOPEN_CRYPT': '96', + '_SC_XOPEN_ENH_I18N': '97', + '_SC_XOPEN_LEGACY': '98', '_SC_XOPEN_REALTIME': '99', - 'SIGPWR': '19', - 'SDL_AUDIO_DRIVER_WINMM': '1', - 'HAVE_STRCHR': '1', - '_PC_PIPE_BUF': '5', - 'SDL_HAPTIC_DINPUT': '1', - 'SIGHUP': '1', - 'F_GETOWN': '5', - 'CLOCK_ALLOWED': '1', - 'HAVE_MEMSET': '1', - 'SIGUSR2': '31', - '_SC_2_PBS_ACCOUNTING': '114', - 'F_TEST': '3', - 'HAVE_VSNPRINTF': '1', - 'ITIMER_REAL': '0', - 'HAVE_SNPRINTF': '1', - 'HAVE_SYS_TYPES_H': '1', - 'HAVE_COPYSIGN': '1', - '_CLOCKS_PER_SEC_': '1000', - 'SDL_HAPTIC_DUMMY': '1', - 'SIGCONT': '19', - 'NULL': '0', - 'FSHLOCK': '128', - 'STDOUT_FILENO': '1', + '_SC_XOPEN_REALTIME_THREADS': '102', + '_SC_XOPEN_SHM': '103', + '_SC_XOPEN_STREAMS': '104', + '_SC_XOPEN_UNIX': '105', + '_SC_XOPEN_UUCP': '124', + '_SC_XOPEN_VERSION': '106', + '_S_IEXEC': '0000100', + '_S_IFCHR': '0020000', + '_S_IFDIR': '0040000', + '_S_IFIFO': '0010000', + '_S_IFMT': '0170000', + '_S_IFREG': '0100000', + '_S_IREAD': '0000400', + '_S_IWRITE': '0000200', + '_TIME_T_': "<type 'long'>", + '_UNIX98_THREAD_MUTEX_ATTRIBUTES': '1', + '_XBS5_ILP32_OFF32': '-1', + '_XBS5_ILP32_OFFBIG': '1', + '_XBS5_LP64_OFF64': '-1', + '_XBS5_LPBIG_OFFBIG': '-1', + '_XOPEN_CRYPT': '1', + '_XOPEN_ENH_I18N': '1', + '_XOPEN_SHM': '1', + '_XOPEN_VERSION': '600', '__AIX__': '1', - 'SDL_JOYSTICK_IOKIT': '1', - '_SC_THREAD_ATTR_STACKSIZE': '44', - 'SIGIO': '23', - 'HAVE_CEIL': '1', - 'HAVE__STRLWR': '1', - 'HAVE_SIGACTION': '1', - '_SC_CPUTIME': '63', - '_SC_2_VERSION': '121', - '_O_BINARY': '65536', + '__ANDROID__': '1', + '__BEOS__': '1', + '__BSDI__': '1', + '__BUFSIZ__': '16', + '__DREAMCAST__': '1', + '__FILENAME_MAX__': '255', + '__FREEBSD__': '1', + '__GNUC_VA_LIST': '1', + '__HAIKU__': '1', + '__HPUX__': '1', + '__INT_MAX__': '2147483647', + '__IPHONEOS__': '1', + '__IRIX__': '1', + '__LARGE64_FILES': '1', + '__LINUX__': '1', + '__long_double_t': "<type 'long'>", + '__LONG_MAX__': '2147483647', + '__MACOSX__': '1', + '__NETBSD__': '1', + '__NINTENDODS__': '1', + '__OPENBSD__': '1', + '__OS2__': '1', + '__OSF__': '1', + '__QNXNTO__': '1', + '__RAND_MAX': '2147483647', + '__RISCOS__': '1', + '__SIGFIRSTNOTRT': '1', + '__SIGLASTNOTRT': '31', + '__SOLARIS__': '1', + '__USE_XOPEN2K': '1', + '__WIN32__': '1', + '___int16_t_defined': '1', + '___int32_t_defined': '1', + '___int64_t_defined': '1', + '___int8_t_defined': '1', '___int_least16_t_defined': '1', - '_FTEXT': '131072', - '_POSIX2_SW_DEV': '200112', - '_LARGEFILE64_SOURCE': '1', - '_XOPEN_CRYPT': '1', - 'FD_SETSIZE': '64', - 'SDL_AUDIO_DRIVER_NDS': '1', - '_FMARK': '16', - '_SC_TYPED_MEMORY_OBJECTS': '91', - '_SC_ASYNCHRONOUS_IO': '21', - '_SC_2_CHAR_TERM': '107', - '_SC_AIO_LISTIO_MAX': '34', - 'FCREAT': '512', - '_RAND48_ADD': '11', - 'MAXPATHLEN': '1024', - '_SC_BC_SCALE_MAX': '59', - 'SDL_IPHONE_MAX_GFORCE': '5.0', - 'MATH_ERREXCEPT': '2', - 'SDL_VIDEO_DRIVER_WINDOWS': '1', - '_CS_POSIX_V7_LPBIG_OFFBIG_CFLAGS': '13', - 'M_SQRT3': '1.73205080757', - 'M_SQRT2': '1.41421356237', - '_POSIX_THREADS': '200112', - 'ABMON_12': '44', - 'ABMON_11': '43', - 'ABMON_10': '42', - 'SI_QUEUE': '2', - 'SDL_TIMER_UNIX': '1', - 'AT_FDCWD': '-2', - 'SIGTTOU': '22', - '_CS_POSIX_V7_LP64_OFF64_LDFLAGS': '10', - '_SC_TTY_NAME_MAX': '41', - 'AF_INET': '2', - 'AF_INET6': '6', - 'PF_INET': '2', - 'PF_INET6': '6', - 'FIONREAD': '1', - 'SOCK_STREAM': '200', - 'SOCK_DGRAM': '20', - 'IPPROTO_TCP': '1', - 'IPPROTO_UDP': '2', - 'EOF': '-1' + '___int_least32_t_defined': '1', + '___int_least8_t_defined': '1', + 'FMODE_READ': '0x1', + 'FMODE_WRITE': '0x2', + 'FMODE_LSEEK': '0x4', + 'FMODE_PREAD': '0x8', + 'FMODE_PWRITE': '0x10', + 'FMODE_EXEC': '0x20', + 'FMODE_NDELAY': '0x40', + 'FMODE_EXCL': '0x80', + 'FMODE_NOCMTIME': '0x800', + 'FMODE_RANDOM': '0x1000', + 'FMODE_UNSIGNED_OFFSET': '0x2000', + 'FMODE_PATH': '0x4000', + 'FMODE_NONOTIFY': '0x1000000', + 'S_IRWXUGO': '511', + 'S_IALLUGO': '4095', + 'S_IRUGO': '292', + 'S_IWUGO': '146', + 'S_IXUGO': '73', + 'LOOKUP_FOLLOW': '0x0001', + 'LOOKUP_DIRECTORY': '0x0002', + 'LOOKUP_PARENT': '0x0010', + 'MAP_SHARED': '0x01', + 'MAP_PRIVATE': '0x02', + 'MAP_TYPE': '0x0f', + 'MAP_FIXED': '0x100', + 'MAP_ANONYMOUS': '0x10', + 'O_NOFOLLOW': '0200000' }; diff --git a/src/utility.js b/src/utility.js index 9cc8d3a3..7d122cef 100644 --- a/src/utility.js +++ b/src/utility.js @@ -312,6 +312,12 @@ function setUnion(x, y) { return ret; } +function setSize(x) { + var ret = 0; + for (var xx in x) ret++; + return ret; +} + function invertArray(x) { var ret = {}; for (var i = 0; i < x.length; i++) { |