summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--LICENSE26
-rwxr-xr-xemcc5
-rw-r--r--src/library.js3413
-rw-r--r--src/library_gl.js5
-rw-r--r--src/library_openal.js14
-rw-r--r--src/library_path.js134
-rw-r--r--src/library_sdl.js4
-rw-r--r--src/modules.js2
-rw-r--r--src/preamble.js9
-rw-r--r--src/settings.js51
-rw-r--r--src/shell.js53
-rw-r--r--src/utility.js6
-rw-r--r--system/include/libc/sys/signal.h4
-rw-r--r--system/include/net/if.h21
-rw-r--r--system/include/net/netinet/in.h67
-rw-r--r--system/include/netdb.h36
-rw-r--r--system/include/sys/ioctl.h58
-rw-r--r--system/include/sys/select.h2
-rw-r--r--system/include/sys/socket.h211
-rw-r--r--system/lib/libc/musl/src/stdlib/ecvt.c19
-rw-r--r--system/lib/libc/musl/src/stdlib/fcvt.c25
-rw-r--r--system/lib/libc/musl/src/stdlib/gcvt.c8
-rw-r--r--system/lib/libcextra.symbols3
-rw-r--r--tests/filesystem/src.js1
-rw-r--r--tests/glbook/CH02_HelloTriangle.pngbin1009 -> 866 bytes
-rw-r--r--tests/glbook/CH08_SimpleVertexShader.pngbin1600 -> 1200 bytes
-rw-r--r--tests/glbook/CH09_SimpleTexture2D.pngbin1943 -> 713 bytes
-rw-r--r--tests/glbook/CH09_TextureCubemap.pngbin3209 -> 2325 bytes
-rw-r--r--tests/glbook/CH09_TextureWrap.pngbin1812 -> 894 bytes
-rw-r--r--tests/glbook/CH10_MultiTexture.pngbin59495 -> 46810 bytes
-rw-r--r--tests/glbook/CH13_ParticleSystem.pngbin4921 -> 4285 bytes
-rw-r--r--tests/htmltest.pngbin743 -> 730 bytes
-rwxr-xr-xtests/runner.py222
-rw-r--r--tests/sdl_gfx_primitives.pngbin2357 -> 2261 bytes
-rw-r--r--tests/sdl_maprgba.pngbin1875 -> 1862 bytes
-rw-r--r--tests/sdl_rotozoom.pngbin431921 -> 431168 bytes
-rw-r--r--tests/stat/test_chmod.c24
-rw-r--r--tests/stat/test_mknod.c5
-rw-r--r--tests/stat/test_stat.c3
-rw-r--r--tests/stdio/test_rename.c107
-rw-r--r--tools/asm_module.py2
-rw-r--r--tools/file_packager.py2
-rw-r--r--tools/js-optimizer.js32
43 files changed, 3141 insertions, 1433 deletions
diff --git a/LICENSE b/LICENSE
index 6d090777..f742692e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -66,3 +66,29 @@ IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR
ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE.
+
+==============================================================================
+
+This program uses portions of Node.js source code located in src/library_path.js,
+in accordance with the terms of the MIT license. Node's license follows:
+
+ """
+ Copyright Joyent, Inc. and other Node contributors. All rights reserved.
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to
+ deal in the Software without restriction, including without limitation the
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+ sell copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in
+ all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+ IN THE SOFTWARE.
+ """ \ No newline at end of file
diff --git a/emcc b/emcc
index 8f71883d..a5b30b97 100755
--- a/emcc
+++ b/emcc
@@ -1285,6 +1285,11 @@ try:
'wctob.c',
'wctomb.c',
]],
+ ['stdlib', [
+ 'ecvt.c',
+ 'fcvt.c',
+ 'gcvt.c',
+ ]],
['string', [
'wcpcpy.c',
'wcpncpy.c',
diff --git a/src/library.js b/src/library.js
index 85d1d13f..a6a38cfe 100644
--- a/src/library.js
+++ b/src/library.js
@@ -28,7 +28,7 @@ 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__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 } });' +
@@ -42,287 +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) {
- 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);
+ },
+ standardizePath: function(path) {
+ return PATH.normalize(path);
+ },
+ 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;
},
- // Creates a folder.
createFolder: function(parent, name, canRead, canWrite) {
- var properties = {isFolder: true, isDevice: false, contents: {}};
- return FS.createObject(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.mkdir(path, mode);
},
- // 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();
+ 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);
+ 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);
+ 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 && 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,212 +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()
+ });
},
- staticInit: function () {
- // 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: {}
- };
- // Create the temporary folder, if not already created
+ lchmod: function(path, mode) {
+ VFS.chmod(path, mode, true);
+ },
+ fchmod: function(fd, mode) {
+ var stream = FS.getStream(fd);
+ if (!stream) {
+ throw new FS.ErrnoError(ERRNO_CODES.EBADF);
+ }
+ 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 (!node.node_ops.setattr) {
+ throw new FS.ErrnoError(ERRNO_CODES.EPERM);
+ }
+ 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);
+ }
+ 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 {
+ 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,
+ 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);
+ }
+ return stream;
+ },
+ close: function(stream) {
try {
- FS.createFolder('/', 'tmp', true, true);
- } catch(e) {}
- FS.createFolder('/', 'dev', true, true);
+ if (stream.stream_ops.close) {
+ stream.stream_ops.close(stream);
+ }
+ } catch (e) {
+ throw e;
+ } finally {
+ FS.closeStream(stream.fd);
+ }
},
- // 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;
+ 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);
+ }
+ },
- // 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();
+ $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);
}
- if (!result) result = '';
- input.cache = intArrayFromString(result + '\n', true);
}
- return input.cache.shift();
- };
- }
- 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));
+ }
+ // 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);
}
- };
- return fn;
- }
- if (!output) {
- stdoutOverridden = false;
- output = createSimpleOutput();
- }
- if (!output.printer) output.printer = Module['print'];
- if (!output.buffer) output.buffer = [];
- if (!error) {
- stderrOverridden = false;
- error = createSimpleOutput();
- }
- if (!error.printer) error.printer = Module['printErr'];
- if (!error.buffer) error.buffer = [];
-
- // Create the I/O devices.
- var stdin = FS.createDevice('/dev', 'stdin', input);
- stdin.isTerminal = !stdinOverridden;
- var stdout = FS.createDevice('/dev', 'stdout', null, output);
- stdout.isTerminal = !stdoutOverridden;
- var stderr = FS.createDevice('/dev', 'stderr', null, error);
- stderr.isTerminal = !stderrOverridden;
- FS.createDevice('/dev', 'tty', input, output);
- FS.createDevice('/dev', '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,
- 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
- }
- 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
+ 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
- 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') }}});
+ {
+ 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;
},
-
- 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') }}});
+ node_ops: {
},
-
- // 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;
+ 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
@@ -686,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 0;
}
+ 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.
@@ -864,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' },
@@ -960,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) {
@@ -1032,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);
- 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);
+ // 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;
}
- 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;
}
},
@@ -1074,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;
}
},
@@ -1095,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__deps: ['chmod'],
lchmod: function(path, mode) {
- return _chmod(path, mode, true);
+ path = Pointer_stringify(path);
+ try {
+ VFS.lchmod(path, mode);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
umask__deps: ['$FS'],
@@ -1205,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) {
@@ -1327,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') }}};
@@ -1339,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') }}}:
@@ -1401,15 +2288,18 @@ 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);
- return 0;
+ try {
+ VFS.allocate(stream, offset, len);
+ return 0;
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
+ }
},
// ==========================================================================
@@ -1442,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 {
@@ -1464,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) {
@@ -1480,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) {
@@ -1501,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) {
@@ -1517,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) {
@@ -1538,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;
}
},
@@ -1563,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;
@@ -1637,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 {
@@ -1651,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;
}
},
@@ -1718,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;
}
@@ -1747,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;
@@ -1760,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) {
@@ -1794,94 +2694,49 @@ 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);
- 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
+ }
+ try {
+ var slab = {{{ makeGetSlabs('buf', 'i8', true) }}};
+#if SAFE_HEAP
+#if USE_TYPED_ARRAYS == 0
+ SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually
#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;
+#endif
+ return VFS.read(stream, slab, buf, nbyte, offset);
+ } catch (e) {
+ FS.handleFSError(e);
+ return -1;
}
},
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) }}};
+#if SAFE_HEAP
+#if USE_TYPED_ARRAYS == 0
+ SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually
+#endif
+#endif
+ 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() {
@@ -1894,26 +2749,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'],
@@ -1921,19 +2762,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'],
@@ -1947,7 +2781,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);
@@ -1959,106 +2793,83 @@ 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) }}};
+#if SAFE_HEAP
+#if USE_TYPED_ARRAYS == 0
+ SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually
+#endif
+#endif
+ 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) }}};
+#if SAFE_HEAP
+#if USE_TYPED_ARRAYS == 0
+ SAFE_HEAP_FILL_HISTORY(buf, buf+nbyte, 'i8'); // VFS does not use makeSetValues, so we need to do it manually
+#endif
+#endif
+ 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) {
@@ -2186,7 +2997,7 @@ LibraryManager.library = {
},
// TODO: Implement initgroups (grp.h).
setgroups__deps: ['__setErrNo', '$ERRNO_CODES', 'sysconf'],
- setgroups: function (ngroups, gidset) {
+ setgroups: function(ngroups, gidset) {
// int setgroups(int ngroups, const gid_t *gidset);
// https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/setgroups.2.html
if (ngroups < 1 || ngroups > _sysconf({{{ cDefine('_SC_NGROUPS_MAX') }}})) {
@@ -3157,7 +3968,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) {
@@ -3170,68 +3982,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) {
@@ -3256,28 +4051,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++) {
@@ -3355,7 +4148,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;
@@ -3399,7 +4193,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--;
@@ -3419,12 +4213,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);
@@ -3437,10 +4232,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',
@@ -3448,38 +4243,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',
@@ -3491,7 +4284,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);
@@ -3535,30 +4329,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'],
@@ -3566,7 +4347,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);
@@ -3625,7 +4407,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;
}
@@ -3650,20 +4432,20 @@ LibraryManager.library = {
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 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);
- } 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) {
@@ -3803,38 +4585,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;
}
}
@@ -4389,7 +5159,7 @@ LibraryManager.library = {
// FIXME: memcpy, memmove and memset should all return their destination pointers.
- memcpy__inline: function (dest, src, num, align) {
+ memcpy__inline: function(dest, src, num, align) {
var ret = '';
#if ASSERTIONS
#if ASM_JS == 0
@@ -4402,7 +5172,7 @@ LibraryManager.library = {
memcpy__asm: true,
memcpy__sig: 'iiii',
- memcpy: function (dest, src, num) {
+ memcpy: function(dest, src, num) {
dest = dest|0; src = src|0; num = num|0;
var ret = 0;
ret = dest|0;
@@ -4954,17 +5724,7 @@ LibraryManager.library = {
(chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}});
},
isspace: function(chr) {
- switch(chr) {
- case 32:
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- return true;
- default:
- return false;
- };
+ return (chr == 32) || (chr >= 9 && chr <= 13);
},
isblank: function(chr) {
return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}};
@@ -5616,7 +6376,7 @@ LibraryManager.library = {
// http://www.digitalmars.com/archives/cplusplus/3634.html
// and mruby source code at
// https://github.com/mruby/mruby/blob/master/src/math.c
- erfc: function (x) {
+ erfc: function(x) {
var MATH_TOLERANCE = 1E-12;
var ONE_SQRTPI = 0.564189583547756287;
var a = 1;
@@ -5648,7 +6408,7 @@ LibraryManager.library = {
},
erfcf: 'erfcf',
erf__deps: ['erfc'],
- erf: function (x) {
+ erf: function(x) {
var MATH_TOLERANCE = 1E-12;
var TWO_SQRTPI = 1.128379167095512574;
var sum = x;
@@ -6987,8 +7747,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));
@@ -7835,42 +8593,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);
@@ -7879,6 +8648,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:
@@ -8007,18 +8952,19 @@ LibraryManager.library = {
* Module['webrtc']['ondisconnect']: function(peer), invoked when an existing connection is closed
* Module['webrtc']['onerror']: function(error), invoked when an error occurs
*/
- socket__deps: ['$Sockets'],
+ socket__deps: ['$FS', '$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
@@ -8132,7 +9078,7 @@ LibraryManager.library = {
};
};
- return fd;
+ return stream.fd;
},
mkport__deps: ['$Sockets'],
@@ -8151,9 +9097,9 @@ LibraryManager.library = {
// Stub: connection-oriented sockets are not supported yet.
},
- bind__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'mkport'],
+ bind__deps: ['$FS', '$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'));
@@ -8163,7 +9109,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;
}
@@ -8172,9 +9118,9 @@ LibraryManager.library = {
info.bound = true;
},
- sendmsg__deps: ['$Sockets', 'bind', '_inet_ntop_raw', 'ntohs'],
+ sendmsg__deps: ['$FS', '$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) {
@@ -8186,7 +9132,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);
@@ -8228,9 +9174,9 @@ LibraryManager.library = {
connection.send('unreliable', buffer.buffer);
},
- recvmsg__deps: ['$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'],
+ recvmsg__deps: ['$FS', '$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) {
@@ -8280,15 +9226,17 @@ LibraryManager.library = {
return ret;
},
+ shutdown__deps: ['$FS'],
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__deps: ['$FS'],
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()) {
@@ -8304,11 +9252,12 @@ LibraryManager.library = {
return 0;
},
+ accept__deps: ['$FS'],
accept: function(fd, addr, addrlen) {
// 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');
@@ -8318,6 +9267,7 @@ LibraryManager.library = {
return fd;
},
+ select__deps: ['$FS'],
select: function(nfds, readfds, writefds, exceptfds, timeout) {
// readfds are supported,
// writefds checks socket open status
@@ -8349,7 +9299,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);
@@ -8372,29 +9322,30 @@ LibraryManager.library = {
}
},
#else
- socket__deps: ['$Sockets'],
+ socket__deps: ['$FS', '$Sockets'],
socket: function(family, type, protocol) {
var stream = type == {{{ cDefine('SOCK_STREAM') }}};
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') {
@@ -8507,7 +9458,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
@@ -8533,7 +9484,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;
@@ -8541,7 +9492,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) {
@@ -8576,7 +9527,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) {
@@ -8634,7 +9585,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) {
@@ -8644,15 +9595,17 @@ LibraryManager.library = {
return _recv(fd, buf, len, flags);
},
+ shutdown__deps: ['$FS'],
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__deps: ['$FS'],
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()) {
@@ -8682,7 +9635,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');
@@ -8738,7 +9691,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 54b2f956..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;
diff --git a/src/library_openal.js b/src/library_openal.js
index d2516559..c55415b8 100644
--- a/src/library_openal.js
+++ b/src/library_openal.js
@@ -85,7 +85,7 @@ var LibraryOpenAL = {
err: 0,
src: [],
buf: [],
- interval: setInterval(function () { _updateSources(context); }, AL.QUEUE_INTERVAL)
+ interval: setInterval(function() { _updateSources(context); }, AL.QUEUE_INTERVAL)
};
AL.contexts.push(context);
return AL.contexts.length;
@@ -95,14 +95,14 @@ var LibraryOpenAL = {
},
updateSources__deps: ['updateSource'],
- updateSources: function (context) {
+ updateSources: function(context) {
for (var i = 0; i < context.src.length; i++) {
_updateSource(context.src[i]);
}
},
updateSource__deps: ['setSourceState'],
- updateSource: function (src) {
+ updateSource: function(src) {
#if OPENAL_DEBUG
var idx = AL.currentContext.src.indexOf(src);
#endif
@@ -154,7 +154,7 @@ var LibraryOpenAL = {
},
setSourceState__deps: ['updateSource', 'stopSourceQueue'],
- setSourceState: function (src, state) {
+ setSourceState: function(src, state) {
#if OPENAL_DEBUG
var idx = AL.currentContext.src.indexOf(src);
#endif
@@ -208,7 +208,7 @@ var LibraryOpenAL = {
}
},
- stopSourceQueue: function (src) {
+ stopSourceQueue: function(src) {
for (var i = 0; i < src.queue.length; i++) {
var entry = src.queue[i];
if (entry.src) {
@@ -906,7 +906,7 @@ var LibraryOpenAL = {
return 0;
},
- alGetString: function (param) {
+ alGetString: function(param) {
return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL);
},
@@ -914,7 +914,7 @@ var LibraryOpenAL = {
return 0;
},
- alcGetString: function (param) {
+ alcGetString: function(param) {
return allocate(intArrayFromString('NA'), 'i8', ALLOC_NORMAL);
},
diff --git a/src/library_path.js b/src/library_path.js
new file mode 100644
index 00000000..2c2c016a
--- /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 80c7ac07..19152646 100644
--- a/src/library_sdl.js
+++ b/src/library_sdl.js
@@ -318,7 +318,7 @@ var LibrarySDL = {
usePageCanvas: usePageCanvas,
source: source,
- isFlagSet: function (flag) {
+ isFlagSet: function(flag) {
return flags & flag;
}
};
@@ -2112,7 +2112,7 @@ var LibrarySDL = {
return -1;
},
- SDL_SetGammaRamp: function (redTable, greenTable, blueTable) {
+ SDL_SetGammaRamp: function(redTable, greenTable, blueTable) {
return -1;
},
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/preamble.js b/src/preamble.js
index 2955c885..585db832 100644
--- a/src/preamble.js
+++ b/src/preamble.js
@@ -156,6 +156,15 @@ function SAFE_HEAP_COPY_HISTORY(dest, src) {
SAFE_HEAP_ACCESS(dest, HEAP_HISTORY[dest] || null, true, false);
}
+function SAFE_HEAP_FILL_HISTORY(from, to, type) {
+#if SAFE_HEAP_LOG
+ Module.print('SAFE_HEAP fill: ' + [from, to, type]);
+#endif
+ for (var i = from; i < to; i++) {
+ HEAP_HISTORY[i] = type;
+ }
+}
+
//==========================================
#endif
diff --git a/src/settings.js b/src/settings.js
index 87ab820d..df9a830d 100644
--- a/src/settings.js
+++ b/src/settings.js
@@ -249,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.
@@ -622,8 +620,8 @@ var C_DEFINES = {
'HAVE__ULTOA': '1',
'HUGE_VAL': 'inf',
'INT_MAX': '2147483647',
- 'IPPROTO_TCP': '1',
- 'IPPROTO_UDP': '2',
+ 'IPPROTO_TCP': '6',
+ 'IPPROTO_UDP': '17',
'ITIMER_PROF': '2',
'ITIMER_REAL': '0',
'ITIMER_VIRTUAL': '1',
@@ -678,7 +676,7 @@ var C_DEFINES = {
'NOSTR': '55',
'NO_ARG': '0',
'no_argument': '0',
- 'NSIG': '32',
+ 'NSIG': '64',
'NULL': '0',
'OPTIONAL_ARG': '2',
'optional_argument': '2',
@@ -832,8 +830,8 @@ var C_DEFINES = {
'SIGPROF': '27',
'SIGPWR': '19',
'SIGQUIT': '3',
- 'SIGRTMAX': '31',
- 'SIGRTMIN': '27',
+ 'SIGRTMAX': '64',
+ 'SIGRTMIN': '32',
'SIGSEGV': '11',
'SIGSTOP': '17',
'SIGSYS': '12',
@@ -859,8 +857,8 @@ var C_DEFINES = {
'SI_QUEUE': '2',
'SI_TIMER': '3',
'SI_USER': '1',
- 'SOCK_DGRAM': '20',
- 'SOCK_STREAM': '200',
+ 'SOCK_DGRAM': '2',
+ 'SOCK_STREAM': '1',
'STDC_HEADERS': '1',
'STDERR_FILENO': '2',
'STDIN_FILENO': '0',
@@ -880,9 +878,9 @@ var C_DEFINES = {
'S_IRGRP': '0000040',
'S_IROTH': '0000004',
'S_IRUSR': '0000400',
- 'S_IRWXG': '0000040',
- 'S_IRWXO': '0000004',
- 'S_IRWXU': '0000400',
+ 'S_IRWXG': '0000070',
+ 'S_IRWXO': '0000007',
+ 'S_IRWXU': '0000700',
'S_ISGID': '0002000',
'S_ISUID': '0004000',
'S_ISVTX': '0001000',
@@ -1297,6 +1295,33 @@ var C_DEFINES = {
'___int8_t_defined': '1',
'___int_least16_t_defined': '1',
'___int_least32_t_defined': '1',
- '___int_least8_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/shell.js b/src/shell.js
index 2082eeae..bac4eaa3 100644
--- a/src/shell.js
+++ b/src/shell.js
@@ -70,8 +70,7 @@ if (ENVIRONMENT_IS_NODE) {
module.exports = Module;
}
-
-if (ENVIRONMENT_IS_SHELL) {
+else if (ENVIRONMENT_IS_SHELL) {
Module['print'] = print;
if (typeof printErr != 'undefined') Module['printErr'] = printErr; // not present in v8 or older sm
@@ -88,20 +87,7 @@ if (ENVIRONMENT_IS_SHELL) {
this['{{{ EXPORT_NAME }}}'] = Module;
}
-
-if (ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_WORKER) {
- Module['print'] = function(x) {
- console.log(x);
- };
-
- Module['printErr'] = function(x) {
- console.log(x);
- };
-
- this['{{{ EXPORT_NAME }}}'] = Module;
-}
-
-if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
+else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
Module['read'] = function(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
@@ -112,21 +98,30 @@ if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
if (typeof arguments != 'undefined') {
Module['arguments'] = arguments;
}
-}
-if (ENVIRONMENT_IS_WORKER) {
- // We can do very little here...
- var TRY_USE_DUMP = false;
- Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) {
- dump(x);
- }) : (function(x) {
- // self.postMessage(x); // enable this if you want stdout to be sent as messages
- }));
-
- Module['load'] = importScripts;
+ if (ENVIRONMENT_IS_WEB) {
+ Module['print'] = function(x) {
+ console.log(x);
+ };
+
+ Module['printErr'] = function(x) {
+ console.log(x);
+ };
+
+ this['{{{ EXPORT_NAME }}}'] = Module;
+ } else if (ENVIRONMENT_IS_WORKER) {
+ // We can do very little here...
+ var TRY_USE_DUMP = false;
+ Module['print'] = (TRY_USE_DUMP && (typeof(dump) !== "undefined") ? (function(x) {
+ dump(x);
+ }) : (function(x) {
+ // self.postMessage(x); // enable this if you want stdout to be sent as messages
+ }));
+
+ Module['load'] = importScripts;
+ }
}
-
-if (!ENVIRONMENT_IS_WORKER && !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_SHELL) {
+else {
// Unreachable because SHELL is dependant on the others
throw 'Unknown runtime environment. Where are we?';
}
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++) {
diff --git a/system/include/libc/sys/signal.h b/system/include/libc/sys/signal.h
index 49a94d80..fc9b67d5 100644
--- a/system/include/libc/sys/signal.h
+++ b/system/include/libc/sys/signal.h
@@ -298,7 +298,9 @@ int _EXFUN(sigqueue, (pid_t pid, int signo, const union sigval value));
#define SIGLOST 29 /* resource lost (eg, record-lock lost) */
#define SIGUSR1 30 /* user defined signal 1 */
#define SIGUSR2 31 /* user defined signal 2 */
-#define NSIG 32 /* signal 0 implied */
+#define NSIG 64 /* signal 0 implied */
+#define SIGRTMIN 32
+#define SIGRTMAX NSIG
#endif
#endif
diff --git a/system/include/net/if.h b/system/include/net/if.h
index dd7884aa..0ef18520 100644
--- a/system/include/net/if.h
+++ b/system/include/net/if.h
@@ -77,7 +77,26 @@ char *if_indextoname(unsigned int a, char *b);
struct if_nameindex *if_nameindex();
void if_freenameindex(struct if_nameindex *a);
-
+#define IFF_UP 0x1
+#define IFF_BROADCAST 0x2
+#define IFF_DEBUG 0x4
+#define IFF_LOOPBACK 0x8
+#define IFF_POINTOPOINT 0x10
+#define IFF_NOTRAILERS 0x20
+#define IFF_RUNNING 0x40
+#define IFF_NOARP 0x80
+#define IFF_PROMISC 0x100
+#define IFF_ALLMULTI 0x200
+#define IFF_MASTER 0x400
+#define IFF_SLAVE 0x800
+#define IFF_MULTICAST 0x1000
+#define IFF_PORTSEL 0x2000
+#define IFF_AUTOMEDIA 0x4000
+#define IFF_DYNAMIC 0x8000
+#define IFF_LOWER_UP 0x10000
+#define IFF_DORMANT 0x20000
+#define IFF_ECHO 0x40000
+#define IFF_VOLATILE (IFF_LOOPBACK|IFF_POINTOPOINT|IFF_BROADCAST|IFF_ECHO|IFF_MASTER|IFF_SLAVE|IFF_RUNNING|IFF_LOWER_UP|IFF_DORMANT)
#ifdef __cplusplus
}
diff --git a/system/include/net/netinet/in.h b/system/include/net/netinet/in.h
index fba1a7b3..1d3952f5 100644
--- a/system/include/net/netinet/in.h
+++ b/system/include/net/netinet/in.h
@@ -11,11 +11,63 @@ extern "C" {
enum {
IPPROTO_IP = 0,
-#define IPPROTO_IP IPPROTO_IP
- IPPROTO_TCP = 1,
-#define IPPROTO_TCP IPPROTO_TCP
- IPPROTO_UDP = 2,
-#define IPPROTO_UDP IPPROTO_UDP
+#define IPPROTO_IP IPPROTO_IP
+ IPPROTO_HOPOPTS = 0,
+#define IPPROTO_HOPOPTS IPPROTO_HOPOPTS
+ IPPROTO_ICMP = 1,
+#define IPPROTO_ICMP IPPROTO_ICMP
+ IPPROTO_IGMP = 2,
+#define IPPROTO_IGMP IPPROTO_IGMP
+ IPPROTO_IPIP = 4,
+#define IPPROTO_IPIP IPPROTO_IPIP
+ IPPROTO_TCP = 6,
+#define IPPROTO_TCP IPPROTO_TCP
+ IPPROTO_EGP = 8,
+#define IPPROTO_EGP IPPROTO_EGP
+ IPPROTO_PUP = 12,
+#define IPPROTO_PUP IPPROTO_PUP
+ IPPROTO_UDP = 17,
+#define IPPROTO_UDP IPPROTO_UDP
+ IPPROTO_IDP = 22,
+#define IPPROTO_IDP IPPROTO_IDP
+ IPPROTO_TP = 29,
+#define IPPROTO_TP IPPROTO_TP
+ IPPROTO_DCCP = 33,
+#define IPPROTO_DCCP IPPROTO_DCCP
+ IPPROTO_IPV6 = 41,
+#define IPPROTO_IPV6 IPPROTO_IPV6
+ IPPROTO_ROUTING = 43,
+#define IPPROTO_ROUTING IPPROTO_ROUTING
+ IPPROTO_FRAGMENT = 44,
+#define IPPROTO_FRAGMENT IPPROTO_FRAGMENT
+ IPPROTO_RSVP = 46,
+#define IPPROTO_RSVP IPPROTO_RSVP
+ IPPROTO_GRE = 47,
+#define IPPROTO_GRE IPPROTO_GRE
+ IPPROTO_ESP = 50,
+#define IPPROTO_ESP IPPROTO_ESP
+ IPPROTO_AH = 51,
+#define IPPROTO_AH IPPROTO_AH
+ IPPROTO_ICMPV6 = 58,
+#define IPPROTO_ICMPV6 IPPROTO_ICMPV6
+ IPPROTO_NONE = 59,
+#define IPPROTO_NONE IPPROTO_NONE
+ IPPROTO_DSTOPTS = 60,
+#define IPPROTO_DSTOPTS IPPROTO_DSTOPTS
+ IPPROTO_MTP = 92,
+#define IPPROTO_MTP IPPROTO_MTP
+ IPPROTO_ENCAP = 98,
+#define IPPROTO_ENCAP IPPROTO_ENCAP
+ IPPROTO_PIM = 103,
+#define IPPROTO_PIM IPPROTO_PIM
+ IPPROTO_COMP = 108,
+#define IPPROTO_COMP IPPROTO_COMP
+ IPPROTO_SCTP = 132,
+#define IPPROTO_SCTP IPPROTO_SCTP
+ IPPROTO_UDPLITE = 136,
+#define IPPROTO_UDPLITE IPPROTO_UDPLITE
+ IPPROTO_RAW = 255,
+#define IPPROTO_RAW IPPROTO_RAW
IPPROTO_MAX
};
@@ -75,6 +127,11 @@ struct ip_mreq {
struct in_addr imr_interface;
};
+#define IP_PMTUDISC_DONT 0
+#define IP_PMTUDISC_WANT 1
+#define IP_PMTUDISC_DO 2
+#define IP_PMTUDISC_PROBE 3
+
#define IP_MULTICAST_IF 32
#define IP_MULTICAST_TTL 33
#define IP_MULTICAST_LOOP 34
diff --git a/system/include/netdb.h b/system/include/netdb.h
index 48acdcc4..df74a117 100644
--- a/system/include/netdb.h
+++ b/system/include/netdb.h
@@ -11,6 +11,20 @@ extern "C" {
#define NO_DATA 4
#define NO_ADDRESS 5
+#define AI_PASSIVE 0x0001
+#define AI_CANONNAME 0x0002
+#define AI_NUMERICHOST 0x0004
+#define AI_V4MAPPED 0x0008
+#define AI_ALL 0x0010
+#define AI_ADDRCONFIG 0x0020
+#ifdef __USE_GNU
+# define AI_IDN 0x0040
+# define AI_CANONIDN 0x0080
+# define AI_IDN_ALLOW_UNASSIGNED 0x0100
+# define AI_IDN_USE_STD3_ASCII_RULES 0x0200
+#endif
+#define AI_NUMERICSERV 0x0400
+
#define EAI_ADDRFAMILY 1
#define EAI_AGAIN 2
#define EAI_BADFLAGS 3
@@ -47,6 +61,15 @@ extern "C" {
#define IP_PASSSEC 18
#define IP_TRANSPARENT 19
+#define NI_MAXHOST 1025
+#define NI_MAXSERV 32
+
+#define NI_NOFQDN 0x00000001
+#define NI_NUMERICHOST 0x00000002
+#define NI_NAMEREQD 0x00000004
+#define NI_NUMERICSERV 0x00000008
+#define NI_DGRAM 0x00000010
+
typedef int socklen_t;
struct addrinfo
@@ -85,6 +108,19 @@ const char* hstrerror(int err);
extern int h_errno;
+struct servent {
+ char *s_name;
+ char **s_aliases;
+ int s_port;
+ char *s_proto;
+};
+
+struct servent *getservent(void);
+struct servent *getservbyname(const char *name, const char *proto);
+struct servent *getservbyport(int port, const char *proto);
+void setservent(int stayopen);
+void endservent(void);
+
#include <netinet/in.h>
#ifdef __cplusplus
diff --git a/system/include/sys/ioctl.h b/system/include/sys/ioctl.h
index c54d4ccc..047329cb 100644
--- a/system/include/sys/ioctl.h
+++ b/system/include/sys/ioctl.h
@@ -5,9 +5,6 @@
extern "C" {
#endif
-#define SIOCGIFCONF 1 // bogus value
-#define SIOCGIFNETMASK 2 // bogus value
-
#define TIOCGSIZE 80 // bogus
#define TIOCGWINSZ 80 // bogus
@@ -19,6 +16,61 @@ int ioctl(int d, int request, ...);
#define SO_RCVTIMEO 1000
#define SO_SNDTIMEO 2000
+#define SIOCADDRT 0x890B
+#define SIOCDELRT 0x890C
+#define SIOCRTMSG 0x890D
+#define SIOCGIFNAME 0x8910
+#define SIOCSIFLINK 0x8911
+#define SIOCGIFCONF 0x8912
+#define SIOCGIFFLAGS 0x8913
+#define SIOCSIFFLAGS 0x8914
+#define SIOCGIFADDR 0x8915
+#define SIOCSIFADDR 0x8916
+#define SIOCGIFDSTADDR 0x8917
+#define SIOCSIFDSTADDR 0x8918
+#define SIOCGIFBRDADDR 0x8919
+#define SIOCSIFBRDADDR 0x891a
+#define SIOCGIFNETMASK 0x891b
+#define SIOCSIFNETMASK 0x891c
+#define SIOCGIFMETRIC 0x891d
+#define SIOCSIFMETRIC 0x891e
+#define SIOCGIFMEM 0x891f
+#define SIOCSIFMEM 0x8920
+#define SIOCGIFMTU 0x8921
+#define SIOCSIFMTU 0x8922
+#define SIOCSIFNAME 0x8923
+#define SIOCSIFHWADDR 0x8924
+#define SIOCGIFENCAP 0x8925
+#define SIOCSIFENCAP 0x8926
+#define SIOCGIFHWADDR 0x8927
+#define SIOCGIFSLAVE 0x8929
+#define SIOCSIFSLAVE 0x8930
+#define SIOCADDMULTI 0x8931
+#define SIOCDELMULTI 0x8932
+#define SIOCGIFINDEX 0x8933
+#define SIOGIFINDEX SIOCGIFINDEX
+#define SIOCSIFPFLAGS 0x8934
+#define SIOCGIFPFLAGS 0x8935
+#define SIOCDIFADDR 0x8936
+#define SIOCSIFHWBROADCAST 0x8937
+#define SIOCGIFCOUNT 0x8938
+#define SIOCGIFBR 0x8940
+#define SIOCSIFBR 0x8941
+#define SIOCGIFTXQLEN 0x8942
+#define SIOCSIFTXQLEN 0x8943
+#define SIOCDARP 0x8953
+#define SIOCGARP 0x8954
+#define SIOCSARP 0x8955
+#define SIOCDRARP 0x8960
+#define SIOCGRARP 0x8961
+#define SIOCSRARP 0x8962
+#define SIOCGIFMAP 0x8970
+#define SIOCSIFMAP 0x8971
+#define SIOCADDDLCI 0x8980
+#define SIOCDELDLCI 0x8981
+#define SIOCDEVPRIVATE 0x89F0
+#define SIOCPROTOPRIVATE 0x89E0
+
#ifdef __cplusplus
}
#endif
diff --git a/system/include/sys/select.h b/system/include/sys/select.h
index a5c73147..d6957fea 100644
--- a/system/include/sys/select.h
+++ b/system/include/sys/select.h
@@ -1,6 +1,8 @@
#ifndef _SELECT_H
#define _SELECT_H
+#include <unistd.h>
+
#ifdef __cplusplus
extern "C" {
#endif
diff --git a/system/include/sys/socket.h b/system/include/sys/socket.h
index 9650bb9a..abc0aa62 100644
--- a/system/include/sys/socket.h
+++ b/system/include/sys/socket.h
@@ -4,44 +4,196 @@
#include <netdb.h>
#include <sys/select.h>
#include <sys/uio.h>
+#include <unistd.h>
#ifdef __cplusplus
extern "C" {
#endif
-// Note that the values of these constants are mostly arbitrary numbers.
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define SOCK_RAW 3
+#define SOCK_RDM 4
+#define SOCK_SEQPACKET 5
+#define SOCK_DCCP 6
+#define SOCK_PACKET 10
+#define SOCK_CLOEXEC 02000000
+#define SOCK_NONBLOCK 04000
+
+#define PF_UNSPEC 0
+#define PF_LOCAL 1
+#define PF_UNIX PF_LOCAL
+#define PF_FILE PF_LOCAL
+#define PF_INET 2
+#define PF_AX25 3
+#define PF_IPX 4
+#define PF_APPLETALK 5
+#define PF_NETROM 6
+#define PF_BRIDGE 7
+#define PF_ATMPVC 8
+#define PF_X25 9
+#define PF_INET6 10
+#define PF_ROSE 11
+#define PF_DECnet 12
+#define PF_NETBEUI 13
+#define PF_SECURITY 14
+#define PF_KEY 15
+#define PF_NETLINK 16
+#define PF_ROUTE PF_NETLINK
+#define PF_PACKET 17
+#define PF_ASH 18
+#define PF_ECONET 19
+#define PF_ATMSVC 20
+#define PF_RDS 21
+#define PF_SNA 22
+#define PF_IRDA 23
+#define PF_PPPOX 24
+#define PF_WANPIPE 25
+#define PF_LLC 26
+#define PF_CAN 29
+#define PF_TIPC 30
+#define PF_BLUETOOTH 31
+#define PF_IUCV 32
+#define PF_RXRPC 33
+#define PF_ISDN 34
+#define PF_PHONET 35
+#define PF_IEEE802154 36
+#define PF_CAIF 37
+#define PF_ALG 38
+#define PF_NFC 39
+#define PF_MAX 40
+
+#define AF_UNSPEC PF_UNSPEC
+#define AF_LOCAL PF_LOCAL
+#define AF_UNIX PF_UNIX
+#define AF_FILE PF_FILE
+#define AF_INET PF_INET
+#define AF_AX25 PF_AX25
+#define AF_IPX PF_IPX
+#define AF_APPLETALK PF_APPLETALK
+#define AF_NETROM PF_NETROM
+#define AF_BRIDGE PF_BRIDGE
+#define AF_ATMPVC PF_ATMPVC
+#define AF_X25 PF_X25
+#define AF_INET6 PF_INET6
+#define AF_ROSE PF_ROSE
+#define AF_DECnet PF_DECnet
+#define AF_NETBEUI PF_NETBEUI
+#define AF_SECURITY PF_SECURITY
+#define AF_KEY PF_KEY
+#define AF_NETLINK PF_NETLINK
+#define AF_ROUTE PF_ROUTE
+#define AF_PACKET PF_PACKET
+#define AF_ASH PF_ASH
+#define AF_ECONET PF_ECONET
+#define AF_ATMSVC PF_ATMSVC
+#define AF_RDS PF_RDS
+#define AF_SNA PF_SNA
+#define AF_IRDA PF_IRDA
+#define AF_PPPOX PF_PPPOX
+#define AF_WANPIPE PF_WANPIPE
+#define AF_LLC PF_LLC
+#define AF_CAN PF_CAN
+#define AF_TIPC PF_TIPC
+#define AF_BLUETOOTH PF_BLUETOOTH
+#define AF_IUCV PF_IUCV
+#define AF_RXRPC PF_RXRPC
+#define AF_ISDN PF_ISDN
+#define AF_PHONET PF_PHONET
+#define AF_IEEE802154 PF_IEEE802154
+#define AF_CAIF PF_CAIF
+#define AF_ALG PF_ALG
+#define AF_NFC PF_NFC
+#define AF_MAX PF_MAX
+
#define SOMAXCONN 128
-#define PF_LOCAL 1
-#define PF_UNIX PF_LOCAL
-#define PF_INET 2
-#define SO_BROADCAST 6
-#define AF_UNIX PF_UNIX
-
-#define AF_UNSPEC 0
-#define SOCK_STREAM 200
-#define SOL_SOCKET 50
-#define SO_ERROR 10
-#define SOCK_DGRAM 20
-#define SO_REUSEADDR 30
-#define SO_SNDBUF 40
-#define SO_RCVBUF 60
-#define SO_LINGER 70
-#define SO_NOSIGPIPE 80
-#define SO_KEEPALIVE 90
-#define SO_OOBINLINE 100
-#define SO_NO_CHECK 110
-#define SO_PRIORITY 120
-#define SO_LINGER 130
-#define SO_BSDCOMPAT 140
+
+#define SOL_SOCKET 1
+
+#define SO_DEBUG 1
+#define SO_REUSEADDR 2
+#define SO_TYPE 3
+#define SO_ERROR 4
+#define SO_DONTROUTE 5
+#define SO_BROADCAST 6
+#define SO_SNDBUF 7
+#define SO_RCVBUF 8
+#define SO_SNDBUFFORCE 32
+#define SO_RCVBUFFORCE 33
+#define SO_KEEPALIVE 9
+#define SO_OOBINLINE 10
+#define SO_NO_CHECK 11
+#define SO_PRIORITY 12
+#define SO_LINGER 13
+#define SO_BSDCOMPAT 14
+#ifndef SO_PASSCRED
+#define SO_PASSCRED 16
+#define SO_PEERCRED 17
+#define SO_RCVLOWAT 18
+#define SO_SNDLOWAT 19
+#define SO_RCVTIMEO 20
+#define SO_SNDTIMEO 21
+#endif
+#define SO_SECURITY_AUTHENTICATION 22
+#define SO_SECURITY_ENCRYPTION_TRANSPORT 23
+#define SO_SECURITY_ENCRYPTION_NETWORK 24
+#define SO_BINDTODEVICE 25
+#define SO_ATTACH_FILTER 26
+#define SO_DETACH_FILTER 27
+#define SO_PEERNAME 28
+#define SO_TIMESTAMP 29
+#define SCM_TIMESTAMP SO_TIMESTAMP
+#define SO_ACCEPTCONN 30
+#define SO_PEERSEC 31
+#define SO_PASSSEC 34
+#define SO_TIMESTAMPNS 35
+#define SCM_TIMESTAMPNS SO_TIMESTAMPNS
+#define SO_MARK 36
+#define SO_TIMESTAMPING 37
+#define SCM_TIMESTAMPING SO_TIMESTAMPING
+#define SO_PROTOCOL 38
+#define SO_DOMAIN 39
+#define SO_RXQ_OVFL 40
+
+#if __BSD_VISIBLE
+#define SO_NOSIGPIPE 0x0800
+#endif
+
+#define MSG_OOB 0x01
+#define MSG_PEEK 0x02
+#define MSG_DONTROUTE 0x04
+#ifdef __USE_GNU
+# define MSG_TRYHARD MSG_DONTROUTE
+#endif
+#define MSG_CTRUNC 0x08
+#define MSG_PROXY 0x10
+#define MSG_TRUNC 0x20
+#define MSG_DONTWAIT 0x40
+#define MSG_EOR 0x80
+#define MSG_WAITALL 0x100
+#define MSG_FIN 0x200
+#define MSG_SYN 0x400
+#define MSG_CONFIRM 0x800
+#define MSG_RST 0x1000
+#define MSG_ERRQUEUE 0x2000
+#define MSG_NOSIGNAL 0x4000
+#define MSG_MORE 0x8000
+#define MSG_WAITFORONE 0x10000
+#define MSG_CMSG_CLOEXEC 0x40000000
#define SHUT_RD 0
#define SHUT_WR 1
#define SHUT_RDWR 2
+#define FIOSETOWN 0x8901
+#define SIOCSPGRP 0x8902
+#define FIOGETOWN 0x8903
+#define SIOCGPGRP 0x8904
+#define SIOCATMARK 0x8905
+#define SIOCGSTAMP 0x8906
+#define SIOCGSTAMPNS 0x8907
+
typedef unsigned int sa_family_t;
-#define AF_INET PF_INET
-#define AF_INET6 6
-#define PF_INET6 AF_INET6
struct sockaddr {
sa_family_t sa_family;
@@ -87,13 +239,6 @@ struct linger {
int l_linger;
};
-#define SIOCATMARK 0x8905
-
-#define SOCK_RAW 111
-#define SOCK_SEQPACKET 555
-
-#define PF_APPLETALK 5
-
#ifdef __cplusplus
}
#endif
diff --git a/system/lib/libc/musl/src/stdlib/ecvt.c b/system/lib/libc/musl/src/stdlib/ecvt.c
new file mode 100644
index 00000000..79c3de63
--- /dev/null
+++ b/system/lib/libc/musl/src/stdlib/ecvt.c
@@ -0,0 +1,19 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+char *ecvt(double x, int n, int *dp, int *sign)
+{
+ static char buf[16];
+ char tmp[32];
+ int i, j;
+
+ if (n-1U > 15) n = 15;
+ sprintf(tmp, "%.*e", n-1, x);
+ i = *sign = (tmp[0]=='-');
+ for (j=0; tmp[i]!='e'; j+=(tmp[i++]!='.'))
+ buf[j] = tmp[i];
+ buf[j] = 0;
+ *dp = atoi(tmp+i+1)+1;
+
+ return buf;
+}
diff --git a/system/lib/libc/musl/src/stdlib/fcvt.c b/system/lib/libc/musl/src/stdlib/fcvt.c
new file mode 100644
index 00000000..f90928fe
--- /dev/null
+++ b/system/lib/libc/musl/src/stdlib/fcvt.c
@@ -0,0 +1,25 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+char *fcvt(double x, int n, int *dp, int *sign)
+{
+ char tmp[1500];
+ int i, lz;
+
+ if (n > 1400U) n = 1400;
+ sprintf(tmp, "%.*f", n, x);
+ i = (tmp[0] == '-');
+ if (tmp[i] == '0') lz = strspn(tmp+i+2, "0");
+ else lz = -(int)strcspn(tmp+i, ".");
+
+ if (n<=lz) {
+ *sign = i;
+ *dp = 1;
+ if (n>14U) n = 14;
+ return "000000000000000"+14-n;
+ }
+
+ return ecvt(x, n-lz, dp, sign);
+}
diff --git a/system/lib/libc/musl/src/stdlib/gcvt.c b/system/lib/libc/musl/src/stdlib/gcvt.c
new file mode 100644
index 00000000..6c075e25
--- /dev/null
+++ b/system/lib/libc/musl/src/stdlib/gcvt.c
@@ -0,0 +1,8 @@
+#include <stdlib.h>
+#include <stdio.h>
+
+char *gcvt(double x, int n, char *b)
+{
+ sprintf(b, "%.*g", n, x);
+ return b;
+}
diff --git a/system/lib/libcextra.symbols b/system/lib/libcextra.symbols
index 42e66b51..a365271d 100644
--- a/system/lib/libcextra.symbols
+++ b/system/lib/libcextra.symbols
@@ -1,4 +1,7 @@
T btowc
+ T ecvt
+ T fcvt
+ T gcvt
T iswalnum
T iswalpha
T iswblank
diff --git a/tests/filesystem/src.js b/tests/filesystem/src.js
index 801b45d6..ceea348d 100644
--- a/tests/filesystem/src.js
+++ b/tests/filesystem/src.js
@@ -8,6 +8,7 @@ FS.createDevice('/def', 'deviceB', function() {}, function() {});
FS.createLink('/abc', 'localLink', '123', true, true);
FS.createLink('/abc', 'rootLink', '/', true, true);
FS.createLink('/abc', 'relativeLink', '../def', true, true);
+FS.ignorePermissions = false;
function explore(path) {
Module.print(path);
diff --git a/tests/glbook/CH02_HelloTriangle.png b/tests/glbook/CH02_HelloTriangle.png
index d6351e2a..7b8c176e 100644
--- a/tests/glbook/CH02_HelloTriangle.png
+++ b/tests/glbook/CH02_HelloTriangle.png
Binary files differ
diff --git a/tests/glbook/CH08_SimpleVertexShader.png b/tests/glbook/CH08_SimpleVertexShader.png
index 84b276fd..6711784d 100644
--- a/tests/glbook/CH08_SimpleVertexShader.png
+++ b/tests/glbook/CH08_SimpleVertexShader.png
Binary files differ
diff --git a/tests/glbook/CH09_SimpleTexture2D.png b/tests/glbook/CH09_SimpleTexture2D.png
index e33539e9..7cb01698 100644
--- a/tests/glbook/CH09_SimpleTexture2D.png
+++ b/tests/glbook/CH09_SimpleTexture2D.png
Binary files differ
diff --git a/tests/glbook/CH09_TextureCubemap.png b/tests/glbook/CH09_TextureCubemap.png
index b592b047..2ca61b63 100644
--- a/tests/glbook/CH09_TextureCubemap.png
+++ b/tests/glbook/CH09_TextureCubemap.png
Binary files differ
diff --git a/tests/glbook/CH09_TextureWrap.png b/tests/glbook/CH09_TextureWrap.png
index 3367e254..1fb5a319 100644
--- a/tests/glbook/CH09_TextureWrap.png
+++ b/tests/glbook/CH09_TextureWrap.png
Binary files differ
diff --git a/tests/glbook/CH10_MultiTexture.png b/tests/glbook/CH10_MultiTexture.png
index 8e006eb3..dc7a0ed6 100644
--- a/tests/glbook/CH10_MultiTexture.png
+++ b/tests/glbook/CH10_MultiTexture.png
Binary files differ
diff --git a/tests/glbook/CH13_ParticleSystem.png b/tests/glbook/CH13_ParticleSystem.png
index 4b69414f..e0e4b31b 100644
--- a/tests/glbook/CH13_ParticleSystem.png
+++ b/tests/glbook/CH13_ParticleSystem.png
Binary files differ
diff --git a/tests/htmltest.png b/tests/htmltest.png
index 980245ee..36e87dd8 100644
--- a/tests/htmltest.png
+++ b/tests/htmltest.png
Binary files differ
diff --git a/tests/runner.py b/tests/runner.py
index 86ae5bdb..14de2db0 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -16,7 +16,7 @@ so you may prefer to use fewer cores here.
'''
from subprocess import Popen, PIPE, STDOUT
-import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, re, difflib, webbrowser, hashlib, threading, platform, BaseHTTPServer, multiprocessing, functools, stat
+import os, unittest, tempfile, shutil, time, inspect, sys, math, glob, re, difflib, webbrowser, hashlib, threading, platform, BaseHTTPServer, multiprocessing, functools, stat, string
if len(sys.argv) == 1:
print '''
@@ -353,7 +353,10 @@ process(sys.argv[1])
build_dir = self.get_build_dir()
output_dir = self.get_dir()
- cache_name = name + cache_name_extra + (self.env.get('EMCC_LLVM_TARGET') or '')
+ cache_name = name + str(Building.COMPILER_TEST_OPTS) + cache_name_extra + (self.env.get('EMCC_LLVM_TARGET') or '')
+
+ valid_chars = "_%s%s" % (string.ascii_letters, string.digits)
+ cache_name = ''.join([(c if c in valid_chars else '_') for c in cache_name])
if self.library_cache is not None:
if cache and self.library_cache.get(cache_name):
@@ -1984,6 +1987,24 @@ Succeeded!
self.do_run(src, "1.0 2.0 -1.0 -2.0 2.0 3.0 -2.0 -3.0 "
"1 2 -1 -2 2 2 -2 -2")
+ # This example borrowed from MSDN documentation
+ def test_fcvt(self):
+ src = '''
+ #include <stdlib.h>
+ #include <stdio.h>
+
+ int main() {
+ int decimal, sign;
+ char *buffer;
+ double source = 3.1415926535;
+
+ buffer = fcvt(source, 7, &decimal, &sign);
+ printf("source: %2.10f buffer: '%s' decimal: %d sign: %d\\n",
+ source, buffer, decimal, sign);
+ }
+ '''
+ self.do_run(src, "source: 3.1415926535 buffer: '31415927' decimal: 1 sign: 0");
+
def test_llrint(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
src = r'''
@@ -3591,33 +3612,8 @@ Exiting setjmp function, level: 0, prev_jmp: -1
self.do_run(src, 'z:1*', force_c=True)
def test_rename(self):
- src = '''
- #include <stdio.h>
- #include <sys/stat.h>
- #include <sys/types.h>
- #include <assert.h>
-
- int main() {
- int err;
- FILE* fid;
-
- err = mkdir("/foo", 0777);
- err = mkdir("/bar", 0777);
- fid = fopen("/foo/bar", "w+");
- fclose(fid);
-
- err = rename("/foo/bar", "/foo/bar2");
- printf("%d\\n", err);
-
- err = rename("/foo", "/foo/foo");
- printf("%d\\n", err);
-
- err = rename("/foo", "/bar/foo");
- printf("%d\\n", err);
- return 0;
- }
- '''
- self.do_run(src, '0\n-1\n0\n', force_c=True)
+ src = open(path_from_root('tests', 'stdio', 'test_rename.c'), 'r').read()
+ self.do_run(src, 'success', force_c=True)
def test_alloca_stack(self):
if self.emcc_args is None: return # too slow in other modes
@@ -7114,7 +7110,7 @@ def process(filename):
other.close()
src = open(path_from_root('tests', 'files.cpp'), 'r').read()
- self.do_run(src, 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n',
+ self.do_run(src, ('size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\ntexte\n', 'size: 7\ndata: 100,-56,50,25,10,77,123\nloop: 100 -56 50 25 10 77 123 \ninput:hi there!\ntexto\ntexte\n$\n5 : 10,30,20,11,88\nother=some data.\nseeked=me da.\nseeked=ata.\nseeked=ta.\nfscanfed: 10 - hello\nok.\n'),
post_build=post, extra_emscripten_args=['-H', 'libc/fcntl.h'])
def test_files_m(self):
@@ -7146,7 +7142,7 @@ def process(filename):
return 0;
}
'''
- self.do_run(src, 'got: 35\ngot: 45\ngot: 25\ngot: 15\nisatty? 0,0,1\n', post_build=post)
+ self.do_run(src, ('got: 35\ngot: 45\ngot: 25\ngot: 15\nisatty? 0,0,1\n', 'isatty? 0,0,1\ngot: 35\ngot: 45\ngot: 25\ngot: 15\n'), post_build=post)
def test_fwrite_0(self):
src = r'''
@@ -7244,17 +7240,14 @@ def process(filename):
self.do_run(src, 'success', force_c=True)
def test_stat(self):
- Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)]
src = open(path_from_root('tests', 'stat', 'test_stat.c'), 'r').read()
self.do_run(src, 'success', force_c=True)
def test_stat_chmod(self):
- Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)]
src = open(path_from_root('tests', 'stat', 'test_chmod.c'), 'r').read()
self.do_run(src, 'success', force_c=True)
def test_stat_mknod(self):
- Building.COMPILER_TEST_OPTS += ['-DUSE_OLD_FS='+str(Settings.USE_OLD_FS)]
src = open(path_from_root('tests', 'stat', 'test_mknod.c'), 'r').read()
self.do_run(src, 'success', force_c=True)
@@ -7773,6 +7766,93 @@ def process(filename):
'''
self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+ def test_inet3(self):
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+ int main() {
+ char dst[64];
+ struct in_addr x, x2;
+ int *y = (int*)&x;
+ *y = 0x12345678;
+ printf("%s\n", inet_ntop(AF_INET,&x,dst,sizeof dst));
+ int r = inet_aton(inet_ntoa(x), &x2);
+ printf("%s\n", inet_ntop(AF_INET,&x2,dst,sizeof dst));
+ return 0;
+ }
+ '''
+ self.do_run(src, '120.86.52.18\n120.86.52.18\n')
+
+ def test_inet4(self):
+ if Settings.USE_TYPED_ARRAYS != 2: return self.skip('requires ta2')
+
+ src = r'''
+ #include <stdio.h>
+ #include <arpa/inet.h>
+ #include <sys/socket.h>
+
+ void test(char *test_addr){
+ char str[40];
+ struct in6_addr addr;
+ unsigned char *p = (unsigned char*)&addr;
+ int ret;
+ ret = inet_pton(AF_INET6,test_addr,&addr);
+ if(ret == -1) return;
+ if(ret == 0) return;
+ if(inet_ntop(AF_INET6,&addr,str,sizeof(str)) == NULL ) return;
+ printf("%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x:%02x%02x - %s\n",
+ p[0],p[1],p[2],p[3],p[4],p[5],p[6],p[7],p[8],p[9],p[10],p[11],p[12],p[13],p[14],p[15],str);
+ }
+ int main(){
+ test("::");
+ test("::1");
+ test("::1.2.3.4");
+ test("::17.18.19.20");
+ test("::ffff:1.2.3.4");
+ test("1::ffff");
+ test("::255.255.255.255");
+ test("0:ff00:1::");
+ test("0:ff::");
+ test("abcd::");
+ test("ffff::a");
+ test("ffff::a:b");
+ test("ffff::a:b:c");
+ test("ffff::a:b:c:d");
+ test("ffff::a:b:c:d:e");
+ test("::1:2:0:0:0");
+ test("0:0:1:2:3::");
+ test("ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
+ test("1::255.255.255.255");
+
+ //below should fail and not produce results..
+ test("1.2.3.4");
+ test("");
+ test("-");
+ }
+ '''
+ self.do_run(src,
+ "0000:0000:0000:0000:0000:0000:0000:0000 - ::\n"
+ "0000:0000:0000:0000:0000:0000:0000:0001 - ::1\n"
+ "0000:0000:0000:0000:0000:0000:0102:0304 - ::1.2.3.4\n"
+ "0000:0000:0000:0000:0000:0000:1112:1314 - ::17.18.19.20\n"
+ "0000:0000:0000:0000:0000:ffff:0102:0304 - ::ffff:1.2.3.4\n"
+ "0001:0000:0000:0000:0000:0000:0000:ffff - 1::ffff\n"
+ "0000:0000:0000:0000:0000:0000:ffff:ffff - ::255.255.255.255\n"
+ "0000:ff00:0001:0000:0000:0000:0000:0000 - 0:ff00:1::\n"
+ "0000:00ff:0000:0000:0000:0000:0000:0000 - 0:ff::\n"
+ "abcd:0000:0000:0000:0000:0000:0000:0000 - abcd::\n"
+ "ffff:0000:0000:0000:0000:0000:0000:000a - ffff::a\n"
+ "ffff:0000:0000:0000:0000:0000:000a:000b - ffff::a:b\n"
+ "ffff:0000:0000:0000:0000:000a:000b:000c - ffff::a:b:c\n"
+ "ffff:0000:0000:0000:000a:000b:000c:000d - ffff::a:b:c:d\n"
+ "ffff:0000:0000:000a:000b:000c:000d:000e - ffff::a:b:c:d:e\n"
+ "0000:0000:0000:0001:0002:0000:0000:0000 - ::1:2:0:0:0\n"
+ "0000:0000:0001:0002:0003:0000:0000:0000 - 0:0:1:2:3::\n"
+ "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff - ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff\n"
+ "0001:0000:0000:0000:0000:0000:ffff:ffff - 1::ffff:ffff\n"
+ )
+
def test_gethostbyname(self):
if Settings.USE_TYPED_ARRAYS != 2: return self.skip("assume t2 in gethostbyname")
@@ -9104,24 +9184,14 @@ def process(filename):
}
int main(int argc, char **argv) {
- // keep them alive
- if (argc == 10) return get_int();
- if (argc == 11) return get_float();
- if (argc == 12) return get_string()[0];
- if (argc == 13) print_int(argv[0][0]);
- if (argc == 14) print_float(argv[0][0]);
- if (argc == 15) print_string(argv[0]);
- if (argc == 16) pointer((int*)argv[0]);
- if (argc % 17 == 12) return multi(argc, float(argc)/2, argc+1, argv[0]);
- // return 0;
- exit(0);
+ return 0;
}
'''
post = '''
def process(filename):
src = \'\'\'
- var Module = { noInitialRun: true };
+ var Module = { 'noInitialRun': true };
\'\'\' + open(filename, 'r').read() + \'\'\'
Module.addOnExit(function () {
Module.print('*');
@@ -10994,6 +11064,24 @@ f.close()
voidfunc sidey(voidfunc f) { return f; }
''', 'hello from funcptr\n')
+ # function pointers with 'return' in the name
+ test('fp2', 'typedef void (*voidfunc)();', r'''
+ #include <stdio.h>
+ #include "header.h"
+ int sidey(voidfunc f);
+ void areturn0() { printf("hello 0\n"); }
+ void areturn1() { printf("hello 1\n"); }
+ void areturn2() { printf("hello 2\n"); }
+ int main(int argc, char **argv) {
+ voidfunc table[3] = { areturn0, areturn1, areturn2 };
+ table[sidey(NULL)]();
+ return 0;
+ }
+ ''', '''
+ #include "header.h"
+ int sidey(voidfunc f) { if (f) f(); return 1; }
+ ''', 'hello 1\n')
+
# Global initializer
test('global init', '', r'''
#include <stdio.h>
@@ -11143,7 +11231,7 @@ f.close()
def test_outline(self):
- def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp'):
+ def test(name, src, libs, expected, expected_ranges, args=[], suffix='cpp', test_sizes=True):
print name
def measure_funcs(filename):
@@ -11183,21 +11271,23 @@ f.close()
seen = max(measure_funcs('test.js').values())
high = expected_ranges[outlining_limit][1]
print outlining_limit, ' ', low, '<=', seen, '<=', high
- assert low <= seen <= high
-
- test('zlib', path_from_root('tests', 'zlib', 'example.c'),
- self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']),
- open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
- {
- 100: (190, 250),
- 250: (300, 330),
- 500: (250, 310),
- 1000: (230, 300),
- 2000: (380, 450),
- 5000: (800, 1100),
- 0: (1500, 1800)
- },
- args=['-I' + path_from_root('tests', 'zlib')], suffix='c')
+ if test_sizes: assert low <= seen <= high
+
+ for test_opts, test_sizes in [([], True), (['-O2'], False)]:
+ Building.COMPILER_TEST_OPTS = test_opts
+ test('zlib', path_from_root('tests', 'zlib', 'example.c'),
+ self.get_library('zlib', os.path.join('libz.a'), make_args=['libz.a']),
+ open(path_from_root('tests', 'zlib', 'ref.txt'), 'r').read(),
+ {
+ 100: (190, 250),
+ 250: (200, 330),
+ 500: (250, 310),
+ 1000: (230, 300),
+ 2000: (380, 450),
+ 5000: (800, 1100),
+ 0: (1500, 1800)
+ },
+ args=['-I' + path_from_root('tests', 'zlib')], suffix='c', test_sizes=test_sizes)
def test_symlink(self):
if os.name == 'nt':
@@ -12123,7 +12213,7 @@ int main(int argc, char const *argv[])
open(os.path.join(self.get_dir(), 'test.file'), 'w').write('''ay file..............,,,,,,,,,,,,,,''')
open(os.path.join(self.get_dir(), 'stdin'), 'w').write('''inter-active''')
Popen([PYTHON, EMCC, os.path.join(self.get_dir(), 'files.cpp'), '-c']).communicate()
- Popen([PYTHON, path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')]).communicate(input)[0]
+ Popen([PYTHON, path_from_root('tools', 'nativize_llvm.py'), os.path.join(self.get_dir(), 'files.o')], stdout=PIPE, stderr=PIPE).communicate(input)
output = Popen([os.path.join(self.get_dir(), 'files.o.run')], stdin=open(os.path.join(self.get_dir(), 'stdin')), stdout=PIPE, stderr=PIPE).communicate()
self.assertContained('''size: 37
data: 119,97,107,97,32,119,97,107,97,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35,35
@@ -12428,6 +12518,8 @@ elif 'browser' in str(sys.argv):
''' + code
def reftest(self, expected):
+ # make sure the pngs used here have no color correction, using e.g.
+ # pngcrush -rem gAMA -rem cHRM -rem iCCP -rem sRGB infile outfile
basename = os.path.basename(expected)
shutil.copyfile(expected, os.path.join(self.get_dir(), basename))
open(os.path.join(self.get_dir(), 'reftest.js'), 'w').write('''
@@ -13718,7 +13810,7 @@ Press any key to continue.'''
def test_sdl_rotozoom(self):
shutil.copyfile(path_from_root('tests', 'screenshot.png'), os.path.join(self.get_dir(), 'screenshot.png'))
- self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'screenshot.png'], reference_slack=3)
+ self.btest('sdl_rotozoom.c', reference='sdl_rotozoom.png', args=['--preload-file', 'screenshot.png'], reference_slack=5)
def test_sdl_gfx_primitives(self):
self.btest('sdl_gfx_primitives.c', reference='sdl_gfx_primitives.png', reference_slack=1)
@@ -14136,6 +14228,8 @@ elif 'benchmark' in str(sys.argv):
total_native_times = map(lambda x: 0., range(TOTAL_TESTS))
class benchmark(RunnerCore):
+ save_dir = True
+
def print_stats(self, times, native_times, last=False, reps=TEST_REPS):
if reps == 0:
print '(no reps)'
diff --git a/tests/sdl_gfx_primitives.png b/tests/sdl_gfx_primitives.png
index 525b4f8f..706af9ae 100644
--- a/tests/sdl_gfx_primitives.png
+++ b/tests/sdl_gfx_primitives.png
Binary files differ
diff --git a/tests/sdl_maprgba.png b/tests/sdl_maprgba.png
index 4f64c7cd..caca4a8b 100644
--- a/tests/sdl_maprgba.png
+++ b/tests/sdl_maprgba.png
Binary files differ
diff --git a/tests/sdl_rotozoom.png b/tests/sdl_rotozoom.png
index 288dd303..5933754f 100644
--- a/tests/sdl_rotozoom.png
+++ b/tests/sdl_rotozoom.png
Binary files differ
diff --git a/tests/stat/test_chmod.c b/tests/stat/test_chmod.c
index bf0e2cbe..3574760b 100644
--- a/tests/stat/test_chmod.c
+++ b/tests/stat/test_chmod.c
@@ -56,11 +56,7 @@ void test() {
memset(&s, 0, sizeof s);
stat("file", &s);
-#if USE_OLD_FS
- assert(s.st_mode == (0222 | S_IFREG));
-#else
assert(s.st_mode == (0200 | S_IFREG));
-#endif
assert(s.st_ctime != lastctime);
//
@@ -74,11 +70,7 @@ void test() {
memset(&s, 0, sizeof s);
stat("file", &s);
-#if USE_OLD_FS
- assert(s.st_mode == (0000 | S_IFREG));
-#else
assert(s.st_mode == (0100 | S_IFREG));
-#endif
assert(s.st_ctime != lastctime);
//
@@ -95,11 +87,7 @@ void test() {
assert(!err);
memset(&s, 0, sizeof s);
stat("folder", &s);
-#if USE_OLD_FS
- assert(s.st_mode == (0222 | S_IFDIR));
-#else
assert(s.st_mode == (0300 | S_IFDIR));
-#endif
assert(s.st_ctime != lastctime);
//
@@ -110,11 +98,7 @@ void test() {
// make sure the file it references changed
stat("file-link", &s);
-#if USE_OLD_FS
- assert(s.st_mode == (0555 | S_IFREG));
-#else
assert(s.st_mode == (0400 | S_IFREG));
-#endif
// but the link didn't
lstat("file-link", &s);
@@ -128,19 +112,11 @@ void test() {
// make sure the file it references didn't change
stat("file-link", &s);
-#if USE_OLD_FS
- assert(s.st_mode == (0555 | S_IFREG));
-#else
assert(s.st_mode == (0400 | S_IFREG));
-#endif
// but the link did
lstat("file-link", &s);
-#if USE_OLD_FS
- assert(s.st_mode == (0555 | S_IFLNK));
-#else
assert(s.st_mode == (0500 | S_IFLNK));
-#endif
puts("success");
}
diff --git a/tests/stat/test_mknod.c b/tests/stat/test_mknod.c
index 2b8f650d..361b2315 100644
--- a/tests/stat/test_mknod.c
+++ b/tests/stat/test_mknod.c
@@ -53,15 +53,10 @@ void test() {
// mknod a character device
err = mknod("mknod-device", S_IFCHR | 0777, 123);
-#if USE_OLD_FS
- assert(err);
- assert(errno == EPERM);
-#else
assert(!err);
memset(&s, 0, sizeof s);
stat("mknod-device", &s);
assert(S_ISCHR(s.st_mode));
-#endif
#endif
diff --git a/tests/stat/test_stat.c b/tests/stat/test_stat.c
index 2c2bf53d..f59fb3c3 100644
--- a/tests/stat/test_stat.c
+++ b/tests/stat/test_stat.c
@@ -104,13 +104,10 @@ void test() {
assert(s.st_ino);
assert(S_ISCHR(s.st_mode));
assert(s.st_nlink);
-#if !USE_OLD_FS
- // old FS doesn't store proper device ids
#ifndef __APPLE__
// mac uses makedev(3, 2) for /dev/null
assert(s.st_rdev == makedev(1, 3));
#endif
-#endif
assert(!s.st_size);
assert(s.st_atime);
assert(s.st_mtime);
diff --git a/tests/stdio/test_rename.c b/tests/stdio/test_rename.c
new file mode 100644
index 00000000..20ffb513
--- /dev/null
+++ b/tests/stdio/test_rename.c
@@ -0,0 +1,107 @@
+#include <assert.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+void create_file(const char *path, const char *buffer, int mode) {
+ int fd = open(path, O_WRONLY | O_CREAT | O_EXCL, mode);
+ assert(fd >= 0);
+
+ int err = write(fd, buffer, sizeof(char) * strlen(buffer));
+ assert(err == (sizeof(char) * strlen(buffer)));
+
+ close(fd);
+}
+
+void setup() {
+ create_file("file", "abcdef", 0777);
+ mkdir("dir", 0777);
+ create_file("dir/file", "abcdef", 0777);
+ mkdir("dir/subdir", 0777);
+ mkdir("dir-readonly", 0555);
+ mkdir("dir-nonempty", 0777);
+ create_file("dir-nonempty/file", "abcdef", 0777);
+}
+
+void cleanup() {
+ // we're hulk-smashing and removing original + renamed files to
+ // make sure we get it all regardless of anything failing
+ unlink("file");
+ unlink("dir/file");
+ unlink("dir/file1");
+ unlink("dir/file2");
+ rmdir("dir/subdir");
+ rmdir("dir/subdir1");
+ rmdir("dir/subdir2");
+ rmdir("dir");
+ rmdir("dir-readonly");
+ unlink("dir-nonempty/file");
+ rmdir("dir-nonempty");
+}
+
+void test() {
+ int err;
+
+ // can't rename something that doesn't exist
+ err = rename("noexist", "dir");
+ assert(err == -1);
+ assert(errno == ENOENT);
+
+ // can't overwrite a folder with a file
+ err = rename("file", "dir");
+ assert(err == -1);
+ assert(errno == EISDIR);
+
+ // can't overwrite a file with a folder
+ err = rename("dir", "file");
+ assert(err == -1);
+ assert(errno == ENOTDIR);
+
+ // can't overwrite a non-empty folder
+ err = rename("dir", "dir-nonempty");
+ assert(err == -1);
+ assert(errno == ENOTEMPTY);
+
+ // can't create anything in a read-only directory
+ err = rename("dir", "dir-readonly/dir");
+ assert(err == -1);
+ assert(errno == EACCES);
+
+ // source should not be ancestor of target
+ err = rename("dir", "dir/somename");
+ assert(err == -1);
+ assert(errno == EINVAL);
+
+ // target should not be an ancestor of source
+ err = rename("dir/subdir", "dir");
+ assert(err == -1);
+ assert(errno == ENOTEMPTY);
+
+ // do some valid renaming
+ err = rename("dir/file", "dir/file1");
+ assert(!err);
+ err = rename("dir/file1", "dir/file2");
+ assert(!err);
+ err = access("dir/file2", F_OK);
+ assert(!err);
+ err = rename("dir/subdir", "dir/subdir1");
+ assert(!err);
+ err = rename("dir/subdir1", "dir/subdir2");
+ assert(!err);
+ err = access("dir/subdir2", F_OK);
+ assert(!err);
+
+ puts("success");
+}
+
+int main() {
+ atexit(cleanup);
+ signal(SIGABRT, cleanup);
+ setup();
+ test();
+ return EXIT_SUCCESS;
+} \ No newline at end of file
diff --git a/tools/asm_module.py b/tools/asm_module.py
index e54cfc21..bf7fa71d 100644
--- a/tools/asm_module.py
+++ b/tools/asm_module.py
@@ -49,7 +49,7 @@ class AsmModule():
# tables and exports
post_js = self.js[self.end_funcs:self.end_asm]
- ret = post_js.find('return')
+ ret = post_js.find('return ')
self.tables_js = post_js[:ret]
self.exports_js = post_js[ret:]
self.tables = self.parse_tables(self.tables_js)
diff --git a/tools/file_packager.py b/tools/file_packager.py
index 136da609..1a9d925b 100644
--- a/tools/file_packager.py
+++ b/tools/file_packager.py
@@ -206,7 +206,7 @@ for file_ in data_files:
abspath = os.path.abspath(path)
if DEBUG: print >> sys.stderr, path, abspath, curr_abspath
if not abspath.startswith(curr_abspath):
- print >> sys.stderr, 'Error: Embedding "%s" which is below the current directory. This is invalid since the current directory becomes the root that the generated code will see' % path
+ print >> sys.stderr, 'Error: Embedding "%s" which is below the current directory "%s". This is invalid since the current directory becomes the root that the generated code will see' % (path, curr_abspath)
sys.exit(1)
file_['dstpath'] = abspath[len(curr_abspath)+1:]
if os.path.isabs(path):
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 29b36cad..4192ddd1 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -2817,7 +2817,7 @@ function relocate(ast) {
var other = node[3];
if (base === 0) return other;
if (other[0] == 'num') {
- other[1] += base;
+ other[1] = (other[1] + base)|0;
return other;
} else {
node[2] = ['num', base];
@@ -3106,7 +3106,7 @@ function outline(ast) {
// Reserve an extra two spots per possible outlining: one for control flow var, the other for control flow data
// The control variables are zeroed out when calling an outlined function, and after using
// the value after they return.
- asmData.maxOutlinings = Math.round(3*measureSize(func)/sizeToOutline);
+ asmData.maxOutlinings = Math.round(3*measureSize(func)/extraInfo.sizeToOutline);
asmData.totalStackSize = stackSize + (stack.length + 2*asmData.maxOutlinings)*8;
asmData.controlStackPos = function(i) { return stackSize + (stack.length + i)*8 };
asmData.controlDataStackPos = function(i) { return stackSize + (stack.length + i)*8 + 4 };
@@ -3210,22 +3210,30 @@ function outline(ast) {
var CONTROL_BREAK = 1, CONTROL_BREAK_LABEL = 2, CONTROL_CONTINUE = 3, CONTROL_CONTINUE_LABEL = 4, CONTROL_RETURN_VOID = 5, CONTROL_RETURN_INT = 6, CONTROL_RETURN_DOUBLE = 7;
- var sizeToOutline = extraInfo.sizeToOutline;
- var level = 0;
+ var sizeToOutline = null; // customized per function and as we make progress
+ function calculateThreshold(func) {
+ sizeToOutline = extraInfo.sizeToOutline;
+ var size = measureSize(func);
+ //var desiredChunks = Math.ceil(size/extraInfo.sizeToOutline);
+ ////sizeToOutline = Math.round((extraInfo.sizeToOutline + (2*size/desiredChunks))/3);
+ //sizeToOutline = Math.round(size/desiredChunks);
+ printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + extraInfo.sizeToOutline + '), aim for ' + sizeToOutline);
+ }
+ var level = 0;
var outliningParents = {}; // function name => parent it was outlined from
function doOutline(func, asmData, stats, start, end) {
if (asmData.splitCounter === asmData.maxOutlinings) return [];
if (!extraInfo.allowCostlyOutlines) var originalStats = copy(stats);
var code = stats.slice(start, end+1);
+ var originalCodeSize = measureSize(code);
var funcSize = measureSize(func);
var outlineIndex = asmData.splitCounter++;
var newIdent = func[1] + '$' + outlineIndex;
// analyze variables, and find 'owned' variables - that only appear in the outlined code, and do not need any spill support
var codeInfo = analyzeCode(func, asmData, code);
var allCodeInfo = analyzeCode(func, asmData, func);
- //printErr(' do outline ' + [func[1], level, 'range:', start, end, 'of', stats.length, newIdent, measureSize(code), JSON.stringify(codeInfo.labels), JSON.stringify(codeInfo.breaks), JSON.stringify(codeInfo.continues)]);
var owned = { sp: 1 }; // sp is always owned, each has its own
keys(setUnion(codeInfo.reads, codeInfo.writes)).forEach(function(v) {
if (allCodeInfo.reads[v] === codeInfo.reads[v] && allCodeInfo.writes[v] === codeInfo.writes[v] && !(v in asmData.params)) {
@@ -3409,9 +3417,11 @@ function outline(ast) {
stats.splice.apply(stats, [start, end-start+1].concat(reps));
// final evaluation and processing
if (!extraInfo.allowCostlyOutlines && (measureSize(func) >= funcSize || measureSize(newFunc) >= funcSize)) {
+ //printErr('aborted outline attempt ' + [measureSize(func), measureSize(newFunc), ' one of which >= ', funcSize]);
// abort, this was pointless
stats.length = originalStats.length;
for (var i = 0; i < stats.length; i++) stats[i] = originalStats[i];
+ asmData.splitCounter--;
return [];
}
for (var v in owned) {
@@ -3426,6 +3436,8 @@ function outline(ast) {
}
}
outliningParents[newIdent] = func[1];
+ printErr('performed outline ' + [func[1], newIdent, 'code sizes (pre/post):', originalCodeSize, measureSize(code), 'overhead (w/r):', setSize(setSub(codeInfo.writes, owned)), setSize(setSub(codeInfo.reads, owned))]);
+ calculateThreshold(func);
return [newFunc];
}
@@ -3452,7 +3464,7 @@ function outline(ast) {
}
while (1) {
i--;
- calcMinIndex();
+ calcMinIndex(); // TODO: optimize
if (i < minIndex) {
// we might be done. but, if we have just outlined, do a further attempt from the beginning.
// (but only if the total costs are not extravagant)
@@ -3509,7 +3521,7 @@ function outline(ast) {
// If this is big enough to outline, but not too big (if very close to the size of the full function,
// outlining is pointless; remove stats from the end to try to achieve the good case), then outline.
// Also, try to reduce the size if it is much larger than the hoped-for size
- while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && end > i+1 && stats[end][0] !== 'begin-outline-call' && stats[end][0] !== 'end-outline-call') {
+ while ((sizeSeen > maxSize || sizeSeen > 2*sizeToOutline) && end > i && stats[end][0] !== 'begin-outline-call' && stats[end][0] !== 'end-outline-call') {
sizeSeen -= measureSize(stats[end]);
if (sizeSeen >= sizeToOutline) {
end--;
@@ -3531,12 +3543,12 @@ function outline(ast) {
});
assert(sum == 0);
// final decision and action
+ //printErr(' will try done working on sizeSeen due to ' + [(sizeSeen > maxSize || sizeSeen > 2*sizeToOutline), end > i , stats[end][0] !== 'begin-outline-call' , stats[end][0] !== 'end-outline-call'] + ' ... ' + [sizeSeen, sizeToOutline, maxSize, sizeSeen >= sizeToOutline, sizeSeen <= maxSize]);
if (sizeSeen >= sizeToOutline && sizeSeen <= maxSize) {
assert(i >= minIndex);
var newFuncs = doOutline(func, asmData, stats, i, end); // outline [i, .. ,end] inclusive
if (newFuncs.length) {
ret.push.apply(ret, newFuncs);
- printErr('performed outline on ' + func[1] + ' of ' + sizeSeen + ', func is now size ' + measureSize(func) + ' ==> ' + newFuncs[0][1]);
}
sizeSeen = 0;
end = i-1;
@@ -3566,10 +3578,10 @@ function outline(ast) {
funcs.forEach(function(func) {
var asmData = normalizeAsm(func);
var size = measureSize(func);
- if (size >= sizeToOutline) {
- printErr('trying to reduce the size of ' + func[1] + ' which is ' + size + ' (>= ' + sizeToOutline + ')');
+ if (size >= extraInfo.sizeToOutline) {
aggressiveVariableElimination(func, asmData);
flatten(func, asmData);
+ calculateThreshold(func);
analyzeFunction(func, asmData);
var stats = getStatements(func);
var ret = outlineStatements(func, asmData, stats, 0.9*size);