| //"use strict"; |
| |
| // An implementation of a libc for the web. Basically, implementations of |
| // the various standard C libraries, that can be called from compiled code, |
| // and work using the actual JavaScript environment. |
| // |
| // We search the Library object when there is an external function. If the |
| // entry in the Library is a function, we insert it. If it is a string, we |
| // do another lookup in the library (a simple way to write a function once, |
| // if it can be called by different names). We also allow dependencies, |
| // using __deps. Initialization code to be run after allocating all |
| // global constants can be defined by __postset. |
| // |
| // Note that the full function name will be '_' + the name in the Library |
| // object. For convenience, the short name appears here. Note that if you add a |
| // new function with an '_', it will not be found. |
| |
| // Memory allocated during startup, in postsets, should only be ALLOC_STATIC |
| |
| LibraryManager.library = { |
| // ========================================================================== |
| // File system base. |
| // ========================================================================== |
| |
| // keep this low in memory, because we flatten arrays with them in them |
| stdin: 'allocate(1, "i32*", ALLOC_STATIC)', |
| stdout: 'allocate(1, "i32*", ALLOC_STATIC)', |
| stderr: 'allocate(1, "i32*", ALLOC_STATIC)', |
| _impure_ptr: 'allocate(1, "i32*", ALLOC_STATIC)', |
| |
| $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr', '_impure_ptr'], |
| $FS__postset: '__ATINIT__.unshift({ func: function() { if (!Module["noFSInit"] && !FS.init.initialized) FS.init() } });' + |
| '__ATMAIN__.push({ func: function() { FS.ignorePermissions = false } });' + |
| '__ATEXIT__.push({ func: function() { FS.quit() } });' + |
| // export some names through closure |
| 'Module["FS_createFolder"] = FS.createFolder;' + |
| 'Module["FS_createPath"] = FS.createPath;' + |
| 'Module["FS_createDataFile"] = FS.createDataFile;' + |
| 'Module["FS_createPreloadedFile"] = FS.createPreloadedFile;' + |
| 'Module["FS_createLazyFile"] = FS.createLazyFile;' + |
| '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. |
| 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 |
| // 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 |
| } |
| } |
| } |
| // 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(); |
| } |
| FS.streams[fd] = stream; |
| return fd; |
| }, |
| removeFileHandle: function(fd) { |
| FS.streams[fd] = null; |
| }, |
| 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]; |
| } |
| 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); |
| } |
| } |
| return absolute.length == 1 ? '/' : absolute.join('/'); |
| }, |
| // Analyzes a relative or absolute path returning a description, containing: |
| // isRoot: Whether the path points to the root. |
| // exists: Whether the object at the path exists. |
| // error: If !exists, this will contain the errno code of the cause. |
| // name: The base name of the object (null if !parentExists). |
| // path: The absolute path to the object, with all links resolved. |
| // object: The filesystem record of the object referenced by the path. |
| // parentExists: Whether the parent of the object exist and is a folder. |
| // parentPath: The absolute path to the parent folder. |
| // parentObject: The filesystem record of the parent folder. |
| analyzePath: function(path, dontResolveLastLink, linksVisited) { |
| var ret = { |
| isRoot: false, |
| exists: false, |
| error: 0, |
| name: null, |
| path: null, |
| object: null, |
| parentExists: false, |
| parentPath: null, |
| parentObject: null |
| }; |
| #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 + '}'); |
| } |
| #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; |
| } |
| 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; |
| }, |
| // Finds the file system object at a given path. If dontResolveLastLink is |
| // set to true and the object is a symbolic link, it will be returned as is |
| // instead of being resolved. Links embedded in the path are still resolved. |
| findObject: function(path, dontResolveLastLink) { |
| FS.ensureRoot(); |
| var ret = FS.analyzePath(path, dontResolveLastLink); |
| if (ret.exists) { |
| return ret.object; |
| } else { |
| ___setErrNo(ret.error); |
| return null; |
| } |
| }, |
| // Creates a file system record: file, link, device or folder. |
| createObject: function(parent, name, properties, canRead, canWrite) { |
| #if 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.'); |
| } |
| if (!parent.isFolder) { |
| ___setErrNo(ERRNO_CODES.ENOTDIR); |
| throw new Error('Parent must be a folder.'); |
| } |
| if (!parent.write && !FS.ignorePermissions) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| throw new Error('Parent folder must be writeable.'); |
| } |
| if (!name || name == '.' || name == '..') { |
| ___setErrNo(ERRNO_CODES.ENOENT); |
| throw new Error('Name must not be empty.'); |
| } |
| if (parent.contents.hasOwnProperty(name)) { |
| ___setErrNo(ERRNO_CODES.EEXIST); |
| throw new Error("Can't overwrite object."); |
| } |
| |
| parent.contents[name] = { |
| read: canRead === undefined ? true : canRead, |
| write: canWrite === undefined ? false : canWrite, |
| timestamp: Date.now(), |
| inodeNumber: FS.nextInode++ |
| }; |
| for (var key in properties) { |
| if (properties.hasOwnProperty(key)) { |
| parent.contents[name][key] = properties[key]; |
| } |
| } |
| |
| return parent.contents[name]; |
| }, |
| // Creates a folder. |
| createFolder: function(parent, name, canRead, canWrite) { |
| var properties = {isFolder: true, isDevice: false, contents: {}}; |
| return FS.createObject(parent, name, properties, canRead, canWrite); |
| }, |
| // Creates a folder and all its missing parents. |
| createPath: function(parent, path, canRead, canWrite) { |
| var current = FS.findObject(parent); |
| if (current === null) throw new Error('Invalid parent.'); |
| path = path.split('/').reverse(); |
| while (path.length) { |
| var part = path.pop(); |
| if (!part) continue; |
| if (!current.contents.hasOwnProperty(part)) { |
| FS.createFolder(current, part, canRead, canWrite); |
| } |
| current = current.contents[part]; |
| } |
| return current; |
| }, |
| |
| // Creates a file record, given specific properties. |
| createFile: function(parent, name, properties, canRead, canWrite) { |
| properties.isFolder = false; |
| return FS.createObject(parent, name, properties, canRead, canWrite); |
| }, |
| // Creates a file record from existing data. |
| createDataFile: function(parent, name, data, canRead, canWrite) { |
| if (typeof data === 'string') { |
| var dataArray = 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); |
| }, |
| // 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. |
| var LazyUint8Array = function() { |
| this.lengthKnown = false; |
| this.chunks = []; // Loaded chunks. Index is the chunk number |
| } |
| LazyUint8Array.prototype.get = function(idx) { |
| if (idx > this.length-1 || idx < 0) { |
| return undefined; |
| } |
| var chunkOffset = idx % this.chunkSize; |
| var chunkNum = Math.floor(idx / this.chunkSize); |
| return this.getter(chunkNum)[chunkOffset]; |
| } |
| LazyUint8Array.prototype.setDataGetter = function(getter) { |
| this.getter = getter; |
| } |
| |
| LazyUint8Array.prototype.cacheLength = function() { |
| // Find length |
| var xhr = new XMLHttpRequest(); |
| xhr.open('HEAD', url, false); |
| xhr.send(null); |
| if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); |
| var datalength = Number(xhr.getResponseHeader("Content-length")); |
| var header; |
| var hasByteServing = (header = xhr.getResponseHeader("Accept-Ranges")) && header === "bytes"; |
| #if SMALL_XHR_CHUNKS |
| var chunkSize = 1024; // Chunk size in bytes |
| #else |
| var chunkSize = 1024*1024; // Chunk size in bytes |
| #endif |
| |
| if (!hasByteServing) chunkSize = datalength; |
| |
| // Function to get a range from the remote URL. |
| var doXHR = (function(from, to) { |
| if (from > to) throw new Error("invalid range (" + from + ", " + to + ") or no bytes requested!"); |
| if (to > datalength-1) throw new Error("only " + datalength + " bytes available! programmer error!"); |
| |
| // TODO: Use mozResponseArrayBuffer, responseStream, etc. if available. |
| var xhr = new XMLHttpRequest(); |
| xhr.open('GET', url, false); |
| if (datalength !== chunkSize) xhr.setRequestHeader("Range", "bytes=" + from + "-" + to); |
| |
| // Some hints to the browser that we want binary data. |
| if (typeof Uint8Array != 'undefined') xhr.responseType = 'arraybuffer'; |
| if (xhr.overrideMimeType) { |
| xhr.overrideMimeType('text/plain; charset=x-user-defined'); |
| } |
| |
| xhr.send(null); |
| if (!(xhr.status >= 200 && xhr.status < 300 || xhr.status === 304)) throw new Error("Couldn't load " + url + ". Status: " + xhr.status); |
| if (xhr.response !== undefined) { |
| return new Uint8Array(xhr.response || []); |
| } else { |
| return intArrayFromString(xhr.responseText || '', true); |
| } |
| }); |
| var lazyArray = this; |
| lazyArray.setDataGetter(function(chunkNum) { |
| var start = chunkNum * chunkSize; |
| var end = (chunkNum+1) * chunkSize - 1; // including this byte |
| end = Math.min(end, datalength-1); // if datalength-1 is selected, this is the last block |
| if (typeof(lazyArray.chunks[chunkNum]) === "undefined") { |
| lazyArray.chunks[chunkNum] = doXHR(start, end); |
| } |
| if (typeof(lazyArray.chunks[chunkNum]) === "undefined") throw new Error("doXHR failed!"); |
| return lazyArray.chunks[chunkNum]; |
| }); |
| |
| this._length = datalength; |
| this._chunkSize = chunkSize; |
| this.lengthKnown = true; |
| } |
| |
| var lazyArray = new LazyUint8Array(); |
| Object.defineProperty(lazyArray, "length", { |
| get: function() { |
| if(!this.lengthKnown) { |
| this.cacheLength(); |
| } |
| return this._length; |
| } |
| }); |
| Object.defineProperty(lazyArray, "chunkSize", { |
| get: function() { |
| if(!this.lengthKnown) { |
| this.cacheLength(); |
| } |
| return this._chunkSize; |
| } |
| }); |
| |
| var properties = { isDevice: false, contents: lazyArray }; |
| } else { |
| var properties = { isDevice: false, url: url }; |
| } |
| |
| return FS.createFile(parent, name, properties, canRead, canWrite); |
| }, |
| // 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. |
| // If you call it after run(), you may want to pause the main loop until it |
| // completes, if so, you can use the onload parameter to be notified when |
| // that happens. |
| // In addition to normally creating the file, we also asynchronously preload |
| // the browser-friendly versions of it: For an image, we preload an Image |
| // element and for an audio, and Audio. These are necessary for SDL_Image |
| // and _Mixer to find the files in preloadedImages/Audios. |
| // You can also call this with a typed array instead of a url. It will then |
| // do preloading for the Image/Audio part, as if the typed array were the |
| // result of an XHR that you did manually. |
| createPreloadedFile: function(parent, name, url, canRead, canWrite, onload, onerror, dontCreateFile) { |
| Browser.init(); |
| var fullname = FS.joinPath([parent, name], true); |
| function processData(byteArray) { |
| function finish(byteArray) { |
| if (!dontCreateFile) { |
| FS.createDataFile(parent, name, byteArray, canRead, canWrite); |
| } |
| if (onload) onload(); |
| removeRunDependency('cp ' + fullname); |
| } |
| var handled = false; |
| Module['preloadPlugins'].forEach(function(plugin) { |
| if (handled) return; |
| if (plugin['canHandle'](fullname)) { |
| plugin['handle'](byteArray, fullname, finish, function() { |
| if (onerror) onerror(); |
| removeRunDependency('cp ' + fullname); |
| }); |
| handled = true; |
| } |
| }); |
| if (!handled) finish(byteArray); |
| } |
| addRunDependency('cp ' + fullname); |
| if (typeof url == 'string') { |
| Browser.asyncLoad(url, function(byteArray) { |
| processData(byteArray); |
| }, onerror); |
| } else { |
| processData(url); |
| } |
| }, |
| // 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); |
| }, |
| // Creates a character device with input and output callbacks: |
| // input: Takes no parameters, returns a byte value or null if no data is |
| // currently available. |
| // output: Takes a byte value; doesn't return anything. Can also be passed |
| // null to perform a flush of any cached data. |
| createDevice: function(parent, name, input, output) { |
| if (!(input || output)) { |
| throw new Error('A device must have at least one callback defined.'); |
| } |
| var ops = {isDevice: true, input: input, output: output}; |
| return FS.createFile(parent, name, ops, Boolean(input), Boolean(output)); |
| }, |
| // Makes sure a file's contents are loaded. Returns whether the file has |
| // been loaded successfully. No-op for files that have been loaded already. |
| forceLoadFile: function(obj) { |
| if (obj.isDevice || obj.isFolder || obj.link || obj.contents) return true; |
| var success = true; |
| if (typeof XMLHttpRequest !== 'undefined') { |
| 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; |
| }, |
| ensureRoot: function() { |
| if (FS.root) return; |
| // The main file system tree. All the contents are inside this. |
| FS.root = { |
| read: true, |
| write: true, |
| isFolder: true, |
| isDevice: false, |
| timestamp: Date.now(), |
| inodeNumber: 1, |
| contents: {} |
| }; |
| }, |
| // Initializes the filesystems with stdin/stdout/stderr devices, given |
| // optional handlers. |
| init: function(input, output, error) { |
| // Make sure we initialize only once. |
| assert(!FS.init.initialized, 'FS.init was previously called. If you want to initialize later with custom parameters, remove any earlier calls (note that one is automatically added to the generated code)'); |
| FS.init.initialized = true; |
| |
| FS.ensureRoot(); |
| |
| // Allow Module.stdin etc. to provide defaults, if none explicitly passed to us here |
| input = input || Module['stdin']; |
| output = output || Module['stdout']; |
| error = error || Module['stderr']; |
| |
| // Default handlers. |
| var stdinOverridden = true, stdoutOverridden = true, stderrOverridden = true; |
| if (!input) { |
| stdinOverridden = false; |
| input = function() { |
| if (!input.cache || !input.cache.length) { |
| var result; |
| if (typeof window != 'undefined' && |
| typeof window.prompt == 'function') { |
| // Browser. |
| result = window.prompt('Input: '); |
| if (result === null) result = String.fromCharCode(0); // cancel ==> EOF |
| } else if (typeof readline == 'function') { |
| // Command line. |
| result = readline(); |
| } |
| if (!result) result = ''; |
| input.cache = intArrayFromString(result + '\n', true); |
| } |
| return input.cache.shift(); |
| }; |
| } |
| var utf8 = new Runtime.UTF8Processor(); |
| function simpleOutput(val) { |
| if (val === null || val === {{{ charCode('\n') }}}) { |
| output.printer(output.buffer.join('')); |
| output.buffer = []; |
| } else { |
| output.buffer.push(utf8.processCChar(val)); |
| } |
| } |
| if (!output) { |
| stdoutOverridden = false; |
| output = simpleOutput; |
| } |
| if (!output.printer) output.printer = Module['print']; |
| if (!output.buffer) output.buffer = []; |
| if (!error) { |
| stderrOverridden = false; |
| error = simpleOutput; |
| } |
| if (!error.printer) error.printer = Module['print']; |
| if (!error.buffer) error.buffer = []; |
| |
| // Create the temporary folder, if not already created |
| try { |
| FS.createFolder('/', 'tmp', true, true); |
| } catch(e) {} |
| |
| // Create the I/O devices. |
| var devFolder = FS.createFolder('/', 'dev', true, true); |
| var stdin = FS.createDevice(devFolder, 'stdin', input); |
| var stdout = FS.createDevice(devFolder, 'stdout', null, output); |
| var stderr = FS.createDevice(devFolder, 'stderr', null, error); |
| FS.createDevice(devFolder, 'tty', input, output); |
| FS.createDevice(devFolder, 'null', function(){}, function(){}); |
| |
| // Create default streams. |
| FS.streams[1] = { |
| path: '/dev/stdin', |
| object: stdin, |
| position: 0, |
| isRead: true, |
| isWrite: false, |
| isAppend: false, |
| isTerminal: !stdinOverridden, |
| error: false, |
| eof: false, |
| ungotten: [] |
| }; |
| FS.streams[2] = { |
| path: '/dev/stdout', |
| object: stdout, |
| position: 0, |
| isRead: false, |
| isWrite: true, |
| isAppend: false, |
| isTerminal: !stdoutOverridden, |
| error: false, |
| eof: false, |
| ungotten: [] |
| }; |
| FS.streams[3] = { |
| path: '/dev/stderr', |
| object: stderr, |
| position: 0, |
| isRead: false, |
| isWrite: true, |
| isAppend: false, |
| isTerminal: !stderrOverridden, |
| 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 |
| #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') }}}); |
| }, |
| |
| 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') }}}); |
| }, |
| |
| // 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; |
| }, |
| |
| deleteFile: function(path) { |
| path = FS.analyzePath(path); |
| if (!path.parentExists || !path.exists) { |
| throw 'Invalid path ' + path; |
| } |
| delete path.parentObject.contents[path.name]; |
| } |
| }, |
| |
| // ========================================================================== |
| // dirent.h |
| // ========================================================================== |
| |
| __dirent_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'd_ino'], |
| ['b1024', 'd_name'], |
| ['i32', 'd_off'], |
| ['i32', 'd_reclen'], |
| ['i32', 'd_type']]), |
| opendir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], |
| opendir: function(dirname) { |
| // DIR *opendir(const char *dirname); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/opendir.html |
| // NOTE: Calculating absolute path redundantly since we need to associate it |
| // with the opened stream. |
| var path = FS.absolutePath(Pointer_stringify(dirname)); |
| if (path === null) { |
| ___setErrNo(ERRNO_CODES.ENOENT); |
| return 0; |
| } |
| var target = FS.findObject(path); |
| if (target === null) return 0; |
| if (!target.isFolder) { |
| ___setErrNo(ERRNO_CODES.ENOTDIR); |
| return 0; |
| } else if (!target.read) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return 0; |
| } |
| var 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; |
| }, |
| closedir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| closedir: function(dirp) { |
| // int closedir(DIR *dirp); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html |
| if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { |
| return ___setErrNo(ERRNO_CODES.EBADF); |
| } else { |
| _free(FS.streams[dirp].currentEntry); |
| FS.streams[dirp] = null; |
| return 0; |
| } |
| }, |
| telldir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| telldir: function(dirp) { |
| // long int telldir(DIR *dirp); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html |
| if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { |
| return ___setErrNo(ERRNO_CODES.EBADF); |
| } else { |
| return FS.streams[dirp].position; |
| } |
| }, |
| seekdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| seekdir: function(dirp, loc) { |
| // void seekdir(DIR *dirp, long int loc); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/seekdir.html |
| if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| } else { |
| var entries = 0; |
| for (var key in FS.streams[dirp].contents) entries++; |
| if (loc >= entries) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| } else { |
| FS.streams[dirp].position = loc; |
| } |
| } |
| }, |
| rewinddir__deps: ['seekdir'], |
| rewinddir: function(dirp) { |
| // void rewinddir(DIR *dirp); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/rewinddir.html |
| _seekdir(dirp, -2); |
| }, |
| readdir_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], |
| readdir_r: function(dirp, entry, result) { |
| // int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html |
| if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { |
| return ___setErrNo(ERRNO_CODES.EBADF); |
| } |
| var stream = FS.streams[dirp]; |
| var loc = stream.position; |
| var entries = 0; |
| for (var key in stream.contents) entries++; |
| if (loc < -2 || loc >= entries) { |
| {{{ makeSetValue('result', '0', '0', 'i8*') }}} |
| } else { |
| var name, inode, 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; |
| }, |
| readdir__deps: ['readdir_r', '__setErrNo', '$ERRNO_CODES'], |
| readdir: function(dirp) { |
| // struct dirent *readdir(DIR *dirp); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/readdir_r.html |
| if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return 0; |
| } else { |
| if (!_readdir.result) _readdir.result = _malloc(4); |
| _readdir_r(dirp, FS.streams[dirp].currentEntry, _readdir.result); |
| if ({{{ makeGetValue(0, '_readdir.result', 'i8*') }}} === 0) { |
| return 0; |
| } else { |
| return FS.streams[dirp].currentEntry; |
| } |
| } |
| }, |
| __01readdir64_: 'readdir', |
| // TODO: Check if we need to link any other aliases. |
| |
| // ========================================================================== |
| // utime.h |
| // ========================================================================== |
| |
| __utimbuf_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'actime'], |
| ['i32', 'modtime']]), |
| utime__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__utimbuf_struct_layout'], |
| utime: function(path, times) { |
| // int utime(const char *path, const struct utimbuf *times); |
| // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/utime.h.html |
| var time; |
| if (times) { |
| // NOTE: We don't keep track of access timestamps. |
| var offset = ___utimbuf_struct_layout.modtime; |
| time = {{{ makeGetValue('times', 'offset', 'i32') }}} |
| time *= 1000; |
| } else { |
| time = Date.now(); |
| } |
| var file = FS.findObject(Pointer_stringify(path)); |
| if (file === null) return -1; |
| if (!file.write) { |
| ___setErrNo(ERRNO_CODES.EPERM); |
| return -1; |
| } |
| file.timestamp = time; |
| return 0; |
| }, |
| |
| utimes: function() { throw 'utimes not implemented' }, |
| |
| // ========================================================================== |
| // libgen.h |
| // ========================================================================== |
| |
| __libgenSplitName: function(path) { |
| if (path === 0 || {{{ makeGetValue('path', 0, 'i8') }}} === 0) { |
| // Null or empty results in '.'. |
| var me = ___libgenSplitName; |
| if (!me.ret) { |
| me.ret = allocate([{{{ charCode('.') }}}, 0], 'i8', ALLOC_NORMAL); |
| } |
| return [me.ret, -1]; |
| } else { |
| var slash = {{{ charCode('/') }}}; |
| var allSlashes = true; |
| var slashPositions = []; |
| for (var i = 0; {{{ makeGetValue('path', 'i', 'i8') }}} !== 0; i++) { |
| if ({{{ makeGetValue('path', 'i', 'i8') }}} === slash) { |
| slashPositions.push(i); |
| } else { |
| allSlashes = false; |
| } |
| } |
| var length = i; |
| if (allSlashes) { |
| // All slashes result in a single slash. |
| {{{ makeSetValue('path', '1', '0', 'i8') }}} |
| return [path, -1]; |
| } else { |
| // Strip trailing slashes. |
| while (slashPositions.length && |
| slashPositions[slashPositions.length - 1] == length - 1) { |
| {{{ makeSetValue('path', 'slashPositions.pop(i)', '0', 'i8') }}} |
| length--; |
| } |
| return [path, slashPositions.pop()]; |
| } |
| } |
| }, |
| basename__deps: ['__libgenSplitName'], |
| basename: function(path) { |
| // char *basename(char *path); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/basename.html |
| var result = ___libgenSplitName(path); |
| return result[0] + result[1] + 1; |
| }, |
| __xpg_basename: 'basename', |
| dirname__deps: ['__libgenSplitName'], |
| dirname: function(path) { |
| // char *dirname(char *path); |
| // http://pubs.opengroup.org/onlinepubs/007908799/xsh/dirname.html |
| var result = ___libgenSplitName(path); |
| if (result[1] == 0) { |
| {{{ makeSetValue('result[0]', 1, '0', 'i8') }}} |
| } else if (result[1] !== -1) { |
| {{{ makeSetValue('result[0]', 'result[1]', '0', 'i8') }}} |
| } |
| return result[0]; |
| }, |
| |
| // ========================================================================== |
| // sys/stat.h |
| // ========================================================================== |
| |
| __stat_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'st_dev'], |
| ['i32', 'st_ino'], |
| ['i32', 'st_mode'], |
| ['i32', 'st_nlink'], |
| ['i32', 'st_uid'], |
| ['i32', 'st_gid'], |
| ['i32', 'st_rdev'], |
| ['i32', 'st_size'], |
| ['i32', 'st_atime'], |
| ['i32', 'st_spare1'], |
| ['i32', 'st_mtime'], |
| ['i32', 'st_spare2'], |
| ['i32', 'st_ctime'], |
| ['i32', 'st_spare3'], |
| ['i32', 'st_blksize'], |
| ['i32', 'st_blocks'], |
| ['i32', 'st_spare4']]), |
| stat__deps: ['$FS', '__stat_struct_layout'], |
| stat: function(path, buf, dontResolveLastLink) { |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html |
| // int stat(const char *path, struct stat *buf); |
| // NOTE: dontResolveLastLink is a shortcut for lstat(). It should never be |
| // used in client code. |
| var obj = FS.findObject(Pointer_stringify(path), dontResolveLastLink); |
| if (obj === null || !FS.forceLoadFile(obj)) return -1; |
| |
| var offsets = ___stat_struct_layout; |
| |
| // Constants. |
| {{{ makeSetValue('buf', 'offsets.st_nlink', '1', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.st_uid', '0', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.st_gid', '0', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.st_blksize', '4096', 'i32') }}} |
| |
| // Variables. |
| {{{ makeSetValue('buf', 'offsets.st_ino', 'obj.inodeNumber', 'i32') }}} |
| var time = Math.floor(obj.timestamp / 1000); |
| if (offsets.st_atime === undefined) { |
| offsets.st_atime = offsets.st_atim.tv_sec; |
| offsets.st_mtime = offsets.st_mtim.tv_sec; |
| offsets.st_ctime = offsets.st_ctim.tv_sec; |
| var nanosec = (obj.timestamp % 1000) * 1000; |
| {{{ makeSetValue('buf', 'offsets.st_atim.tv_nsec', 'nanosec', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.st_mtim.tv_nsec', 'nanosec', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.st_ctim.tv_nsec', 'nanosec', 'i32') }}} |
| } |
| {{{ makeSetValue('buf', 'offsets.st_atime', 'time', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.st_mtime', 'time', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.st_ctime', 'time', 'i32') }}} |
| var mode = 0; |
| var size = 0; |
| var blocks = 0; |
| var dev = 0; |
| var rdev = 0; |
| if (obj.isDevice) { |
| // Device numbers reuse inode numbers. |
| dev = rdev = obj.inodeNumber; |
| size = blocks = 0; |
| mode = 0x2000; // S_IFCHR. |
| } else { |
| dev = 1; |
| rdev = 0; |
| // NOTE: In our implementation, st_blocks = Math.ceil(st_size/st_blksize), |
| // but this is not required by the standard. |
| if (obj.isFolder) { |
| size = 4096; |
| blocks = 1; |
| mode = 0x4000; // S_IFDIR. |
| } else { |
| var data = obj.contents || obj.link; |
| size = data.length; |
| blocks = Math.ceil(data.length / 4096); |
| mode = obj.link === undefined ? 0x8000 : 0xA000; // S_IFREG, S_IFLNK. |
| } |
| } |
| {{{ makeSetValue('buf', 'offsets.st_dev', 'dev', '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) { |
| // int lstat(const char *path, struct stat *buf); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/lstat.html |
| return _stat(path, buf, true); |
| }, |
| fstat__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'stat'], |
| fstat: function(fildes, buf) { |
| // int fstat(int fildes, struct stat *buf); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fstat.html |
| if (!FS.streams[fildes]) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else { |
| var pathArray = intArrayFromString(FS.streams[fildes].path); |
| return _stat(allocate(pathArray, 'i8', ALLOC_STACK), buf); |
| } |
| }, |
| mknod__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| mknod: function(path, mode, dev) { |
| // int mknod(const char *path, mode_t mode, dev_t dev); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mknod.html |
| if (dev !== 0 || !(mode & 0xC000)) { // S_IFREG | S_IFDIR. |
| // Can't create devices or pipes through mknod(). |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| var properties = {contents: [], isFolder: Boolean(mode & 0x4000)}; // S_IFDIR. |
| path = FS.analyzePath(Pointer_stringify(path)); |
| try { |
| FS.createObject(path.parentObject, path.name, properties, |
| mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. |
| return 0; |
| } catch (e) { |
| return -1; |
| } |
| } |
| }, |
| mkdir__deps: ['mknod'], |
| mkdir: function(path, mode) { |
| // int mkdir(const char *path, mode_t mode); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkdir.html |
| return _mknod(path, 0x4000 | (mode & 0x180), 0); // S_IFDIR, S_IRUSR | S_IWUSR. |
| }, |
| mkfifo__deps: ['__setErrNo', '$ERRNO_CODES'], |
| mkfifo: function(path, mode) { |
| // int mkfifo(const char *path, mode_t mode); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/mkfifo.html |
| // NOTE: We support running only a single process, and named pipes require |
| // blocking, which we can't provide. The error code is not very |
| // accurate, but it's the closest among those allowed in the standard |
| // and unlikely to result in retries. |
| ___setErrNo(ERRNO_CODES.EROFS); |
| return -1; |
| }, |
| chmod__deps: ['$FS'], |
| chmod: function(path, mode) { |
| // int chmod(const char *path, mode_t mode); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chmod.html |
| var obj = FS.findObject(Pointer_stringify(path)); |
| if (obj === null) return -1; |
| obj.read = mode & 0x100; // S_IRUSR. |
| obj.write = mode & 0x80; // S_IWUSR. |
| obj.timestamp = Date.now(); |
| return 0; |
| }, |
| fchmod__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chmod'], |
| fchmod: function(fildes, mode) { |
| // int fchmod(int fildes, mode_t mode); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/fchmod.html |
| if (!FS.streams[fildes]) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else { |
| var pathArray = intArrayFromString(FS.streams[fildes].path); |
| return _chmod(allocate(pathArray, 'i8', ALLOC_STACK), mode); |
| } |
| }, |
| lchmod: function() { throw 'TODO: lchmod' }, |
| |
| umask__deps: ['$FS'], |
| umask: function(newMask) { |
| // mode_t umask(mode_t cmask); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/umask.html |
| // NOTE: This value isn't actually used for anything. |
| if (_umask.cmask === undefined) _umask.cmask = 0x1FF; // S_IRWXU | S_IRWXG | S_IRWXO. |
| var oldMask = _umask.cmask; |
| _umask.cmask = newMask; |
| return oldMask; |
| }, |
| stat64: 'stat', |
| fstat64: 'fstat', |
| __01fstat64_: 'fstat', |
| __01stat64_: 'stat', |
| __01lstat64_: 'lstat', |
| |
| // TODO: Check if other aliases are needed. |
| |
| // ========================================================================== |
| // sys/statvfs.h |
| // ========================================================================== |
| |
| __statvfs_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'f_bsize'], |
| ['i32', 'f_frsize'], |
| ['i32', 'f_blocks'], |
| ['i32', 'f_bfree'], |
| ['i32', 'f_bavail'], |
| ['i32', 'f_files'], |
| ['i32', 'f_ffree'], |
| ['i32', 'f_favail'], |
| ['i32', 'f_fsid'], |
| ['i32', 'f_flag'], |
| ['i32', 'f_namemax']]), |
| statvfs__deps: ['$FS', '__statvfs_struct_layout'], |
| statvfs: function(path, buf) { |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/stat.html |
| // int statvfs(const char *restrict path, struct statvfs *restrict buf); |
| var offsets = ___statvfs_struct_layout; |
| // NOTE: None of the constants here are true. We're just returning safe and |
| // sane values. |
| {{{ makeSetValue('buf', 'offsets.f_bsize', '4096', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_frsize', '4096', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_blocks', '1000000', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_bfree', '500000', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_bavail', '500000', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_files', 'FS.nextInode', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_ffree', '1000000', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_favail', '1000000', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_fsid', '42', 'i32') }}} |
| {{{ makeSetValue('buf', 'offsets.f_flag', '2', 'i32') }}} // ST_NOSUID |
| {{{ makeSetValue('buf', 'offsets.f_namemax', '255', 'i32') }}} |
| return 0; |
| }, |
| fstatvfs__deps: ['statvfs'], |
| fstatvfs: function(fildes, buf) { |
| // int fstatvfs(int fildes, struct statvfs *buf); |
| // http://pubs.opengroup.org/onlinepubs/009604499/functions/statvfs.html |
| return _statvfs(0, buf); |
| }, |
| __01statvfs64_: 'statvfs', |
| __01fstatvfs64_: 'fstatvfs', |
| |
| // ========================================================================== |
| // fcntl.h |
| // ========================================================================== |
| |
| __flock_struct_layout: Runtime.generateStructInfo([ |
| ['i16', 'l_type'], |
| ['i16', 'l_whence'], |
| ['i32', 'l_start'], |
| ['i32', 'l_len'], |
| ['i16', 'l_pid'], |
| ['i16', 'l_xxx']]), |
| open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'], |
| open: function(path, oflag, varargs) { |
| // int open(const char *path, int oflag, ...); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html |
| // NOTE: This implementation tries to mimic glibc rather 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); |
| return -1; |
| } |
| var target = path.object || null; |
| var finalPath; |
| |
| // Verify the file exists, create if needed and allowed. |
| if (target) { |
| if (isCreate && isExistCheck) { |
| ___setErrNo(ERRNO_CODES.EEXIST); |
| return -1; |
| } |
| if ((isWrite || isCreate || isTruncate) && target.isFolder) { |
| ___setErrNo(ERRNO_CODES.EISDIR); |
| return -1; |
| } |
| if (isRead && !target.read || isWrite && !target.write) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } |
| if (isTruncate && !target.isDevice) { |
| target.contents = []; |
| } else { |
| if (!FS.forceLoadFile(target)) { |
| ___setErrNo(ERRNO_CODES.EIO); |
| return -1; |
| } |
| } |
| finalPath = path.path; |
| } else { |
| if (!isCreate) { |
| ___setErrNo(ERRNO_CODES.ENOENT); |
| return -1; |
| } |
| if (!path.parentObject.write) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } |
| target = FS.createDataFile(path.parentObject, path.name, [], |
| mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR. |
| finalPath = path.parentPath + '/' + path.name; |
| } |
| // Actually create an open stream. |
| var id; |
| 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) { |
| // int creat(const char *path, mode_t mode); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/creat.html |
| return _open(path, {{{ cDefine('O_WRONLY') }}} | {{{ cDefine('O_CREAT') }}} | {{{ cDefine('O_TRUNC') }}}, allocate([mode, 0, 0, 0], 'i32', ALLOC_STACK)); |
| }, |
| mkstemp__deps: ['creat'], |
| mkstemp: function(template) { |
| if (!_mkstemp.counter) _mkstemp.counter = 0; |
| var c = (_mkstemp.counter++).toString(); |
| var rep = 'XXXXXX'; |
| while (c.length < rep.length) c = '0' + c; |
| writeArrayToMemory(intArrayFromString(c), template + Pointer_stringify(template).indexOf(rep)); |
| return _creat(template, 0600); |
| }, |
| fcntl__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__flock_struct_layout'], |
| 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]) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| var stream = FS.streams[fildes]; |
| switch (cmd) { |
| case {{{ cDefine('F_DUPFD') }}}: |
| var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; |
| if (arg < 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| var newStream = {}; |
| for (var member in stream) { |
| newStream[member] = stream[member]; |
| } |
| 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; |
| case {{{ cDefine('F_GETFD') }}}: |
| case {{{ cDefine('F_SETFD') }}}: |
| return 0; // FD_CLOEXEC makes no sense for a single process. |
| case {{{ cDefine('F_GETFL') }}}: |
| var flags = 0; |
| if (stream.isRead && stream.isWrite) flags = {{{ cDefine('O_RDWR') }}}; |
| else if (!stream.isRead && stream.isWrite) flags = {{{ cDefine('O_WRONLY') }}}; |
| else if (stream.isRead && !stream.isWrite) flags = {{{ cDefine('O_RDONLY') }}}; |
| if (stream.isAppend) flags |= {{{ cDefine('O_APPEND') }}}; |
| // Synchronization and blocking flags are irrelevant to us. |
| return flags; |
| case {{{ cDefine('F_SETFL') }}}: |
| var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; |
| stream.isAppend = Boolean(arg | {{{ cDefine('O_APPEND') }}}); |
| // Synchronization and blocking flags are irrelevant to us. |
| return 0; |
| case {{{ cDefine('F_GETLK') }}}: |
| case {{{ cDefine('F_GETLK64') }}}: |
| var arg = {{{ makeGetValue('varargs', 0, 'i32') }}}; |
| var offset = ___flock_struct_layout.l_type; |
| // We're always unlocked. |
| {{{ makeSetValue('arg', 'offset', cDefine('F_UNLCK'), 'i16') }}} |
| return 0; |
| case {{{ cDefine('F_SETLK') }}}: |
| case {{{ cDefine('F_SETLKW') }}}: |
| case {{{ cDefine('F_SETLK64') }}}: |
| case {{{ cDefine('F_SETLKW64') }}}: |
| // Pretend that the locking is successful. |
| return 0; |
| case {{{ cDefine('F_SETOWN') }}}: |
| case {{{ cDefine('F_GETOWN') }}}: |
| // These are for sockets. We don't have them fully implemented yet. |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| default: |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| // Should never be reached. Only to silence strict warnings. |
| return -1; |
| }, |
| posix_fadvise: function(fd, offset, len, advice) { |
| // int posix_fadvise(int fd, off_t offset, off_t len, int advice); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fadvise.html |
| // Advise as much as you wish. We don't care. |
| return 0; |
| }, |
| posix_madvise: 'posix_fadvise', |
| posix_fallocate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| posix_fallocate: function(fd, offset, len) { |
| // int posix_fallocate(int fd, off_t offset, off_t len); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/posix_fallocate.html |
| if (!FS.streams[fd] || FS.streams[fd].link || |
| FS.streams[fd].isFolder || FS.streams[fd].isDevice) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| var contents = FS.streams[fd].object.contents; |
| var limit = offset + len; |
| while (limit > contents.length) contents.push(0); |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // poll.h |
| // ========================================================================== |
| |
| __pollfd_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'fd'], |
| ['i16', 'events'], |
| ['i16', 'revents']]), |
| poll__deps: ['$FS', '__pollfd_struct_layout'], |
| poll: function(fds, nfds, timeout) { |
| // int poll(struct pollfd fds[], nfds_t nfds, int timeout); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/poll.html |
| // NOTE: This is pretty much a no-op mimicking glibc. |
| var offsets = ___pollfd_struct_layout; |
| var nonzero = 0; |
| for (var i = 0; i < nfds; i++) { |
| var pollfd = fds + ___pollfd_struct_layout.__size__ * i; |
| var fd = {{{ makeGetValue('pollfd', 'offsets.fd', 'i32') }}}; |
| var events = {{{ makeGetValue('pollfd', 'offsets.events', 'i16') }}}; |
| var revents = 0; |
| if (FS.streams[fd]) { |
| var stream = FS.streams[fd]; |
| if (events & {{{ cDefine('POLLIN') }}}) revents |= {{{ cDefine('POLLIN') }}}; |
| if (events & {{{ cDefine('POLLOUT') }}}) revents |= {{{ cDefine('POLLOUT') }}}; |
| } else { |
| if (events & {{{ cDefine('POLLNVAL') }}}) revents |= {{{ cDefine('POLLNVAL') }}}; |
| } |
| if (revents) nonzero++; |
| {{{ makeSetValue('pollfd', 'offsets.revents', 'revents', 'i16') }}} |
| } |
| return nonzero; |
| }, |
| |
| // ========================================================================== |
| // unistd.h |
| // ========================================================================== |
| |
| access__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| access: function(path, amode) { |
| // int access(const char *path, int amode); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/access.html |
| path = Pointer_stringify(path); |
| var target = FS.findObject(path); |
| if (target === null) return -1; |
| if ((amode & 2 && !target.write) || // W_OK. |
| ((amode & 1 || amode & 4) && !target.read)) { // X_OK, R_OK. |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else { |
| return 0; |
| } |
| }, |
| chdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| chdir: function(path) { |
| // int chdir(const char *path); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/chdir.html |
| // NOTE: The path argument may be a string, to simplify fchdir(). |
| if (typeof path !== 'string') path = Pointer_stringify(path); |
| path = FS.analyzePath(path); |
| if (!path.exists) { |
| ___setErrNo(path.error); |
| return -1; |
| } else if (!path.object.isFolder) { |
| ___setErrNo(ERRNO_CODES.ENOTDIR); |
| return -1; |
| } else { |
| FS.currentPath = path.path; |
| return 0; |
| } |
| }, |
| chown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| chown: function(path, owner, group, dontResolveLastLink) { |
| // int chown(const char *path, uid_t owner, gid_t group); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/chown.html |
| // We don't support multiple users, so changing ownership makes no sense. |
| // NOTE: The path argument may be a string, to simplify fchown(). |
| // NOTE: dontResolveLastLink is a shortcut for lchown(). It should never be |
| // used in client code. |
| if (typeof path !== 'string') path = Pointer_stringify(path); |
| var target = FS.findObject(path, dontResolveLastLink); |
| if (target === null) return -1; |
| target.timestamp = Date.now(); |
| return 0; |
| }, |
| chroot__deps: ['__setErrNo', '$ERRNO_CODES'], |
| chroot: function(path) { |
| // int chroot(const char *path); |
| // http://pubs.opengroup.org/onlinepubs/7908799/xsh/chroot.html |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| }, |
| close__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| close: function(fildes) { |
| // int close(int fildes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/close.html |
| if (FS.streams[fildes]) { |
| if (FS.streams[fildes].currentEntry) { |
| _free(FS.streams[fildes].currentEntry); |
| } |
| FS.streams[fildes] = null; |
| return 0; |
| } else { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| dup__deps: ['fcntl'], |
| dup: function(fildes) { |
| // int dup(int fildes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/dup.html |
| return _fcntl(fildes, 0, allocate([0, 0, 0, 0], 'i32', ALLOC_STACK)); // F_DUPFD. |
| }, |
| dup2__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'fcntl', 'close'], |
| dup2: function(fildes, fildes2) { |
| // int dup2(int fildes, int fildes2); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/dup.html |
| if (fildes2 < 0) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else if (fildes === fildes2 && FS.streams[fildes]) { |
| return fildes; |
| } else { |
| _close(fildes2); |
| return _fcntl(fildes, 0, allocate([fildes2, 0, 0, 0], 'i32', ALLOC_STACK), true); // F_DUPFD. |
| } |
| }, |
| fchown__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chown'], |
| fchown: function(fildes, owner, group) { |
| // int fchown(int fildes, uid_t owner, gid_t group); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchown.html |
| if (FS.streams[fildes]) { |
| return _chown(FS.streams[fildes].path, owner, group); |
| } else { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| fchdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'chdir'], |
| fchdir: function(fildes) { |
| // int fchdir(int fildes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fchdir.html |
| if (FS.streams[fildes]) { |
| return _chdir(FS.streams[fildes].path); |
| } else { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| ctermid__deps: ['strcpy'], |
| ctermid: function(s) { |
| // char *ctermid(char *s); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/ctermid.html |
| if (!_ctermid.ret) { |
| var arr = intArrayFromString('/dev/tty'); |
| _ctermid.ret = allocate(arr, 'i8', ALLOC_NORMAL); |
| } |
| return s ? _strcpy(s, _ctermid.ret) : _ctermid.ret; |
| }, |
| crypt: function(key, salt) { |
| // char *(const char *, const char *); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/crypt.html |
| // TODO: Implement (probably compile from C). |
| ___setErrNo(ERRNO_CODES.ENOSYS); |
| return 0; |
| }, |
| encrypt: function(block, edflag) { |
| // void encrypt(char block[64], int edflag); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html |
| // TODO: Implement (probably compile from C). |
| ___setErrNo(ERRNO_CODES.ENOSYS); |
| }, |
| fpathconf__deps: ['__setErrNo', '$ERRNO_CODES'], |
| fpathconf: function(fildes, name) { |
| // long fpathconf(int fildes, int name); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/encrypt.html |
| // NOTE: The first parameter is ignored, so pathconf == fpathconf. |
| // The constants here aren't real values. Just mimicking glibc. |
| switch (name) { |
| case {{{ cDefine('_PC_LINK_MAX') }}}: |
| return 32000; |
| case {{{ cDefine('_PC_MAX_CANON') }}}: |
| case {{{ cDefine('_PC_MAX_INPUT') }}}: |
| case {{{ cDefine('_PC_NAME_MAX') }}}: |
| return 255; |
| case {{{ cDefine('_PC_PATH_MAX') }}}: |
| case {{{ cDefine('_PC_PIPE_BUF') }}}: |
| case {{{ cDefine('_PC_REC_MIN_XFER_SIZE') }}}: |
| case {{{ cDefine('_PC_REC_XFER_ALIGN') }}}: |
| case {{{ cDefine('_PC_ALLOC_SIZE_MIN') }}}: |
| return 4096; |
| case {{{ cDefine('_PC_CHOWN_RESTRICTED') }}}: |
| case {{{ cDefine('_PC_NO_TRUNC') }}}: |
| case {{{ cDefine('_PC_2_SYMLINKS') }}}: |
| return 1; |
| case {{{ cDefine('_PC_VDISABLE') }}}: |
| return 0; |
| case {{{ cDefine('_PC_SYNC_IO') }}}: |
| case {{{ cDefine('_PC_ASYNC_IO') }}}: |
| case {{{ cDefine('_PC_PRIO_IO') }}}: |
| case {{{ cDefine('_PC_SOCK_MAXBUF') }}}: |
| case {{{ cDefine('_PC_REC_INCR_XFER_SIZE') }}}: |
| case {{{ cDefine('_PC_REC_MAX_XFER_SIZE') }}}: |
| case {{{ cDefine('_PC_SYMLINK_MAX') }}}: |
| return -1; |
| case {{{ cDefine('_PC_FILESIZEBITS') }}}: |
| return 64; |
| } |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| }, |
| pathconf: 'fpathconf', |
| fsync__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| fsync: function(fildes) { |
| // int fsync(int fildes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsync.html |
| if (FS.streams[fildes]) { |
| // We write directly to the file system, so there's nothing to do here. |
| return 0; |
| } else { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| fdatasync: 'fsync', |
| truncate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| truncate: function(path, length) { |
| // int truncate(const char *path, off_t length); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/truncate.html |
| // NOTE: The path argument may be a string, to simplify ftruncate(). |
| if (length < 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| if (typeof path !== 'string') path = Pointer_stringify(path); |
| var target = FS.findObject(path); |
| if (target === null) return -1; |
| if (target.isFolder) { |
| ___setErrNo(ERRNO_CODES.EISDIR); |
| return -1; |
| } else if (target.isDevice) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else if (!target.write) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else { |
| var contents = target.contents; |
| if (length < contents.length) contents.length = length; |
| else while (length > contents.length) contents.push(0); |
| target.timestamp = Date.now(); |
| return 0; |
| } |
| } |
| }, |
| ftruncate__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'truncate'], |
| ftruncate: function(fildes, length) { |
| // int ftruncate(int fildes, off_t length); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftruncate.html |
| if (FS.streams[fildes] && FS.streams[fildes].isWrite) { |
| return _truncate(FS.streams[fildes].path, length); |
| } else if (FS.streams[fildes]) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| getcwd__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| getcwd: function(buf, size) { |
| // char *getcwd(char *buf, size_t size); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getcwd.html |
| if (size == 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return 0; |
| } else if (size < FS.currentPath.length + 1) { |
| ___setErrNo(ERRNO_CODES.ERANGE); |
| return 0; |
| } else { |
| for (var i = 0; i < FS.currentPath.length; i++) { |
| {{{ makeSetValue('buf', 'i', 'FS.currentPath.charCodeAt(i)', 'i8') }}} |
| } |
| {{{ makeSetValue('buf', 'i', '0', 'i8') }}} |
| return buf; |
| } |
| }, |
| getwd__deps: ['getcwd'], |
| getwd: function(path_name) { |
| // char *getwd(char *path_name); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getwd.html |
| return _getcwd(path_name, 4096); // PATH_MAX. |
| }, |
| isatty__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| isatty: function(fildes) { |
| // int isatty(int fildes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/isatty.html |
| if (!FS.streams[fildes]) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return 0; |
| } |
| if (FS.streams[fildes].isTerminal) return 1; |
| ___setErrNo(ERRNO_CODES.ENOTTY); |
| return 0; |
| }, |
| lchown__deps: ['chown'], |
| lchown: function(path, owner, group) { |
| // int lchown(const char *path, uid_t owner, gid_t group); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/lchown.html |
| return _chown(path, owner, group, true); |
| }, |
| link__deps: ['__setErrNo', '$ERRNO_CODES'], |
| link: function(path1, path2) { |
| // int link(const char *path1, const char *path2); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/link.html |
| // We don't support hard links. |
| ___setErrNo(ERRNO_CODES.EMLINK); |
| return -1; |
| }, |
| lockf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| lockf: function(fildes, func, size) { |
| // int lockf(int fildes, int function, off_t size); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/lockf.html |
| if (FS.streams[fildes]) { |
| // Pretend whatever locking or unlocking operation succeeded. Locking does |
| // not make much sense, since we have a single process/thread. |
| return 0; |
| } else { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| lseek__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| lseek: function(fildes, offset, whence) { |
| // off_t lseek(int fildes, off_t offset, int whence); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/lseek.html |
| if (FS.streams[fildes] && !FS.streams[fildes].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 { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| pipe__deps: ['__setErrNo', '$ERRNO_CODES'], |
| pipe: function(fildes) { |
| // int pipe(int fildes[2]); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/pipe.html |
| // It is possible to implement this using two device streams, but pipes make |
| // little sense in a single-threaded environment, so we do not support them. |
| ___setErrNo(ERRNO_CODES.ENOSYS); |
| return -1; |
| }, |
| pread__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| pread: function(fildes, buf, nbyte, offset) { |
| // ssize_t pread(int fildes, void *buf, size_t nbyte, off_t offset); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html |
| var stream = FS.streams[fildes]; |
| if (!stream || stream.object.isDevice) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else if (!stream.isRead) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else if (stream.object.isFolder) { |
| ___setErrNo(ERRNO_CODES.EISDIR); |
| return -1; |
| } else if (nbyte < 0 || offset < 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| var bytesRead = 0; |
| while (stream.ungotten.length && nbyte > 0) { |
| {{{ makeSetValue('buf++', '0', 'stream.ungotten.pop()', 'i8') }}} |
| nbyte--; |
| bytesRead++; |
| } |
| var contents = stream.object.contents; |
| var size = Math.min(contents.length - offset, nbyte); |
| #if USE_TYPED_ARRAYS == 2 |
| if (contents.subarray) { // typed array |
| HEAPU8.set(contents.subarray(offset, offset+size), buf); |
| } else |
| #endif |
| if (contents.slice) { // normal array |
| for (var i = 0; i < size; i++) { |
| {{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}} |
| } |
| } else { |
| for (var i = 0; i < size; i++) { // LazyUint8Array from sync binary XHR |
| {{{ makeSetValue('buf', 'i', 'contents.get(offset + i)', 'i8') }}} |
| } |
| } |
| bytesRead += size; |
| return bytesRead; |
| } |
| }, |
| read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'recv', 'pread'], |
| read: function(fildes, buf, nbyte) { |
| // ssize_t read(int fildes, void *buf, size_t nbyte); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/read.html |
| var stream = FS.streams[fildes]; |
| if (stream && ('socket' in stream)) { |
| return _recv(fildes, buf, nbyte, 0); |
| } else if (!stream) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else if (!stream.isRead) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else if (nbyte < 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| var bytesRead; |
| if (stream.object.isDevice) { |
| if (stream.object.input) { |
| bytesRead = 0; |
| while (stream.ungotten.length && nbyte > 0) { |
| {{{ makeSetValue('buf++', '0', 'stream.ungotten.pop()', 'i8') }}} |
| nbyte--; |
| bytesRead++; |
| } |
| for (var i = 0; i < nbyte; i++) { |
| try { |
| var result = stream.object.input(); |
| } catch (e) { |
| ___setErrNo(ERRNO_CODES.EIO); |
| return -1; |
| } |
| if (result === 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 { |
| var ungotSize = stream.ungotten.length; |
| bytesRead = _pread(fildes, buf, nbyte, stream.position); |
| if (bytesRead != -1) { |
| stream.position += (stream.ungotten.length - ungotSize) + bytesRead; |
| } |
| return bytesRead; |
| } |
| } |
| }, |
| sync: function() { |
| // void sync(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/sync.html |
| // All our writing is already synchronized. This is a no-op. |
| }, |
| rmdir__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| rmdir: function(path) { |
| // int rmdir(const char *path); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/rmdir.html |
| path = FS.analyzePath(Pointer_stringify(path)); |
| if (!path.parentExists || !path.exists) { |
| ___setErrNo(path.error); |
| return -1; |
| } else if (!path.object.write || path.isRoot) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else if (!path.object.isFolder) { |
| ___setErrNo(ERRNO_CODES.ENOTDIR); |
| return -1; |
| } else { |
| for (var i in path.object.contents) { |
| ___setErrNo(ERRNO_CODES.ENOTEMPTY); |
| return -1; |
| } |
| if (path.path == FS.currentPath) { |
| ___setErrNo(ERRNO_CODES.EBUSY); |
| return -1; |
| } else { |
| delete path.parentObject.contents[path.name]; |
| return 0; |
| } |
| } |
| }, |
| unlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| unlink: function(path) { |
| // int unlink(const char *path); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/unlink.html |
| path = FS.analyzePath(Pointer_stringify(path)); |
| if (!path.parentExists || !path.exists) { |
| ___setErrNo(path.error); |
| return -1; |
| } else if (path.object.isFolder) { |
| ___setErrNo(ERRNO_CODES.EISDIR); |
| return -1; |
| } else if (!path.object.write) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else { |
| delete path.parentObject.contents[path.name]; |
| return 0; |
| } |
| }, |
| ttyname__deps: ['ttyname_r'], |
| ttyname: function(fildes) { |
| // char *ttyname(int fildes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html |
| if (!_ttyname.ret) _ttyname.ret = _malloc(256); |
| return _ttyname_r(fildes, _ttyname.ret, 256) ? 0 : _ttyname.ret; |
| }, |
| ttyname_r__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| ttyname_r: function(fildes, name, namesize) { |
| // int ttyname_r(int fildes, char *name, size_t namesize); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/ttyname.html |
| var stream = FS.streams[fildes]; |
| if (!stream) { |
| return ___setErrNo(ERRNO_CODES.EBADF); |
| } else { |
| var object = stream.object; |
| if (!object.isDevice || !object.input || !object.output) { |
| return ___setErrNo(ERRNO_CODES.ENOTTY); |
| } else { |
| var ret = stream.path; |
| if (namesize < ret.length + 1) { |
| return ___setErrNo(ERRNO_CODES.ERANGE); |
| } else { |
| for (var i = 0; i < ret.length; i++) { |
| {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}} |
| } |
| {{{ makeSetValue('name', 'i', '0', 'i8') }}} |
| return 0; |
| } |
| } |
| } |
| }, |
| symlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| symlink: function(path1, path2) { |
| // int symlink(const char *path1, const char *path2); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/symlink.html |
| var path = FS.analyzePath(Pointer_stringify(path2), true); |
| if (!path.parentExists) { |
| ___setErrNo(path.error); |
| return -1; |
| } else if (path.exists) { |
| ___setErrNo(ERRNO_CODES.EEXIST); |
| return -1; |
| } else { |
| FS.createLink(path.parentPath, path.name, |
| Pointer_stringify(path1), true, true); |
| return 0; |
| } |
| }, |
| readlink__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| readlink: function(path, buf, bufsize) { |
| // ssize_t readlink(const char *restrict path, char *restrict buf, size_t bufsize); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/readlink.html |
| var target = FS.findObject(Pointer_stringify(path), true); |
| if (target === null) return -1; |
| if (target.link !== undefined) { |
| var length = Math.min(bufsize - 1, target.link.length); |
| for (var i = 0; i < length; i++) { |
| {{{ makeSetValue('buf', 'i', 'target.link.charCodeAt(i)', 'i8') }}} |
| } |
| if (bufsize - 1 > length) {{{ makeSetValue('buf', 'i', '0', 'i8') }}} |
| return i; |
| } else { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| }, |
| pwrite__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| pwrite: function(fildes, buf, nbyte, offset) { |
| // ssize_t pwrite(int fildes, const void *buf, size_t nbyte, off_t offset); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/write.html |
| var stream = FS.streams[fildes]; |
| if (!stream || stream.object.isDevice) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else if (!stream.isWrite) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else if (stream.object.isFolder) { |
| ___setErrNo(ERRNO_CODES.EISDIR); |
| return -1; |
| } else if (nbyte < 0 || offset < 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| var contents = stream.object.contents; |
| while (contents.length < offset) contents.push(0); |
| for (var i = 0; i < nbyte; i++) { |
| contents[offset + i] = {{{ makeGetValue('buf', 'i', 'i8', 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) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else if (!stream.isWrite) { |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } else if (nbyte < 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| if (stream.object.isDevice) { |
| if (stream.object.output) { |
| for (var i = 0; i < nbyte; i++) { |
| try { |
| stream.object.output({{{ makeGetValue('buf', 'i', 'i8') }}}); |
| } catch (e) { |
| ___setErrNo(ERRNO_CODES.EIO); |
| return -1; |
| } |
| } |
| stream.object.timestamp = Date.now(); |
| return i; |
| } else { |
| ___setErrNo(ERRNO_CODES.ENXIO); |
| return -1; |
| } |
| } else { |
| var bytesWritten = _pwrite(fildes, buf, nbyte, stream.position); |
| if (bytesWritten != -1) stream.position += bytesWritten; |
| return bytesWritten; |
| } |
| } |
| }, |
| alarm: function(seconds) { |
| // unsigned alarm(unsigned seconds); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/alarm.html |
| // We don't support signals, and there's no way to indicate failure, so just |
| // fail silently. |
| return 0; |
| }, |
| ualarm: 'alarm', |
| confstr__deps: ['__setErrNo', '$ERRNO_CODES', '$ENV'], |
| confstr: function(name, buf, len) { |
| // size_t confstr(int name, char *buf, size_t len); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/confstr.html |
| var value; |
| switch (name) { |
| case {{{ cDefine('_CS_PATH') }}}: |
| value = ENV['PATH'] || '/'; |
| break; |
| case {{{ cDefine('_CS_POSIX_V6_WIDTH_RESTRICTED_ENVS') }}}: |
| // Mimicing glibc. |
| value = 'POSIX_V6_ILP32_OFF32\nPOSIX_V6_ILP32_OFFBIG'; |
| break; |
| case {{{ cDefine('_CS_GNU_LIBC_VERSION') }}}: |
| // This JS implementation was tested against this glibc version. |
| value = 'glibc 2.14'; |
| break; |
| case {{{ cDefine('_CS_GNU_LIBPTHREAD_VERSION') }}}: |
| // We don't support pthreads. |
| value = ''; |
| break; |
| case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LIBS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LIBS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_CFLAGS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LDFLAGS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_LP64_OFF64_LIBS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_CFLAGS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LDFLAGS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_LPBIG_OFFBIG_LIBS') }}}: |
| value = ''; |
| break; |
| case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_CFLAGS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_ILP32_OFF32_LDFLAGS') }}}: |
| case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_LDFLAGS') }}}: |
| value = '-m32'; |
| break; |
| case {{{ cDefine('_CS_POSIX_V6_ILP32_OFFBIG_CFLAGS') }}}: |
| value = '-m32 -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64'; |
| break; |
| default: |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return 0; |
| } |
| if (len == 0 || buf == 0) { |
| return value.length + 1; |
| } else { |
| var length = Math.min(len, value.length); |
| for (var i = 0; i < length; i++) { |
| {{{ makeSetValue('buf', 'i', 'value.charCodeAt(i)', 'i8') }}} |
| } |
| if (len > length) {{{ makeSetValue('buf', 'i++', '0', 'i8') }}} |
| return i; |
| } |
| }, |
| execl__deps: ['__setErrNo', '$ERRNO_CODES'], |
| execl: function(/* ... */) { |
| // int execl(const char *path, const char *arg0, ... /*, (char *)0 */); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/exec.html |
| // We don't support executing external code. |
| ___setErrNo(ERRNO_CODES.ENOEXEC); |
| return -1; |
| }, |
| execle: 'execl', |
| execlp: 'execl', |
| execv: 'execl', |
| execve: 'execl', |
| execvp: 'execl', |
| _exit: function(status) { |
| // void _exit(int status); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/exit.html |
| |
| function ExitStatus() { |
| this.name = "ExitStatus"; |
| this.message = "Program terminated with exit(" + status + ")"; |
| this.status = status; |
| Module.print('Exit Status: ' + status); |
| }; |
| ExitStatus.prototype = new Error(); |
| ExitStatus.prototype.constructor = ExitStatus; |
| |
| exitRuntime(); |
| ABORT = true; |
| |
| throw new ExitStatus(); |
| }, |
| fork__deps: ['__setErrNo', '$ERRNO_CODES'], |
| fork: function() { |
| // pid_t fork(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fork.html |
| // We don't support multiple processes. |
| ___setErrNo(ERRNO_CODES.EAGAIN); |
| return -1; |
| }, |
| vfork: 'fork', |
| getgid: function() { |
| // gid_t getgid(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getgid.html |
| // We have just one process/group/user, all with ID 0. |
| return 0; |
| }, |
| getegid: 'getgid', |
| getuid: 'getgid', |
| geteuid: 'getgid', |
| getpgrp: 'getgid', |
| getpid: 'getgid', |
| getppid: 'getgid', |
| getresuid: function(ruid, euid, suid) { |
| // int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); |
| // http://linux.die.net/man/2/getresuid |
| // We have just one process/group/user, all with ID 0. |
| {{{ makeSetValue('ruid', '0', '0', 'i32') }}} |
| {{{ makeSetValue('euid', '0', '0', 'i32') }}} |
| {{{ makeSetValue('suid', '0', '0', 'i32') }}} |
| return 0; |
| }, |
| getresgid: 'getresuid', |
| getgroups__deps: ['__setErrNo', '$ERRNO_CODES'], |
| getgroups: function(gidsetsize, grouplist) { |
| // int getgroups(int gidsetsize, gid_t grouplist[]); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getgroups.html |
| if (gidsetsize < 1) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| {{{ makeSetValue('grouplist', '0', '0', 'i32') }}} |
| return 1; |
| } |
| }, |
| // TODO: Implement initgroups (grp.h). |
| setgroups__deps: ['__setErrNo', '$ERRNO_CODES', 'sysconf'], |
| setgroups: function (ngroups, gidset) { |
| // int setgroups(int ngroups, const gid_t *gidset); |
| // https://developer.apple.com/library/mac/#documentation/Darwin/Reference/ManPages/man2/setgroups.2.html |
| if (ngroups < 1 || ngroups > _sysconf({{{ cDefine('_SC_NGROUPS_MAX') }}})) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else { |
| // We have just one process/user/group, so it makes no sense to set groups. |
| ___setErrNo(ERRNO_CODES.EPERM); |
| return -1; |
| } |
| }, |
| gethostid: function() { |
| // long gethostid(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/gethostid.html |
| return 42; |
| }, |
| gethostname__deps: ['__setErrNo', '$ERRNO_CODES'], |
| gethostname: function(name, namelen) { |
| // int gethostname(char *name, size_t namelen); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/gethostname.html |
| var host = 'emscripten'; |
| if (typeof window !== 'undefined' && window.location.host) { |
| host = window.location.host; |
| } |
| var length = Math.min(namelen, host.length); |
| for (var i = 0; i < length; i++) { |
| {{{ makeSetValue('name', 'i', 'host.charCodeAt(i)', 'i8') }}} |
| } |
| if (namelen > length) { |
| {{{ makeSetValue('name', 'i', '0', 'i8') }}} |
| return 0; |
| } else { |
| ___setErrNo(ERRNO_CODES.ENAMETOOLONG); |
| return -1; |
| } |
| }, |
| getlogin__deps: ['getlogin_r'], |
| getlogin: function() { |
| // char *getlogin(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getlogin.html |
| if (!_getlogin.ret) _getlogin.ret = _malloc(8); |
| return _getlogin_r(_getlogin.ret, 8) ? 0 : _getlogin.ret; |
| }, |
| getlogin_r__deps: ['__setErrNo', '$ERRNO_CODES'], |
| getlogin_r: function(name, namesize) { |
| // int getlogin_r(char *name, size_t namesize); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getlogin.html |
| var ret = 'root'; |
| if (namesize < ret.length + 1) { |
| return ___setErrNo(ERRNO_CODES.ERANGE); |
| } else { |
| for (var i = 0; i < ret.length; i++) { |
| {{{ makeSetValue('name', 'i', 'ret.charCodeAt(i)', 'i8') }}} |
| } |
| {{{ makeSetValue('name', 'i', '0', 'i8') }}} |
| return 0; |
| } |
| }, |
| getpagesize: function() { |
| // int getpagesize(void); |
| return PAGE_SIZE; |
| }, |
| getopt: function(argc, argv, optstring) { |
| // int getopt(int argc, char * const argv[], const char *optstring); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getopt.html |
| // TODO: Implement (probably compile from C). |
| return -1; |
| }, |
| getpgid: function(pid) { |
| // pid_t getpgid(pid_t pid); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getpgid.html |
| // There can only be one process, and its group ID is 0. |
| return 0; |
| }, |
| getsid: 'getpgid', |
| nice__deps: ['__setErrNo', '$ERRNO_CODES'], |
| nice: function(incr) { |
| // int nice(int incr); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/nice.html |
| // Niceness makes no sense in a single-process environment. |
| ___setErrNo(ERRNO_CODES.EPERM); |
| return 0; |
| }, |
| pause__deps: ['__setErrNo', '$ERRNO_CODES'], |
| pause: function() { |
| // int pause(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/pause.html |
| // We don't support signals, so we return immediately. |
| ___setErrNo(ERRNO_CODES.EINTR); |
| return -1; |
| }, |
| setgid__deps: ['__setErrNo', '$ERRNO_CODES'], |
| setgid: function(gid) { |
| // int setgid(gid_t gid); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/setgid.html |
| // We have just one process/group/user, so it makes no sense to set IDs. |
| ___setErrNo(ERRNO_CODES.EPERM); |
| return -1; |
| }, |
| setegid: 'setgid', |
| setuid: 'setgid', |
| seteuid: 'setgid', |
| setsid: 'setgid', |
| setpgrp: 'setgid', |
| setpgid__deps: ['__setErrNo', '$ERRNO_CODES'], |
| setpgid: function(pid, pgid) { |
| // int setpgid(pid_t pid, pid_t pgid); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getpgid.html |
| // We have just one process/group/user, so it makes no sense to set IDs. |
| ___setErrNo(ERRNO_CODES.EPERM); |
| return -1; |
| }, |
| setregid: 'setpgid', |
| setreuid: 'setpgid', |
| // NOTE: These do not match the signatures, but they all use the same stub. |
| setresuid: 'setpgid', |
| setresgid: 'setpgid', |
| sleep__deps: ['usleep'], |
| sleep: function(seconds) { |
| // unsigned sleep(unsigned seconds); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/sleep.html |
| return _usleep(seconds * 1e6); |
| }, |
| usleep: function(useconds) { |
| // int usleep(useconds_t useconds); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/usleep.html |
| // We're single-threaded, so use a busy loop. Super-ugly. |
| var msec = useconds / 1000; |
| var start = Date.now(); |
| while (Date.now() - start < msec) { |
| // Do nothing. |
| } |
| return 0; |
| }, |
| swab: function(src, dest, nbytes) { |
| // void swab(const void *restrict src, void *restrict dest, ssize_t nbytes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/swab.html |
| if (nbytes < 0) return; |
| nbytes -= nbytes % 2; |
| for (var i = 0; i < nbytes; i += 2) { |
| var first = {{{ makeGetValue('src', 'i', 'i8') }}}; |
| var second = {{{ makeGetValue('src', 'i + 1', 'i8') }}}; |
| {{{ makeSetValue('dest', 'i', 'second', 'i8') }}} |
| {{{ makeSetValue('dest', 'i + 1', 'first', 'i8') }}} |
| } |
| }, |
| tcgetpgrp: function(fildes) { |
| // pid_t tcgetpgrp(int fildes); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/tcgetpgrp.html |
| // Our only process always runs with group ID 0. |
| return 0; |
| }, |
| tcsetpgrp__deps: ['__setErrNo', '$ERRNO_CODES'], |
| tcsetpgrp: function(fildes, pgid_id) { |
| // int tcsetpgrp(int fildes, pid_t pgid_id); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/tcsetpgrp.html |
| // We don't support multiple processes or groups with ID other than 0. |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| }, |
| sysconf__deps: ['__setErrNo', '$ERRNO_CODES'], |
| sysconf: function(name) { |
| // long sysconf(int name); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/sysconf.html |
| switch(name) { |
| case {{{ cDefine('_SC_PAGE_SIZE') }}}: return PAGE_SIZE; |
| case {{{ cDefine('_SC_ADVISORY_INFO') }}}: |
| case {{{ cDefine('_SC_BARRIERS') }}}: |
| case {{{ cDefine('_SC_ASYNCHRONOUS_IO') }}}: |
| case {{{ cDefine('_SC_CLOCK_SELECTION') }}}: |
| case {{{ cDefine('_SC_CPUTIME') }}}: |
| case {{{ cDefine('_SC_FSYNC') }}}: |
| case {{{ cDefine('_SC_IPV6') }}}: |
| case {{{ cDefine('_SC_MAPPED_FILES') }}}: |
| case {{{ cDefine('_SC_MEMLOCK') }}}: |
| case {{{ cDefine('_SC_MEMLOCK_RANGE') }}}: |
| case {{{ cDefine('_SC_MEMORY_PROTECTION') }}}: |
| case {{{ cDefine('_SC_MESSAGE_PASSING') }}}: |
| case {{{ cDefine('_SC_MONOTONIC_CLOCK') }}}: |
| case {{{ cDefine('_SC_PRIORITIZED_IO') }}}: |
| case {{{ cDefine('_SC_PRIORITY_SCHEDULING') }}}: |
| case {{{ cDefine('_SC_RAW_SOCKETS') }}}: |
| case {{{ cDefine('_SC_READER_WRITER_LOCKS') }}}: |
| case {{{ cDefine('_SC_REALTIME_SIGNALS') }}}: |
| case {{{ cDefine('_SC_SEMAPHORES') }}}: |
| case {{{ cDefine('_SC_SHARED_MEMORY_OBJECTS') }}}: |
| case {{{ cDefine('_SC_SPAWN') }}}: |
| case {{{ cDefine('_SC_SPIN_LOCKS') }}}: |
| case {{{ cDefine('_SC_SYNCHRONIZED_IO') }}}: |
| case {{{ cDefine('_SC_THREAD_ATTR_STACKADDR') }}}: |
| case {{{ cDefine('_SC_THREAD_ATTR_STACKSIZE') }}}: |
| case {{{ cDefine('_SC_THREAD_CPUTIME') }}}: |
| case {{{ cDefine('_SC_THREAD_PRIO_INHERIT') }}}: |
| case {{{ cDefine('_SC_THREAD_PRIO_PROTECT') }}}: |
| case {{{ cDefine('_SC_THREAD_PRIORITY_SCHEDULING') }}}: |
| case {{{ cDefine('_SC_THREAD_PROCESS_SHARED') }}}: |
| case {{{ cDefine('_SC_THREAD_SAFE_FUNCTIONS') }}}: |
| case {{{ cDefine('_SC_THREADS') }}}: |
| case {{{ cDefine('_SC_TIMEOUTS') }}}: |
| case {{{ cDefine('_SC_TIMERS') }}}: |
| case {{{ cDefine('_SC_VERSION') }}}: |
| case {{{ cDefine('_SC_2_C_BIND') }}}: |
| case {{{ cDefine('_SC_2_C_DEV') }}}: |
| case {{{ cDefine('_SC_2_CHAR_TERM') }}}: |
| case {{{ cDefine('_SC_2_LOCALEDEF') }}}: |
| case {{{ cDefine('_SC_2_SW_DEV') }}}: |
| case {{{ cDefine('_SC_2_VERSION') }}}: |
| return 200809; |
| case {{{ cDefine('_SC_MQ_OPEN_MAX') }}}: |
| case {{{ cDefine('_SC_XOPEN_STREAMS') }}}: |
| case {{{ cDefine('_SC_XBS5_LP64_OFF64') }}}: |
| case {{{ cDefine('_SC_XBS5_LPBIG_OFFBIG') }}}: |
| case {{{ cDefine('_SC_AIO_LISTIO_MAX') }}}: |
| case {{{ cDefine('_SC_AIO_MAX') }}}: |
| case {{{ cDefine('_SC_SPORADIC_SERVER') }}}: |
| case {{{ cDefine('_SC_THREAD_SPORADIC_SERVER') }}}: |
| case {{{ cDefine('_SC_TRACE') }}}: |
| case {{{ cDefine('_SC_TRACE_EVENT_FILTER') }}}: |
| case {{{ cDefine('_SC_TRACE_EVENT_NAME_MAX') }}}: |
| case {{{ cDefine('_SC_TRACE_INHERIT') }}}: |
| case {{{ cDefine('_SC_TRACE_LOG') }}}: |
| case {{{ cDefine('_SC_TRACE_NAME_MAX') }}}: |
| case {{{ cDefine('_SC_TRACE_SYS_MAX') }}}: |
| case {{{ cDefine('_SC_TRACE_USER_EVENT_MAX') }}}: |
| case {{{ cDefine('_SC_TYPED_MEMORY_OBJECTS') }}}: |
| case {{{ cDefine('_SC_V6_LP64_OFF64') }}}: |
| case {{{ cDefine('_SC_V6_LPBIG_OFFBIG') }}}: |
| case {{{ cDefine('_SC_2_FORT_DEV') }}}: |
| case {{{ cDefine('_SC_2_FORT_RUN') }}}: |
| case {{{ cDefine('_SC_2_PBS') }}}: |
| case {{{ cDefine('_SC_2_PBS_ACCOUNTING') }}}: |
| case {{{ cDefine('_SC_2_PBS_CHECKPOINT') }}}: |
| case {{{ cDefine('_SC_2_PBS_LOCATE') }}}: |
| case {{{ cDefine('_SC_2_PBS_MESSAGE') }}}: |
| case {{{ cDefine('_SC_2_PBS_TRACK') }}}: |
| case {{{ cDefine('_SC_2_UPE') }}}: |
| case {{{ cDefine('_SC_THREAD_THREADS_MAX') }}}: |
| case {{{ cDefine('_SC_SEM_NSEMS_MAX') }}}: |
| case {{{ cDefine('_SC_SYMLOOP_MAX') }}}: |
| case {{{ cDefine('_SC_TIMER_MAX') }}}: |
| return -1; |
| case {{{ cDefine('_SC_V6_ILP32_OFF32') }}}: |
| case {{{ cDefine('_SC_V6_ILP32_OFFBIG') }}}: |
| case {{{ cDefine('_SC_JOB_CONTROL') }}}: |
| case {{{ cDefine('_SC_REGEXP') }}}: |
| case {{{ cDefine('_SC_SAVED_IDS') }}}: |
| case {{{ cDefine('_SC_SHELL') }}}: |
| case {{{ cDefine('_SC_XBS5_ILP32_OFF32') }}}: |
| case {{{ cDefine('_SC_XBS5_ILP32_OFFBIG') }}}: |
| case {{{ cDefine('_SC_XOPEN_CRYPT') }}}: |
| case {{{ cDefine('_SC_XOPEN_ENH_I18N') }}}: |
| case {{{ cDefine('_SC_XOPEN_LEGACY') }}}: |
| case {{{ cDefine('_SC_XOPEN_REALTIME') }}}: |
| case {{{ cDefine('_SC_XOPEN_REALTIME_THREADS') }}}: |
| case {{{ cDefine('_SC_XOPEN_SHM') }}}: |
| case {{{ cDefine('_SC_XOPEN_UNIX') }}}: |
| return 1; |
| case {{{ cDefine('_SC_THREAD_KEYS_MAX') }}}: |
| case {{{ cDefine('_SC_IOV_MAX') }}}: |
| case {{{ cDefine('_SC_GETGR_R_SIZE_MAX') }}}: |
| case {{{ cDefine('_SC_GETPW_R_SIZE_MAX') }}}: |
| case {{{ cDefine('_SC_OPEN_MAX') }}}: |
| return 1024; |
| case {{{ cDefine('_SC_RTSIG_MAX') }}}: |
| case {{{ cDefine('_SC_EXPR_NEST_MAX') }}}: |
| case {{{ cDefine('_SC_TTY_NAME_MAX') }}}: |
| return 32; |
| case {{{ cDefine('_SC_ATEXIT_MAX') }}}: |
| case {{{ cDefine('_SC_DELAYTIMER_MAX') }}}: |
| case {{{ cDefine('_SC_SEM_VALUE_MAX') }}}: |
| return 2147483647; |
| case {{{ cDefine('_SC_SIGQUEUE_MAX') }}}: |
| case {{{ cDefine('_SC_CHILD_MAX') }}}: |
| return 47839; |
| case {{{ cDefine('_SC_BC_SCALE_MAX') }}}: |
| case {{{ cDefine('_SC_BC_BASE_MAX') }}}: |
| return 99; |
| case {{{ cDefine('_SC_LINE_MAX') }}}: |
| case {{{ cDefine('_SC_BC_DIM_MAX') }}}: |
| return 2048; |
| case {{{ cDefine('_SC_ARG_MAX') }}}: return 2097152; |
| case {{{ cDefine('_SC_NGROUPS_MAX') }}}: return 65536; |
| case {{{ cDefine('_SC_MQ_PRIO_MAX') }}}: return 32768; |
| case {{{ cDefine('_SC_RE_DUP_MAX') }}}: return 32767; |
| case {{{ cDefine('_SC_THREAD_STACK_MIN') }}}: return 16384; |
| case {{{ cDefine('_SC_BC_STRING_MAX') }}}: return 1000; |
| case {{{ cDefine('_SC_XOPEN_VERSION') }}}: return 700; |
| case {{{ cDefine('_SC_LOGIN_NAME_MAX') }}}: return 256; |
| case {{{ cDefine('_SC_COLL_WEIGHTS_MAX') }}}: return 255; |
| case {{{ cDefine('_SC_CLK_TCK') }}}: return 100; |
| case {{{ cDefine('_SC_HOST_NAME_MAX') }}}: return 64; |
| case {{{ cDefine('_SC_AIO_PRIO_DELTA_MAX') }}}: return 20; |
| case {{{ cDefine('_SC_STREAM_MAX') }}}: return 16; |
| case {{{ cDefine('_SC_TZNAME_MAX') }}}: return 6; |
| case {{{ cDefine('_SC_THREAD_DESTRUCTOR_ITERATIONS') }}}: return 4; |
| case {{{ cDefine('_SC_NPROCESSORS_ONLN') }}}: return 1; |
| } |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| }, |
| sbrk: function(bytes) { |
| // Implement a Linux-like 'memory area' for our 'process'. |
| // Changes the size of the memory area by |bytes|; returns the |
| // address of the previous top ('break') of the memory area |
| // We control the "dynamic" memory - DYNAMIC_BASE to DYNAMICTOP |
| var self = _sbrk; |
| if (!self.called) { |
| DYNAMICTOP = alignMemoryPage(DYNAMICTOP); // make sure we start out aligned |
| self.called = true; |
| assert(Runtime.dynamicAlloc); |
| self.alloc = Runtime.dynamicAlloc; |
| Runtime.dynamicAlloc = function() { abort('cannot dynamically allocate, sbrk now has control') }; |
| } |
| var ret = DYNAMICTOP; |
| if (bytes != 0) self.alloc(bytes); |
| return ret; // Previous break location. |
| }, |
| open64: 'open', |
| lseek64: 'lseek', |
| ftruncate64: 'ftruncate', |
| __01open64_: 'open', |
| __01lseek64_: 'lseek', |
| __01truncate64_: 'truncate', |
| __01ftruncate64_: 'ftruncate', |
| // TODO: Check if any other aliases are needed. |
| |
| // ========================================================================== |
| // stdio.h |
| // ========================================================================== |
| |
| _isFloat: function(text) { |
| return !!(/^[+-]?[0-9]*\.?[0-9]+([eE][+-]?[0-9]+)?$/.exec(text)); |
| }, |
| |
| // TODO: Document. |
| _scanString__deps: ['_isFloat'], |
| _scanString: function(format, get, unget, varargs) { |
| if (!__scanString.whiteSpace) { |
| __scanString.whiteSpace = {}; |
| __scanString.whiteSpace[{{{ charCode(' ') }}}] = 1; |
| __scanString.whiteSpace[{{{ charCode('\t') }}}] = 1; |
| __scanString.whiteSpace[{{{ charCode('\n') }}}] = 1; |
| __scanString.whiteSpace[{{{ charCode('\v') }}}] = 1; |
| __scanString.whiteSpace[{{{ charCode('\f') }}}] = 1; |
| __scanString.whiteSpace[{{{ charCode('\r') }}}] = 1; |
| __scanString.whiteSpace[' '] = 1; |
| __scanString.whiteSpace['\t'] = 1; |
| __scanString.whiteSpace['\n'] = 1; |
| __scanString.whiteSpace['\v'] = 1; |
| __scanString.whiteSpace['\f'] = 1; |
| __scanString.whiteSpace['\r'] = 1; |
| } |
| // Supports %x, %4x, %d.%d, %lld, %s, %f, %lf. |
| // TODO: Support all format specifiers. |
| format = Pointer_stringify(format); |
| var soFar = 0; |
| if (format.indexOf('%n') >= 0) { |
| // need to track soFar |
| var _get = get; |
| get = function() { |
| soFar++; |
| return _get(); |
| } |
| var _unget = unget; |
| unget = function() { |
| soFar--; |
| return _unget(); |
| } |
| } |
| var formatIndex = 0; |
| var argsi = 0; |
| var fields = 0; |
| var argIndex = 0; |
| var next; |
| |
| mainLoop: |
| for (var formatIndex = 0; formatIndex < format.length;) { |
| if (format[formatIndex] === '%' && format[formatIndex+1] == 'n') { |
| var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; |
| argIndex += Runtime.getAlignSize('void*', null, true); |
| {{{ makeSetValue('argPtr', 0, 'soFar', 'i32') }}}; |
| formatIndex += 2; |
| continue; |
| } |
| |
| // TODO: Support strings like "%5c" etc. |
| if (format[formatIndex] === '%' && format[formatIndex+1] == 'c') { |
| var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; |
| argIndex += Runtime.getAlignSize('void*', null, true); |
| fields++; |
| next = get(); |
| {{{ makeSetValue('argPtr', 0, 'next', 'i8') }}} |
| formatIndex += 2; |
| continue; |
| } |
| |
| // remove whitespace |
| while (1) { |
| next = get(); |
| if (next == 0) return fields; |
| if (!(next in __scanString.whiteSpace)) break; |
| } |
| unget(); |
| |
| if (format[formatIndex] === '%') { |
| formatIndex++; |
| var suppressAssignment = false; |
| if (format[formatIndex] == '*') { |
| suppressAssignment = true; |
| formatIndex++; |
| } |
| var maxSpecifierStart = formatIndex; |
| while (format[formatIndex].charCodeAt(0) >= {{{ charCode('0') }}} && |
| format[formatIndex].charCodeAt(0) <= {{{ charCode('9') }}}) { |
| formatIndex++; |
| } |
| var max_; |
| if (formatIndex != maxSpecifierStart) { |
| max_ = parseInt(format.slice(maxSpecifierStart, formatIndex), 10); |
| } |
| var long_ = false; |
| var half = false; |
| var longLong = false; |
| if (format[formatIndex] == 'l') { |
| long_ = true; |
| formatIndex++; |
| if (format[formatIndex] == 'l') { |
| longLong = true; |
| formatIndex++; |
| } |
| } else if (format[formatIndex] == 'h') { |
| half = true; |
| formatIndex++; |
| } |
| var type = format[formatIndex]; |
| formatIndex++; |
| var curr = 0; |
| var buffer = []; |
| // Read characters according to the format. floats are trickier, they may be in an unfloat state in the middle, then be a valid float later |
| if (type == 'f' || type == 'e' || type == 'g' || |
| type == 'F' || type == 'E' || type == 'G') { |
| var last = 0; |
| next = get(); |
| while (next > 0) { |
| buffer.push(String.fromCharCode(next)); |
| if (__isFloat(buffer.join(''))) { |
| last = buffer.length; |
| } |
| next = get(); |
| } |
| for (var i = 0; i < buffer.length - last + 1; i++) { |
| unget(); |
| } |
| buffer.length = last; |
| } else { |
| next = get(); |
| var first = true; |
| while ((curr < max_ || isNaN(max_)) && next > 0) { |
| if (!(next in __scanString.whiteSpace) && // stop on whitespace |
| (type == 's' || |
| ((type === 'd' || type == 'u' || type == 'i') && ((next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}}) || |
| (first && next == {{{ charCode('-') }}}))) || |
| ((type === 'x' || type === 'X') && (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}} || |
| next >= {{{ charCode('a') }}} && next <= {{{ charCode('f') }}} || |
| next >= {{{ charCode('A') }}} && next <= {{{ charCode('F') }}}))) && |
| (formatIndex >= format.length || next !== format[formatIndex].charCodeAt(0))) { // Stop when we read something that is coming up |
| buffer.push(String.fromCharCode(next)); |
| next = get(); |
| curr++; |
| first = false; |
| } else { |
| break; |
| } |
| } |
| unget(); |
| } |
| if (buffer.length === 0) return 0; // Failure. |
| if (suppressAssignment) continue; |
| |
| var text = buffer.join(''); |
| var argPtr = {{{ makeGetValue('varargs', 'argIndex', 'void*') }}}; |
| argIndex += Runtime.getAlignSize('void*', null, true); |
| switch (type) { |
| case 'd': case 'u': case 'i': |
| if (half) { |
| {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i16') }}}; |
| } else if (longLong) { |
| {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i64') }}}; |
| } else { |
| {{{ makeSetValue('argPtr', 0, 'parseInt(text, 10)', 'i32') }}}; |
| } |
| break; |
| case 'X': |
| case 'x': |
| {{{ makeSetValue('argPtr', 0, 'parseInt(text, 16)', 'i32') }}} |
| break; |
| case 'F': |
| case 'f': |
| case 'E': |
| case 'e': |
| case 'G': |
| case 'g': |
| case 'E': |
| // fallthrough intended |
| if (long_) { |
| {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'double') }}} |
| } else { |
| {{{ makeSetValue('argPtr', 0, 'parseFloat(text)', 'float') }}} |
| } |
| break; |
| case 's': |
| var array = intArrayFromString(text); |
| for (var j = 0; j < array.length; j++) { |
| {{{ makeSetValue('argPtr', 'j', 'array[j]', 'i8') }}} |
| } |
| break; |
| } |
| fields++; |
| } else if (format[formatIndex] in __scanString.whiteSpace) { |
| next = get(); |
| while (next in __scanString.whiteSpace) { |
| if (next <= 0) break mainLoop; // End of input. |
| next = get(); |
| } |
| unget(next); |
| formatIndex++; |
| } else { |
| // Not a specifier. |
| next = get(); |
| if (format[formatIndex].charCodeAt(0) !== next) { |
| unget(next); |
| break mainLoop; |
| } |
| formatIndex++; |
| } |
| } |
| return fields; |
| }, |
| // Performs printf-style formatting. |
| // format: A pointer to the format string. |
| // varargs: A pointer to the start of the arguments list. |
| // Returns the resulting string string as a character array. |
| _formatString__deps: ['strlen', '_reallyNegative'], |
| _formatString: function(format, varargs) { |
| var textIndex = format; |
| var argIndex = 0; |
| function getNextArg(type) { |
| // NOTE: Explicitly ignoring type safety. Otherwise this fails: |
| // int x = 4; printf("%c\n", (char)x); |
| var ret; |
| if (type === 'double') { |
| ret = {{{ makeGetValue('varargs', 'argIndex', 'double', undefined, undefined, true) }}}; |
| #if USE_TYPED_ARRAYS == 2 |
| } else if (type == 'i64') { |
| |
| #if TARGET_LE32 |
| ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, |
| {{{ makeGetValue('varargs', 'argIndex+8', 'i32', undefined, undefined, true) }}}]; |
| argIndex += {{{ STACK_ALIGN }}}; // each 32-bit chunk is in a 64-bit block |
| #else |
| ret = [{{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}, |
| {{{ makeGetValue('varargs', 'argIndex+4', 'i32', undefined, undefined, true) }}}]; |
| #endif |
| |
| #else |
| } else if (type == 'i64') { |
| ret = {{{ makeGetValue('varargs', 'argIndex', 'i64', undefined, undefined, true) }}}; |
| #endif |
| } else { |
| type = 'i32'; // varargs are always i32, i64, or double |
| ret = {{{ makeGetValue('varargs', 'argIndex', 'i32', undefined, undefined, true) }}}; |
| } |
| argIndex += Math.max(Runtime.getNativeFieldSize(type), Runtime.getAlignSize(type, null, true)); |
| return ret; |
| } |
| |
| var ret = []; |
| var curr, next, currArg; |
| while(1) { |
| var startTextIndex = textIndex; |
| curr = {{{ makeGetValue(0, 'textIndex', 'i8') }}}; |
| if (curr === 0) break; |
| next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| if (curr == {{{ charCode('%') }}}) { |
| // Handle flags. |
| var flagAlwaysSigned = false; |
| var flagLeftAlign = false; |
| var flagAlternative = false; |
| var flagZeroPad = false; |
| flagsLoop: while (1) { |
| switch (next) { |
| case {{{ charCode('+') }}}: |
| flagAlwaysSigned = true; |
| break; |
| case {{{ charCode('-') }}}: |
| flagLeftAlign = true; |
| break; |
| case {{{ charCode('#') }}}: |
| flagAlternative = true; |
| break; |
| case {{{ charCode('0') }}}: |
| if (flagZeroPad) { |
| break flagsLoop; |
| } else { |
| flagZeroPad = true; |
| break; |
| } |
| default: |
| break flagsLoop; |
| } |
| textIndex++; |
| next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| } |
| |
| // Handle width. |
| var width = 0; |
| if (next == {{{ charCode('*') }}}) { |
| width = getNextArg('i32'); |
| textIndex++; |
| next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| } else { |
| while (next >= {{{ charCode('0') }}} && next <= {{{ charCode('9') }}}) { |
| width = width * 10 + (next - {{{ charCode('0') }}}); |
| textIndex++; |
| next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| } |
| } |
| |
| // Handle precision. |
| var precisionSet = false; |
| if (next == {{{ charCode('.') }}}) { |
| var precision = 0; |
| precisionSet = true; |
| textIndex++; |
| next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| if (next == {{{ charCode('*') }}}) { |
| precision = getNextArg('i32'); |
| textIndex++; |
| } else { |
| while(1) { |
| var precisionChr = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| if (precisionChr < {{{ charCode('0') }}} || |
| precisionChr > {{{ charCode('9') }}}) break; |
| precision = precision * 10 + (precisionChr - {{{ charCode('0') }}}); |
| textIndex++; |
| } |
| } |
| next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| } else { |
| var precision = 6; // Standard default. |
| } |
| |
| // Handle integer sizes. WARNING: These assume a 32-bit architecture! |
| var argSize; |
| switch (String.fromCharCode(next)) { |
| case 'h': |
| var nextNext = {{{ makeGetValue(0, 'textIndex+2', 'i8') }}}; |
| if (nextNext == {{{ charCode('h') }}}) { |
| textIndex++; |
| argSize = 1; // char (actually i32 in varargs) |
| } else { |
| argSize = 2; // short (actually i32 in varargs) |
| } |
| break; |
| case 'l': |
| var nextNext = {{{ makeGetValue(0, 'textIndex+2', 'i8') }}}; |
| if (nextNext == {{{ charCode('l') }}}) { |
| textIndex++; |
| argSize = 8; // long long |
| } else { |
| argSize = 4; // long |
| } |
| break; |
| case 'L': // long long |
| case 'q': // int64_t |
| case 'j': // intmax_t |
| argSize = 8; |
| break; |
| case 'z': // size_t |
| case 't': // ptrdiff_t |
| case 'I': // signed ptrdiff_t or unsigned size_t |
| argSize = 4; |
| break; |
| default: |
| argSize = null; |
| } |
| if (argSize) textIndex++; |
| next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}}; |
| |
| // Handle type specifier. |
| switch (String.fromCharCode(next)) { |
| case 'd': case 'i': case 'u': case 'o': case 'x': case 'X': case 'p': { |
| // Integer. |
| var signed = next == {{{ charCode('d') }}} || next == {{{ charCode('i') }}}; |
| argSize = argSize || 4; |
| var currArg = getNextArg('i' + (argSize * 8)); |
| #if PRECISE_I64_MATH |
| var origArg = currArg; |
| #endif |
| var argText; |
| #if USE_TYPED_ARRAYS == 2 |
| // Flatten i64-1 [low, high] into a (slightly rounded) double |
| if (argSize == 8) { |
| currArg = Runtime.makeBigInt(currArg[0], currArg[1], next == {{{ charCode('u') }}}); |
| } |
| #endif |
| // Truncate to requested size. |
| if (argSize <= 4) { |
| var limit = Math.pow(256, argSize) - 1; |
| currArg = (signed ? reSign : unSign)(currArg & limit, argSize * 8); |
| } |
| // Format the number. |
| var currAbsArg = Math.abs(currArg); |
| var prefix = ''; |
| if (next == {{{ charCode('d') }}} || next == {{{ charCode('i') }}}) { |
| #if PRECISE_I64_MATH |
| if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], null); else |
| #endif |
| argText = reSign(currArg, 8 * argSize, 1).toString(10); |
| } else if (next == {{{ charCode('u') }}}) { |
| #if PRECISE_I64_MATH |
| if (argSize == 8 && i64Math) argText = i64Math.stringify(origArg[0], origArg[1], true); else |
| #endif |
| argText = unSign(currArg, 8 * argSize, 1).toString(10); |
| currArg = Math.abs(currArg); |
| } else if (next == {{{ charCode('o') }}}) { |
| argText = (flagAlternative ? '0' : '') + currAbsArg.toString(8); |
| } else if (next == {{{ charCode('x') }}} || next == {{{ charCode('X') }}}) { |
| prefix = (flagAlternative && currArg != 0) ? '0x' : ''; |
| #if PRECISE_I64_MATH |
| if (argSize == 8 && i64Math) { |
| if (origArg[1]) { |
| argText = (origArg[1]>>>0).toString(16); |
| var lower = (origArg[0]>>>0).toString(16); |
| while (lower.length < 8) lower = '0' + lower; |
| argText += lower; |
| } else { |
| argText = (origArg[0]>>>0).toString(16); |
| } |
| } else |
| #endif |
| if (currArg < 0) { |
| // Represent negative numbers in hex as 2's complement. |
| currArg = -currArg; |
| argText = (currAbsArg - 1).toString(16); |
| var buffer = []; |
| for (var i = 0; i < argText.length; i++) { |
| buffer.push((0xF - parseInt(argText[i], 16)).toString(16)); |
| } |
| argText = buffer.join(''); |
| while (argText.length < argSize * 2) argText = 'f' + argText; |
| } else { |
| argText = currAbsArg.toString(16); |
| } |
| if (next == {{{ charCode('X') }}}) { |
| prefix = prefix.toUpperCase(); |
| argText = argText.toUpperCase(); |
| } |
| } else if (next == {{{ charCode('p') }}}) { |
| if (currAbsArg === 0) { |
| argText = '(nil)'; |
| } else { |
| prefix = '0x'; |
| argText = currAbsArg.toString(16); |
| } |
| } |
| if (precisionSet) { |
| while (argText.length < precision) { |
| argText = '0' + argText; |
| } |
| } |
| |
| // Add sign if needed |
| if (flagAlwaysSigned) { |
| if (currArg < 0) { |
| prefix = '-' + prefix; |
| } else { |
| prefix = '+' + prefix; |
| } |
| } |
| |
| // Add padding. |
| while (prefix.length + argText.length < width) { |
| if (flagLeftAlign) { |
| argText += ' '; |
| } else { |
| if (flagZeroPad) { |
| argText = '0' + argText; |
| } else { |
| prefix = ' ' + prefix; |
| } |
| } |
| } |
| |
| // Insert the result into the buffer. |
| argText = prefix + argText; |
| argText.split('').forEach(function(chr) { |
| ret.push(chr.charCodeAt(0)); |
| }); |
| break; |
| } |
| case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': { |
| // Float. |
| var currArg = getNextArg('double'); |
| var argText; |
| if (isNaN(currArg)) { |
| argText = 'nan'; |
| flagZeroPad = false; |
| } else if (!isFinite(currArg)) { |
| argText = (currArg < 0 ? '-' : '') + 'inf'; |
| flagZeroPad = false; |
| } else { |
| var isGeneral = false; |
| var effectivePrecision = Math.min(precision, 20); |
| |
| // Convert g/G to f/F or e/E, as per: |
| // http://pubs.opengroup.org/onlinepubs/9699919799/functions/printf.html |
| if (next == {{{ charCode('g') }}} || next == {{{ charCode('G') }}}) { |
| isGeneral = true; |
| precision = precision || 1; |
| var exponent = parseInt(currArg.toExponential(effectivePrecision).split('e')[1], 10); |
| if (precision > exponent && exponent >= -4) { |
| next = ((next == {{{ charCode('g') }}}) ? 'f' : 'F').charCodeAt(0); |
| precision -= exponent + 1; |
| } else { |
| next = ((next == {{{ charCode('g') }}}) ? 'e' : 'E').charCodeAt(0); |
| precision--; |
| } |
| effectivePrecision = Math.min(precision, 20); |
| } |
| |
| if (next == {{{ charCode('e') }}} || next == {{{ charCode('E') }}}) { |
| argText = currArg.toExponential(effectivePrecision); |
| // Make sure the exponent has at least 2 digits. |
| if (/[eE][-+]\d$/.test(argText)) { |
| argText = argText.slice(0, -1) + '0' + argText.slice(-1); |
| } |
| } else if (next == {{{ charCode('f') }}} || next == {{{ charCode('F') }}}) { |
| argText = currArg.toFixed(effectivePrecision); |
| if (currArg === 0 && __reallyNegative(currArg)) { |
| argText = '-' + argText; |
| } |
| } |
| |
| var parts = argText.split('e'); |
| if (isGeneral && !flagAlternative) { |
| // Discard trailing zeros and periods. |
| while (parts[0].length > 1 && parts[0].indexOf('.') != -1 && |
| (parts[0].slice(-1) == '0' || parts[0].slice(-1) == '.')) { |
| parts[0] = parts[0].slice(0, -1); |
| } |
| } else { |
| // Make sure we have a period in alternative mode. |
| if (flagAlternative && argText.indexOf('.') == -1) parts[0] += '.'; |
| // Zero pad until required precision. |
| while (precision > effectivePrecision++) parts[0] += '0'; |
| } |
| argText = parts[0] + (parts.length > 1 ? 'e' + parts[1] : ''); |
| |
| // Capitalize 'E' if needed. |
| if (next == {{{ charCode('E') }}}) argText = argText.toUpperCase(); |
| |
| // Add sign. |
| if (flagAlwaysSigned && currArg >= 0) { |
| argText = '+' + argText; |
| } |
| } |
| |
| // Add padding. |
| while (argText.length < width) { |
| if (flagLeftAlign) { |
| argText += ' '; |
| } else { |
| if (flagZeroPad && (argText[0] == '-' || argText[0] == '+')) { |
| argText = argText[0] + '0' + argText.slice(1); |
| } else { |
| argText = (flagZeroPad ? '0' : ' ') + argText; |
| } |
| } |
| } |
| |
| // Adjust case. |
| if (next < {{{ charCode('a') }}}) argText = argText.toUpperCase(); |
| |
| // Insert the result into the buffer. |
| argText.split('').forEach(function(chr) { |
| ret.push(chr.charCodeAt(0)); |
| }); |
| break; |
| } |
| case 's': { |
| // String. |
| var arg = getNextArg('i8*'); |
| var argLength = arg ? _strlen(arg) : '(null)'.length; |
| if (precisionSet) argLength = Math.min(argLength, precision); |
| if (!flagLeftAlign) { |
| while (argLength < width--) { |
| ret.push({{{ charCode(' ') }}}); |
| } |
| } |
| if (arg) { |
| for (var i = 0; i < argLength; i++) { |
| ret.push({{{ makeGetValue('arg++', 0, 'i8', null, true) }}}); |
| } |
| } else { |
| ret = ret.concat(intArrayFromString('(null)'.substr(0, argLength), true)); |
| } |
| if (flagLeftAlign) { |
| while (argLength < width--) { |
| ret.push({{{ charCode(' ') }}}); |
| } |
| } |
| break; |
| } |
| case 'c': { |
| // Character. |
| if (flagLeftAlign) ret.push(getNextArg('i8')); |
| while (--width > 0) { |
| ret.push({{{ charCode(' ') }}}); |
| } |
| if (!flagLeftAlign) ret.push(getNextArg('i8')); |
| break; |
| } |
| case 'n': { |
| // Write the length written so far to the next parameter. |
| var ptr = getNextArg('i32*'); |
| {{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}} |
| break; |
| } |
| case '%': { |
| // Literal percent sign. |
| ret.push(curr); |
| break; |
| } |
| default: { |
| // Unknown specifiers remain untouched. |
| for (var i = startTextIndex; i < textIndex + 2; i++) { |
| ret.push({{{ makeGetValue(0, 'i', 'i8') }}}); |
| } |
| } |
| } |
| textIndex += 2; |
| // TODO: Support a/A (hex float) and m (last error) specifiers. |
| // TODO: Support %1${specifier} for arg selection. |
| } else { |
| ret.push(curr); |
| textIndex += 1; |
| } |
| } |
| return ret; |
| }, |
| // NOTE: Invalid stream pointers passed to these functions would cause a crash |
| // in native code. We, on the other hand, just ignore them, since it's |
| // easier. |
| clearerr__deps: ['$FS'], |
| clearerr: function(stream) { |
| // void clearerr(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html |
| if (FS.streams[stream]) FS.streams[stream].error = false; |
| }, |
| fclose__deps: ['close', 'fsync'], |
| fclose: function(stream) { |
| // int fclose(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fclose.html |
| _fsync(stream); |
| return _close(stream); |
| }, |
| fdopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| fdopen: function(fildes, mode) { |
| // FILE *fdopen(int fildes, const char *mode); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fdopen.html |
| if (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 { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return 0; |
| } |
| }, |
| feof__deps: ['$FS'], |
| feof: function(stream) { |
| // int feof(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html |
| return Number(FS.streams[stream] && FS.streams[stream].eof); |
| }, |
| ferror__deps: ['$FS'], |
| ferror: function(stream) { |
| // int ferror(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/ferror.html |
| return Number(FS.streams[stream] && FS.streams[stream].error); |
| }, |
| fflush__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| fflush: function(stream) { |
| // int fflush(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fflush.html |
| var flush = function(filedes) { |
| // Right now we write all data directly, except for output devices. |
| if (FS.streams[filedes] && FS.streams[filedes].object.output) { |
| if (!FS.streams[filedes].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; |
| } |
| }, |
| fgetc__deps: ['$FS', 'read'], |
| fgetc__postset: '_fgetc.ret = allocate([0], "i8", ALLOC_STATIC);', |
| fgetc: function(stream) { |
| // int fgetc(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetc.html |
| if (!FS.streams[stream]) return -1; |
| var streamObj = FS.streams[stream]; |
| if (streamObj.eof || streamObj.error) return -1; |
| var ret = _read(stream, _fgetc.ret, 1); |
| if (ret == 0) { |
| streamObj.eof = true; |
| return -1; |
| } else if (ret == -1) { |
| streamObj.error = true; |
| return -1; |
| } else { |
| return {{{ makeGetValue('_fgetc.ret', '0', 'i8', null, 1) }}}; |
| } |
| }, |
| getc: 'fgetc', |
| getc_unlocked: 'fgetc', |
| getchar__deps: ['fgetc', 'stdin'], |
| getchar: function() { |
| // int getchar(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/getchar.html |
| return _fgetc({{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}); |
| }, |
| fgetpos__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| fgetpos: function(stream, pos) { |
| // int fgetpos(FILE *restrict stream, fpos_t *restrict pos); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetpos.html |
| if (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 { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| fgets__deps: ['fgetc'], |
| fgets: function(s, n, stream) { |
| // char *fgets(char *restrict s, int n, FILE *restrict stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgets.html |
| if (!FS.streams[stream]) return 0; |
| var streamObj = FS.streams[stream]; |
| if (streamObj.error || streamObj.eof) return 0; |
| var byte_; |
| for (var i = 0; i < n - 1 && byte_ != {{{ charCode('\n') }}}; i++) { |
| byte_ = _fgetc(stream); |
| if (byte_ == -1) { |
| if (streamObj.error || (streamObj.eof && i == 0)) return 0; |
| else if (streamObj.eof) break; |
| } |
| {{{ makeSetValue('s', 'i', 'byte_', 'i8') }}} |
| } |
| {{{ makeSetValue('s', 'i', '0', 'i8') }}} |
| return s; |
| }, |
| gets__deps: ['fgets'], |
| gets: function(s) { |
| // char *gets(char *s); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/gets.html |
| return _fgets(s, 1e6, {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}); |
| }, |
| fileno: function(stream) { |
| // int fileno(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fileno.html |
| // We use file descriptor numbers and FILE* streams interchangeably. |
| return stream; |
| }, |
| ftrylockfile: function() { |
| // int ftrylockfile(FILE *file); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/flockfile.html |
| // Locking is useless in a single-threaded environment. Pretend to succeed. |
| return 0; |
| }, |
| flockfile: 'ftrylockfile', |
| funlockfile: 'ftrylockfile', |
| fopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'open'], |
| fopen: function(filename, mode) { |
| // FILE *fopen(const char *restrict filename, const char *restrict mode); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fopen.html |
| var flags; |
| mode = Pointer_stringify(mode); |
| if (mode[0] == 'r') { |
| if (mode.indexOf('+') != -1) { |
| flags = {{{ cDefine('O_RDWR') }}}; |
| } else { |
| flags = {{{ cDefine('O_RDONLY') }}}; |
| } |
| } else if (mode[0] == 'w') { |
| if (mode.indexOf('+') != -1) { |
| flags = {{{ cDefine('O_RDWR') }}}; |
| } else { |
| flags = {{{ cDefine('O_WRONLY') }}}; |
| } |
| flags |= {{{ cDefine('O_CREAT') }}}; |
| flags |= {{{ cDefine('O_TRUNC') }}}; |
| } else if (mode[0] == 'a') { |
| if (mode.indexOf('+') != -1) { |
| flags = {{{ cDefine('O_RDWR') }}}; |
| } else { |
| flags = {{{ cDefine('O_WRONLY') }}}; |
| } |
| flags |= {{{ cDefine('O_CREAT') }}}; |
| flags |= {{{ cDefine('O_APPEND') }}}; |
| } else { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return 0; |
| } |
| var ret = _open(filename, flags, allocate([0x1FF, 0, 0, 0], 'i32', ALLOC_STACK)); // All creation permissions. |
| return (ret == -1) ? 0 : ret; |
| }, |
| fputc__deps: ['$FS', 'write'], |
| fputc__postset: '_fputc.ret = allocate([0], "i8", ALLOC_STATIC);', |
| fputc: function(c, stream) { |
| // int fputc(int c, FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputc.html |
| var chr = unSign(c & 0xFF); |
| {{{ makeSetValue('_fputc.ret', '0', 'chr', 'i8') }}} |
| var ret = _write(stream, _fputc.ret, 1); |
| if (ret == -1) { |
| if (FS.streams[stream]) FS.streams[stream].error = true; |
| return -1; |
| } else { |
| return chr; |
| } |
| }, |
| putc: 'fputc', |
| putc_unlocked: 'fputc', |
| putchar__deps: ['fputc', 'stdout'], |
| putchar: function(c) { |
| // int putchar(int c); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/putchar.html |
| return _fputc(c, {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); |
| }, |
| putchar_unlocked: 'putchar', |
| fputs__deps: ['write', 'strlen'], |
| fputs: function(s, stream) { |
| // int fputs(const char *restrict s, FILE *restrict stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fputs.html |
| return _write(stream, s, _strlen(s)); |
| }, |
| puts__deps: ['fputs', 'fputc', 'stdout'], |
| puts: function(s) { |
| // int puts(const char *s); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/puts.html |
| // NOTE: puts() always writes an extra newline. |
| var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; |
| var ret = _fputs(s, stdout); |
| if (ret < 0) { |
| return ret; |
| } else { |
| var newlineRet = _fputc({{{ charCode('\n') }}}, stdout); |
| return (newlineRet < 0) ? -1 : ret + 1; |
| } |
| }, |
| fread__deps: ['$FS', 'read'], |
| fread: function(ptr, size, nitems, stream) { |
| // size_t fread(void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fread.html |
| var bytesToRead = nitems * size; |
| if (bytesToRead == 0) return 0; |
| var bytesRead = _read(stream, ptr, bytesToRead); |
| var streamObj = FS.streams[stream]; |
| if (bytesRead == -1) { |
| if (streamObj) streamObj.error = true; |
| return 0; |
| } else { |
| if (bytesRead < bytesToRead) streamObj.eof = true; |
| return Math.floor(bytesRead / size); |
| } |
| }, |
| freopen__deps: ['$FS', 'fclose', 'fopen', '__setErrNo', '$ERRNO_CODES'], |
| freopen: function(filename, mode, stream) { |
| // FILE *freopen(const char *restrict filename, const char *restrict mode, FILE *restrict stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/freopen.html |
| if (!filename) { |
| if (!FS.streams[stream]) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return 0; |
| } |
| if (_freopen.buffer) _free(_freopen.buffer); |
| filename = intArrayFromString(FS.streams[stream].path); |
| filename = allocate(filename, 'i8', ALLOC_NORMAL); |
| } |
| _fclose(stream); |
| return _fopen(filename, mode); |
| }, |
| fseek__deps: ['$FS', 'lseek'], |
| fseek: function(stream, offset, whence) { |
| // int fseek(FILE *stream, long offset, int whence); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fseek.html |
| var ret = _lseek(stream, offset, whence); |
| if (ret == -1) { |
| return -1; |
| } else { |
| FS.streams[stream].eof = false; |
| return 0; |
| } |
| }, |
| fseeko: 'fseek', |
| fseeko64: 'fseek', |
| fsetpos__deps: ['$FS', 'lseek', '__setErrNo', '$ERRNO_CODES'], |
| fsetpos: function(stream, pos) { |
| // int fsetpos(FILE *stream, const fpos_t *pos); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fsetpos.html |
| if (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 { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| ftell__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'], |
| ftell: function(stream) { |
| // long ftell(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/ftell.html |
| if (FS.streams[stream]) { |
| stream = FS.streams[stream]; |
| if (stream.object.isDevice) { |
| ___setErrNo(ERRNO_CODES.ESPIPE); |
| return -1; |
| } else { |
| return stream.position; |
| } |
| } else { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } |
| }, |
| ftello: 'ftell', |
| ftello64: 'ftell', |
| fwrite__deps: ['$FS', 'write'], |
| fwrite: function(ptr, size, nitems, stream) { |
| // size_t fwrite(const void *restrict ptr, size_t size, size_t nitems, FILE *restrict stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/fwrite.html |
| var bytesToWrite = nitems * size; |
| if (bytesToWrite == 0) return 0; |
| var bytesWritten = _write(stream, ptr, bytesToWrite); |
| if (bytesWritten == -1) { |
| if (FS.streams[stream]) FS.streams[stream].error = true; |
| return 0; |
| } else { |
| return Math.floor(bytesWritten / size); |
| } |
| }, |
| popen__deps: ['__setErrNo', '$ERRNO_CODES'], |
| popen: function(command, mode) { |
| // FILE *popen(const char *command, const char *mode); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/popen.html |
| // We allow only one process, so no pipes. |
| ___setErrNo(ERRNO_CODES.EMFILE); |
| return 0; |
| }, |
| pclose__deps: ['__setErrNo', '$ERRNO_CODES'], |
| pclose: function(stream) { |
| // int pclose(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/pclose.html |
| // We allow only one process, so no pipes. |
| ___setErrNo(ERRNO_CODES.ECHILD); |
| return -1; |
| }, |
| perror__deps: ['puts', 'fputs', 'fputc', 'strerror', '__errno_location'], |
| perror: function(s) { |
| // void perror(const char *s); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/perror.html |
| var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; |
| if (s) { |
| _fputs(s, stdout); |
| _fputc({{{ charCode(':') }}}, stdout); |
| _fputc({{{ charCode(' ') }}}, stdout); |
| } |
| var errnum = {{{ makeGetValue('___errno_location()', '0', 'i32') }}}; |
| _puts(_strerror(errnum)); |
| }, |
| remove__deps: ['unlink', 'rmdir'], |
| remove: function(path) { |
| // int remove(const char *path); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/remove.html |
| var ret = _unlink(path); |
| if (ret == -1) ret = _rmdir(path); |
| return ret; |
| }, |
| rename__deps: ['__setErrNo', '$ERRNO_CODES'], |
| rename: function(old, new_) { |
| // int rename(const char *old, const char *new); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/rename.html |
| var oldObj = FS.analyzePath(Pointer_stringify(old)); |
| var newObj = FS.analyzePath(Pointer_stringify(new_)); |
| if (newObj.path == oldObj.path) { |
| return 0; |
| } else if (!oldObj.exists) { |
| ___setErrNo(oldObj.error); |
| return -1; |
| } else if (oldObj.isRoot || oldObj.path == FS.currentPath) { |
| ___setErrNo(ERRNO_CODES.EBUSY); |
| return -1; |
| } else if (newObj.parentPath && |
| newObj.parentPath.indexOf(oldObj.path) == 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } else if (newObj.exists && newObj.object.isFolder) { |
| ___setErrNo(ERRNO_CODES.EISDIR); |
| return -1; |
| } else { |
| delete oldObj.parentObject.contents[oldObj.name]; |
| newObj.parentObject.contents[newObj.name] = oldObj.object; |
| return 0; |
| } |
| }, |
| rewind__deps: ['$FS', 'fseek'], |
| rewind: function(stream) { |
| // void rewind(FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/rewind.html |
| _fseek(stream, 0, 0); // SEEK_SET. |
| if (FS.streams[stream]) FS.streams[stream].error = false; |
| }, |
| setvbuf: function(stream, buf, type, size) { |
| // int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/setvbuf.html |
| // TODO: Implement custom buffering. |
| return 0; |
| }, |
| setbuf__deps: ['setvbuf'], |
| setbuf: function(stream, buf) { |
| // void setbuf(FILE *restrict stream, char *restrict buf); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/setbuf.html |
| if (buf) _setvbuf(stream, buf, 0, 8192); // _IOFBF, BUFSIZ. |
| else _setvbuf(stream, buf, 2, 8192); // _IONBF, BUFSIZ. |
| }, |
| tmpnam__deps: ['$FS'], |
| tmpnam: function(s, dir, prefix) { |
| // char *tmpnam(char *s); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpnam.html |
| // NOTE: The dir and prefix arguments are for internal use only. |
| var folder = FS.findObject(dir || '/tmp'); |
| if (!folder || !folder.isFolder) { |
| dir = '/tmp'; |
| folder = FS.findObject(dir); |
| if (!folder || !folder.isFolder) return 0; |
| } |
| var name = prefix || 'file'; |
| do { |
| name += String.fromCharCode(65 + Math.floor(Math.random() * 25)); |
| } while (name in folder.contents); |
| var result = dir + '/' + name; |
| if (!_tmpnam.buffer) _tmpnam.buffer = _malloc(256); |
| if (!s) s = _tmpnam.buffer; |
| for (var i = 0; i < result.length; i++) { |
| {{{ makeSetValue('s', 'i', 'result.charCodeAt(i)', 'i8') }}}; |
| } |
| {{{ makeSetValue('s', 'i', '0', 'i8') }}}; |
| return s; |
| }, |
| tempnam__deps: ['tmpnam'], |
| tempnam: function(dir, pfx) { |
| // char *tempnam(const char *dir, const char *pfx); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/tempnam.html |
| return _tmpnam(0, Pointer_stringify(dir), Pointer_stringify(pfx)); |
| }, |
| tmpfile__deps: ['tmpnam', 'fopen'], |
| tmpfile: function() { |
| // FILE *tmpfile(void); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/tmpfile.html |
| // TODO: Delete the created file on closing. |
| if (_tmpfile.mode) { |
| _tmpfile.mode = allocate(intArrayFromString('w+'), 'i8', ALLOC_NORMAL); |
| } |
| return _fopen(_tmpnam(0), _tmpfile.mode); |
| }, |
| ungetc__deps: ['$FS'], |
| ungetc: function(c, stream) { |
| // int ungetc(int c, FILE *stream); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/ungetc.html |
| if (FS.streams[stream]) { |
| c = unSign(c & 0xFF); |
| FS.streams[stream].ungotten.push(c); |
| return c; |
| } else { |
| return -1; |
| } |
| }, |
| system__deps: ['__setErrNo', '$ERRNO_CODES'], |
| system: function(command) { |
| // int system(const char *command); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/system.html |
| // Can't call external programs. |
| ___setErrNo(ERRNO_CODES.EAGAIN); |
| return -1; |
| }, |
| fscanf__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', |
| '_scanString', 'fgetc', 'fseek', 'ftell'], |
| fscanf: function(stream, format, varargs) { |
| // int fscanf(FILE *restrict stream, const char *restrict format, ... ); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html |
| if (FS.streams[stream]) { |
| var i = _ftell(stream), SEEK_SET = 0; |
| var get = function () { i++; return _fgetc(stream); }; |
| var unget = function () { _fseek(stream, --i, SEEK_SET); }; |
| return __scanString(format, get, unget, varargs); |
| } else { |
| return -1; |
| } |
| }, |
| scanf__deps: ['fscanf'], |
| scanf: function(format, varargs) { |
| // int scanf(const char *restrict format, ... ); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html |
| var stdin = {{{ makeGetValue(makeGlobalUse('_stdin'), '0', 'void*') }}}; |
| return _fscanf(stdin, format, varargs); |
| }, |
| sscanf__deps: ['_scanString'], |
| sscanf: function(s, format, varargs) { |
| // int sscanf(const char *restrict s, const char *restrict format, ... ); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/scanf.html |
| var index = 0; |
| var get = function() { return {{{ makeGetValue('s', 'index++', 'i8') }}}; }; |
| var unget = function() { index--; }; |
| return __scanString(format, get, unget, varargs); |
| }, |
| snprintf__deps: ['_formatString'], |
| snprintf: function(s, n, format, varargs) { |
| // int snprintf(char *restrict s, size_t n, const char *restrict format, ...); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html |
| var result = __formatString(format, varargs); |
| var limit = (n === undefined) ? result.length |
| : Math.min(result.length, Math.max(n - 1, 0)); |
| if (s < 0) { |
| s = -s; |
| var buf = _malloc(limit+1); |
| {{{ makeSetValue('s', '0', 'buf', 'i8*') }}}; |
| s = buf; |
| } |
| for (var i = 0; i < limit; i++) { |
| {{{ makeSetValue('s', 'i', 'result[i]', 'i8') }}}; |
| } |
| if (limit < n || (n === undefined)) {{{ makeSetValue('s', 'i', '0', 'i8') }}}; |
| return result.length; |
| }, |
| fprintf__deps: ['fwrite', '_formatString'], |
| fprintf: function(stream, format, varargs) { |
| // int fprintf(FILE *restrict stream, const char *restrict format, ...); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html |
| var result = __formatString(format, varargs); |
| var stack = Runtime.stackSave(); |
| var ret = _fwrite(allocate(result, 'i8', ALLOC_STACK), 1, result.length, stream); |
| Runtime.stackRestore(stack); |
| return ret; |
| }, |
| printf__deps: ['fprintf'], |
| printf: function(format, varargs) { |
| // int printf(const char *restrict format, ...); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html |
| var stdout = {{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}; |
| return _fprintf(stdout, format, varargs); |
| }, |
| sprintf__deps: ['snprintf'], |
| sprintf: function(s, format, varargs) { |
| // int sprintf(char *restrict s, const char *restrict format, ...); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/printf.html |
| return _snprintf(s, undefined, format, varargs); |
| }, |
| asprintf__deps: ['sprintf'], |
| asprintf: function(s, format, varargs) { |
| return _sprintf(-s, format, varargs); |
| }, |
| |
| #if TARGET_X86 |
| // va_arg is just like our varargs |
| vfprintf: 'fprintf', |
| vsnprintf: 'snprintf', |
| vprintf: 'printf', |
| vsprintf: 'sprintf', |
| vasprintf: 'asprintf', |
| vscanf: 'scanf', |
| vfscanf: 'fscanf', |
| vsscanf: 'sscanf', |
| #endif |
| |
| #if TARGET_LE32 |
| // convert va_arg into varargs |
| vfprintf__deps: ['fprintf'], |
| vfprintf: function(s, f, va_arg) { |
| return _fprintf(s, f, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| vsnprintf__deps: ['snprintf'], |
| vsnprintf: function(s, n, format, va_arg) { |
| return _snprintf(s, n, format, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| vprintf__deps: ['printf'], |
| vprintf: function(format, va_arg) { |
| return _printf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| vsprintf__deps: ['sprintf'], |
| vsprintf: function(s, format, va_arg) { |
| return _sprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| vasprintf__deps: ['asprintf'], |
| vasprintf: function(s, format, va_arg) { |
| return _asprintf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| vscanf__deps: ['scanf'], |
| vscanf: function(format, va_arg) { |
| return _scanf(format, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| vfscanf__deps: ['fscanf'], |
| vfscanf: function(s, format, va_arg) { |
| return _fscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| vsscanf__deps: ['sscanf'], |
| vsscanf: function(s, format, va_arg) { |
| return _sscanf(s, format, {{{ makeGetValue('va_arg', 0, '*') }}}); |
| }, |
| #endif |
| |
| fopen64: 'fopen', |
| __01fopen64_: 'fopen', |
| __01freopen64_: 'freopen', |
| __01fseeko64_: 'fseek', |
| __01ftello64_: 'ftell', |
| __01tmpfile64_: 'tmpfile', |
| __isoc99_fscanf: 'fscanf', |
| // TODO: Check if any other aliases are needed. |
| _IO_getc: 'getc', |
| _IO_putc: 'putc', |
| _ZNSo3putEc: 'putchar', |
| _ZNSo5flushEv__deps: ['fflush', 'stdout'], |
| _ZNSo5flushEv: function() { |
| _fflush({{{ makeGetValue(makeGlobalUse('_stdout'), '0', 'void*') }}}); |
| }, |
| |
| // ========================================================================== |
| // sys/mman.h |
| // ========================================================================== |
| |
| mmap__deps: ['$FS'], |
| mmap: function(start, num, prot, flags, stream, offset) { |
| /* FIXME: Since mmap is normally implemented at the kernel level, |
| * this implementation simply uses malloc underneath the call to |
| * mmap. |
| */ |
| var MAP_PRIVATE = 2; |
| var allocated = false; |
| |
| if (!_mmap.mappings) _mmap.mappings = {}; |
| |
| if (stream == -1) { |
| var ptr = _malloc(num); |
| if (!ptr) return -1; |
| _memset(ptr, 0, num); |
| allocated = true; |
| } else { |
| var info = FS.streams[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; |
| } |
| } |
| |
| _mmap.mappings[ptr] = { malloc: ptr, num: num, allocated: allocated }; |
| return ptr; |
| }, |
| __01mmap64_: 'mmap', |
| |
| munmap: function(start, num) { |
| if (!_mmap.mappings) _mmap.mappings = {}; |
| // TODO: support unmmap'ing parts of allocations |
| var info = _mmap.mappings[start]; |
| if (!info) return 0; |
| if (num == info.num) { |
| _mmap.mappings[start] = null; |
| if (info.allocated) { |
| _free(info.malloc); |
| } |
| } |
| return 0; |
| }, |
| |
| // TODO: Implement mremap. |
| |
| // ========================================================================== |
| // stdlib.h |
| // ========================================================================== |
| |
| // tiny, fake malloc/free implementation. If the program actually uses malloc, |
| // a compiled version will be used; this will only be used if the runtime |
| // needs to allocate something, for which this is good enough if otherwise |
| // no malloc is needed. |
| malloc: function(bytes) { |
| /* Over-allocate to make sure it is byte-aligned by 8. |
| * This will leak memory, but this is only the dummy |
| * implementation (replaced by dlmalloc normally) so |
| * not an issue. |
| */ |
| #if ASSERTIONS == 2 |
| Runtime.warnOnce('using stub malloc (reference it from C to have the real one included)'); |
| #endif |
| var ptr = Runtime.dynamicAlloc(bytes + 8); |
| return (ptr+8) & 0xFFFFFFF8; |
| }, |
| free: function() { |
| #if ASSERTIONS == 2 |
| Runtime.warnOnce('using stub free (reference it from C to have the real one included)'); |
| #endif |
| }, |
| |
| calloc__deps: ['malloc'], |
| calloc: function(n, s) { |
| var ret = _malloc(n*s); |
| _memset(ret, 0, n*s); |
| return ret; |
| }, |
| |
| abs: 'Math.abs', |
| labs: 'Math.abs', |
| #if USE_TYPED_ARRAYS == 2 |
| llabs__deps: [function() { Types.preciseI64MathUsed = 1 }], |
| llabs: function(lo, hi) { |
| i64Math.abs(lo, hi); |
| {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32')]) }}}; |
| }, |
| #else |
| llabs: function(lo, hi) { |
| throw 'unsupported llabs'; |
| }, |
| #endif |
| |
| exit__deps: ['_exit'], |
| exit: function(status) { |
| __exit(status); |
| }, |
| |
| _ZSt9terminatev__deps: ['exit'], |
| _ZSt9terminatev: function() { |
| _exit(-1234); |
| }, |
| |
| atexit: function(func, arg) { |
| __ATEXIT__.unshift({ func: func, arg: arg }); |
| }, |
| __cxa_atexit: 'atexit', |
| |
| abort: function() { |
| ABORT = true; |
| throw 'abort() at ' + (new Error().stack); |
| }, |
| |
| bsearch: function(key, base, num, size, compar) { |
| var cmp = function(x, y) { |
| return Runtime.dynCall('iii', compar, [x, y]) |
| }; |
| var left = 0; |
| var right = num; |
| var mid, test, addr; |
| |
| while (left < right) { |
| mid = (left + right) >>> 1; |
| addr = base + (mid * size); |
| test = cmp(key, addr); |
| |
| if (test < 0) { |
| right = mid; |
| } else if (test > 0) { |
| left = mid + 1; |
| } else { |
| return addr; |
| } |
| } |
| |
| return 0; |
| }, |
| |
| realloc__deps: ['memcpy'], |
| realloc: function(ptr, size) { |
| // Very simple, inefficient implementation - if you use a real malloc, best to use |
| // a real realloc with it |
| if (!size) { |
| if (ptr) _free(ptr); |
| return 0; |
| } |
| var ret = _malloc(size); |
| if (ptr) { |
| _memcpy(ret, ptr, size); // might be some invalid reads |
| _free(ptr); |
| } |
| return ret; |
| }, |
| |
| _parseInt__deps: ['isspace', '__setErrNo', '$ERRNO_CODES'], |
| _parseInt: function(str, endptr, base, min, max, bits, unsign) { |
| // Skip space. |
| while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; |
| |
| // Check for a plus/minus sign. |
| var multiplier = 1; |
| if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) { |
| multiplier = -1; |
| str++; |
| } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) { |
| str++; |
| } |
| |
| // Find base. |
| var finalBase = base; |
| if (!finalBase) { |
| if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { |
| if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || |
| {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { |
| finalBase = 16; |
| str += 2; |
| } else { |
| finalBase = 8; |
| str++; |
| } |
| } |
| } else if (finalBase==16) { |
| if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { |
| if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || |
| {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { |
| str += 2; |
| } |
| } |
| } |
| if (!finalBase) finalBase = 10; |
| |
| // Get digits. |
| var chr; |
| var ret = 0; |
| while ((chr = {{{ makeGetValue('str', 0, 'i8') }}}) != 0) { |
| var digit = parseInt(String.fromCharCode(chr), finalBase); |
| if (isNaN(digit)) { |
| break; |
| } else { |
| ret = ret * finalBase + digit; |
| str++; |
| } |
| } |
| |
| // Apply sign. |
| ret *= multiplier; |
| |
| // Set end pointer. |
| if (endptr) { |
| {{{ makeSetValue('endptr', 0, 'str', '*') }}} |
| } |
| |
| // Unsign if needed. |
| if (unsign) { |
| if (Math.abs(ret) > max) { |
| ret = max; |
| ___setErrNo(ERRNO_CODES.ERANGE); |
| } else { |
| ret = unSign(ret, bits); |
| } |
| } |
| |
| // Validate range. |
| if (ret > max || ret < min) { |
| ret = ret > max ? max : min; |
| ___setErrNo(ERRNO_CODES.ERANGE); |
| } |
| |
| #if USE_TYPED_ARRAYS == 2 |
| if (bits == 64) { |
| {{{ makeStructuralReturn(splitI64('ret')) }}}; |
| } |
| #endif |
| |
| return ret; |
| }, |
| #if USE_TYPED_ARRAYS == 2 |
| _parseInt64__deps: ['isspace', '__setErrNo', '$ERRNO_CODES', function() { Types.preciseI64MathUsed = 1 }], |
| _parseInt64: function(str, endptr, base, min, max, unsign) { |
| var isNegative = false; |
| // Skip space. |
| while (_isspace({{{ makeGetValue('str', 0, 'i8') }}})) str++; |
| |
| // Check for a plus/minus sign. |
| if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('-') }}}) { |
| str++; |
| isNegative = true; |
| } else if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('+') }}}) { |
| str++; |
| } |
| |
| // Find base. |
| var ok = false; |
| var finalBase = base; |
| if (!finalBase) { |
| if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { |
| if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || |
| {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { |
| finalBase = 16; |
| str += 2; |
| } else { |
| finalBase = 8; |
| ok = true; // we saw an initial zero, perhaps the entire thing is just "0" |
| } |
| } |
| } else if (finalBase==16) { |
| if ({{{ makeGetValue('str', 0, 'i8') }}} == {{{ charCode('0') }}}) { |
| if ({{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('x') }}} || |
| {{{ makeGetValue('str+1', 0, 'i8') }}} == {{{ charCode('X') }}}) { |
| str += 2; |
| } |
| } |
| } |
| if (!finalBase) finalBase = 10; |
| start = str; |
| |
| // Get digits. |
| var chr; |
| while ((chr = {{{ makeGetValue('str', 0, 'i8') }}}) != 0) { |
| var digit = parseInt(String.fromCharCode(chr), finalBase); |
| if (isNaN(digit)) { |
| break; |
| } else { |
| str++; |
| ok = true; |
| } |
| } |
| |
| if (!ok) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| {{{ makeStructuralReturn(['0', '0']) }}}; |
| } |
| |
| // Set end pointer. |
| if (endptr) { |
| {{{ makeSetValue('endptr', 0, 'str', '*') }}} |
| } |
| |
| try { |
| var numberString = isNegative ? '-'+Pointer_stringify(start, str - start) : Pointer_stringify(start, str - start); |
| i64Math.fromString(numberString, finalBase, min, max, unsign); |
| } catch(e) { |
| ___setErrNo(ERRNO_CODES.ERANGE); // not quite correct |
| } |
| |
| {{{ makeStructuralReturn([makeGetTempDouble(0, 'i32'), makeGetTempDouble(1, 'i32')]) }}}; |
| }, |
| #endif |
| strtoll__deps: ['_parseInt64'], |
| strtoll: function(str, endptr, base) { |
| return __parseInt64(str, endptr, base, '-9223372036854775808', '9223372036854775807'); // LLONG_MIN, LLONG_MAX. |
| }, |
| strtoll_l: 'strtoll', // no locale support yet |
| strtol__deps: ['_parseInt'], |
| strtol: function(str, endptr, base) { |
| return __parseInt(str, endptr, base, -2147483648, 2147483647, 32); // LONG_MIN, LONG_MAX. |
| }, |
| strtol_l: 'strtol', // no locale support yet |
| strtoul__deps: ['_parseInt'], |
| strtoul: function(str, endptr, base) { |
| return __parseInt(str, endptr, base, 0, 4294967295, 32, true); // ULONG_MAX. |
| }, |
| strtoul_l: 'strtoul', // no locale support yet |
| strtoull__deps: ['_parseInt64'], |
| strtoull: function(str, endptr, base) { |
| return __parseInt64(str, endptr, base, 0, '18446744073709551615', true); // ULONG_MAX. |
| }, |
| strtoull_l: 'strtoull', // no locale support yet |
| |
| atoi__deps: ['strtol'], |
| atoi: function(ptr) { |
| return _strtol(ptr, null, 10); |
| }, |
| atol: 'atoi', |
| |
| atoll__deps: ['strtoll'], |
| atoll: function(ptr) { |
| return _strtoll(ptr, null, 10); |
| }, |
| |
| qsort__deps: ['memcpy'], |
| qsort: function(base, num, size, cmp) { |
| if (num == 0 || size == 0) return; |
| // forward calls to the JavaScript sort method |
| // first, sort the items logically |
| var comparator = function(x, y) { |
| return Runtime.dynCall('iii', cmp, [x, y]); |
| } |
| var keys = []; |
| for (var i = 0; i < num; i++) keys.push(i); |
| keys.sort(function(a, b) { |
| return comparator(base+a*size, base+b*size); |
| }); |
| // apply the sort |
| var temp = _malloc(num*size); |
| _memcpy(temp, base, num*size); |
| for (var i = 0; i < num; i++) { |
| if (keys[i] == i) continue; // already in place |
| _memcpy(base+i*size, temp+keys[i]*size, size); |
| } |
| _free(temp); |
| }, |
| |
| environ: 'allocate(1, "i32*", ALLOC_STATIC)', |
| __environ__deps: ['environ'], |
| __environ: '_environ', |
| __buildEnvironment__deps: ['__environ'], |
| __buildEnvironment: function(env) { |
| // WARNING: Arbitrary limit! |
| var MAX_ENV_VALUES = 64; |
| var TOTAL_ENV_SIZE = 1024; |
| |
| // Statically allocate memory for the environment. |
| var poolPtr; |
| var envPtr; |
| if (!___buildEnvironment.called) { |
| ___buildEnvironment.called = true; |
| // Set default values. Use string keys for Closure Compiler compatibility. |
| ENV['USER'] = 'root'; |
| ENV['PATH'] = '/'; |
| ENV['PWD'] = '/'; |
| ENV['HOME'] = '/home/emscripten'; |
| ENV['LANG'] = 'en_US.UTF-8'; |
| ENV['_'] = './this.program'; |
| // Allocate memory. |
| poolPtr = allocate(TOTAL_ENV_SIZE, 'i8', ALLOC_STATIC); |
| envPtr = allocate(MAX_ENV_VALUES * {{{ Runtime.QUANTUM_SIZE }}}, |
| 'i8*', ALLOC_STATIC); |
| {{{ makeSetValue('envPtr', '0', 'poolPtr', 'i8*') }}} |
| {{{ makeSetValue(makeGlobalUse('_environ'), 0, 'envPtr', 'i8*') }}}; |
| } else { |
| envPtr = {{{ makeGetValue(makeGlobalUse('_environ'), '0', 'i8**') }}}; |
| poolPtr = {{{ makeGetValue('envPtr', '0', 'i8*') }}}; |
| } |
| |
| // Collect key=value lines. |
| var strings = []; |
| var totalSize = 0; |
| for (var key in env) { |
| if (typeof env[key] === 'string') { |
| var line = key + '=' + env[key]; |
| strings.push(line); |
| totalSize += line.length; |
| } |
| } |
| if (totalSize > TOTAL_ENV_SIZE) { |
| throw new Error('Environment size exceeded TOTAL_ENV_SIZE!'); |
| } |
| |
| // Make new. |
| var ptrSize = {{{ Runtime.getNativeTypeSize('i8*') }}}; |
| for (var i = 0; i < strings.length; i++) { |
| var line = strings[i]; |
| for (var j = 0; j < line.length; j++) { |
| {{{ makeSetValue('poolPtr', 'j', 'line.charCodeAt(j)', 'i8') }}}; |
| } |
| {{{ makeSetValue('poolPtr', 'j', '0', 'i8') }}}; |
| {{{ makeSetValue('envPtr', 'i * ptrSize', 'poolPtr', 'i8*') }}}; |
| poolPtr += line.length + 1; |
| } |
| {{{ makeSetValue('envPtr', 'strings.length * ptrSize', '0', 'i8*') }}}; |
| }, |
| $ENV__deps: ['__buildEnvironment'], |
| $ENV__postset: '___buildEnvironment(ENV);', |
| $ENV: {}, |
| getenv__deps: ['$ENV'], |
| getenv: function(name) { |
| // char *getenv(const char *name); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/getenv.html |
| if (name === 0) return 0; |
| name = Pointer_stringify(name); |
| if (!ENV.hasOwnProperty(name)) return 0; |
| |
| if (_getenv.ret) _free(_getenv.ret); |
| _getenv.ret = allocate(intArrayFromString(ENV[name]), 'i8', ALLOC_NORMAL); |
| return _getenv.ret; |
| }, |
| clearenv__deps: ['$ENV', '__buildEnvironment'], |
| clearenv: function(name) { |
| // int clearenv (void); |
| // http://www.gnu.org/s/hello/manual/libc/Environment-Access.html#index-clearenv-3107 |
| ENV = {}; |
| ___buildEnvironment(ENV); |
| return 0; |
| }, |
| setenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], |
| setenv: function(envname, envval, overwrite) { |
| // int setenv(const char *envname, const char *envval, int overwrite); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/setenv.html |
| if (envname === 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| var name = Pointer_stringify(envname); |
| var val = Pointer_stringify(envval); |
| if (name === '' || name.indexOf('=') !== -1) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| if (ENV.hasOwnProperty(name) && !overwrite) return 0; |
| ENV[name] = val; |
| ___buildEnvironment(ENV); |
| return 0; |
| }, |
| unsetenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], |
| unsetenv: function(name) { |
| // int unsetenv(const char *name); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/unsetenv.html |
| if (name === 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| name = Pointer_stringify(name); |
| if (name === '' || name.indexOf('=') !== -1) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| if (ENV.hasOwnProperty(name)) { |
| delete ENV[name]; |
| ___buildEnvironment(ENV); |
| } |
| return 0; |
| }, |
| putenv__deps: ['$ENV', '__buildEnvironment', '$ERRNO_CODES', '__setErrNo'], |
| putenv: function(string) { |
| // int putenv(char *string); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/putenv.html |
| // WARNING: According to the standard (and the glibc implementation), the |
| // string is taken by reference so future changes are reflected. |
| // We copy it instead, possibly breaking some uses. |
| if (string === 0) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| string = Pointer_stringify(string); |
| var splitPoint = string.indexOf('=') |
| if (string === '' || string.indexOf('=') === -1) { |
| ___setErrNo(ERRNO_CODES.EINVAL); |
| return -1; |
| } |
| var name = string.slice(0, splitPoint); |
| var value = string.slice(splitPoint + 1); |
| if (!(name in ENV) || ENV[name] !== value) { |
| ENV[name] = value; |
| ___buildEnvironment(ENV); |
| } |
| return 0; |
| }, |
| |
| getloadavg: function(loadavg, nelem) { |
| // int getloadavg(double loadavg[], int nelem); |
| // http://linux.die.net/man/3/getloadavg |
| var limit = Math.min(nelem, 3); |
| var doubleSize = {{{ Runtime.getNativeTypeSize('double') }}}; |
| for (var i = 0; i < limit; i++) { |
| {{{ makeSetValue('loadavg', 'i * doubleSize', '0.1', 'double') }}} |
| } |
| return limit; |
| }, |
| |
| // Use browser's Math.random(). We can't set a seed, though. |
| srand: function(seed) {}, // XXX ignored |
| rand: function() { |
| return Math.floor(Math.random()*0x80000000); |
| }, |
| rand_r: function(seed) { // XXX ignores the seed |
| return Math.floor(Math.random()*0x80000000); |
| }, |
| |
| drand48: function() { |
| return Math.random(); |
| }, |
| |
| realpath__deps: ['$FS', '__setErrNo'], |
| realpath: function(file_name, resolved_name) { |
| // char *realpath(const char *restrict file_name, char *restrict resolved_name); |
| // http://pubs.opengroup.org/onlinepubs/009604499/functions/realpath.html |
| var absolute = FS.analyzePath(Pointer_stringify(file_name)); |
| if (absolute.error) { |
| ___setErrNo(absolute.error); |
| return 0; |
| } else { |
| var size = Math.min(4095, absolute.path.length); // PATH_MAX - 1. |
| for (var i = 0; i < size; i++) { |
| {{{ makeSetValue('resolved_name', 'i', 'absolute.path.charCodeAt(i)', 'i8') }}} |
| } |
| {{{ makeSetValue('resolved_name', 'size', '0', 'i8') }}} |
| return resolved_name; |
| } |
| }, |
| |
| arc4random: 'rand', |
| |
| // ========================================================================== |
| // string.h |
| // ========================================================================== |
| |
| // FIXME: memcpy, memmove and memset should all return their destination pointers. |
| |
| memcpy__inline: function (dest, src, num, align) { |
| var ret = ''; |
| #if ASSERTIONS |
| #if ASM_JS == 0 |
| ret += "assert(" + num + " % 1 === 0);"; //, 'memcpy given ' + " + num + " + ' bytes to copy. Problem with quantum=1 corrections perhaps?');"; |
| #endif |
| #endif |
| ret += makeCopyValues(dest, src, num, 'null', null, align); |
| return ret; |
| }, |
| |
| memcpy__asm: true, |
| memcpy__sig: 'iiii', |
| memcpy: function (dest, src, num) { |
| dest = dest|0; src = src|0; num = num|0; |
| var ret = 0; |
| ret = dest|0; |
| if ((dest&3) == (src&3)) { |
| while (dest & 3) { |
| if ((num|0) == 0) return ret|0; |
| {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; |
| dest = (dest+1)|0; |
| src = (src+1)|0; |
| num = (num-1)|0; |
| } |
| while ((num|0) >= 4) { |
| {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i32'), 'i32') }}}; |
| dest = (dest+4)|0; |
| src = (src+4)|0; |
| num = (num-4)|0; |
| } |
| } |
| while ((num|0) > 0) { |
| {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; |
| dest = (dest+1)|0; |
| src = (src+1)|0; |
| num = (num-1)|0; |
| } |
| return ret|0; |
| }, |
| |
| llvm_memcpy_i32: 'memcpy', |
| llvm_memcpy_i64: 'memcpy', |
| llvm_memcpy_p0i8_p0i8_i32: 'memcpy', |
| llvm_memcpy_p0i8_p0i8_i64: 'memcpy', |
| |
| memmove__sig: 'viii', |
| memmove__asm: true, |
| memmove__deps: ['memcpy'], |
| memmove: function(dest, src, num) { |
| dest = dest|0; src = src|0; num = num|0; |
| if (((src|0) < (dest|0)) & ((dest|0) < ((src + num)|0))) { |
| // Unlikely case: Copy backwards in a safe manner |
| src = (src + num)|0; |
| dest = (dest + num)|0; |
| while ((num|0) > 0) { |
| dest = (dest - 1)|0; |
| src = (src - 1)|0; |
| num = (num - 1)|0; |
| {{{ makeSetValueAsm('dest', 0, makeGetValueAsm('src', 0, 'i8'), 'i8') }}}; |
| } |
| } else { |
| _memcpy(dest, src, num) | 0; |
| } |
| }, |
| llvm_memmove_i32: 'memmove', |
| llvm_memmove_i64: 'memmove', |
| llvm_memmove_p0i8_p0i8_i32: 'memmove', |
| llvm_memmove_p0i8_p0i8_i64: 'memmove', |
| |
| memset__inline: function(ptr, value, num, align) { |
| return makeSetValues(ptr, 0, value, 'null', num, align); |
| }, |
| memset__sig: 'viii', |
| memset__asm: true, |
| memset: function(ptr, value, num) { |
| #if USE_TYPED_ARRAYS == 2 |
| ptr = ptr|0; value = value|0; num = num|0; |
| var stop = 0, value4 = 0, stop4 = 0, unaligned = 0; |
| stop = (ptr + num)|0; |
| if ((num|0) >= {{{ Math.round(2.5*UNROLL_LOOP_MAX) }}}) { |
| // This is unaligned, but quite large, so work hard to get to aligned settings |
| value = value & 0xff; |
| unaligned = ptr & 3; |
| value4 = value | (value << 8) | (value << 16) | (value << 24); |
| stop4 = stop & ~3; |
| if (unaligned) { |
| unaligned = (ptr + 4 - unaligned)|0; |
| while ((ptr|0) < (unaligned|0)) { // no need to check for stop, since we have large num |
| {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; |
| ptr = (ptr+1)|0; |
| } |
| } |
| while ((ptr|0) < (stop4|0)) { |
| {{{ makeSetValueAsm('ptr', 0, 'value4', 'i32') }}}; |
| ptr = (ptr+4)|0; |
| } |
| } |
| while ((ptr|0) < (stop|0)) { |
| {{{ makeSetValueAsm('ptr', 0, 'value', 'i8') }}}; |
| ptr = (ptr+1)|0; |
| } |
| #else |
| {{{ makeSetValues('ptr', '0', 'value', 'null', 'num') }}}; |
| #endif |
| }, |
| llvm_memset_i32: 'memset', |
| llvm_memset_p0i8_i32: 'memset', |
| llvm_memset_p0i8_i64: 'memset', |
| |
| strlen__sig: 'ii', |
| strlen__asm: true, |
| strlen: function(ptr) { |
| ptr = ptr|0; |
| var curr = 0; |
| curr = ptr; |
| while ({{{ makeGetValueAsm('curr', '0', 'i8') }}}) { |
| curr = (curr + 1)|0; |
| } |
| return (curr - ptr)|0; |
| }, |
| |
| strspn: function(pstr, pset) { |
| var str = pstr, set, strcurr, setcurr; |
| while (1) { |
| strcurr = {{{ makeGetValue('str', '0', 'i8') }}}; |
| if (!strcurr) return str - pstr; |
| set = pset; |
| while (1) { |
| setcurr = {{{ makeGetValue('set', '0', 'i8') }}}; |
| if (!setcurr || setcurr == strcurr) break; |
| set++; |
| } |
| if (!setcurr) return str - pstr; |
| str++; |
| } |
| }, |
| |
| strcspn: function(pstr, pset) { |
| var str = pstr, set, strcurr, setcurr; |
| while (1) { |
| strcurr = {{{ makeGetValue('str', '0', 'i8') }}}; |
| if (!strcurr) return str - pstr; |
| set = pset; |
| while (1) { |
| setcurr = {{{ makeGetValue('set', '0', 'i8') }}}; |
| if (!setcurr || setcurr == strcurr) break; |
| set++; |
| } |
| if (setcurr) return str - pstr; |
| str++; |
| } |
| }, |
| |
| strcpy__asm: true, |
| strcpy__sig: 'iii', |
| strcpy: function(pdest, psrc) { |
| pdest = pdest|0; psrc = psrc|0; |
| var i = 0; |
| do { |
| {{{ makeCopyValues('(pdest+i)|0', '(psrc+i)|0', 1, 'i8', null, 1) }}}; |
| i = (i+1)|0; |
| } while ({{{ makeGetValueAsm('psrc', 'i-1', 'i8') }}}); |
| return pdest|0; |
| }, |
| |
| stpcpy: function(pdest, psrc) { |
| var i = 0; |
| do { |
| {{{ makeCopyValues('pdest+i', 'psrc+i', 1, 'i8', null, 1) }}}; |
| i ++; |
| } while ({{{ makeGetValue('psrc', 'i-1', 'i8') }}} != 0); |
| return pdest + i - 1; |
| }, |
| |
| strncpy__asm: true, |
| strncpy__sig: 'iiii', |
| strncpy: function(pdest, psrc, num) { |
| pdest = pdest|0; psrc = psrc|0; num = num|0; |
| var padding = 0, curr = 0, i = 0; |
| while ((i|0) < (num|0)) { |
| curr = padding ? 0 : {{{ makeGetValueAsm('psrc', 'i', 'i8') }}}; |
| {{{ makeSetValue('pdest', 'i', 'curr', 'i8') }}} |
| padding = padding ? 1 : ({{{ makeGetValueAsm('psrc', 'i', 'i8') }}} == 0); |
| i = (i+1)|0; |
| } |
| return pdest|0; |
| }, |
| |
| strlwr__deps:['tolower'], |
| strlwr: function(pstr){ |
| var i = 0; |
| while(1) { |
| var x = {{{ makeGetValue('pstr', 'i', 'i8') }}}; |
| if (x == 0) break; |
| {{{ makeSetValue('pstr', 'i', '_tolower(x)', 'i8') }}}; |
| i++; |
| } |
| }, |
| |
| strupr__deps:['toupper'], |
| strupr: function(pstr){ |
| var i = 0; |
| while(1) { |
| var x = {{{ makeGetValue('pstr', 'i', 'i8') }}}; |
| if (x == 0) break; |
| {{{ makeSetValue('pstr', 'i', '_toupper(x)', 'i8') }}}; |
| i++; |
| } |
| }, |
| |
| strcat__asm: true, |
| strcat__sig: 'iii', |
| strcat__deps: ['strlen'], |
| strcat: function(pdest, psrc) { |
| pdest = pdest|0; psrc = psrc|0; |
| var i = 0; |
| var pdestEnd = 0; |
| pdestEnd = (pdest + (_strlen(pdest)|0))|0; |
| do { |
| {{{ makeCopyValues('pdestEnd+i', 'psrc+i', 1, 'i8', null, 1) }}}; |
| i = (i+1)|0; |
| } while ({{{ makeGetValueAsm('psrc', 'i-1', 'i8') }}}); |
| return pdest|0; |
| }, |
| |
| strncat__deps: ['strlen'], |
| strncat: function(pdest, psrc, num) { |
| var len = _strlen(pdest); |
| var i = 0; |
| while(1) { |
| {{{ makeCopyValues('pdest+len+i', 'psrc+i', 1, 'i8', null, 1) }}}; |
| if ({{{ makeGetValue('pdest', 'len+i', 'i8') }}} == 0) break; |
| i ++; |
| if (i == num) { |
| {{{ makeSetValue('pdest', 'len+i', 0, 'i8') }}} |
| break; |
| } |
| } |
| return pdest; |
| }, |
| |
| strcmp__deps: ['strncmp'], |
| strcmp: function(px, py) { |
| return _strncmp(px, py, TOTAL_MEMORY); |
| }, |
| // We always assume ASCII locale. |
| strcoll: 'strcmp', |
| |
| strcasecmp__asm: true, |
| strcasecmp__sig: 'iii', |
| strcasecmp__deps: ['strncasecmp'], |
| strcasecmp: function(px, py) { |
| px = px|0; py = py|0; |
| return _strncasecmp(px, py, -1)|0; |
| }, |
| |
| strncmp: function(px, py, n) { |
| var i = 0; |
| while (i < n) { |
| var x = {{{ makeGetValue('px', 'i', 'i8', 0, 1) }}}; |
| var y = {{{ makeGetValue('py', 'i', 'i8', 0, 1) }}}; |
| if (x == y && x == 0) return 0; |
| if (x == 0) return -1; |
| if (y == 0) return 1; |
| if (x == y) { |
| i ++; |
| continue; |
| } else { |
| return x > y ? 1 : -1; |
| } |
| } |
| return 0; |
| }, |
| |
| strncasecmp__asm: true, |
| strncasecmp__sig: 'iiii', |
| strncasecmp__deps: ['tolower'], |
| strncasecmp: function(px, py, n) { |
| px = px|0; py = py|0; n = n|0; |
| var i = 0, x = 0, y = 0; |
| while ((i>>>0) < (n>>>0)) { |
| x = _tolower({{{ makeGetValueAsm('px', 'i', 'i8', 0, 1) }}})|0; |
| y = _tolower({{{ makeGetValueAsm('py', 'i', 'i8', 0, 1) }}})|0; |
| if (((x|0) == (y|0)) & ((x|0) == 0)) return 0; |
| if ((x|0) == 0) return -1; |
| if ((y|0) == 0) return 1; |
| if ((x|0) == (y|0)) { |
| i = (i + 1)|0; |
| continue; |
| } else { |
| return ((x>>>0) > (y>>>0) ? 1 : -1)|0; |
| } |
| } |
| return 0; |
| }, |
| |
| memcmp__asm: true, |
| memcmp__sig: 'iiii', |
| memcmp: function(p1, p2, num) { |
| p1 = p1|0; p2 = p2|0; num = num|0; |
| var i = 0, v1 = 0, v2 = 0; |
| while ((i|0) < (num|0)) { |
| var v1 = {{{ makeGetValueAsm('p1', 'i', 'i8', true) }}}; |
| var v2 = {{{ makeGetValueAsm('p2', 'i', 'i8', true) }}}; |
| if ((v1|0) != (v2|0)) return ((v1|0) > (v2|0) ? 1 : -1)|0; |
| i = (i+1)|0; |
| } |
| return 0; |
| }, |
| |
| memchr: function(ptr, chr, num) { |
| chr = unSign(chr); |
| for (var i = 0; i < num; i++) { |
| if ({{{ makeGetValue('ptr', 0, 'i8') }}} == chr) return ptr; |
| ptr++; |
| } |
| return 0; |
| }, |
| |
| strnlen: function(ptr, num) { |
| for (var i = 0; i < num; i++) { |
| if ({{{ makeGetValue('ptr', 0, 'i8') }}} == 0) return i; |
| ptr++; |
| } |
| return num; |
| }, |
| |
| strstr: function(ptr1, ptr2) { |
| var check = 0, start; |
| do { |
| if (!check) { |
| start = ptr1; |
| check = ptr2; |
| } |
| var curr1 = {{{ makeGetValue('ptr1++', 0, 'i8') }}}; |
| var curr2 = {{{ makeGetValue('check++', 0, 'i8') }}}; |
| if (curr2 == 0) return start; |
| if (curr2 != curr1) { |
| // rewind to one character after start, to find ez in eeez |
| ptr1 = start + 1; |
| check = 0; |
| } |
| } while (curr1); |
| return 0; |
| }, |
| |
| strchr: function(ptr, chr) { |
| ptr--; |
| do { |
| ptr++; |
| var val = {{{ makeGetValue('ptr', 0, 'i8') }}}; |
| if (val == chr) return ptr; |
| } while (val); |
| return 0; |
| }, |
| index: 'strchr', |
| |
| strrchr__deps: ['strlen'], |
| strrchr: function(ptr, chr) { |
| var ptr2 = ptr + _strlen(ptr); |
| do { |
| if ({{{ makeGetValue('ptr2', 0, 'i8') }}} == chr) return ptr2; |
| ptr2--; |
| } while (ptr2 >= ptr); |
| return 0; |
| }, |
| rindex: 'strrchr', |
| |
| strdup__deps: ['strlen'], |
| strdup: function(ptr) { |
| var len = _strlen(ptr); |
| var newStr = _malloc(len + 1); |
| {{{ makeCopyValues('newStr', 'ptr', 'len', 'null', null, 1) }}}; |
| {{{ makeSetValue('newStr', 'len', '0', 'i8') }}}; |
| return newStr; |
| }, |
| |
| strndup__deps: ['strdup', 'strlen'], |
| strndup: function(ptr, size) { |
| var len = _strlen(ptr); |
| |
| if (size >= len) { |
| return _strdup(ptr); |
| } |
| |
| if (size < 0) { |
| size = 0; |
| } |
| |
| var newStr = _malloc(size + 1); |
| {{{ makeCopyValues('newStr', 'ptr', 'size', 'null', null, 1) }}}; |
| {{{ makeSetValue('newStr', 'size', '0', 'i8') }}}; |
| return newStr; |
| }, |
| |
| strpbrk: function(ptr1, ptr2) { |
| var curr; |
| var searchSet = {}; |
| while (1) { |
| var curr = {{{ makeGetValue('ptr2++', 0, 'i8') }}}; |
| if (!curr) break; |
| searchSet[curr] = 1; |
| } |
| while (1) { |
| curr = {{{ makeGetValue('ptr1', 0, 'i8') }}}; |
| if (!curr) break; |
| if (curr in searchSet) return ptr1; |
| ptr1++; |
| } |
| return 0; |
| }, |
| |
| __strtok_state: 0, |
| strtok__deps: ['__strtok_state', 'strtok_r'], |
| strtok__postset: '___strtok_state = Runtime.staticAlloc(4);', |
| strtok: function(s, delim) { |
| return _strtok_r(s, delim, ___strtok_state); |
| }, |
| |
| // Translated from newlib; for the original source and licensing, see library_strtok_r.c |
| strtok_r: function(s, delim, lasts) { |
| var skip_leading_delim = 1; |
| var spanp; |
| var c, sc; |
| var tok; |
| |
| |
| if (s == 0 && (s = getValue(lasts, 'i8*')) == 0) { |
| return 0; |
| } |
| |
| cont: while (1) { |
| c = getValue(s++, 'i8'); |
| for (spanp = delim; (sc = getValue(spanp++, 'i8')) != 0;) { |
| if (c == sc) { |
| if (skip_leading_delim) { |
| continue cont; |
| } else { |
| setValue(lasts, s, 'i8*'); |
| setValue(s - 1, 0, 'i8'); |
| return s - 1; |
| } |
| } |
| } |
| break; |
| } |
| |
| if (c == 0) { |
| setValue(lasts, 0, 'i8*'); |
| return 0; |
| } |
| tok = s - 1; |
| |
| for (;;) { |
| c = getValue(s++, 'i8'); |
| spanp = delim; |
| do { |
| if ((sc = getValue(spanp++, 'i8')) == c) { |
| if (c == 0) { |
| s = 0; |
| } else { |
| setValue(s - 1, 0, 'i8'); |
| } |
| setValue(lasts, s, 'i8*'); |
| return tok; |
| } |
| } while (sc != 0); |
| } |
| abort('strtok_r error!'); |
| }, |
| |
| strerror_r__deps: ['$ERRNO_CODES', '$ERRNO_MESSAGES', '__setErrNo'], |
| strerror_r: function(errnum, strerrbuf, buflen) { |
| if (errnum in ERRNO_MESSAGES) { |
| if (ERRNO_MESSAGES[errnum].length > buflen - 1) { |
| return ___setErrNo(ERRNO_CODES.ERANGE); |
| } else { |
| var msg = ERRNO_MESSAGES[errnum]; |
| for (var i = 0; i < msg.length; i++) { |
| {{{ makeSetValue('strerrbuf', 'i', 'msg.charCodeAt(i)', 'i8') }}} |
| } |
| {{{ makeSetValue('strerrbuf', 'i', 0, 'i8') }}} |
| return 0; |
| } |
| } else { |
| return ___setErrNo(ERRNO_CODES.EINVAL); |
| } |
| }, |
| strerror__deps: ['strerror_r'], |
| strerror: function(errnum) { |
| if (!_strerror.buffer) _strerror.buffer = _malloc(256); |
| _strerror_r(errnum, _strerror.buffer, 256); |
| return _strerror.buffer; |
| }, |
| |
| // ========================================================================== |
| // ctype.h |
| // ========================================================================== |
| |
| isascii: function(chr) { |
| return chr >= 0 && (chr & 0x80) == 0; |
| }, |
| toascii: function(chr) { |
| return chr & 0x7F; |
| }, |
| toupper: function(chr) { |
| if (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) { |
| return chr - {{{ charCode('a') }}} + {{{ charCode('A') }}}; |
| } else { |
| return chr; |
| } |
| }, |
| _toupper: 'toupper', |
| |
| tolower__asm: true, |
| tolower__sig: 'ii', |
| tolower: function(chr) { |
| chr = chr|0; |
| if ((chr|0) < {{{ charCode('A') }}}) return chr|0; |
| if ((chr|0) > {{{ charCode('Z') }}}) return chr|0; |
| return (chr - {{{ charCode('A') }}} + {{{ charCode('a') }}})|0; |
| }, |
| _tolower: 'tolower', |
| |
| // The following functions are defined as macros in glibc. |
| islower: function(chr) { |
| return chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}; |
| }, |
| isupper: function(chr) { |
| return chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}; |
| }, |
| isalpha: function(chr) { |
| return (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || |
| (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); |
| }, |
| isdigit: function(chr) { |
| return chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}; |
| }, |
| isdigit_l: 'isdigit', // no locale support yet |
| isxdigit: function(chr) { |
| return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || |
| (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('f') }}}) || |
| (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('F') }}}); |
| }, |
| isxdigit_l: 'isxdigit', // no locale support yet |
| isalnum: function(chr) { |
| return (chr >= {{{ charCode('0') }}} && chr <= {{{ charCode('9') }}}) || |
| (chr >= {{{ charCode('a') }}} && chr <= {{{ charCode('z') }}}) || |
| (chr >= {{{ charCode('A') }}} && chr <= {{{ charCode('Z') }}}); |
| }, |
| ispunct: function(chr) { |
| return (chr >= {{{ charCode('!') }}} && chr <= {{{ charCode('/') }}}) || |
| (chr >= {{{ charCode(':') }}} && chr <= {{{ charCode('@') }}}) || |
| (chr >= {{{ charCode('[') }}} && chr <= {{{ charCode('`') }}}) || |
| (chr >= {{{ charCode('{') }}} && chr <= {{{ charCode('~') }}}); |
| }, |
| isspace: function(chr) { |
| return chr in { 32: 0, 9: 0, 10: 0, 11: 0, 12: 0, 13: 0 }; |
| }, |
| isblank: function(chr) { |
| return chr == {{{ charCode(' ') }}} || chr == {{{ charCode('\t') }}}; |
| }, |
| iscntrl: function(chr) { |
| return (0 <= chr && chr <= 0x1F) || chr === 0x7F; |
| }, |
| isprint: function(chr) { |
| return 0x1F < chr && chr < 0x7F; |
| }, |
| isgraph: 'isprint', |
| // Lookup tables for glibc ctype implementation. |
| __ctype_b_loc: function() { |
| // http://refspecs.freestandards.org/LSB_3.0.0/LSB-Core-generic/LSB-Core-generic/baselib---ctype-b-loc.html |
| var me = ___ctype_b_loc; |
| if (!me.ret) { |
| var values = [ |
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| 0,0,0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2,2,8195,8194,8194,8194,8194,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,24577,49156,49156,49156, |
| 49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,49156,55304,55304,55304,55304,55304,55304,55304,55304, |
| 55304,55304,49156,49156,49156,49156,49156,49156,49156,54536,54536,54536,54536,54536,54536,50440,50440,50440,50440,50440, |
| 50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,50440,49156,49156,49156,49156,49156, |
| 49156,54792,54792,54792,54792,54792,54792,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696,50696, |
| 50696,50696,50696,50696,50696,50696,50696,49156,49156,49156,49156,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, |
| 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 |
| ]; |
| var i16size = {{{ Runtime.getNativeTypeSize('i16') }}}; |
| var arr = _malloc(values.length * i16size); |
| for (var i = 0; i < values.length; i++) { |
| {{{ makeSetValue('arr', 'i * i16size', 'values[i]', 'i16') }}} |
| } |
| me.ret = allocate([arr + 128 * i16size], 'i16*', ALLOC_NORMAL); |
| } |
| return me.ret; |
| }, |
| __ctype_tolower_loc: function() { |
| // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-tolower-loc.html |
| var me = ___ctype_tolower_loc; |
| if (!me.ret) { |
| var values = [ |
| 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, |
| 158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187, |
| 188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217, |
| 218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, |
| 248,249,250,251,252,253,254,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, |
| 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,97,98,99,100,101,102,103, |
| 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,91,92,93,94,95,96,97,98,99,100,101,102,103, |
| 104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133, |
| 134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163, |
| 164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193, |
| 194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, |
| 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253, |
| 254,255 |
| ]; |
| var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; |
| var arr = _malloc(values.length * i32size); |
| for (var i = 0; i < values.length; i++) { |
| {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} |
| } |
| me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); |
| } |
| return me.ret; |
| }, |
| __ctype_toupper_loc: function() { |
| // http://refspecs.freestandards.org/LSB_3.1.1/LSB-Core-generic/LSB-Core-generic/libutil---ctype-toupper-loc.html |
| var me = ___ctype_toupper_loc; |
| if (!me.ret) { |
| var values = [ |
| 128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157, |
| 158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187, |
| 188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217, |
| 218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247, |
| 248,249,250,251,252,253,254,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, |
| 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72, |
| 73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80, |
| 81,82,83,84,85,86,87,88,89,90,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144, |
| 145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174, |
| 175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204, |
| 205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234, |
| 235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255 |
| ]; |
| var i32size = {{{ Runtime.getNativeTypeSize('i32') }}}; |
| var arr = _malloc(values.length * i32size); |
| for (var i = 0; i < values.length; i++) { |
| {{{ makeSetValue('arr', 'i * i32size', 'values[i]', 'i32') }}} |
| } |
| me.ret = allocate([arr + 128 * i32size], 'i32*', ALLOC_NORMAL); |
| } |
| return me.ret; |
| }, |
| |
| // ========================================================================== |
| // LLVM specifics |
| // ========================================================================== |
| |
| llvm_va_start__inline: function(ptr) { |
| // varargs - we received a pointer to the varargs as a final 'extra' parameter called 'varrp' |
| #if TARGET_X86 |
| return makeSetValue(ptr, 0, 'varrp', 'void*'); |
| #endif |
| #if TARGET_LE32 |
| // 2-word structure: struct { void* start; void* currentOffset; } |
| return makeSetValue(ptr, 0, 'varrp', 'void*') + ';' + makeSetValue(ptr, Runtime.QUANTUM_SIZE, 0, 'void*'); |
| #endif |
| }, |
| |
| llvm_va_end: function() {}, |
| |
| llvm_va_copy: function(ppdest, ppsrc) { |
| // copy the list start |
| {{{ makeCopyValues('ppdest', 'ppsrc', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; |
| |
| // copy the list's current offset (will be advanced with each call to va_arg) |
| {{{ makeCopyValues('(ppdest+'+Runtime.QUANTUM_SIZE+')', '(ppsrc+'+Runtime.QUANTUM_SIZE+')', Runtime.QUANTUM_SIZE, 'null', null, 1) }}}; |
| }, |
| |
| llvm_bswap_i16: function(x) { |
| return ((x&0xff)<<8) | ((x>>8)&0xff); |
| }, |
| |
| llvm_bswap_i32: function(x) { |
| return ((x&0xff)<<24) | (((x>>8)&0xff)<<16) | (((x>>16)&0xff)<<8) | (x>>>24); |
| }, |
| |
| llvm_bswap_i64__deps: ['llvm_bswap_i32'], |
| llvm_bswap_i64: function(l, h) { |
| var retl = _llvm_bswap_i32(h)>>>0; |
| var reth = _llvm_bswap_i32(l)>>>0; |
| #if USE_TYPED_ARRAYS == 2 |
| {{{ makeStructuralReturn(['retl', 'reth']) }}}; |
| #else |
| throw 'unsupported'; |
| #endif |
| }, |
| |
| llvm_ctlz_i32__deps: [function() { |
| function ctlz(x) { |
| for (var i = 0; i < 8; i++) { |
| if (x & (1 << (7-i))) { |
| return i; |
| } |
| } |
| return 8; |
| } |
| return 'var ctlz_i8 = allocate([' + range(256).map(function(x) { return ctlz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; |
| }], |
| llvm_ctlz_i32__asm: true, |
| llvm_ctlz_i32__sig: 'ii', |
| llvm_ctlz_i32: function(x) { |
| x = x|0; |
| var ret = 0; |
| ret = {{{ makeGetValueAsm('ctlz_i8', 'x >>> 24', 'i8') }}}; |
| if ((ret|0) < 8) return ret|0; |
| var ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 16)&0xff', 'i8') }}}; |
| if ((ret|0) < 8) return (ret + 8)|0; |
| var ret = {{{ makeGetValueAsm('ctlz_i8', '(x >> 8)&0xff', 'i8') }}}; |
| if ((ret|0) < 8) return (ret + 16)|0; |
| return ({{{ makeGetValueAsm('ctlz_i8', 'x&0xff', 'i8') }}} + 24)|0; |
| }, |
| |
| llvm_ctlz_i64__deps: ['llvm_ctlz_i32'], |
| llvm_ctlz_i64: function(l, h) { |
| var ret = _llvm_ctlz_i32(h); |
| if (ret == 32) ret += _llvm_ctlz_i32(l); |
| #if USE_TYPED_ARRAYS == 2 |
| {{{ makeStructuralReturn(['ret', '0']) }}}; |
| #else |
| return ret; |
| #endif |
| }, |
| |
| llvm_cttz_i32__deps: [function() { |
| function cttz(x) { |
| for (var i = 0; i < 8; i++) { |
| if (x & (1 << i)) { |
| return i; |
| } |
| } |
| return 8; |
| } |
| return 'var cttz_i8 = allocate([' + range(256).map(function(x) { return cttz(x) }).join(',') + '], "i8", ALLOC_STATIC);'; |
| }], |
| llvm_cttz_i32__asm: true, |
| llvm_cttz_i32__sig: 'ii', |
| llvm_cttz_i32: function(x) { |
| x = x|0; |
| var ret = 0; |
| ret = {{{ makeGetValueAsm('cttz_i8', 'x & 0xff', 'i8') }}}; |
| if ((ret|0) < 8) return ret|0; |
| var ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 8)&0xff', 'i8') }}}; |
| if ((ret|0) < 8) return (ret + 8)|0; |
| var ret = {{{ makeGetValueAsm('cttz_i8', '(x >> 16)&0xff', 'i8') }}}; |
| if ((ret|0) < 8) return (ret + 16)|0; |
| return ({{{ makeGetValueAsm('cttz_i8', 'x >>> 24', 'i8') }}} + 24)|0; |
| }, |
| |
| llvm_cttz_i64__deps: ['llvm_cttz_i32'], |
| llvm_cttz_i64: function(l, h) { |
| var ret = _llvm_cttz_i32(l); |
| if (ret == 32) ret += _llvm_cttz_i32(h); |
| #if USE_TYPED_ARRAYS == 2 |
| {{{ makeStructuralReturn(['ret', '0']) }}}; |
| #else |
| return ret; |
| #endif |
| }, |
| |
| llvm_ctpop_i32: function(x) { |
| var ret = 0; |
| while (x) { |
| if (x&1) ret++; |
| x >>>= 1; |
| } |
| return ret; |
| }, |
| |
| llvm_ctpop_i64__deps: ['llvm_ctpop_i32'], |
| llvm_ctpop_i64: function(l, h) { |
| return _llvm_ctpop_i32(l) + _llvm_ctpop_i32(h); |
| }, |
| |
| llvm_trap: function() { |
| throw 'trap! ' + new Error().stack; |
| }, |
| |
| __assert_fail: function(condition, file, line) { |
| ABORT = true; |
| throw 'Assertion failed: ' + Pointer_stringify(condition) + ' at ' + new Error().stack; |
| }, |
| |
| __assert_func: function(filename, line, func, condition) { |
| throw 'Assertion failed: ' + (condition ? Pointer_stringify(condition) : 'unknown condition') + ', at: ' + [filename ? Pointer_stringify(filename) : 'unknown filename', line, func ? Pointer_stringify(func) : 'unknown function'] + ' at ' + new Error().stack; |
| }, |
| |
| __cxa_guard_acquire: function(variable) { |
| if (!{{{ makeGetValue(0, 'variable', 'i8', null, null, 1) }}}) { // ignore SAFE_HEAP stuff because llvm mixes i64 and i8 here |
| {{{ makeSetValue(0, 'variable', '1', 'i8') }}}; |
| return 1; |
| } |
| return 0; |
| }, |
| __cxa_guard_release: function() {}, |
| __cxa_guard_abort: function() {}, |
| |
| _ZTVN10__cxxabiv119__pointer_type_infoE: [0], // is a pointer |
| _ZTVN10__cxxabiv117__class_type_infoE: [1], // no inherited classes |
| _ZTVN10__cxxabiv120__si_class_type_infoE: [2], // yes inherited classes |
| |
| // Exceptions |
| __cxa_allocate_exception: function(size) { |
| return _malloc(size); |
| }, |
| __cxa_free_exception: function(ptr) { |
| try { |
| return _free(ptr); |
| } catch(e) { // XXX FIXME |
| #if ASSERTIONS |
| Module.printErr('exception during cxa_free_exception: ' + e); |
| #endif |
| } |
| }, |
| __cxa_throw__sig: 'viii', |
| __cxa_throw__deps: ['llvm_eh_exception', '_ZSt18uncaught_exceptionv', '__cxa_find_matching_catch'], |
| __cxa_throw: function(ptr, type, destructor) { |
| if (!___cxa_throw.initialized) { |
| try { |
| {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv119__pointer_type_infoE'), '0', '0', 'i32') }}}; // Workaround for libcxxabi integration bug |
| } catch(e){} |
| try { |
| {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv117__class_type_infoE'), '0', '1', 'i32') }}}; // Workaround for libcxxabi integration bug |
| } catch(e){} |
| try { |
| {{{ makeSetValue(makeGlobalUse('__ZTVN10__cxxabiv120__si_class_type_infoE'), '0', '2', 'i32') }}}; // Workaround for libcxxabi integration bug |
| } catch(e){} |
| ___cxa_throw.initialized = true; |
| } |
| #if EXCEPTION_DEBUG |
| Module.printErr('Compiled code throwing an exception, ' + [ptr,type,destructor] + ', at ' + new Error().stack); |
| #endif |
| {{{ makeSetValue('_llvm_eh_exception.buf', '0', 'ptr', 'void*') }}} |
| {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'type', 'void*') }}} |
| {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'destructor', 'void*') }}} |
| if (!("uncaught_exception" in __ZSt18uncaught_exceptionv)) { |
| __ZSt18uncaught_exceptionv.uncaught_exception = 1; |
| } else { |
| __ZSt18uncaught_exceptionv.uncaught_exception++; |
| } |
| {{{ makeThrow('ptr') }}}; |
| }, |
| __cxa_rethrow__deps: ['llvm_eh_exception', '__cxa_end_catch'], |
| __cxa_rethrow: function() { |
| ___cxa_end_catch.rethrown = true; |
| {{{ makeThrow(makeGetValue('_llvm_eh_exception.buf', '0', 'void*')) }}}; |
| }, |
| llvm_eh_exception__postset: '_llvm_eh_exception.buf = allocate(12, "void*", ALLOC_STATIC);', |
| llvm_eh_exception: function() { |
| return {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; |
| }, |
| llvm_eh_selector__jsargs: true, |
| llvm_eh_selector: function(unused_exception_value, personality/*, varargs*/) { |
| var type = {{{ makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') }}} |
| for (var i = 2; i < arguments.length; i++) { |
| if (arguments[i] == type) return type; |
| } |
| return 0; |
| }, |
| llvm_eh_typeid_for: function(type) { |
| return type; |
| }, |
| __cxa_begin_catch__deps: ['_ZSt18uncaught_exceptionv'], |
| __cxa_begin_catch: function(ptr) { |
| __ZSt18uncaught_exceptionv.uncaught_exception--; |
| return ptr; |
| }, |
| __cxa_end_catch__deps: ['llvm_eh_exception', '__cxa_free_exception'], |
| __cxa_end_catch: function() { |
| if (___cxa_end_catch.rethrown) { |
| ___cxa_end_catch.rethrown = false; |
| return; |
| } |
| // Clear state flag. |
| #if ASM_JS |
| asm['setThrew'](0); |
| #else |
| __THREW__ = 0; |
| #endif |
| // Clear type. |
| {{{ makeSetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, '0', 'void*') }}} |
| // Call destructor if one is registered then clear it. |
| var ptr = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; |
| var destructor = {{{ makeGetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, 'void*') }}}; |
| if (destructor) { |
| Runtime.dynCall('vi', destructor, [ptr]); |
| {{{ makeSetValue('_llvm_eh_exception.buf', 2 * QUANTUM_SIZE, '0', 'i32') }}} |
| } |
| // Free ptr if it isn't null. |
| if (ptr) { |
| ___cxa_free_exception(ptr); |
| {{{ makeSetValue('_llvm_eh_exception.buf', '0', '0', 'void*') }}} |
| } |
| }, |
| __cxa_get_exception_ptr__deps: ['llvm_eh_exception'], |
| __cxa_get_exception_ptr: function(ptr) { |
| return ptr; |
| }, |
| _ZSt18uncaught_exceptionv: function() { // std::uncaught_exception() |
| return !!__ZSt18uncaught_exceptionv.uncaught_exception; |
| }, |
| __cxa_uncaught_exception__deps: ['_Zst18uncaught_exceptionv'], |
| __cxa_uncaught_exception: function() { |
| return !!__ZSt18uncaught_exceptionv.uncaught_exception; |
| }, |
| |
| __cxa_call_unexpected: function(exception) { |
| Module.printErr('Unexpected exception thrown, this is not properly supported - aborting'); |
| ABORT = true; |
| throw exception; |
| }, |
| |
| _Unwind_Resume_or_Rethrow: function(ptr) { |
| {{{ makeThrow('ptr') }}}; |
| }, |
| _Unwind_RaiseException: function(ptr) { |
| {{{ makeThrow('ptr') }}}; |
| }, |
| _Unwind_DeleteException: function(ptr) {}, |
| |
| terminate: '__cxa_call_unexpected', |
| |
| __gxx_personality_v0: function() { |
| }, |
| |
| __cxa_is_number_type: function(type) { |
| var isNumber = false; |
| try { if (type == {{{ makeGlobalUse('__ZTIi') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIj') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIl') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIm') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIx') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIy') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIf') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTId') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIe') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIc') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIa') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIh') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIs') }}}) isNumber = true } catch(e){} |
| try { if (type == {{{ makeGlobalUse('__ZTIt') }}}) isNumber = true } catch(e){} |
| return isNumber; |
| }, |
| |
| // Finds a suitable catch clause for when an exception is thrown. |
| // In normal compilers, this functionality is handled by the C++ |
| // 'personality' routine. This is passed a fairly complex structure |
| // relating to the context of the exception and makes judgements |
| // about how to handle it. Some of it is about matching a suitable |
| // catch clause, and some of it is about unwinding. We already handle |
| // unwinding using 'if' blocks around each function, so the remaining |
| // functionality boils down to picking a suitable 'catch' block. |
| // We'll do that here, instead, to keep things simpler. |
| |
| __cxa_find_matching_catch__deps: ['__cxa_does_inherit', '__cxa_is_number_type', '__resumeException'], |
| __cxa_find_matching_catch: function(thrown, throwntype) { |
| if (thrown == -1) thrown = {{{ makeGetValue('_llvm_eh_exception.buf', '0', 'void*') }}}; |
| if (throwntype == -1) throwntype = {{{ makeGetValue('_llvm_eh_exception.buf', QUANTUM_SIZE, 'void*') }}}; |
| var typeArray = Array.prototype.slice.call(arguments, 2); |
| |
| // If throwntype is a pointer, this means a pointer has been |
| // thrown. When a pointer is thrown, actually what's thrown |
| // is a pointer to the pointer. We'll dereference it. |
| if (throwntype != 0 && !___cxa_is_number_type(throwntype)) { |
| var throwntypeInfoAddr= {{{ makeGetValue('throwntype', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; |
| var throwntypeInfo= {{{ makeGetValue('throwntypeInfoAddr', '0', '*') }}}; |
| if (throwntypeInfo == 0) |
| thrown = {{{ makeGetValue('thrown', '0', '*') }}}; |
| } |
| // The different catch blocks are denoted by different types. |
| // Due to inheritance, those types may not precisely match the |
| // type of the thrown object. Find one which matches, and |
| // return the type of the catch block which should be called. |
| for (var i = 0; i < typeArray.length; i++) { |
| if (___cxa_does_inherit(typeArray[i], throwntype, thrown)) |
| {{{ makeStructuralReturn(['thrown', 'typeArray[i]']) }}}; |
| } |
| // Shouldn't happen unless we have bogus data in typeArray |
| // or encounter a type for which emscripten doesn't have suitable |
| // typeinfo defined. Best-efforts match just in case. |
| {{{ makeStructuralReturn(['thrown', 'throwntype']) }}}; |
| }, |
| |
| __resumeException__deps: [function() { Functions.libraryFunctions['__resumeException'] = 1 }], // will be called directly from compiled code |
| __resumeException: function(ptr) { |
| #if EXCEPTION_DEBUG |
| Module.print("Resuming exception"); |
| #endif |
| if ({{{ makeGetValue('_llvm_eh_exception.buf', 0, 'void*') }}} == 0) {{{ makeSetValue('_llvm_eh_exception.buf', 0, 'ptr', 'void*') }}}; |
| {{{ makeThrow('ptr') }}}; |
| }, |
| |
| // Recursively walks up the base types of 'possibilityType' |
| // to see if any of them match 'definiteType'. |
| __cxa_does_inherit__deps: ['__cxa_is_number_type'], |
| __cxa_does_inherit: function(definiteType, possibilityType, possibility) { |
| if (possibility == 0) return false; |
| if (possibilityType == 0 || possibilityType == definiteType) |
| return true; |
| var possibility_type_info; |
| if (___cxa_is_number_type(possibilityType)) { |
| possibility_type_info = possibilityType; |
| } else { |
| var possibility_type_infoAddr = {{{ makeGetValue('possibilityType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; |
| possibility_type_info = {{{ makeGetValue('possibility_type_infoAddr', '0', '*') }}}; |
| } |
| switch (possibility_type_info) { |
| case 0: // possibility is a pointer |
| // See if definite type is a pointer |
| var definite_type_infoAddr = {{{ makeGetValue('definiteType', '0', '*') }}} - {{{ Runtime.QUANTUM_SIZE*2 }}}; |
| var definite_type_info = {{{ makeGetValue('definite_type_infoAddr', '0', '*') }}}; |
| if (definite_type_info == 0) { |
| // Also a pointer; compare base types of pointers |
| var defPointerBaseAddr = definiteType+{{{ Runtime.QUANTUM_SIZE*2 }}}; |
| var defPointerBaseType = {{{ makeGetValue('defPointerBaseAddr', '0', '*') }}}; |
| var possPointerBaseAddr = possibilityType+{{{ Runtime.QUANTUM_SIZE*2 }}}; |
| var possPointerBaseType = {{{ makeGetValue('possPointerBaseAddr', '0', '*') }}}; |
| return ___cxa_does_inherit(defPointerBaseType, possPointerBaseType, possibility); |
| } else |
| return false; // one pointer and one non-pointer |
| case 1: // class with no base class |
| return false; |
| case 2: // class with base class |
| var parentTypeAddr = possibilityType + {{{ Runtime.QUANTUM_SIZE*2 }}}; |
| var parentType = {{{ makeGetValue('parentTypeAddr', '0', '*') }}}; |
| return ___cxa_does_inherit(definiteType, parentType, possibility); |
| default: |
| return false; // some unencountered type |
| } |
| }, |
| |
| _ZNSt9exceptionD2Ev: function(){}, // XXX a dependency of dlmalloc, but not actually needed if libcxx is not anyhow included |
| |
| _ZNSt9type_infoD2Ev: function(){}, |
| |
| // RTTI hacks for exception handling, defining type_infos for common types. |
| // The values are dummies. We simply use the addresses of these statically |
| // allocated variables as unique identifiers. |
| _ZTIb: [0], // bool |
| _ZTIi: [0], // int |
| _ZTIj: [0], // unsigned int |
| _ZTIl: [0], // long |
| _ZTIm: [0], // unsigned long |
| _ZTIx: [0], // long long |
| _ZTIy: [0], // unsigned long long |
| _ZTIf: [0], // float |
| _ZTId: [0], // double |
| _ZTIe: [0], // long double |
| _ZTIc: [0], // char |
| _ZTIa: [0], // signed char |
| _ZTIh: [0], // unsigned char |
| _ZTIs: [0], // short |
| _ZTIt: [0], // unsigned short |
| _ZTIv: [0], // void |
| _ZTIPv: [0], // void* |
| |
| llvm_uadd_with_overflow_i8: function(x, y) { |
| x = x & 0xff; |
| y = y & 0xff; |
| {{{ makeStructuralReturn(['(x+y) & 0xff', 'x+y > 255']) }}}; |
| }, |
| |
| llvm_umul_with_overflow_i8: function(x, y) { |
| x = x & 0xff; |
| y = y & 0xff; |
| {{{ makeStructuralReturn(['(x*y) & 0xff', 'x*y > 255']) }}}; |
| }, |
| |
| llvm_uadd_with_overflow_i16: function(x, y) { |
| x = x & 0xffff; |
| y = y & 0xffff; |
| {{{ makeStructuralReturn(['(x+y) & 0xffff', 'x+y > 65535']) }}}; |
| }, |
| |
| llvm_umul_with_overflow_i16: function(x, y) { |
| x = x & 0xffff; |
| y = y & 0xffff; |
| {{{ makeStructuralReturn(['(x*y) & 0xffff', 'x*y > 65535']) }}}; |
| }, |
| |
| llvm_uadd_with_overflow_i32: function(x, y) { |
| x = x>>>0; |
| y = y>>>0; |
| {{{ makeStructuralReturn(['(x+y)>>>0', 'x+y > 4294967295']) }}}; |
| }, |
| |
| llvm_umul_with_overflow_i32: function(x, y) { |
| x = x>>>0; |
| y = y>>>0; |
| {{{ makeStructuralReturn(['(x*y)>>>0', 'x*y > 4294967295']) }}}; |
| }, |
| |
| llvm_umul_with_overflow_i64__deps: [function() { Types.preciseI64MathUsed = 1 }], |
| llvm_umul_with_overflow_i64: function(xl, xh, yl, yh) { |
| #if ASSERTIONS |
| Runtime.warnOnce('no overflow support in llvm_umul_with_overflow_i64'); |
| #endif |
| var low = ___muldi3(xl, xh, yl, yh); |
| {{{ makeStructuralReturn(['low', 'tempRet0', '0']) }}}; |
| }, |
| |
| llvm_stacksave: function() { |
| var self = _llvm_stacksave; |
| if (!self.LLVM_SAVEDSTACKS) { |
| self.LLVM_SAVEDSTACKS = []; |
| } |
| self.LLVM_SAVEDSTACKS.push(Runtime.stackSave()); |
| return self.LLVM_SAVEDSTACKS.length-1; |
| }, |
| llvm_stackrestore: function(p) { |
| var self = _llvm_stacksave; |
| var ret = self.LLVM_SAVEDSTACKS[p]; |
| self.LLVM_SAVEDSTACKS.splice(p, 1); |
| Runtime.stackRestore(ret); |
| }, |
| |
| __cxa_pure_virtual: function() { |
| ABORT = true; |
| throw 'Pure virtual function called!'; |
| }, |
| |
| llvm_flt_rounds: function() { |
| return -1; // 'indeterminable' for FLT_ROUNDS |
| }, |
| |
| llvm_memory_barrier: function(){}, |
| |
| llvm_atomic_load_add_i32_p0i32: function(ptr, delta) { |
| var ret = {{{ makeGetValue('ptr', '0', 'i32') }}}; |
| {{{ makeSetValue('ptr', '0', 'ret+delta', 'i32') }}}; |
| return ret; |
| }, |
| |
| llvm_expect_i32__inline: function(val, expected) { |
| return '(' + val + ')'; |
| }, |
| |
| llvm_lifetime_start: function() {}, |
| llvm_lifetime_end: function() {}, |
| |
| llvm_invariant_start: function() {}, |
| llvm_invariant_end: function() {}, |
| |
| llvm_objectsize_i32: function() { return -1 }, // TODO: support this |
| |
| llvm_dbg_declare__inline: function() { throw 'llvm_debug_declare' }, // avoid warning |
| |
| // ========================================================================== |
| // llvm-mono integration |
| // ========================================================================== |
| |
| llvm_mono_load_i8_p0i8: function(ptr) { |
| return {{{ makeGetValue('ptr', 0, 'i8') }}}; |
| }, |
| |
| llvm_mono_store_i8_p0i8: function(value, ptr) { |
| {{{ makeSetValue('ptr', 0, 'value', 'i8') }}}; |
| }, |
| |
| llvm_mono_load_i16_p0i16: function(ptr) { |
| return {{{ makeGetValue('ptr', 0, 'i16') }}}; |
| }, |
| |
| llvm_mono_store_i16_p0i16: function(value, ptr) { |
| {{{ makeSetValue('ptr', 0, 'value', 'i16') }}}; |
| }, |
| |
| llvm_mono_load_i32_p0i32: function(ptr) { |
| return {{{ makeGetValue('ptr', 0, 'i32') }}}; |
| }, |
| |
| llvm_mono_store_i32_p0i32: function(value, ptr) { |
| {{{ makeSetValue('ptr', 0, 'value', 'i32') }}}; |
| }, |
| |
| // ========================================================================== |
| // math.h |
| // ========================================================================== |
| |
| cos: 'Math.cos', |
| cosf: 'Math.cos', |
| sin: 'Math.sin', |
| sinf: 'Math.sin', |
| tan: 'Math.tan', |
| tanf: 'Math.tan', |
| acos: 'Math.acos', |
| acosf: 'Math.acos', |
| asin: 'Math.asin', |
| asinf: 'Math.asin', |
| atan: 'Math.atan', |
| atanf: 'Math.atan', |
| atan2: 'Math.atan2', |
| atan2f: 'Math.atan2', |
| exp: 'Math.exp', |
| expf: 'Math.exp', |
| |
| // The erf and erfc functions are inspired from |
| // 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) { |
| var MATH_TOLERANCE = 1E-12; |
| var ONE_SQRTPI = 0.564189583547756287; |
| var a = 1; |
| var b = x; |
| var c = x; |
| var d = x * x + 0.5; |
| var n = 1.0; |
| var q2 = b / d; |
| var q1, t; |
| |
| if (Math.abs(x) < 2.2) { |
| return 1.0 - _erf(x); |
| } |
| if (x < 0) { |
| return 2.0 - _erfc(-x); |
| } |
| do { |
| t = a * n + b * x; |
| a = b; |
| b = t; |
| t = c * n + d * x; |
| c = d; |
| d = t; |
| n += 0.5; |
| q1 = q2; |
| q2 = b / d; |
| } while (Math.abs(q1 - q2) / q2 > MATH_TOLERANCE); |
| return (ONE_SQRTPI * Math.exp(- x * x) * q2); |
| }, |
| erfcf: 'erfcf', |
| erf__deps: ['erfc'], |
| erf: function (x) { |
| var MATH_TOLERANCE = 1E-12; |
| var TWO_SQRTPI = 1.128379167095512574; |
| var sum = x; |
| var term = x; |
| var xsqr = x*x; |
| var j = 1; |
| |
| if (Math.abs(x) > 2.2) { |
| return 1.0 - _erfc(x); |
| } |
| do { |
| term *= xsqr / j; |
| sum -= term / (2 * j + 1); |
| ++j; |
| term *= xsqr / j; |
| sum += term / (2 * j + 1); |
| ++j; |
| } while (Math.abs(term / sum) > MATH_TOLERANCE); |
| return (TWO_SQRTPI * sum); |
| }, |
| erff: 'erf', |
| log: 'Math.log', |
| logf: 'Math.log', |
| sqrt: 'Math.sqrt', |
| sqrtf: 'Math.sqrt', |
| fabs: 'Math.abs', |
| fabsf: 'Math.abs', |
| ceil: 'Math.ceil', |
| ceilf: 'Math.ceil', |
| floor: 'Math.floor', |
| floorf: 'Math.floor', |
| pow: 'Math.pow', |
| powf: 'Math.pow', |
| llvm_sqrt_f32: 'Math.sqrt', |
| llvm_sqrt_f64: 'Math.sqrt', |
| llvm_pow_f32: 'Math.pow', |
| llvm_pow_f64: 'Math.pow', |
| llvm_log_f32: 'Math.log', |
| llvm_log_f64: 'Math.log', |
| llvm_exp_f32: 'Math.exp', |
| llvm_exp_f64: 'Math.exp', |
| ldexp: function(x, exp_) { |
| return x * Math.pow(2, exp_); |
| }, |
| ldexpf: 'ldexp', |
| scalb: 'ldexp', |
| scalbn: 'ldexp', |
| scalbnf: 'ldexp', |
| scalbln: 'ldexp', |
| scalblnf: 'ldexp', |
| cbrt: function(x) { |
| return Math.pow(x, 1/3); |
| }, |
| cbrtf: 'cbrt', |
| cbrtl: 'cbrt', |
| |
| modf: function(x, intpart) { |
| {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'double') }}} |
| return x - {{{ makeGetValue('intpart', 0, 'double') }}}; |
| }, |
| modff: function(x, intpart) { |
| {{{ makeSetValue('intpart', 0, 'Math.floor(x)', 'float') }}} |
| return x - {{{ makeGetValue('intpart', 0, 'float') }}}; |
| }, |
| frexp: function(x, exp_addr) { |
| var sig = 0, exp_ = 0; |
| if (x !== 0) { |
| var sign = 1; |
| if (x < 0) { |
| x = -x; |
| sign = -1; |
| } |
| var raw_exp = Math.log(x)/Math.log(2); |
| exp_ = Math.ceil(raw_exp); |
| if (exp_ === raw_exp) exp_ += 1; |
| sig = sign*x/Math.pow(2, exp_); |
| } |
| {{{ makeSetValue('exp_addr', 0, 'exp_', 'i32') }}} |
| return sig; |
| }, |
| frexpf: 'frexp', |
| finite: function(x) { |
| return isFinite(x); |
| }, |
| __finite: 'finite', |
| isinf: function(x) { |
| return !isNaN(x) && !isFinite(x); |
| }, |
| __isinf: 'isinf', |
| isnan: function(x) { |
| return isNaN(x); |
| }, |
| __isnan: 'isnan', |
| |
| _reallyNegative: function(x) { |
| return x < 0 || (x === 0 && (1/x) === -Infinity); |
| }, |
| |
| copysign__deps: ['_reallyNegative'], |
| copysign: function(a, b) { |
| return __reallyNegative(a) === __reallyNegative(b) ? a : -a; |
| }, |
| copysignf: 'copysign', |
| __signbit__deps: ['copysign'], |
| __signbit: function(x) { |
| // We implement using copysign so that we get support |
| // for negative zero (once copysign supports that). |
| return _copysign(1.0, x) < 0; |
| }, |
| __signbitf: '__signbit', |
| __signbitd: '__signbit', |
| hypot: function(a, b) { |
| return Math.sqrt(a*a + b*b); |
| }, |
| hypotf: 'hypot', |
| sinh: function(x) { |
| var p = Math.pow(Math.E, x); |
| return (p - (1 / p)) / 2; |
| }, |
| sinhf: 'sinh', |
| cosh: function(x) { |
| var p = Math.pow(Math.E, x); |
| return (p + (1 / p)) / 2; |
| }, |
| coshf: 'cosh', |
| tanh__deps: ['sinh', 'cosh'], |
| tanh: function(x) { |
| return _sinh(x) / _cosh(x); |
| }, |
| tanhf: 'tanh', |
| asinh: function(x) { |
| return Math.log(x + Math.sqrt(x * x + 1)); |
| }, |
| asinhf: 'asinh', |
| acosh: function(x) { |
| return Math.log(x * 1 + Math.sqrt(x * x - 1)); |
| }, |
| acoshf: 'acosh', |
| atanh: function(x) { |
| return Math.log((1 + x) / (1 - x)) / 2; |
| }, |
| atanhf: 'atanh', |
| exp2: function(x) { |
| return Math.pow(2, x); |
| }, |
| exp2f: 'exp2', |
| expm1: function(x) { |
| return Math.exp(x) - 1; |
| }, |
| expm1f: 'expm1', |
| round: function(x) { |
| return (x < 0) ? -Math.round(-x) : Math.round(x); |
| }, |
| roundf: 'round', |
| lround: 'round', |
| lroundf: 'round', |
| llround: 'round', |
| llroundf: 'round', |
| rint: function(x) { |
| if (Math.abs(x % 1) !== 0.5) return Math.round(x); |
| return x + x % 2 + ((x < 0) ? 1 : -1); |
| }, |
| rintf: 'rint', |
| lrint: 'rint', |
| lrintf: 'rint', |
| llrint: 'rint', |
| llrintf: 'rint', |
| nearbyint: 'rint', |
| nearbyintf: 'rint', |
| trunc: function(x) { |
| return (x < 0) ? Math.ceil(x) : Math.floor(x); |
| }, |
| truncf: 'trunc', |
| fdim: function(x, y) { |
| return (x > y) ? x - y : 0; |
| }, |
| fdimf: 'fdim', |
| fmax: function(x, y) { |
| return isNaN(x) ? y : isNaN(y) ? x : Math.max(x, y); |
| }, |
| fmaxf: 'fmax', |
| fmin: function(x, y) { |
| return isNaN(x) ? y : isNaN(y) ? x : Math.min(x, y); |
| }, |
| fminf: 'fmin', |
| fma: function(x, y, z) { |
| return x * y + z; |
| }, |
| fmaf: 'fma', |
| fmod: function(x, y) { |
| return x % y; |
| }, |
| fmodf: 'fmod', |
| remainder: 'fmod', |
| remainderf: 'fmod', |
| log10: function(x) { |
| return Math.log(x) / Math.LN10; |
| }, |
| log10f: 'log10', |
| log1p: function(x) { |
| return Math.log(1 + x); |
| }, |
| log1pf: 'log1p', |
| log2: function(x) { |
| return Math.log(x) / Math.LN2; |
| }, |
| log2f: 'log2', |
| nan: function(x) { |
| return NaN; |
| }, |
| nanf: 'nan', |
| |
| sincos: function(x, sine, cosine) { |
| var sineVal = Math.sin(x), |
| cosineVal = Math.cos(x); |
| {{{ makeSetValue('sine', '0', 'sineVal', 'double') }}}; |
| {{{ makeSetValue('cosine', '0', 'cosineVal', 'double') }}}; |
| }, |
| |
| sincosf: function(x, sine, cosine) { |
| var sineVal = Math.sin(x), |
| cosineVal = Math.cos(x); |
| {{{ makeSetValue('sine', '0', 'sineVal', 'float') }}}; |
| {{{ makeSetValue('cosine', '0', 'cosineVal', 'float') }}}; |
| }, |
| |
| __div_t_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'quot'], |
| ['i32', 'rem'], |
| ]), |
| div__deps: ['__div_t_struct_layout'], |
| div: function(divt, numer, denom) { |
| var quot = Math.floor(numer / denom); |
| var rem = numer - quot * denom; |
| var offset = ___div_t_struct_layout.rem; |
| {{{ makeSetValue('divt', '0', 'quot', 'i32') }}}; |
| {{{ makeSetValue('divt', 'offset', 'rem', 'i32') }}}; |
| return divt; |
| }, |
| |
| __fpclassifyf: function(x) { |
| if (isNaN(x)) return {{{ cDefine('FP_NAN') }}}; |
| if (!isFinite(x)) return {{{ cDefine('FP_INFINITE') }}}; |
| if (x == 0) return {{{ cDefine('FP_ZERO') }}}; |
| // FP_SUBNORMAL..? |
| return {{{ cDefine('FP_NORMAL') }}}; |
| }, |
| __fpclassifyd: '__fpclassifyf', |
| |
| // ========================================================================== |
| // sys/utsname.h |
| // ========================================================================== |
| |
| __utsname_struct_layout: Runtime.generateStructInfo([ |
| ['b32', 'sysname'], |
| ['b32', 'nodename'], |
| ['b32', 'release'], |
| ['b32', 'version'], |
| ['b32', 'machine']]), |
| uname__deps: ['__utsname_struct_layout'], |
| uname: function(name) { |
| // int uname(struct utsname *name); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/uname.html |
| function copyString(element, value) { |
| var offset = ___utsname_struct_layout[element]; |
| for (var i = 0; i < value.length; i++) { |
| {{{ makeSetValue('name', 'offset + i', 'value.charCodeAt(i)', 'i8') }}} |
| } |
| {{{ makeSetValue('name', 'offset + i', '0', 'i8') }}} |
| } |
| if (name === 0) { |
| return -1; |
| } else { |
| copyString('sysname', 'Emscripten'); |
| copyString('nodename', 'emscripten'); |
| copyString('release', '1.0'); |
| copyString('version', '#1'); |
| copyString('machine', 'x86-JS'); |
| return 0; |
| } |
| }, |
| |
| // ========================================================================== |
| // dlfcn.h - Dynamic library loading |
| // |
| // Some limitations: |
| // |
| // * Minification on each file separately may not work, as they will |
| // have different shortened names. You can in theory combine them, then |
| // minify, then split... perhaps. |
| // |
| // * LLVM optimizations may fail. If the child wants to access a function |
| // in the parent, LLVM opts may remove it from the parent when it is |
| // being compiled. Not sure how to tell LLVM to not do so. |
| // ========================================================================== |
| |
| // Data for dlfcn.h. |
| $DLFCN_DATA: { |
| error: null, |
| errorMsg: null, |
| loadedLibs: {}, // handle -> [refcount, name, lib_object] |
| loadedLibNames: {}, // name -> handle |
| }, |
| // void* dlopen(const char* filename, int flag); |
| dlopen__deps: ['$DLFCN_DATA', '$FS', '$ENV'], |
| dlopen: function(filename, flag) { |
| // void *dlopen(const char *file, int mode); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlopen.html |
| filename = (ENV['LD_LIBRARY_PATH'] || '/') + Pointer_stringify(filename); |
| |
| if (DLFCN_DATA.loadedLibNames[filename]) { |
| // Already loaded; increment ref count and return. |
| var handle = DLFCN_DATA.loadedLibNames[filename]; |
| DLFCN_DATA.loadedLibs[handle].refcount++; |
| return handle; |
| } |
| |
| var target = FS.findObject(filename); |
| if (!target || target.isFolder || target.isDevice) { |
| DLFCN_DATA.errorMsg = 'Could not find dynamic lib: ' + filename; |
| return 0; |
| } else { |
| FS.forceLoadFile(target); |
| var lib_data = intArrayToString(target.contents); |
| } |
| |
| try { |
| var lib_module = eval(lib_data)({{{ Functions.getTable('x') }}}.length); |
| } catch (e) { |
| #if ASSERTIONS |
| Module.printErr('Error in loading dynamic library: ' + e); |
| #endif |
| DLFCN_DATA.errorMsg = 'Could not evaluate dynamic lib: ' + filename; |
| return 0; |
| } |
| |
| // Not all browsers support Object.keys(). |
| var handle = 1; |
| for (var key in DLFCN_DATA.loadedLibs) { |
| if (DLFCN_DATA.loadedLibs.hasOwnProperty(key)) handle++; |
| } |
| |
| DLFCN_DATA.loadedLibs[handle] = { |
| refcount: 1, |
| name: filename, |
| module: lib_module, |
| cached_functions: {} |
| }; |
| DLFCN_DATA.loadedLibNames[filename] = handle; |
| |
| // We don't care about RTLD_NOW and RTLD_LAZY. |
| if (flag & 256) { // RTLD_GLOBAL |
| for (var ident in lib_module) { |
| if (lib_module.hasOwnProperty(ident)) { |
| Module[ident] = lib_module[ident]; |
| } |
| } |
| } |
| |
| return handle; |
| }, |
| // int dlclose(void* handle); |
| dlclose__deps: ['$DLFCN_DATA'], |
| dlclose: function(handle) { |
| // int dlclose(void *handle); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlclose.html |
| if (!DLFCN_DATA.loadedLibs[handle]) { |
| DLFCN_DATA.errorMsg = 'Tried to dlclose() unopened handle: ' + handle; |
| return 1; |
| } else { |
| var lib_record = DLFCN_DATA.loadedLibs[handle]; |
| if (lib_record.refcount-- == 0) { |
| delete DLFCN_DATA.loadedLibNames[lib_record.name]; |
| delete DLFCN_DATA.loadedLibs[handle]; |
| } |
| return 0; |
| } |
| }, |
| // void* dlsym(void* handle, const char* symbol); |
| dlsym__deps: ['$DLFCN_DATA'], |
| dlsym: function(handle, symbol) { |
| // void *dlsym(void *restrict handle, const char *restrict name); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlsym.html |
| symbol = '_' + Pointer_stringify(symbol); |
| |
| if (!DLFCN_DATA.loadedLibs[handle]) { |
| DLFCN_DATA.errorMsg = 'Tried to dlsym() from an unopened handle: ' + handle; |
| return 0; |
| } else { |
| var lib = DLFCN_DATA.loadedLibs[handle]; |
| if (!lib.module.hasOwnProperty(symbol)) { |
| DLFCN_DATA.errorMsg = ('Tried to lookup unknown symbol "' + symbol + |
| '" in dynamic lib: ' + lib.name); |
| return 0; |
| } else { |
| if (lib.cached_functions.hasOwnProperty(symbol)) { |
| return lib.cached_functions[symbol]; |
| } else { |
| var result = lib.module[symbol]; |
| if (typeof result == 'function') { |
| {{{ Functions.getTable('x') }}}.push(result); |
| {{{ Functions.getTable('x') }}}.push(0); |
| result = {{{ Functions.getTable('x') }}}.length - 2; |
| lib.cached_functions = result; |
| } |
| return result; |
| } |
| } |
| } |
| }, |
| // char* dlerror(void); |
| dlerror__deps: ['$DLFCN_DATA'], |
| dlerror: function() { |
| // char *dlerror(void); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/dlerror.html |
| if (DLFCN_DATA.errorMsg === null) { |
| return 0; |
| } else { |
| if (DLFCN_DATA.error) _free(DLFCN_DATA.error); |
| var msgArr = intArrayFromString(DLFCN_DATA.errorMsg); |
| DLFCN_DATA.error = allocate(msgArr, 'i8', ALLOC_NORMAL); |
| DLFCN_DATA.errorMsg = null; |
| return DLFCN_DATA.error; |
| } |
| }, |
| |
| // ========================================================================== |
| // pwd.h |
| // ========================================================================== |
| |
| // TODO: Implement. |
| // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/pwd.h.html |
| getpwuid: function(uid) { |
| return 0; // NULL |
| }, |
| |
| // ========================================================================== |
| // time.h |
| // ========================================================================== |
| |
| clock: function() { |
| if (_clock.start === undefined) _clock.start = Date.now(); |
| return Math.floor((Date.now() - _clock.start) * ({{{ cDefine('CLOCKS_PER_SEC') }}}/1000)); |
| }, |
| |
| time: function(ptr) { |
| var ret = Math.floor(Date.now()/1000); |
| if (ptr) { |
| {{{ makeSetValue('ptr', 0, 'ret', 'i32') }}} |
| } |
| return ret; |
| }, |
| |
| difftime: function(time1, time0) { |
| return time1 - time0; |
| }, |
| |
| __tm_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'tm_sec'], |
| ['i32', 'tm_min'], |
| ['i32', 'tm_hour'], |
| ['i32', 'tm_mday'], |
| ['i32', 'tm_mon'], |
| ['i32', 'tm_year'], |
| ['i32', 'tm_wday'], |
| ['i32', 'tm_yday'], |
| ['i32', 'tm_isdst'], |
| ['i32', 'tm_gmtoff'], |
| ['i32', 'tm_zone']]), |
| // Statically allocated time struct. |
| __tm_current: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', |
| // Statically allocated timezone strings. |
| __tm_timezones: {}, |
| // Statically allocated time strings. |
| __tm_formatted: 'allocate({{{ Runtime.QUANTUM_SIZE }}}*26, "i8", ALLOC_STATIC)', |
| |
| mktime__deps: ['__tm_struct_layout', 'tzset'], |
| mktime: function(tmPtr) { |
| _tzset(); |
| var offsets = ___tm_struct_layout; |
| var year = {{{ makeGetValue('tmPtr', 'offsets.tm_year', 'i32') }}}; |
| var timestamp = new Date(year >= 1900 ? year : year + 1900, |
| {{{ makeGetValue('tmPtr', 'offsets.tm_mon', 'i32') }}}, |
| {{{ makeGetValue('tmPtr', 'offsets.tm_mday', 'i32') }}}, |
| {{{ makeGetValue('tmPtr', 'offsets.tm_hour', 'i32') }}}, |
| {{{ makeGetValue('tmPtr', 'offsets.tm_min', 'i32') }}}, |
| {{{ makeGetValue('tmPtr', 'offsets.tm_sec', 'i32') }}}, |
| 0).getTime() / 1000; |
| {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'new Date(timestamp).getDay()', 'i32') }}} |
| var yday = Math.round((timestamp - (new Date(year, 0, 1)).getTime()) / (1000 * 60 * 60 * 24)); |
| {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} |
| return timestamp; |
| }, |
| timelocal: 'mktime', |
| |
| gmtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'gmtime_r'], |
| gmtime: function(time) { |
| return _gmtime_r(time, ___tm_current); |
| }, |
| |
| gmtime_r__deps: ['__tm_struct_layout', '__tm_timezones'], |
| gmtime_r: function(time, tmPtr) { |
| var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); |
| var offsets = ___tm_struct_layout; |
| {{{ makeSetValue('tmPtr', 'offsets.tm_sec', 'date.getUTCSeconds()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_min', 'date.getUTCMinutes()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_hour', 'date.getUTCHours()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_mday', 'date.getUTCDate()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_mon', 'date.getUTCMonth()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_year', 'date.getUTCFullYear()-1900', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'date.getUTCDay()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_gmtoff', '0', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', '0', 'i32') }}} |
| |
| var start = new Date(date.getFullYear(), 0, 1); |
| var yday = Math.round((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); |
| {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} |
| |
| var timezone = "GMT"; |
| if (!(timezone in ___tm_timezones)) { |
| ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL); |
| } |
| {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}} |
| |
| return tmPtr; |
| }, |
| |
| timegm__deps: ['mktime'], |
| timegm: function(tmPtr) { |
| _tzset(); |
| var offset = {{{ makeGetValue(makeGlobalUse('__timezone'), 0, 'i32') }}}; |
| var daylight = {{{ makeGetValue(makeGlobalUse('__daylight'), 0, 'i32') }}}; |
| daylight = (daylight == 1) ? 60 * 60 : 0; |
| var ret = _mktime(tmPtr) + offset - daylight; |
| return ret; |
| }, |
| |
| localtime__deps: ['malloc', '__tm_struct_layout', '__tm_current', 'localtime_r'], |
| localtime: function(time) { |
| return _localtime_r(time, ___tm_current); |
| }, |
| |
| localtime_r__deps: ['__tm_struct_layout', '__tm_timezones', 'tzset'], |
| localtime_r: function(time, tmPtr) { |
| _tzset(); |
| var offsets = ___tm_struct_layout; |
| var date = new Date({{{ makeGetValue('time', 0, 'i32') }}}*1000); |
| {{{ makeSetValue('tmPtr', 'offsets.tm_sec', 'date.getSeconds()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_min', 'date.getMinutes()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_hour', 'date.getHours()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_mday', 'date.getDate()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_mon', 'date.getMonth()', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_year', 'date.getFullYear()-1900', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_wday', 'date.getDay()', 'i32') }}} |
| |
| var start = new Date(date.getFullYear(), 0, 1); |
| var yday = Math.floor((date.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)); |
| {{{ makeSetValue('tmPtr', 'offsets.tm_yday', 'yday', 'i32') }}} |
| {{{ makeSetValue('tmPtr', 'offsets.tm_gmtoff', 'start.getTimezoneOffset() * 60', 'i32') }}} |
| |
| var dst = Number(start.getTimezoneOffset() != date.getTimezoneOffset()); |
| {{{ makeSetValue('tmPtr', 'offsets.tm_isdst', 'dst', 'i32') }}} |
| |
| var timezone = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | date.toString().match(/\(([A-Z]+)\)/)[1]; |
| if (!(timezone in ___tm_timezones)) { |
| ___tm_timezones[timezone] = allocate(intArrayFromString(timezone), 'i8', ALLOC_NORMAL); |
| } |
| {{{ makeSetValue('tmPtr', 'offsets.tm_zone', '___tm_timezones[timezone]', 'i32') }}} |
| |
| return tmPtr; |
| }, |
| |
| asctime__deps: ['malloc', '__tm_formatted', 'asctime_r'], |
| asctime: function(tmPtr) { |
| return _asctime_r(tmPtr, ___tm_formatted); |
| }, |
| |
| asctime_r__deps: ['__tm_formatted', 'mktime'], |
| asctime_r: function(tmPtr, buf) { |
| var date = new Date(_mktime(tmPtr)*1000); |
| var formatted = date.toString(); |
| var datePart = formatted.replace(/\d{4}.*/, '').replace(/ 0/, ' '); |
| var timePart = formatted.match(/\d{2}:\d{2}:\d{2}/)[0]; |
| formatted = datePart + timePart + ' ' + date.getFullYear() + '\n'; |
| formatted.split('').forEach(function(chr, index) { |
| {{{ makeSetValue('buf', 'index', 'chr.charCodeAt(0)', 'i8') }}} |
| }); |
| {{{ makeSetValue('buf', '25', '0', 'i8') }}} |
| return buf; |
| }, |
| |
| ctime__deps: ['localtime', 'asctime'], |
| ctime: function(timer) { |
| return _asctime(_localtime(timer)); |
| }, |
| |
| ctime_r__deps: ['localtime', 'asctime'], |
| ctime_r: function(timer, buf) { |
| return _asctime_r(_localtime_r(timer, ___tm_current), buf); |
| }, |
| |
| dysize: function(year) { |
| var leap = ((year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))); |
| return leap ? 366 : 365; |
| }, |
| |
| // TODO: Initialize these to defaults on startup from system settings. |
| // Note: glibc has one fewer underscore for all of these. Also used in other related functions (timegm) |
| _tzname: 'allocate({{{ 2*Runtime.QUANTUM_SIZE }}}, "i32*", ALLOC_STATIC)', |
| _daylight: 'allocate(1, "i32*", ALLOC_STATIC)', |
| _timezone: 'allocate(1, "i32*", ALLOC_STATIC)', |
| tzset__deps: ['_tzname', '_daylight', '_timezone'], |
| tzset: function() { |
| // TODO: Use (malleable) environment variables instead of system settings. |
| if (_tzset.called) return; |
| _tzset.called = true; |
| |
| {{{ makeSetValue(makeGlobalUse('__timezone'), '0', '-(new Date()).getTimezoneOffset() * 60', 'i32') }}} |
| |
| var winter = new Date(2000, 0, 1); |
| var summer = new Date(2000, 6, 1); |
| {{{ makeSetValue(makeGlobalUse('__daylight'), '0', 'Number(winter.getTimezoneOffset() != summer.getTimezoneOffset())', 'i32') }}} |
| |
| var winterName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | winter.toString().match(/\(([A-Z]+)\)/)[1]; |
| var summerName = 'GMT'; // XXX do not rely on browser timezone info, it is very unpredictable | summer.toString().match(/\(([A-Z]+)\)/)[1]; |
| var winterNamePtr = allocate(intArrayFromString(winterName), 'i8', ALLOC_NORMAL); |
| var summerNamePtr = allocate(intArrayFromString(summerName), 'i8', ALLOC_NORMAL); |
| {{{ makeSetValue(makeGlobalUse('__tzname'), '0', 'winterNamePtr', 'i32') }}} |
| {{{ makeSetValue(makeGlobalUse('__tzname'), Runtime.QUANTUM_SIZE, 'summerNamePtr', 'i32') }}} |
| }, |
| |
| stime__deps: ['$ERRNO_CODES', '__setErrNo'], |
| stime: function(when) { |
| ___setErrNo(ERRNO_CODES.EPERM); |
| return -1; |
| }, |
| |
| strftime: function(s, maxsize, format, timeptr) { |
| // size_t strftime(char *restrict s, size_t maxsize, const char *restrict format, const struct tm *restrict timeptr); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/strftime.html |
| // TODO: Implement. |
| return 0; |
| }, |
| strftime_l: 'strftime', // no locale support yet |
| |
| strptime: function(buf, format, tm) { |
| // char *strptime(const char *restrict buf, const char *restrict format, struct tm *restrict tm); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/strptime.html |
| // TODO: Implement. |
| return 0; |
| }, |
| strptime_l: 'strptime', // no locale support yet |
| |
| getdate: function(string) { |
| // struct tm *getdate(const char *string); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/getdate.html |
| // TODO: Implement. |
| return 0; |
| }, |
| |
| setitimer: function() { throw 'setitimer not implemented yet' }, |
| getitimer: function() { throw 'getitimer not implemented yet' }, |
| |
| // ========================================================================== |
| // sys/time.h |
| // ========================================================================== |
| |
| __timespec_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'tv_sec'], |
| ['i32', 'tv_nsec']]), |
| nanosleep__deps: ['usleep', '__timespec_struct_layout'], |
| nanosleep: function(rqtp, rmtp) { |
| // int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); |
| var seconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_sec', 'i32') }}}; |
| var nanoseconds = {{{ makeGetValue('rqtp', '___timespec_struct_layout.tv_nsec', 'i32') }}}; |
| {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_sec', '0', 'i32') }}} |
| {{{ makeSetValue('rmtp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} |
| return _usleep((seconds * 1e6) + (nanoseconds / 1000)); |
| }, |
| // TODO: Implement these for real. |
| clock_gettime__deps: ['__timespec_struct_layout'], |
| clock_gettime: function(clk_id, tp) { |
| // int clock_gettime(clockid_t clk_id, struct timespec *tp); |
| var now = Date.now(); |
| {{{ makeSetValue('tp', '___timespec_struct_layout.tv_sec', 'Math.floor(now/1000)', 'i32') }}}; // seconds |
| {{{ makeSetValue('tp', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}}; // nanoseconds - not supported |
| return 0; |
| }, |
| clock_settime: function(clk_id, tp) { |
| // int clock_settime(clockid_t clk_id, const struct timespec *tp); |
| // Nothing. |
| return 0; |
| }, |
| clock_getres__deps: ['__timespec_struct_layout'], |
| clock_getres: function(clk_id, res) { |
| // int clock_getres(clockid_t clk_id, struct timespec *res); |
| {{{ makeSetValue('res', '___timespec_struct_layout.tv_sec', '1', 'i32') }}} |
| {{{ makeSetValue('res', '___timespec_struct_layout.tv_nsec', '0', 'i32') }}} |
| return 0; |
| }, |
| |
| // http://pubs.opengroup.org/onlinepubs/000095399/basedefs/sys/time.h.html |
| gettimeofday: function(ptr) { |
| // %struct.timeval = type { i32, i32 } |
| {{{ (LibraryManager.structs.gettimeofday = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }), null) }}} |
| var now = Date.now(); |
| {{{ makeSetValue('ptr', LibraryManager.structs.gettimeofday[0], 'Math.floor(now/1000)', 'i32') }}}; // seconds |
| {{{ makeSetValue('ptr', LibraryManager.structs.gettimeofday[1], 'Math.floor((now-1000*Math.floor(now/1000))*1000)', 'i32') }}}; // microseconds |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // sys/timeb.h |
| // ========================================================================== |
| |
| __timeb_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'time'], |
| ['i16', 'millitm'], |
| ['i16', 'timezone'], |
| ['i16', 'dstflag'] |
| ]), |
| ftime__deps: ['__timeb_struct_layout'], |
| ftime: function(p) { |
| var millis = Date.now(); |
| {{{ makeSetValue('p', '___timeb_struct_layout.time', 'Math.floor(millis/1000)', 'i32') }}}; |
| {{{ makeSetValue('p', '___timeb_struct_layout.millitm', 'millis % 1000', 'i16') }}}; |
| {{{ makeSetValue('p', '___timeb_struct_layout.timezone', '0', 'i16') }}}; // TODO |
| {{{ makeSetValue('p', '___timeb_struct_layout.dstflag', '0', 'i16') }}}; // TODO |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // sys/times.h |
| // ========================================================================== |
| |
| __tms_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'tms_utime'], |
| ['i32', 'tms_stime'], |
| ['i32', 'tms_cutime'], |
| ['i32', 'tms_cstime']]), |
| times__deps: ['__tms_struct_layout', 'memset'], |
| times: function(buffer) { |
| // clock_t times(struct tms *buffer); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/times.html |
| // NOTE: This is fake, since we can't calculate real CPU time usage in JS. |
| if (buffer !== 0) { |
| _memset(buffer, 0, ___tms_struct_layout.__size__); |
| } |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // sys/types.h |
| // ========================================================================== |
| |
| // NOTE: These are fake, since we don't support the C device creation API. |
| // http://www.kernel.org/doc/man-pages/online/pages/man3/minor.3.html |
| makedev: function(maj, min) { |
| return 0; |
| }, |
| gnu_dev_makedev: 'makedev', |
| major: function(dev) { |
| return 0; |
| }, |
| gnu_dev_major: 'major', |
| minor: function(dev) { |
| return 0; |
| }, |
| gnu_dev_minor: 'minor', |
| |
| // ========================================================================== |
| // setjmp.h |
| // |
| // Basic support for setjmp/longjmp: enough to run the wikipedia example and |
| // hopefully handle most normal behavior. We do not support cases where |
| // longjmp behavior is undefined (for example, if the setjmp function returns |
| // before longjmp is called). |
| // |
| // Note that we need to emulate functions that use setjmp, and also to create |
| // a new label we can return to. Emulation make such functions slower, this |
| // can be alleviated by making a new function containing just the setjmp |
| // related functionality so the slowdown is more limited - you may need |
| // to prevent inlining to keep this isolated, try __attribute__((noinline)) |
| // ========================================================================== |
| |
| saveSetjmp__asm: true, |
| saveSetjmp__sig: 'iii', |
| saveSetjmp__deps: ['putchar'], |
| saveSetjmp: function(env, label, table) { |
| // Not particularly fast: slow table lookup of setjmpId to label. But setjmp |
| // prevents relooping anyhow, so slowness is to be expected. And typical case |
| // is 1 setjmp per invocation, or less. |
| env = env|0; |
| label = label|0; |
| table = table|0; |
| var i = 0; |
| #if ASSERTIONS |
| if ((label|0) == 0) abort(121); |
| #endif |
| setjmpId = (setjmpId+1)|0; |
| {{{ makeSetValueAsm('env', '0', 'setjmpId', 'i32') }}}; |
| while ((i|0) < {{{ 2*MAX_SETJMPS }}}) { |
| if ({{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}} == 0) { |
| {{{ makeSetValueAsm('table', '(i<<2)', 'setjmpId', 'i32') }}}; |
| {{{ makeSetValueAsm('table', '(i<<2)+4', 'label', 'i32') }}}; |
| // prepare next slot |
| {{{ makeSetValueAsm('table', '(i<<2)+8', '0', 'i32') }}}; |
| return 0; |
| } |
| i = (i+2)|0; |
| } |
| {{{ makePrintChars('too many setjmps in a function call, build with a higher value for MAX_SETJMPS') }}}; |
| abort(0); |
| return 0; |
| }, |
| |
| testSetjmp__asm: true, |
| testSetjmp__sig: 'iii', |
| testSetjmp: function(id, table) { |
| id = id|0; |
| table = table|0; |
| var i = 0, curr = 0; |
| while ((i|0) < {{{ MAX_SETJMPS }}}) { |
| curr = {{{ makeGetValueAsm('table', '(i<<2)', 'i32') }}}; |
| if ((curr|0) == 0) break; |
| if ((curr|0) == (id|0)) { |
| return {{{ makeGetValueAsm('table', '(i<<2)+4', 'i32') }}}; |
| } |
| i = (i+2)|0; |
| } |
| return 0; |
| }, |
| |
| #if ASM_JS |
| setjmp__deps: ['saveSetjmp', 'testSetjmp'], |
| #endif |
| setjmp__inline: function(env) { |
| // Save the label |
| #if ASM_JS |
| return '_saveSetjmp(' + env + ', label, setjmpTable)|0'; |
| #else |
| return '(tempInt = setjmpId++, mySetjmpIds[tempInt] = 1, setjmpLabels[tempInt] = label,' + makeSetValue(env, '0', 'tempInt', 'i32', undefined, undefined, undefined, undefined, ',') + ', 0)'; |
| #endif |
| }, |
| |
| #if ASM_JS |
| longjmp__deps: ['saveSetjmp', 'testSetjmp'], |
| #endif |
| longjmp: function(env, value) { |
| #if ASM_JS |
| asm['setThrew'](env, value || 1); |
| throw 'longjmp'; |
| #else |
| throw { longjmp: true, id: {{{ makeGetValue('env', '0', 'i32') }}}, value: value || 1 }; |
| #endif |
| }, |
| |
| // ========================================================================== |
| // signal.h |
| // ========================================================================== |
| |
| signal: function(sig, func) { |
| // TODO |
| return 0; |
| }, |
| sigemptyset: function(set) { |
| // int sigemptyset(sigset_t *set); |
| {{{ makeSetValue('set', '0', '0', 'i32') }}}; |
| return 0; |
| }, |
| sigfillset: function(set) { |
| {{{ makeSetValue('set', '0', '-1>>>0', 'i32') }}}; |
| return 0; |
| }, |
| sigaddset: function(set, signum) { |
| {{{ makeSetValue('set', '0', makeGetValue('set', '0', 'i32') + '| (1 << (signum-1))', 'i32') }}}; |
| return 0; |
| }, |
| sigdelset: function(set, signum) { |
| {{{ makeSetValue('set', '0', makeGetValue('set', '0', 'i32') + '& (~(1 << (signum-1)))', 'i32') }}}; |
| return 0; |
| }, |
| sigismember: function(set, signum) { |
| return {{{ makeGetValue('set', '0', 'i32') }}} & (1 << (signum-1)); |
| }, |
| sigaction: function(set) { |
| // TODO: |
| return 0; |
| }, |
| sigprocmask: 'sigaction', |
| __libc_current_sigrtmin: function() { |
| return 0; |
| }, |
| __libc_current_sigrtmax: function() { |
| return 0; |
| }, |
| kill__deps: ['$ERRNO_CODES', '__setErrNo'], |
| kill: function(pid, sig) { |
| // int kill(pid_t pid, int sig); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/kill.html |
| // Makes no sense in a single-process environment. |
| ___setErrNo(ERRNO_CODES.EPERM); |
| return -1; |
| }, |
| killpg: 'kill', |
| |
| siginterrupt: function() { throw 'siginterrupt not implemented' }, |
| |
| // ========================================================================== |
| // sys/wait.h |
| // ========================================================================== |
| |
| wait__deps: ['$ERRNO_CODES', '__setErrNo'], |
| wait: function(stat_loc) { |
| // pid_t wait(int *stat_loc); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/wait.html |
| // Makes no sense in a single-process environment. |
| ___setErrNo(ERRNO_CODES.ECHILD); |
| return -1; |
| }, |
| // NOTE: These aren't really the same, but we use the same stub for them all. |
| waitid: 'wait', |
| waitpid: 'wait', |
| wait3: 'wait', |
| wait4: 'wait', |
| |
| // ========================================================================== |
| // locale.h |
| // ========================================================================== |
| |
| newlocale: function(mask, locale, base) { |
| return 0; |
| }, |
| |
| freelocale: function(locale) {}, |
| |
| uselocale: function(locale) { |
| return 0; |
| }, |
| |
| setlocale: function(category, locale) { |
| if (!_setlocale.ret) _setlocale.ret = allocate([0], 'i8', ALLOC_NORMAL); |
| return _setlocale.ret; |
| }, |
| |
| localeconv: function() { |
| // %struct.timeval = type { char* decimal point, other stuff... } |
| // var indexes = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); |
| var me = _localeconv; |
| if (!me.ret) { |
| me.ret = allocate([allocate(intArrayFromString('.'), 'i8', ALLOC_NORMAL)], 'i8*', ALLOC_NORMAL); // just decimal point, for now |
| } |
| return me.ret; |
| }, |
| |
| __locale_mb_cur_max: function() { throw '__locale_mb_cur_max not implemented' }, |
| |
| // ========================================================================== |
| // langinfo.h |
| // ========================================================================== |
| |
| nl_langinfo: function(item) { |
| // char *nl_langinfo(nl_item item); |
| // http://pubs.opengroup.org/onlinepubs/000095399/functions/nl_langinfo.html |
| var result; |
| switch (item) { |
| case {{{ cDefine('CODESET') }}}: |
| result = 'ANSI_X3.4-1968'; |
| break; |
| case {{{ cDefine('D_T_FMT') }}}: |
| result = '%a %b %e %H:%M:%S %Y'; |
| break; |
| case {{{ cDefine('D_FMT') }}}: |
| result = '%m/%d/%y'; |
| break; |
| case {{{ cDefine('T_FMT') }}}: |
| result = '%H:%M:%S'; |
| break; |
| case {{{ cDefine('T_FMT_AMPM') }}}: |
| result = '%I:%M:%S %p'; |
| break; |
| case {{{ cDefine('AM_STR') }}}: |
| result = 'AM'; |
| break; |
| case {{{ cDefine('PM_STR') }}}: |
| result = 'PM'; |
| break; |
| case {{{ cDefine('DAY_1') }}}: |
| result = 'Sunday'; |
| break; |
| case {{{ cDefine('DAY_2') }}}: |
| result = 'Monday'; |
| break; |
| case {{{ cDefine('DAY_3') }}}: |
| result = 'Tuesday'; |
| break; |
| case {{{ cDefine('DAY_4') }}}: |
| result = 'Wednesday'; |
| break; |
| case {{{ cDefine('DAY_5') }}}: |
| result = 'Thursday'; |
| break; |
| case {{{ cDefine('DAY_6') }}}: |
| result = 'Friday'; |
| break; |
| case {{{ cDefine('DAY_7') }}}: |
| result = 'Saturday'; |
| break; |
| case {{{ cDefine('ABDAY_1') }}}: |
| result = 'Sun'; |
| break; |
| case {{{ cDefine('ABDAY_2') }}}: |
| result = 'Mon'; |
| break; |
| case {{{ cDefine('ABDAY_3') }}}: |
| result = 'Tue'; |
| break; |
| case {{{ cDefine('ABDAY_4') }}}: |
| result = 'Wed'; |
| break; |
| case {{{ cDefine('ABDAY_5') }}}: |
| result = 'Thu'; |
| break; |
| case {{{ cDefine('ABDAY_6') }}}: |
| result = 'Fri'; |
| break; |
| case {{{ cDefine('ABDAY_7') }}}: |
| result = 'Sat'; |
| break; |
| case {{{ cDefine('MON_1') }}}: |
| result = 'January'; |
| break; |
| case {{{ cDefine('MON_2') }}}: |
| result = 'February'; |
| break; |
| case {{{ cDefine('MON_3') }}}: |
| result = 'March'; |
| break; |
| case {{{ cDefine('MON_4') }}}: |
| result = 'April'; |
| break; |
| case {{{ cDefine('MON_5') }}}: |
| result = 'May'; |
| break; |
| case {{{ cDefine('MON_6') }}}: |
| result = 'June'; |
| break; |
| case {{{ cDefine('MON_7') }}}: |
| result = 'July'; |
| break; |
| case {{{ cDefine('MON_8') }}}: |
| result = 'August'; |
| break; |
| case {{{ cDefine('MON_9') }}}: |
| result = 'September'; |
| break; |
| case {{{ cDefine('MON_10') }}}: |
| result = 'October'; |
| break; |
| case {{{ cDefine('MON_11') }}}: |
| result = 'November'; |
| break; |
| case {{{ cDefine('MON_12') }}}: |
| result = 'December'; |
| break; |
| case {{{ cDefine('ABMON_1') }}}: |
| result = 'Jan'; |
| break; |
| case {{{ cDefine('ABMON_2') }}}: |
| result = 'Feb'; |
| break; |
| case {{{ cDefine('ABMON_3') }}}: |
| result = 'Mar'; |
| break; |
| case {{{ cDefine('ABMON_4') }}}: |
| result = 'Apr'; |
| break; |
| case {{{ cDefine('ABMON_5') }}}: |
| result = 'May'; |
| break; |
| case {{{ cDefine('ABMON_6') }}}: |
| result = 'Jun'; |
| break; |
| case {{{ cDefine('ABMON_7') }}}: |
| result = 'Jul'; |
| break; |
| case {{{ cDefine('ABMON_8') }}}: |
| result = 'Aug'; |
| break; |
| case {{{ cDefine('ABMON_9') }}}: |
| result = 'Sep'; |
| break; |
| case {{{ cDefine('ABMON_10') }}}: |
| result = 'Oct'; |
| break; |
| case {{{ cDefine('ABMON_11') }}}: |
| result = 'Nov'; |
| break; |
| case {{{ cDefine('ABMON_12') }}}: |
| result = 'Dec'; |
| break; |
| case {{{ cDefine('ALT_DIGITS') }}}: |
| result = ''; |
| break; |
| case {{{ cDefine('RADIXCHAR') }}}: |
| result = '.'; |
| break; |
| case {{{ cDefine('THOUSEP') }}}: |
| result = ''; |
| break; |
| case {{{ cDefine('YESEXPR') }}}: |
| result = '^[yY]'; |
| break; |
| case {{{ cDefine('NOEXPR') }}}: |
| result = '^[nN]'; |
| break; |
| case {{{ cDefine('CRNCYSTR') }}}: |
| result = '-'; |
| break; |
| case {{{ cDefine('ERA') }}}: |
| case {{{ cDefine('ERA_D_FMT') }}}: |
| case {{{ cDefine('ERA_D_T_FMT') }}}: |
| case {{{ cDefine('ERA_T_FMT') }}}: |
| default: |
| result = ''; |
| break; |
| } |
| |
| var me = _nl_langinfo; |
| if (!me.ret) me.ret = _malloc(32); |
| for (var i = 0; i < result.length; i++) { |
| {{{ makeSetValue('me.ret', 'i', 'result.charCodeAt(i)', 'i8') }}} |
| } |
| {{{ makeSetValue('me.ret', 'i', '0', 'i8') }}} |
| return me.ret; |
| }, |
| |
| _Z7catopenPKci: function() { throw 'catopen not implemented' }, |
| _Z7catgetsP8_nl_catdiiPKc: function() { throw 'catgets not implemented' }, |
| _Z8catcloseP8_nl_catd: function() { throw 'catclose not implemented' }, |
| |
| // ========================================================================== |
| // errno.h |
| // ========================================================================== |
| |
| $ERRNO_CODES: { |
| EPERM: 1, |
| ENOENT: 2, |
| ESRCH: 3, |
| EINTR: 4, |
| EIO: 5, |
| ENXIO: 6, |
| E2BIG: 7, |
| ENOEXEC: 8, |
| EBADF: 9, |
| ECHILD: 10, |
| EAGAIN: 11, |
| EWOULDBLOCK: 11, |
| ENOMEM: 12, |
| EACCES: 13, |
| EFAULT: 14, |
| ENOTBLK: 15, |
| EBUSY: 16, |
| EEXIST: 17, |
| EXDEV: 18, |
| ENODEV: 19, |
| ENOTDIR: 20, |
| EISDIR: 21, |
| EINVAL: 22, |
| ENFILE: 23, |
| EMFILE: 24, |
| ENOTTY: 25, |
| ETXTBSY: 26, |
| EFBIG: 27, |
| ENOSPC: 28, |
| ESPIPE: 29, |
| EROFS: 30, |
| EMLINK: 31, |
| EPIPE: 32, |
| EDOM: 33, |
| ERANGE: 34, |
| ENOMSG: 35, |
| EIDRM: 36, |
| ECHRNG: 37, |
| EL2NSYNC: 38, |
| EL3HLT: 39, |
| EL3RST: 40, |
| ELNRNG: 41, |
| EUNATCH: 42, |
| ENOCSI: 43, |
| EL2HLT: 44, |
| EDEADLK: 45, |
| ENOLCK: 46, |
| EBADE: 50, |
| EBADR: 51, |
| EXFULL: 52, |
| ENOANO: 53, |
| EBADRQC: 54, |
| EBADSLT: 55, |
| EDEADLOCK: 56, |
| EBFONT: 57, |
| ENOSTR: 60, |
| ENODATA: 61, |
| ETIME: 62, |
| ENOSR: 63, |
| ENONET: 64, |
| ENOPKG: 65, |
| EREMOTE: 66, |
| ENOLINK: 67, |
| EADV: 68, |
| ESRMNT: 69, |
| ECOMM: 70, |
| EPROTO: 71, |
| EMULTIHOP: 74, |
| ELBIN: 75, |
| EDOTDOT: 76, |
| EBADMSG: 77, |
| EFTYPE: 79, |
| ENOTUNIQ: 80, |
| EBADFD: 81, |
| EREMCHG: 82, |
| ELIBACC: 83, |
| ELIBBAD: 84, |
| ELIBSCN: 85, |
| ELIBMAX: 86, |
| ELIBEXEC: 87, |
| ENOSYS: 88, |
| ENMFILE: 89, |
| ENOTEMPTY: 90, |
| ENAMETOOLONG: 91, |
| ELOOP: 92, |
| EOPNOTSUPP: 95, |
| EPFNOSUPPORT: 96, |
| ECONNRESET: 104, |
| ENOBUFS: 105, |
| EAFNOSUPPORT: 106, |
| EPROTOTYPE: 107, |
| ENOTSOCK: 108, |
| ENOPROTOOPT: 109, |
| ESHUTDOWN: 110, |
| ECONNREFUSED: 111, |
| EADDRINUSE: 112, |
| ECONNABORTED: 113, |
| ENETUNREACH: 114, |
| ENETDOWN: 115, |
| ETIMEDOUT: 116, |
| EHOSTDOWN: 117, |
| EHOSTUNREACH: 118, |
| EINPROGRESS: 119, |
| EALREADY: 120, |
| EDESTADDRREQ: 121, |
| EMSGSIZE: 122, |
| EPROTONOSUPPORT: 123, |
| ESOCKTNOSUPPORT: 124, |
| EADDRNOTAVAIL: 125, |
| ENETRESET: 126, |
| EISCONN: 127, |
| ENOTCONN: 128, |
| ETOOMANYREFS: 129, |
| EPROCLIM: 130, |
| EUSERS: 131, |
| EDQUOT: 132, |
| ESTALE: 133, |
| ENOTSUP: 134, |
| ENOMEDIUM: 135, |
| ENOSHARE: 136, |
| ECASECLASH: 137, |
| EILSEQ: 138, |
| EOVERFLOW: 139, |
| ECANCELED: 140, |
| ENOTRECOVERABLE: 141, |
| EOWNERDEAD: 142, |
| ESTRPIPE: 143 |
| }, |
| $ERRNO_MESSAGES: { |
| 0: 'Success', |
| 1: 'Not super-user', |
| 2: 'No such file or directory', |
| 3: 'No such process', |
| 4: 'Interrupted system call', |
| 5: 'I/O error', |
| 6: 'No such device or address', |
| 7: 'Arg list too long', |
| 8: 'Exec format error', |
| 9: 'Bad file number', |
| 10: 'No children', |
| 11: 'No more processes', |
| 12: 'Not enough core', |
| 13: 'Permission denied', |
| 14: 'Bad address', |
| 15: 'Block device required', |
| 16: 'Mount device busy', |
| 17: 'File exists', |
| 18: 'Cross-device link', |
| 19: 'No such device', |
| 20: 'Not a directory', |
| 21: 'Is a directory', |
| 22: 'Invalid argument', |
| 23: 'Too many open files in system', |
| 24: 'Too many open files', |
| 25: 'Not a typewriter', |
| 26: 'Text file busy', |
| 27: 'File too large', |
| 28: 'No space left on device', |
| 29: 'Illegal seek', |
| 30: 'Read only file system', |
| 31: 'Too many links', |
| 32: 'Broken pipe', |
| 33: 'Math arg out of domain of func', |
| 34: 'Math result not representable', |
| 35: 'No message of desired type', |
| 36: 'Identifier removed', |
| 37: 'Channel number out of range', |
| 38: 'Level 2 not synchronized', |
| 39: 'Level 3 halted', |
| 40: 'Level 3 reset', |
| 41: 'Link number out of range', |
| 42: 'Protocol driver not attached', |
| 43: 'No CSI structure available', |
| 44: 'Level 2 halted', |
| 45: 'Deadlock condition', |
| 46: 'No record locks available', |
| 50: 'Invalid exchange', |
| 51: 'Invalid request descriptor', |
| 52: 'Exchange full', |
| 53: 'No anode', |
| 54: 'Invalid request code', |
| 55: 'Invalid slot', |
| 56: 'File locking deadlock error', |
| 57: 'Bad font file fmt', |
| 60: 'Device not a stream', |
| 61: 'No data (for no delay io)', |
| 62: 'Timer expired', |
| 63: 'Out of streams resources', |
| 64: 'Machine is not on the network', |
| 65: 'Package not installed', |
| 66: 'The object is remote', |
| 67: 'The link has been severed', |
| 68: 'Advertise error', |
| 69: 'Srmount error', |
| 70: 'Communication error on send', |
| 71: 'Protocol error', |
| 74: 'Multihop attempted', |
| 75: 'Inode is remote (not really error)', |
| 76: 'Cross mount point (not really error)', |
| 77: 'Trying to read unreadable message', |
| 79: 'Inappropriate file type or format', |
| 80: 'Given log. name not unique', |
| 81: 'f.d. invalid for this operation', |
| 82: 'Remote address changed', |
| 83: 'Can\t access a needed shared lib', |
| 84: 'Accessing a corrupted shared lib', |
| 85: '.lib section in a.out corrupted', |
| 86: 'Attempting to link in too many libs', |
| 87: 'Attempting to exec a shared library', |
| 88: 'Function not implemented', |
| 89: 'No more files', |
| 90: 'Directory not empty', |
| 91: 'File or path name too long', |
| 92: 'Too many symbolic links', |
| 95: 'Operation not supported on transport endpoint', |
| 96: 'Protocol family not supported', |
| 104: 'Connection reset by peer', |
| 105: 'No buffer space available', |
| 106: 'Address family not supported by protocol family', |
| 107: 'Protocol wrong type for socket', |
| 108: 'Socket operation on non-socket', |
| 109: 'Protocol not available', |
| 110: 'Can\'t send after socket shutdown', |
| 111: 'Connection refused', |
| 112: 'Address already in use', |
| 113: 'Connection aborted', |
| 114: 'Network is unreachable', |
| 115: 'Network interface is not configured', |
| 116: 'Connection timed out', |
| 117: 'Host is down', |
| 118: 'Host is unreachable', |
| 119: 'Connection already in progress', |
| 120: 'Socket already connected', |
| 121: 'Destination address required', |
| 122: 'Message too long', |
| 123: 'Unknown protocol', |
| 124: 'Socket type not supported', |
| 125: 'Address not available', |
| 126: 'ENETRESET', |
| 127: 'Socket is already connected', |
| 128: 'Socket is not connected', |
| 129: 'TOOMANYREFS', |
| 130: 'EPROCLIM', |
| 131: 'EUSERS', |
| 132: 'EDQUOT', |
| 133: 'ESTALE', |
| 134: 'Not supported', |
| 135: 'No medium (in tape drive)', |
| 136: 'No such host or network path', |
| 137: 'Filename exists with different case', |
| 138: 'EILSEQ', |
| 139: 'Value too large for defined data type', |
| 140: 'Operation canceled', |
| 141: 'State not recoverable', |
| 142: 'Previous owner died', |
| 143: 'Streams pipe error', |
| }, |
| __errno_state: 0, |
| __setErrNo__deps: ['__errno_state'], |
| __setErrNo__postset: '___errno_state = Runtime.staticAlloc(4); {{{ makeSetValue("___errno_state", 0, 0, "i32") }}};', |
| __setErrNo: function(value) { |
| // For convenient setting and returning of errno. |
| {{{ makeSetValue('___errno_state', '0', 'value', 'i32') }}} |
| return value; |
| }, |
| __errno_location__deps: ['__setErrNo'], |
| __errno_location: function() { |
| return ___errno_state; |
| }, |
| __errno: '__errno_location', |
| |
| // ========================================================================== |
| // sys/resource.h |
| // ========================================================================== |
| |
| // TODO: Implement for real. |
| __rlimit_struct_layout: Runtime.generateStructInfo([ |
| ['i32', 'rlim_cur'], |
| ['i32', 'rlim_max']]), |
| getrlimit__deps: ['__rlimit_struct_layout'], |
| getrlimit: function(resource, rlp) { |
| // int getrlimit(int resource, struct rlimit *rlp); |
| {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_cur', '-1', 'i32') }}} // RLIM_INFINITY |
| {{{ makeSetValue('rlp', '___rlimit_struct_layout.rlim_max', '-1', 'i32') }}} // RLIM_INFINITY |
| return 0; |
| }, |
| setrlimit: function(resource, rlp) { |
| // int setrlimit(int resource, const struct rlimit *rlp) |
| return 0; |
| }, |
| __01getrlimit64_: 'getrlimit', |
| |
| // TODO: Implement for real. We just do time used, and no useful data |
| __rusage_struct_layout: Runtime.generateStructInfo([ |
| ['i64', 'ru_utime'], |
| ['i64', 'ru_stime'], |
| ['i32', 'ru_maxrss'], |
| ['i32', 'ru_ixrss'], |
| ['i32', 'ru_idrss'], |
| ['i32', 'ru_isrss'], |
| ['i32', 'ru_minflt'], |
| ['i32', 'ru_majflt'], |
| ['i32', 'ru_nswap'], |
| ['i32', 'ru_inblock'], |
| ['i32', 'ru_oublock'], |
| ['i32', 'ru_msgsnd'], |
| ['i32', 'ru_msgrcv'], |
| ['i32', 'ru_nsignals'], |
| ['i32', 'ru_nvcsw'], |
| ['i32', 'ru_nivcsw']]), |
| getrusage__deps: ['__rusage_struct_layout'], |
| getrusage: function(resource, rlp) { |
| // %struct.timeval = type { i32, i32 } |
| var timeval = Runtime.calculateStructAlignment({ fields: ['i32', 'i32'] }); |
| |
| // int getrusage(int resource, struct rusage *rlp); |
| {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_utime+timeval[0]', '1', 'i32') }}} |
| {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_utime+timeval[1]', '2', 'i32') }}} |
| {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_stime+timeval[0]', '3', 'i32') }}} |
| {{{ makeSetValue('rlp', '___rusage_struct_layout.ru_stime+timeval[1]', '4', 'i32') }}} |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // sched.h (stubs only - no thread support yet!) |
| // ========================================================================== |
| sched_yield: function() { |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // pthread.h (stubs for mutexes only - no thread support yet!) |
| // ========================================================================== |
| |
| pthread_mutex_init: function() {}, |
| pthread_mutex_destroy: function() {}, |
| pthread_mutexattr_init: function() {}, |
| pthread_mutexattr_settype: function() {}, |
| pthread_mutexattr_destroy: function() {}, |
| pthread_mutex_lock: function() {}, |
| pthread_mutex_unlock: function() {}, |
| pthread_mutex_trylock: function() { |
| return 0; |
| }, |
| pthread_cond_init: function() {}, |
| pthread_cond_destroy: function() {}, |
| pthread_cond_broadcast: function() { |
| return 0; |
| }, |
| pthread_cond_wait: function() { |
| return 0; |
| }, |
| pthread_cond_timedwait: function() { |
| return 0; |
| }, |
| pthread_self: function() { |
| //FIXME: assumes only a single thread |
| return 0; |
| }, |
| pthread_attr_init: function(attr) { |
| /* int pthread_attr_init(pthread_attr_t *attr); */ |
| //FIXME: should allocate a pthread_attr_t |
| return 0; |
| }, |
| pthread_getattr_np: function(thread, attr) { |
| /* int pthread_getattr_np(pthread_t thread, pthread_attr_t *attr); */ |
| //FIXME: should fill in attributes of the given thread in pthread_attr_t |
| return 0; |
| }, |
| pthread_attr_destroy: function(attr) { |
| /* int pthread_attr_destroy(pthread_attr_t *attr); */ |
| //FIXME: should destroy the pthread_attr_t struct |
| return 0; |
| }, |
| pthread_attr_getstack: function(attr, stackaddr, stacksize) { |
| /* int pthread_attr_getstack(const pthread_attr_t *restrict attr, |
| void **restrict stackaddr, size_t *restrict stacksize); */ |
| /*FIXME: assumes that there is only one thread, and that attr is the |
| current thread*/ |
| {{{ makeSetValue('stackaddr', '0', 'STACK_BASE', 'i8*') }}} |
| {{{ makeSetValue('stacksize', '0', 'TOTAL_STACK', 'i32') }}} |
| return 0; |
| }, |
| |
| pthread_once: function(ptr, func) { |
| if (!_pthread_once.seen) _pthread_once.seen = {}; |
| if (ptr in _pthread_once.seen) return; |
| Runtime.dynCall('v', func); |
| _pthread_once.seen[ptr] = 1; |
| }, |
| |
| pthread_key_create: function(key, destructor) { |
| if (!_pthread_key_create.keys) _pthread_key_create.keys = {}; |
| // values start at 0 |
| _pthread_key_create.keys[key] = 0; |
| }, |
| |
| pthread_getspecific: function(key) { |
| return _pthread_key_create.keys[key] || 0; |
| }, |
| |
| pthread_setspecific: function(key, value) { |
| _pthread_key_create.keys[key] = value; |
| }, |
| |
| pthread_key_delete: ['$ERRNO_CODES'], |
| pthread_key_delete: function(key) { |
| if (_pthread_key_create.keys[key]) { |
| delete _pthread_key_create.keys[key]; |
| return 0; |
| } |
| return ERRNO_CODES.EINVAL; |
| }, |
| |
| pthread_cleanup_push: function(routine, arg) { |
| __ATEXIT__.push({ func: function() { Runtime.dynCall('vi', routine, [arg]) } }) |
| _pthread_cleanup_push.level = __ATEXIT__.length; |
| }, |
| |
| pthread_cleanup_pop: function() { |
| assert(_pthread_cleanup_push.level == __ATEXIT__.length, 'cannot pop if something else added meanwhile!'); |
| __ATEXIT__.pop(); |
| _pthread_cleanup_push.level = __ATEXIT__.length; |
| }, |
| |
| // ========================================================================== |
| // malloc.h |
| // ========================================================================== |
| |
| memalign: function(boundary, size) { |
| // leaks, and even returns an invalid pointer. Horrible hack... but then, this is a deprecated function... |
| var ret = Runtime.staticAlloc(size + boundary); |
| return ret + boundary - (ret % boundary); |
| }, |
| |
| posix_memalign__deps: ['memalign'], |
| posix_memalign: function(memptr, alignment, size) { |
| var ptr = _memalign(alignment, size); |
| {{{ makeSetValue('memptr', '0', 'ptr', 'i8*') }}} |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // arpa/inet.h |
| // ========================================================================== |
| |
| htonl: function(value) { |
| return ((value & 0xff) << 24) + ((value & 0xff00) << 8) + |
| ((value & 0xff0000) >>> 8) + ((value & 0xff000000) >>> 24); |
| }, |
| htons: function(value) { |
| return ((value & 0xff) << 8) + ((value & 0xff00) >> 8); |
| }, |
| 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; |
| }, |
| |
| inet_pton__deps: ['__setErrNo', '$ERRNO_CODES', 'inet_addr'], |
| 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; |
| }, |
| |
| _inet_ntop_raw: function(addr) { |
| return (addr & 0xff) + '.' + ((addr >> 8) & 0xff) + '.' + ((addr >> 16) & 0xff) + '.' + ((addr >> 24) & 0xff) |
| }, |
| |
| 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_ntoa__deps: ['inet_ntop'], |
| inet_ntoa: function(in_addr) { |
| if (!_inet_ntoa.buffer) { |
| _inet_ntoa.buffer = _malloc(1024); |
| } |
| return _inet_ntop(0, in_addr, _inet_ntoa.buffer, 1024); |
| }, |
| |
| inet_aton__deps: ['inet_addr'], |
| inet_aton: function(cp, inp) { |
| var addr = _inet_addr(cp); |
| setValue(inp, addr, 'i32'); |
| if (addr < 0) return 0; |
| return 1; |
| }, |
| |
| // netinet/in.h |
| |
| _in6addr_any: |
| 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0], "i8", ALLOC_STATIC)', |
| _in6addr_loopback: |
| 'allocate([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', |
| _in6addr_linklocal_allnodes: |
| 'allocate([255,2,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', |
| _in6addr_linklocal_allrouters: |
| 'allocate([255,2,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', |
| _in6addr_interfacelocal_allnodes: |
| 'allocate([255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1], "i8", ALLOC_STATIC)', |
| _in6addr_interfacelocal_allrouters: |
| 'allocate([255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', |
| _in6addr_sitelocal_allrouters: |
| 'allocate([255,5,0,0,0,0,0,0,0,0,0,0,0,0,0,2], "i8", ALLOC_STATIC)', |
| |
| // ========================================================================== |
| // netdb.h |
| // ========================================================================== |
| |
| // All we can do is alias names to ips. you give this a name, it returns an |
| // "ip" that we later know to use as a name. There is no way to do actual |
| // name resolving clientside in a browser. |
| // we do the aliasing in 172.29.*.*, giving us 65536 possibilities |
| // note: lots of leaking here! |
| __hostent_struct_layout: Runtime.generateStructInfo([ |
| ['i8*', 'h_name'], |
| ['i8**', 'h_aliases'], |
| ['i32', 'h_addrtype'], |
| ['i32', 'h_length'], |
| ['i8**', 'h_addr_list'], |
| ]), |
| |
| gethostbyname__deps: ['__hostent_struct_layout'], |
| gethostbyname: function(name) { |
| name = Pointer_stringify(name); |
| if (!_gethostbyname.id) { |
| _gethostbyname.id = 1; |
| _gethostbyname.table = {}; |
| } |
| var id = _gethostbyname.id++; |
| assert(id < 65535); |
| var fakeAddr = 172 | (29 << 8) | ((id & 0xff) << 16) | ((id & 0xff00) << 24); |
| _gethostbyname.table[id] = name; |
| // generate hostent |
| var ret = _malloc(___hostent_struct_layout.__size__); |
| var nameBuf = _malloc(name.length+1); |
| writeStringToMemory(name, nameBuf); |
| setValue(ret+___hostent_struct_layout.h_name, nameBuf, 'i8*'); |
| var aliasesBuf = _malloc(4); |
| setValue(aliasesBuf, 0, 'i8*'); |
| setValue(ret+___hostent_struct_layout.h_aliases, aliasesBuf, 'i8**'); |
| setValue(ret+___hostent_struct_layout.h_addrtype, {{{ cDefine("AF_INET") }}}, 'i32'); |
| setValue(ret+___hostent_struct_layout.h_length, 4, 'i32'); |
| var addrListBuf = _malloc(12); |
| setValue(addrListBuf, addrListBuf+8, 'i32*'); |
| setValue(addrListBuf+4, 0, 'i32*'); |
| setValue(addrListBuf+8, fakeAddr, 'i32'); |
| setValue(ret+___hostent_struct_layout.h_addr_list, addrListBuf, 'i8**'); |
| return ret; |
| }, |
| |
| gethostbyname_r__deps: ['gethostbyname'], |
| gethostbyname_r: function(name, hostData, buffer, bufferSize, hostEntry, errnum) { |
| var data = _gethostbyname(name); |
| _memcpy(hostData, data, ___hostent_struct_layout.__size__); |
| _free(data); |
| setValue(errnum, 0, 'i32'); |
| return 0; |
| }, |
| |
| // ========================================================================== |
| // sockets. Note that the implementation assumes all sockets are always |
| // nonblocking |
| // ========================================================================== |
| #if SOCKET_WEBRTC |
| $Sockets__deps: ['__setErrNo', '$ERRNO_CODES', |
| function() { return 'var SocketIO = ' + read('socket.io.js') + ';\n' }, |
| function() { return 'var Peer = ' + read('wrtcp.js') + ';\n' }], |
| #else |
| $Sockets__deps: ['__setErrNo', '$ERRNO_CODES'], |
| #endif |
| $Sockets: { |
| BUFFER_SIZE: 10*1024, // initial size |
| MAX_BUFFER_SIZE: 10*1024*1024, // maximum size we will grow the buffer |
| |
| nextFd: 1, |
| fds: {}, |
| nextport: 1, |
| maxport: 65535, |
| peer: null, |
| connections: {}, |
| portmap: {}, |
| localAddr: 0xfe00000a, // Local address is always 10.0.0.254 |
| addrPool: [ 0x0200000a, 0x0300000a, 0x0400000a, 0x0500000a, |
| 0x0600000a, 0x0700000a, 0x0800000a, 0x0900000a, 0x0a00000a, |
| 0x0b00000a, 0x0c00000a, 0x0d00000a, 0x0e00000a], /* 0x0100000a is reserved */ |
| sockaddr_in_layout: Runtime.generateStructInfo([ |
| ['i32', 'sin_family'], |
| ['i16', 'sin_port'], |
| ['i32', 'sin_addr'], |
| ['i32', 'sin_zero'], |
| ['i16', 'sin_zero_b'], |
| ]), |
| msghdr_layout: Runtime.generateStructInfo([ |
| ['*', 'msg_name'], |
| ['i32', 'msg_namelen'], |
| ['*', 'msg_iov'], |
| ['i32', 'msg_iovlen'], |
| ['*', 'msg_control'], |
| ['i32', 'msg_controllen'], |
| ['i32', 'msg_flags'], |
| ]), |
| }, |
| |
| #if SOCKET_WEBRTC |
| /* WebRTC sockets supports several options on the Module object. |
| |
| * Module['host']: true if this peer is hosting, false otherwise |
| * Module['webrtc']['broker']: hostname for the p2p broker that this peer should use |
| * Module['webrtc']['session']: p2p session for that this peer will join, or undefined if this peer is hosting |
| * Module['webrtc']['hostOptions']: options to pass into p2p library if this peer is hosting |
| * Module['webrtc']['onpeer']: function(peer, route), invoked when this peer is ready to connect |
| * Module['webrtc']['onconnect']: function(peer), invoked when a new peer connection is ready |
| * 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: function(family, type, protocol) { |
| var INCOMING_QUEUE_LENGTH = 64; |
| var fd = FS.createFileHandle({ |
| addr: null, |
| port: null, |
| inQueue: new CircularBuffer(INCOMING_QUEUE_LENGTH), |
| header: new Uint16Array(2), |
| bound: false, |
| socket: true |
| }); |
| assert(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 |
| } |
| |
| // Open the peer connection if we don't have it already |
| if (null == Sockets.peer) { |
| var host = Module['host']; |
| var broker = Module['webrtc']['broker']; |
| var session = Module['webrtc']['session']; |
| var peer = new Peer(broker); |
| var listenOptions = Module['webrtc']['hostOptions'] || {}; |
| peer.onconnection = function(connection) { |
| console.log('connected'); |
| var addr; |
| /* If this peer is connecting to the host, assign 10.0.0.1 to the host so it can be |
| reached at a known address. |
| */ |
| // Assign 10.0.0.1 to the host |
| if (session && session === connection['route']) { |
| addr = 0x0100000a; // 10.0.0.1 |
| } else { |
| addr = Sockets.addrPool.shift(); |
| } |
| connection['addr'] = addr; |
| Sockets.connections[addr] = connection; |
| connection.ondisconnect = function() { |
| console.log('disconnect'); |
| // Don't return the host address (10.0.0.1) to the pool |
| if (!(session && session === Sockets.connections[addr]['route'])) { |
| Sockets.addrPool.push(addr); |
| } |
| delete Sockets.connections[addr]; |
| |
| if (Module['webrtc']['ondisconnect'] && 'function' === typeof Module['webrtc']['ondisconnect']) { |
| Module['webrtc']['ondisconnect'](peer); |
| } |
| }; |
| connection.onerror = function(error) { |
| if (Module['webrtc']['onerror'] && 'function' === typeof Module['webrtc']['onerror']) { |
| Module['webrtc']['onerror'](error); |
| } |
| }; |
| connection.onmessage = function(label, message) { |
| if ('unreliable' === label) { |
| handleMessage(addr, message.data); |
| } |
| } |
| |
| if (Module['webrtc']['onconnect'] && 'function' === typeof Module['webrtc']['onconnect']) { |
| Module['webrtc']['onconnect'](peer); |
| } |
| }; |
| peer.onpending = function(pending) { |
| console.log('pending from: ', pending['route'], '; initiated by: ', (pending['incoming']) ? 'remote' : 'local'); |
| }; |
| peer.onerror = function(error) { |
| console.error(error); |
| }; |
| peer.onroute = function(route) { |
| if (Module['webrtc']['onpeer'] && 'function' === typeof Module['webrtc']['onpeer']) { |
| Module['webrtc']['onpeer'](peer, route); |
| } |
| }; |
| function handleMessage(addr, message) { |
| #if SOCKET_DEBUG |
| Module.print("received " + message.byteLength + " raw bytes"); |
| #endif |
| var header = new Uint16Array(message, 0, 2); |
| if (Sockets.portmap[header[1]]) { |
| Sockets.portmap[header[1]].inQueue.push([addr, message]); |
| } else { |
| console.log("unable to deliver message: ", addr, header[1], message); |
| } |
| } |
| window.onbeforeunload = function() { |
| var ids = Object.keys(Sockets.connections); |
| ids.forEach(function(id) { |
| Sockets.connections[id].close(); |
| }); |
| } |
| Sockets.peer = peer; |
| } |
| |
| function CircularBuffer(max_length) { |
| var buffer = new Array(++ max_length); |
| var head = 0; |
| var tail = 0; |
| var length = 0; |
| |
| return { |
| push: function(element) { |
| buffer[tail ++] = element; |
| length = Math.min(++ length, max_length - 1); |
| tail = tail % max_length; |
| if (tail === head) { |
| head = (head + 1) % max_length; |
| } |
| }, |
| shift: function(element) { |
| if (length < 1) return undefined; |
| |
| var element = buffer[head]; |
| -- length; |
| head = (head + 1) % max_length; |
| return element; |
| }, |
| length: function() { |
| return length; |
| } |
| }; |
| }; |
| |
| return fd; |
| }, |
| |
| mkport__deps: ['$Sockets'], |
| mkport: function() { |
| for(var i = 0; i < Sockets.maxport; ++ i) { |
| var port = Sockets.nextport ++; |
| Sockets.nextport = (Sockets.nextport > Sockets.maxport) ? 1 : Sockets.nextport; |
| if (!Sockets.portmap[port]) { |
| return port; |
| } |
| } |
| assert(false, 'all available ports are in use!'); |
| }, |
| |
| connect: function() { |
| // Stub: connection-oriented sockets are not supported yet. |
| }, |
| |
| bind__deps: ['$Sockets', '_inet_ntop_raw', 'ntohs', 'mkport'], |
| bind: function(fd, addr, addrlen) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| if (addr) { |
| info.port = _ntohs(getValue(addr + Sockets.sockaddr_in_layout.sin_port, 'i16')); |
| // info.addr = getValue(addr + Sockets.sockaddr_in_layout.sin_addr, 'i32'); |
| } |
| if (!info.port) { |
| info.port = _mkport(); |
| } |
| info.addr = Sockets.localAddr; // 10.0.0.254 |
| info.host = __inet_ntop_raw(info.addr); |
| info.close = function() { |
| Sockets.portmap[info.port] = undefined; |
| } |
| Sockets.portmap[info.port] = info; |
| console.log("bind: ", info.host, info.port); |
| info.bound = true; |
| }, |
| |
| sendmsg__deps: ['$Sockets', 'bind', '_inet_ntop_raw', 'ntohs'], |
| sendmsg: function(fd, msg, flags) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| // if we are not connected, use the address info in the message |
| if (!info.bound) { |
| _bind(fd); |
| } |
| |
| var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; |
| assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); |
| 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); |
| |
| if (!(connection && connection.connected)) { |
| ___setErrNo(ERRNO_CODES.EWOULDBLOCK); |
| return -1; |
| } |
| |
| var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; |
| var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; |
| #if SOCKET_DEBUG |
| Module.print('sendmsg vecs: ' + num); |
| #endif |
| var totalSize = 0; |
| for (var i = 0; i < num; i++) { |
| totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; |
| } |
| var data = new Uint8Array(totalSize); |
| var ret = 0; |
| for (var i = 0; i < num; i++) { |
| var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; |
| #if SOCKET_DEBUG |
| Module.print('sendmsg curr size: ' + currNum); |
| #endif |
| if (!currNum) continue; |
| var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; |
| data.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret); |
| ret += currNum; |
| } |
| |
| info.header[0] = info.port; // src port |
| info.header[1] = port; // dst port |
| #if SOCKET_DEBUG |
| Module.print('sendmsg port: ' + info.header[0] + ' -> ' + info.header[1]); |
| Module.print('sendmsg bytes: ' + data.length + ' | ' + Array.prototype.slice.call(data)); |
| #endif |
| var buffer = new Uint8Array(info.header.byteLength + data.byteLength); |
| buffer.set(new Uint8Array(info.header.buffer)); |
| buffer.set(data, info.header.byteLength); |
| |
| connection.send('unreliable', buffer.buffer); |
| }, |
| |
| recvmsg__deps: ['$Sockets', 'bind', '__setErrNo', '$ERRNO_CODES', 'htons'], |
| recvmsg: function(fd, msg, flags) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| // if we are not connected, use the address info in the message |
| if (!info.port) { |
| console.log('recvmsg on unbound socket'); |
| assert(false, 'cannot receive on unbound socket'); |
| } |
| if (info.inQueue.length() == 0) { |
| ___setErrNo(ERRNO_CODES.EWOULDBLOCK); |
| return -1; |
| } |
| |
| var entry = info.inQueue.shift(); |
| var addr = entry[0]; |
| var message = entry[1]; |
| var header = new Uint16Array(message, 0, info.header.length); |
| var buffer = new Uint8Array(message, info.header.byteLength); |
| |
| var bytes = buffer.length; |
| #if SOCKET_DEBUG |
| Module.print('recvmsg port: ' + header[1] + ' <- ' + header[0]); |
| Module.print('recvmsg bytes: ' + bytes + ' | ' + Array.prototype.slice.call(buffer)); |
| #endif |
| // write source |
| var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; |
| {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'addr', 'i32') }}}; |
| {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(header[0])', 'i16') }}}; |
| // write data |
| var ret = bytes; |
| var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; |
| var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; |
| var bufferPos = 0; |
| for (var i = 0; i < num && bytes > 0; i++) { |
| var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; |
| #if SOCKET_DEBUG |
| Module.print('recvmsg loop ' + [i, num, bytes, currNum]); |
| #endif |
| if (!currNum) continue; |
| currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..? |
| bytes -= currNum; |
| var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; |
| #if SOCKET_DEBUG |
| Module.print('recvmsg call recv ' + currNum); |
| #endif |
| HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf); |
| bufferPos += currNum; |
| } |
| return ret; |
| }, |
| |
| shutdown: function(fd, how) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| info.close(); |
| FS.removeFileHandle(fd); |
| }, |
| |
| ioctl: function(fd, request, varargs) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| var bytes = 0; |
| if (info.hasData()) { |
| bytes = info.inQueue[0].length; |
| } |
| var dest = {{{ makeGetValue('varargs', '0', 'i32') }}}; |
| {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}}; |
| return 0; |
| }, |
| |
| setsockopt: function(d, level, optname, optval, optlen) { |
| console.log('ignoring setsockopt command'); |
| return 0; |
| }, |
| |
| 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]; |
| if (!info) return -1; |
| if (addr) { |
| setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32'); |
| setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32'); |
| setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32'); |
| } |
| return fd; |
| }, |
| |
| select: function(nfds, readfds, writefds, exceptfds, timeout) { |
| // readfds are supported, |
| // writefds checks socket open status |
| // exceptfds not supported |
| // timeout is always 0 - fully async |
| assert(!exceptfds); |
| |
| var errorCondition = 0; |
| |
| function canRead(info) { |
| return info.inQueue.length() > 0; |
| } |
| |
| function canWrite(info) { |
| return true; |
| } |
| |
| function checkfds(nfds, fds, can) { |
| if (!fds) return 0; |
| |
| var bitsSet = 0; |
| var dstLow = 0; |
| var dstHigh = 0; |
| var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}}; |
| var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}}; |
| nfds = Math.min(64, nfds); // fd sets have 64 bits |
| |
| for (var fd = 0; fd < nfds; fd++) { |
| 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]; |
| if (info && can(info)) { |
| // set bit |
| fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); |
| bitsSet++; |
| } |
| } |
| } |
| |
| {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}}; |
| {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}}; |
| return bitsSet; |
| } |
| |
| var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite); |
| if (errorCondition) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else { |
| return totalHandles; |
| } |
| }, |
| #else |
| socket__deps: ['$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({ |
| connected: false, |
| stream: stream, |
| socket: true |
| }); |
| assert(fd < 64); // select() assumes socket fd values are in 0..63 |
| return fd; |
| }, |
| |
| connect__deps: ['$FS', '$Sockets', '_inet_ntop_raw', 'ntohs', 'gethostbyname'], |
| connect: function(fd, addr, addrlen) { |
| var info = FS.streams[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); |
| // Support 'fake' ips from gethostbyname |
| var parts = info.host.split('.'); |
| if (parts[0] == '172' && parts[1] == '29') { |
| var low = Number(parts[2]); |
| var high = Number(parts[3]); |
| info.host = _gethostbyname.table[low + 0xff*high]; |
| assert(info.host, 'problem translating fake ip ' + parts); |
| } |
| try { |
| console.log('opening ws://' + info.host + ':' + info.port); |
| info.socket = new WebSocket('ws://' + info.host + ':' + info.port, ['binary']); |
| info.socket.binaryType = 'arraybuffer'; |
| |
| var i32Temp = new Uint32Array(1); |
| var i8Temp = new Uint8Array(i32Temp.buffer); |
| |
| info.inQueue = []; |
| info.hasData = function() { return info.inQueue.length > 0 } |
| if (!info.stream) { |
| var partialBuffer = null; // in datagram mode, inQueue contains full dgram messages; this buffers incomplete data. Must begin with the beginning of a message |
| } |
| |
| info.socket.onmessage = function(event) { |
| assert(typeof event.data !== 'string' && event.data.byteLength); // must get binary data! |
| var data = new Uint8Array(event.data); // make a typed array view on the array buffer |
| #if SOCKET_DEBUG |
| Module.print(['onmessage', data.length, '|', Array.prototype.slice.call(data)]); |
| #endif |
| if (info.stream) { |
| info.inQueue.push(data); |
| } else { |
| // we added headers with message sizes, read those to find discrete messages |
| if (partialBuffer) { |
| // append to the partial buffer |
| var newBuffer = new Uint8Array(partialBuffer.length + data.length); |
| newBuffer.set(partialBuffer); |
| newBuffer.set(data, partialBuffer.length); |
| // forget the partial buffer and work on data |
| data = newBuffer; |
| partialBuffer = null; |
| } |
| var currPos = 0; |
| while (currPos+4 < data.length) { |
| i8Temp.set(data.subarray(currPos, currPos+4)); |
| var currLen = i32Temp[0]; |
| assert(currLen > 0); |
| if (currPos + 4 + currLen > data.length) { |
| break; // not enough data has arrived |
| } |
| currPos += 4; |
| #if SOCKET_DEBUG |
| Module.print(['onmessage message', currLen, '|', Array.prototype.slice.call(data.subarray(currPos, currPos+currLen))]); |
| #endif |
| info.inQueue.push(data.subarray(currPos, currPos+currLen)); |
| currPos += currLen; |
| } |
| // If data remains, buffer it |
| if (currPos < data.length) { |
| partialBuffer = data.subarray(currPos); |
| } |
| } |
| } |
| function send(data) { |
| // TODO: if browser accepts views, can optimize this |
| #if SOCKET_DEBUG |
| Module.print('sender actually sending ' + Array.prototype.slice.call(data)); |
| #endif |
| // ok to use the underlying buffer, we created data and know that the buffer starts at the beginning |
| info.socket.send(data.buffer); |
| } |
| var outQueue = []; |
| var intervalling = false, interval; |
| function trySend() { |
| if (info.socket.readyState != info.socket.OPEN) { |
| if (!intervalling) { |
| intervalling = true; |
| console.log('waiting for socket in order to send'); |
| interval = setInterval(trySend, 100); |
| } |
| return; |
| } |
| for (var i = 0; i < outQueue.length; i++) { |
| send(outQueue[i]); |
| } |
| outQueue.length = 0; |
| if (intervalling) { |
| intervalling = false; |
| clearInterval(interval); |
| } |
| } |
| info.sender = function(data) { |
| if (!info.stream) { |
| // add a header with the message size |
| var header = new Uint8Array(4); |
| i32Temp[0] = data.length; |
| header.set(i8Temp); |
| outQueue.push(header); |
| } |
| outQueue.push(new Uint8Array(data)); |
| trySend(); |
| }; |
| } catch(e) { |
| Module.printErr('Error in connect(): ' + e); |
| ___setErrNo(ERRNO_CODES.EACCES); |
| return -1; |
| } |
| |
| return 0; |
| }, |
| |
| recv__deps: ['$FS'], |
| recv: function(fd, buf, len, flags) { |
| var info = FS.streams[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 |
| return -1; |
| } |
| var buffer = info.inQueue.shift(); |
| #if SOCKET_DEBUG |
| Module.print('recv: ' + [Array.prototype.slice.call(buffer)]); |
| #endif |
| if (len < buffer.length) { |
| if (info.stream) { |
| // This is tcp (reliable), so if not all was read, keep it |
| info.inQueue.unshift(buffer.subarray(len)); |
| #if SOCKET_DEBUG |
| Module.print('recv: put back: ' + (len - buffer.length)); |
| #endif |
| } |
| buffer = buffer.subarray(0, len); |
| } |
| HEAPU8.set(buffer, buf); |
| return buffer.length; |
| }, |
| |
| send__deps: ['$FS'], |
| send: function(fd, buf, len, flags) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| info.sender(HEAPU8.subarray(buf, buf+len)); |
| return len; |
| }, |
| |
| sendmsg__deps: ['$FS', '$Sockets', 'connect'], |
| sendmsg: function(fd, msg, flags) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| // if we are not connected, use the address info in the message |
| if (!info.connected) { |
| var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; |
| assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); |
| _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}}); |
| } |
| var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; |
| var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; |
| #if SOCKET_DEBUG |
| Module.print('sendmsg vecs: ' + num); |
| #endif |
| var totalSize = 0; |
| for (var i = 0; i < num; i++) { |
| totalSize += {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; |
| } |
| var buffer = new Uint8Array(totalSize); |
| var ret = 0; |
| for (var i = 0; i < num; i++) { |
| var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; |
| #if SOCKET_DEBUG |
| Module.print('sendmsg curr size: ' + currNum); |
| #endif |
| if (!currNum) continue; |
| var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; |
| buffer.set(HEAPU8.subarray(currBuf, currBuf+currNum), ret); |
| ret += currNum; |
| } |
| info.sender(buffer); // send all the iovs as a single message |
| return ret; |
| }, |
| |
| recvmsg__deps: ['$FS', '$Sockets', 'connect', 'recv', '__setErrNo', '$ERRNO_CODES', 'htons'], |
| recvmsg: function(fd, msg, flags) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| // if we are not connected, use the address info in the message |
| if (!info.connected) { |
| #if SOCKET_DEBUG |
| Module.print('recvmsg connecting'); |
| #endif |
| var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; |
| assert(name, 'sendmsg on non-connected socket, and no name/address in the message'); |
| _connect(fd, name, {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_namelen', 'i32') }}}); |
| } |
| if (!info.hasData()) { |
| ___setErrNo(ERRNO_CODES.EWOULDBLOCK); |
| return -1; |
| } |
| var buffer = info.inQueue.shift(); |
| var bytes = buffer.length; |
| #if SOCKET_DEBUG |
| Module.print('recvmsg bytes: ' + bytes); |
| #endif |
| // write source |
| var name = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_name', '*') }}}; |
| {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_addr', 'info.addr', 'i32') }}}; |
| {{{ makeSetValue('name', 'Sockets.sockaddr_in_layout.sin_port', '_htons(info.port)', 'i16') }}}; |
| // write data |
| var ret = bytes; |
| var iov = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iov', 'i8*') }}}; |
| var num = {{{ makeGetValue('msg', 'Sockets.msghdr_layout.msg_iovlen', 'i32') }}}; |
| var bufferPos = 0; |
| for (var i = 0; i < num && bytes > 0; i++) { |
| var currNum = {{{ makeGetValue('iov', '8*i + 4', 'i32') }}}; |
| #if SOCKET_DEBUG |
| Module.print('recvmsg loop ' + [i, num, bytes, currNum]); |
| #endif |
| if (!currNum) continue; |
| currNum = Math.min(currNum, bytes); // XXX what should happen when we partially fill a buffer..? |
| bytes -= currNum; |
| var currBuf = {{{ makeGetValue('iov', '8*i', 'i8*') }}}; |
| #if SOCKET_DEBUG |
| Module.print('recvmsg call recv ' + currNum); |
| #endif |
| HEAPU8.set(buffer.subarray(bufferPos, bufferPos + currNum), currBuf); |
| bufferPos += currNum; |
| } |
| if (info.stream) { |
| // This is tcp (reliable), so if not all was read, keep it |
| if (bufferPos < bytes) { |
| info.inQueue.unshift(buffer.subarray(bufferPos)); |
| #if SOCKET_DEBUG |
| Module.print('recvmsg: put back: ' + (bytes - bufferPos)); |
| #endif |
| } |
| } |
| return ret; |
| }, |
| |
| recvfrom__deps: ['$FS', 'connect', 'recv'], |
| recvfrom: function(fd, buf, len, flags, addr, addrlen) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| // if we are not connected, use the address info in the message |
| if (!info.connected) { |
| //var name = {{{ makeGetValue('addr', '0', '*') }}}; |
| _connect(fd, addr, addrlen); |
| } |
| return _recv(fd, buf, len, flags); |
| }, |
| |
| shutdown: function(fd, how) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| info.socket.close(); |
| FS.removeFileHandle(fd); |
| }, |
| |
| ioctl: function(fd, request, varargs) { |
| var info = FS.streams[fd]; |
| if (!info) return -1; |
| var bytes = 0; |
| if (info.hasData()) { |
| bytes = info.inQueue[0].length; |
| } |
| var dest = {{{ makeGetValue('varargs', '0', 'i32') }}}; |
| {{{ makeSetValue('dest', '0', 'bytes', 'i32') }}}; |
| return 0; |
| }, |
| |
| setsockopt: function(d, level, optname, optval, optlen) { |
| console.log('ignoring setsockopt command'); |
| return 0; |
| }, |
| |
| bind__deps: ['connect'], |
| bind: function(fd, addr, addrlen) { |
| return _connect(fd, addr, addrlen); |
| }, |
| |
| listen: function(fd, backlog) { |
| return 0; |
| }, |
| |
| accept__deps: ['$FS', '$Sockets'], |
| 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]; |
| if (!info) return -1; |
| if (addr) { |
| setValue(addr + Sockets.sockaddr_in_layout.sin_addr, info.addr, 'i32'); |
| setValue(addr + Sockets.sockaddr_in_layout.sin_port, info.port, 'i32'); |
| setValue(addrlen, Sockets.sockaddr_in_layout.__size__, 'i32'); |
| } |
| return fd; |
| }, |
| |
| select__deps: ['$FS'], |
| select: function(nfds, readfds, writefds, exceptfds, timeout) { |
| // readfds are supported, |
| // writefds checks socket open status |
| // exceptfds not supported |
| // timeout is always 0 - fully async |
| assert(!exceptfds); |
| |
| var errorCondition = 0; |
| |
| function canRead(info) { |
| // make sure hasData exists. |
| // we do create it when the socket is connected, |
| // but other implementations may create it lazily |
| if ((info.socket.readyState == WebSocket.CLOSING || info.socket.readyState == WebSocket.CLOSED) && info.inQueue.length == 0) { |
| errorCondition = -1; |
| return false; |
| } |
| return info.hasData && info.hasData(); |
| } |
| |
| function canWrite(info) { |
| // make sure socket exists. |
| // we do create it when the socket is connected, |
| // but other implementations may create it lazily |
| if ((info.socket.readyState == WebSocket.CLOSING || info.socket.readyState == WebSocket.CLOSED)) { |
| errorCondition = -1; |
| return false; |
| } |
| return info.socket && (info.socket.readyState == info.socket.OPEN); |
| } |
| |
| function checkfds(nfds, fds, can) { |
| if (!fds) return 0; |
| |
| var bitsSet = 0; |
| var dstLow = 0; |
| var dstHigh = 0; |
| var srcLow = {{{ makeGetValue('fds', 0, 'i32') }}}; |
| var srcHigh = {{{ makeGetValue('fds', 4, 'i32') }}}; |
| nfds = Math.min(64, nfds); // fd sets have 64 bits |
| |
| for (var fd = 0; fd < nfds; fd++) { |
| 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]; |
| if (info && can(info)) { |
| // set bit |
| fd < 32 ? (dstLow = dstLow | mask) : (dstHigh = dstHigh | mask); |
| bitsSet++; |
| } |
| } |
| } |
| |
| {{{ makeSetValue('fds', 0, 'dstLow', 'i32') }}}; |
| {{{ makeSetValue('fds', 4, 'dstHigh', 'i32') }}}; |
| return bitsSet; |
| } |
| |
| var totalHandles = checkfds(nfds, readfds, canRead) + checkfds(nfds, writefds, canWrite); |
| if (errorCondition) { |
| ___setErrNo(ERRNO_CODES.EBADF); |
| return -1; |
| } else { |
| return totalHandles; |
| } |
| }, |
| #endif |
| |
| socketpair__deps: ['__setErrNo', '$ERRNO_CODES'], |
| socketpair: function(domain, type, protocol, sv) { |
| // int socketpair(int domain, int type, int protocol, int sv[2]); |
| // http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html |
| ___setErrNo(ERRNO_CODES.EOPNOTSUPP); |
| return -1; |
| }, |
| |
| // pty.h |
| |
| openpty: function() { throw 'openpty: TODO' }, |
| forkpty: function() { throw 'forkpty: TODO' }, |
| |
| // grp.h |
| |
| initgroups: function() { throw 'initgroups: TODO' }, |
| |
| // pwd.h |
| |
| getpwnam: function() { throw 'getpwnam: TODO' }, |
| setpwent: function() { throw 'setpwent: TODO' }, |
| getpwent: function() { throw 'getpwent: TODO' }, |
| endpwent: function() { throw 'endpwent: TODO' }, |
| |
| // ========================================================================== |
| // emscripten.h |
| // ========================================================================== |
| |
| emscripten_run_script: function(ptr) { |
| eval(Pointer_stringify(ptr)); |
| }, |
| |
| emscripten_run_script_int: function(ptr) { |
| return eval(Pointer_stringify(ptr))|0; |
| }, |
| |
| emscripten_run_script_string: function(ptr) { |
| var s = eval(Pointer_stringify(ptr)); |
| var me = _emscripten_run_script_string; |
| if (!me.bufferSize || me.bufferSize < s.length+1) { |
| if (me.bufferSize) _free(me.buffer); |
| me.bufferSize = s.length+1; |
| me.buffer = _malloc(me.bufferSize); |
| } |
| writeStringToMemory(s, me.buffer); |
| return me.buffer; |
| }, |
| |
| emscripten_random: function() { |
| return Math.random(); |
| }, |
| |
| emscripten_jcache_printf___deps: ['_formatString'], |
| emscripten_jcache_printf_: function(varargs) { |
| var MAX = 10240; |
| if (!_emscripten_jcache_printf_.buffer) { |
| _emscripten_jcache_printf_.buffer = _malloc(MAX); |
| } |
| var i = 0; |
| do { |
| var curr = {{{ makeGetValue('varargs', '0', 'i8') }}}; |
| varargs += {{{ STACK_ALIGN }}}; |
| {{{ makeSetValue('_emscripten_jcache_printf_.buffer', 'i', 'curr', 'i8') }}}; |
| i++; |
| assert(i*{{{ STACK_ALIGN }}} < MAX); |
| } while (curr != 0); |
| Module.print(intArrayToString(__formatString(_emscripten_jcache_printf_.buffer, varargs)).replace('\\n', '')); |
| Runtime.stackAlloc(-4*i); // free up the stack space we know is ok to free |
| }, |
| |
| //============================ |
| // i64 math |
| //============================ |
| |
| i64Add__asm: true, |
| i64Add__sig: 'iiiii', |
| i64Add: function(a, b, c, d) { |
| /* |
| x = a + b*2^32 |
| y = c + d*2^32 |
| result = l + h*2^32 |
| */ |
| a = a|0; b = b|0; c = c|0; d = d|0; |
| var l = 0, h = 0; |
| l = (a + c)>>>0; |
| h = (b + d + (((l>>>0) < (a>>>0))|0))>>>0; // Add carry from low word to high word on overflow. |
| {{{ makeStructuralReturn(['l|0', 'h'], true) }}}; |
| }, |
| llvm_uadd_with_overflow_i64__asm: true, |
| llvm_uadd_with_overflow_i64__sig: 'iiiii', |
| llvm_uadd_with_overflow_i64: function(a, b, c, d) { |
| a = a|0; b = b|0; c = c|0; d = d|0; |
| var l = 0, h = 0, overflow = 0; |
| l = (a + c)>>>0; |
| h = (b + d)>>>0; |
| overflow = ((h>>>0) < (b>>>0))|0; // Return whether addition overflowed even the high word. |
| if ((l>>>0) < (a>>>0)) { |
| h = (h + 1)>>>0; // Add carry from low word to high word on overflow. |
| overflow = overflow | (!h); // Check again for overflow. |
| } |
| {{{ makeStructuralReturn(['l|0', 'h', 'overflow'], true) }}}; |
| }, |
| |
| i64Subtract__asm: true, |
| i64Subtract__sig: 'iiiii', |
| i64Subtract: function(a, b, c, d) { |
| a = a|0; b = b|0; c = c|0; d = d|0; |
| var l = 0, h = 0; |
| l = (a - c)>>>0; |
| h = (b - d)>>>0; |
| h = (b - d - (((c>>>0) > (a>>>0))|0))>>>0; // Borrow one from high word to low word on underflow. |
| {{{ makeStructuralReturn(['l|0', 'h'], true) }}}; |
| }, |
| |
| bitshift64Shl__asm: true, |
| bitshift64Shl__sig: 'iiii', |
| bitshift64Shl: function(low, high, bits) { |
| low = low|0; high = high|0; bits = bits|0; |
| var ander = 0; |
| if ((bits|0) < 32) { |
| ander = ((1 << bits) - 1)|0; |
| tempRet0 = (high << bits) | ((low&(ander << (32 - bits))) >>> (32 - bits)); |
| return low << bits; |
| } |
| tempRet0 = low << (bits - 32); |
| return 0; |
| }, |
| bitshift64Ashr__asm: true, |
| bitshift64Ashr__sig: 'iiii', |
| bitshift64Ashr: function(low, high, bits) { |
| low = low|0; high = high|0; bits = bits|0; |
| var ander = 0; |
| if ((bits|0) < 32) { |
| ander = ((1 << bits) - 1)|0; |
| tempRet0 = high >> bits; |
| return (low >>> bits) | ((high&ander) << (32 - bits)); |
| } |
| tempRet0 = (high|0) < 0 ? -1 : 0; |
| return (high >> (bits - 32))|0; |
| }, |
| bitshift64Lshr__asm: true, |
| bitshift64Lshr__sig: 'iiii', |
| bitshift64Lshr: function(low, high, bits) { |
| low = low|0; high = high|0; bits = bits|0; |
| var ander = 0; |
| if ((bits|0) < 32) { |
| ander = ((1 << bits) - 1)|0; |
| tempRet0 = high >>> bits; |
| return (low >>> bits) | ((high&ander) << (32 - bits)); |
| } |
| tempRet0 = 0; |
| return (high >>> (bits - 32))|0; |
| }, |
| }; |
| |
| function autoAddDeps(object, name) { |
| name = [name]; |
| for (var item in object) { |
| if (item.substr(-6) != '__deps' && !object[item + '__deps']) { |
| object[item + '__deps'] = name; |
| } |
| } |
| } |
| |
| // Add aborting stubs for various libc stuff needed by libc++ |
| ['pthread_cond_signal', 'pthread_equal', 'wcstol', 'wcstoll', 'wcstoul', 'wcstoull', 'wcstof', 'wcstod', 'wcstold', 'swprintf', 'pthread_join', 'pthread_detach', 'strcoll_l', 'strxfrm_l', 'wcscoll_l', 'toupper_l', 'tolower_l', 'iswspace_l', 'iswprint_l', 'iswcntrl_l', 'iswupper_l', 'iswlower_l', 'iswalpha_l', 'iswdigit_l', 'iswpunct_l', 'iswxdigit_l', 'iswblank_l', 'wcsxfrm_l', 'towupper_l', 'towlower_l'].forEach(function(aborter) { |
| LibraryManager.library[aborter] = function() { throw 'TODO: ' + aborter }; |
| }); |
| |