aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/library.js1333
-rw-r--r--src/preamble.js40
-rw-r--r--tests/runner.py50
-rw-r--r--tests/unistd/dup.out12
-rw-r--r--tests/unistd/isatty.js2
5 files changed, 930 insertions, 507 deletions
diff --git a/src/library.js b/src/library.js
index 3e346280..1ef8d0ec 100644
--- a/src/library.js
+++ b/src/library.js
@@ -18,8 +18,12 @@ LibraryManager.library = {
// File system base.
// ==========================================================================
- $FS__deps: ['$ERRNO_CODES', '__setErrNo'],
- $FS__postset: 'FS.ignorePermissions = false;',
+ stdin: 0,
+ stdout: 0,
+ stderr: 0,
+
+ $FS__deps: ['$ERRNO_CODES', '__setErrNo', 'stdin', 'stdout', 'stderr'],
+ $FS__postset: 'FS.init();',
$FS: {
// The main file system tree. All the contents are inside this.
root: {
@@ -240,7 +244,8 @@ LibraryManager.library = {
// 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.
+ // 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.');
@@ -262,11 +267,11 @@ LibraryManager.library = {
xhr.overrideMimeType('text/plain; charset=x-user-defined'); // Binary.
xhr.send(null);
if (xhr.status != 200 && xhr.status != 0) success = false;
- obj.contents = intArrayFromString(xhr.responseText || '');
+ obj.contents = intArrayFromString(xhr.responseText || '', true);
} else if (typeof read !== 'undefined') {
// Command-line.
try {
- obj.contents = intArrayFromString(read(obj.url));
+ obj.contents = intArrayFromString(read(obj.url), true);
} catch (e) {
success = false;
}
@@ -275,6 +280,103 @@ LibraryManager.library = {
}
if (!success) ___setErrNo(ERRNO_CODES.EIO);
return success;
+ },
+ // Initializes the filesystems with stdin/stdout/stderr devices, given
+ // optional handlers.
+ init: function(input, output, error) {
+ // Make sure we initialize only once.
+ if (FS.init.initialized) return;
+ else FS.init.initialized = true;
+
+ // Default handlers.
+ if (!input) input = function() {
+ if (!input.cache) {
+ var result;
+ if (window && typeof window.prompt == 'function') {
+ // Browser.
+ result = window.prompt('Input: ');
+ } else if (typeof readline == 'function') {
+ // Command line.
+ result = readline();
+ }
+ if (!result) return null;
+ input.cache = intArrayFromString(result + '\n', true);
+ }
+ return input.cache.shift();
+ };
+ if (!output) output = function(val) {
+ if (!output.printer) {
+ if (typeof print == 'function') {
+ // Either console or custom print function defined.
+ output.printer = print;
+ } else if (console && typeof console.log == 'function') {
+ // Browser-like environment with a console.
+ output.printer = console.log;
+ } else {
+ // Fallback to a harmless no-op.
+ output.printer = function() {};
+ }
+ }
+ if (!output.buffer) output.buffer = [];
+ if (val === null || val === '\n'.charCodeAt(0)) {
+ output.printer(output.buffer.join(''));
+ output.buffer = [];
+ } else {
+ output.buffer.push(String.fromCharCode(val));
+ }
+ };
+ if (!error) error = output;
+
+ // Create the temporary folder.
+ FS.createFolder('/', 'tmp', true, true);
+
+ // Create the I/O devices.
+ var devFolder = FS.createFolder('/', 'dev', true, false);
+ var stdin = FS.createDevice(devFolder, 'stdin', input);
+ var stdout = FS.createDevice(devFolder, 'stdout', null, output);
+ var stderr = FS.createDevice(devFolder, 'stderr', null, error);
+ FS.createDevice(devFolder, 'tty', input, error);
+
+ // Create default streams.
+ FS.streams[1] = {
+ path: '/dev/stdin',
+ object: stdin,
+ position: 0,
+ isRead: true,
+ isWrite: false,
+ isAppend: false,
+ error: false,
+ eof: false,
+ ungotten: []
+ };
+ FS.streams[2] = {
+ path: '/dev/stdout',
+ object: stdout,
+ position: 0,
+ isRead: false,
+ isWrite: true,
+ isAppend: false,
+ error: false,
+ eof: false,
+ ungotten: []
+ };
+ FS.streams[3] = {
+ path: '/dev/stderr',
+ object: stderr,
+ position: 0,
+ isRead: false,
+ isWrite: true,
+ isAppend: false,
+ error: false,
+ eof: false,
+ ungotten: []
+ };
+ _stdin = allocate([1], 'void*', ALLOC_STATIC);
+ _stdout = allocate([2], 'void*', ALLOC_STATIC);
+ _stderr = allocate([3], 'void*', ALLOC_STATIC);
+
+ // Once initialized, permissions start having effect.
+ FS.ignorePermissions = false;
}
},
@@ -307,14 +409,20 @@ LibraryManager.library = {
var contents = [];
for (var key in target.contents) contents.push(key);
FS.streams[id] = {
- isFolder: true,
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,
- // An index into contents. Special values: -2 is ".", -1 is "..".
- position: -2,
// Each stream has its own area for readdir() returns.
currentEntry: _malloc(___dirent_struct_layout.__size__)
};
@@ -324,7 +432,7 @@ LibraryManager.library = {
closedir: function(dirp) {
// int closedir(DIR *dirp);
// http://pubs.opengroup.org/onlinepubs/007908799/xsh/closedir.html
- if (!FS.streams[dirp] || !FS.streams[dirp].isFolder) {
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
return ___setErrNo(ERRNO_CODES.EBADF);
} else {
_free(FS.streams[dirp].currentEntry);
@@ -336,7 +444,7 @@ LibraryManager.library = {
telldir: function(dirp) {
// long int telldir(DIR *dirp);
// http://pubs.opengroup.org/onlinepubs/007908799/xsh/telldir.html
- if (!FS.streams[dirp] || !FS.streams[dirp].isFolder) {
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
return ___setErrNo(ERRNO_CODES.EBADF);
} else {
return FS.streams[dirp].position;
@@ -346,7 +454,7 @@ LibraryManager.library = {
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].isFolder) {
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
___setErrNo(ERRNO_CODES.EBADF);
} else {
var entries = 0;
@@ -368,7 +476,7 @@ LibraryManager.library = {
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].isFolder) {
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
return ___setErrNo(ERRNO_CODES.EBADF);
}
var stream = FS.streams[dirp];
@@ -381,10 +489,10 @@ LibraryManager.library = {
var name, inode;
if (loc === -2) {
name = '.';
- inode = 1; // Really undefined.
+ inode = 1; // Really undefined.
} else if (loc === -1) {
name = '..';
- inode = 1; // Really undefined.
+ inode = 1; // Really undefined.
} else {
name = stream.contents[loc];
inode = stream.object.contents[name].inodeNumber;
@@ -398,9 +506,9 @@ LibraryManager.library = {
{{{ makeSetValue('entry + offsets.d_name', 'i', 'name.charCodeAt(i)', 'i8') }}}
}
{{{ makeSetValue('entry + offsets.d_name', 'i', '0', 'i8') }}}
- var type = stream.isDevice ? 2 // DT_CHR, character device.
- : stream.isFolder ? 4 // DT_DIR, directory.
- : stream.link !== undefined ? 10 // DT_LNK, symbolic link.
+ var type = stream.object.isDevice ? 2 // DT_CHR, character device.
+ : stream.object.isFolder ? 4 // DT_DIR, directory.
+ : stream.object.link !== undefined ? 10 // DT_LNK, symbolic link.
: 8; // DT_REG, regular file.
{{{ makeSetValue('entry', 'offsets.d_type', 'type', 'i8') }}}
{{{ makeSetValue('result', '0', 'entry', 'i8*') }}}
@@ -411,7 +519,7 @@ LibraryManager.library = {
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].isFolder) {
+ if (!FS.streams[dirp] || !FS.streams[dirp].object.isFolder) {
___setErrNo(ERRNO_CODES.EBADF);
return 0;
} else {
@@ -721,7 +829,7 @@ LibraryManager.library = {
// ==========================================================================
__flock_struct_layout: Runtime.generateStructInfo(null, '%struct.flock'),
- open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
+ open__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', '__dirent_struct_layout'],
open: function(path, oflag, mode) {
// int open(const char *path, int oflag, ...);
// http://pubs.opengroup.org/onlinepubs/009695399/functions/open.html
@@ -738,6 +846,7 @@ LibraryManager.library = {
var isAppend = Boolean(oflag & 0x400); // O_APPEND.
// Verify path.
+ var origPath = path;
path = FS.analyzePath(Pointer_stringify(path));
if (!path.parentExists) {
___setErrNo(path.error);
@@ -779,18 +888,46 @@ LibraryManager.library = {
target = FS.createDataFile(path.parentObject, path.name, [],
mode & 0x100, mode & 0x80); // S_IRUSR, S_IWUSR.
}
-
// Actually create an open stream.
var id = FS.streams.length;
- FS.streams[id] = {
- isFolder: false,
- path: path.path,
- object: target,
- position: 0,
- isRead: isRead,
- isWrite: isWrite,
- isAppend: isAppend
- };
+ if (target.isFolder) {
+ var entryBuffer = 0;
+ if (___dirent_struct_layout) {
+ entryBuffer = _malloc(___dirent_struct_layout.__size__);
+ }
+ var contents = [];
+ for (var key in target.contents) contents.push(key);
+ FS.streams[id] = {
+ path: 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: entryBuffer
+ };
+ } else {
+ FS.streams[id] = {
+ path: path.path,
+ object: target,
+ position: 0,
+ isRead: isRead,
+ isWrite: isWrite,
+ isAppend: isAppend,
+ error: false,
+ eof: false,
+ ungotten: []
+ };
+ }
return id;
},
creat__deps: ['open'],
@@ -965,6 +1102,9 @@ LibraryManager.library = {
// int close(int fildes);
// http://pubs.opengroup.org/onlinepubs/000095399/functions/close.html
if (FS.streams[fildes]) {
+ if (FS.streams[fildes].currentEntry) {
+ _free(FS.streams[fildes].currentEntry);
+ }
delete FS.streams[fildes];
return 0;
} else {
@@ -1220,6 +1360,7 @@ LibraryManager.library = {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
} else {
+ stream.ungotten = [];
stream.position = position;
return position;
}
@@ -1255,12 +1396,19 @@ LibraryManager.library = {
___setErrNo(ERRNO_CODES.EINVAL);
return -1;
} else {
+ var bytesRead = 0;
+ while (stream.ungotten.length && nbyte > 0) {
+ {{{ makeSetValue('buf++', '0', 'stream.ungotten.pop()', 'i8') }}}
+ nbyte--;
+ bytesRead++;
+ }
var contents = stream.object.contents;
var size = Math.min(contents.length - offset, nbyte);
for (var i = 0; i < size; i++) {
{{{ makeSetValue('buf', 'i', 'contents[offset + i]', 'i8') }}}
+ bytesRead++;
}
- return i;
+ return bytesRead;
}
},
read__deps: ['$FS', '__setErrNo', '$ERRNO_CODES', 'pread'],
@@ -1278,8 +1426,15 @@ LibraryManager.library = {
___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();
@@ -1288,15 +1443,16 @@ LibraryManager.library = {
return -1;
}
if (result === null || result === undefined) break;
+ bytesRead++;
{{{ makeSetValue('buf', 'i', 'result', 'i8') }}}
}
- return i;
+ return bytesRead;
} else {
___setErrNo(ERRNO_CODES.ENXIO);
return -1;
}
} else {
- var bytesRead = _pread(fildes, buf, nbyte, stream.position);
+ bytesRead = _pread(fildes, buf, nbyte, stream.position);
if (bytesRead != -1) stream.position += bytesRead;
return bytesRead;
}
@@ -1444,6 +1600,7 @@ LibraryManager.library = {
for (var i = 0; i < nbyte; i++) {
contents[offset + i] = {{{ makeGetValue('buf', 'i', 'i8') }}};
}
+ stream.object.timestamp = new Date();
return i;
}
},
@@ -1472,6 +1629,7 @@ LibraryManager.library = {
return -1;
}
}
+ stream.object.timestamp = new Date();
return i;
} else {
___setErrNo(ERRNO_CODES.ENXIO);
@@ -1891,7 +2049,7 @@ LibraryManager.library = {
// We must control this entirely. So we don't even need to do
// unfreeable allocations - the HEAP is ours, from STATICTOP up.
// TODO: We could in theory slice off the top of the HEAP when
- // sbrk gets a negative increment in |bytes|...
+ // sbrk gets a negative increment in |bytes|...
var self = _sbrk;
if (!self.STATICTOP) {
STATICTOP = alignMemoryPage(STATICTOP);
@@ -1909,97 +2067,114 @@ LibraryManager.library = {
__01lseek64_: 'lseek',
// ==========================================================================
+ // stdio.h
+ // ==========================================================================
- _scanString: function() {
- // Supports %x, %4x, %d.%d, %s
- var str = Pointer_stringify(arguments[0]);
- var stri = 0;
- var fmt = Pointer_stringify(arguments[1]);
- var fmti = 0;
- var args = Array.prototype.slice.call(arguments, 2);
+ // TODO: Document.
+ _scanString: function(format, get, unget, args) {
+ // Supports %x, %4x, %d.%d, %s.
+ // TODO: Support all format specifiers.
+ format = Pointer_stringify(format);
+ var formatIndex = 0;
var argsi = 0;
var fields = 0;
- while (fmti < fmt.length) {
- if (fmt[fmti] === '%') {
- fmti++;
- var max_ = parseInt(fmt[fmti]);
- if (!isNaN(max_)) fmti++;
- var type = fmt[fmti];
- fmti++;
+ for (var formatIndex = 0; formatIndex < format.length; formatIndex++) {
+ var next = get();
+ if (next <= 0) return fields; // End of input.
+ if (format[formatIndex] === '%') {
+ formatIndex++;
+ var maxSpecifierStart = formatIndex;
+ while (format[formatIndex].charCodeAt(0) >= '0'.charCodeAt(0) &&
+ format[formatIndex].charCodeAt(0) <= '9'.charCodeAt(0)) {
+ formatIndex++;
+ }
+ var max_;
+ if (formatIndex != maxSpecifierStart) {
+ max_ = parseInt(format.slice(maxSpecifierStart, formatIndex), 10);
+ }
+ // TODO: Handle type size modifier.
+ var type = format[formatIndex];
+ formatIndex++;
var curr = 0;
- while ((curr < max_ || isNaN(max_)) && stri+curr < str.length) {
- if ((type === 'd' && parseInt(str[stri+curr]) >= 0) ||
- (type === 'x' && parseInt(str[stri+curr].replace(/[a-fA-F]/, 5)) >= 0) ||
+ var buffer = [];
+ while ((curr < max_ || isNaN(max_)) && next > 0) {
+ if ((type === 'd' && next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) ||
+ (type === 'x' && (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0) ||
+ next >= 'a'.charCodeAt(0) && next <= 'f'.charCodeAt(0) ||
+ next >= 'A'.charCodeAt(0) && next <= 'F'.charCodeAt(0))) ||
(type === 's')) {
- curr++;
+ buffer.push(String.fromCharCode(next));
+ next = get();
} else {
break;
}
}
- if (curr === 0) return 0; // failure
- var text = str.substr(stri, curr);
- stri += curr;
+ if (buffer.length === 0) return 0; // Failure.
+ var text = buffer.join('');
switch (type) {
- case 'd': {
- {{{ makeSetValue('args[argsi]', '0', 'parseInt(text)', 'i32') }}}
+ case 'd':
+ {{{ makeSetValue('args.shift()', '0', 'parseInt(text, 10)', 'i32') }}}
break;
- }
- case 'x': {
- {{{ makeSetValue('args[argsi]', '0', 'eval("0x" + text)', 'i32') }}}
+ case 'x':
+ {{{ makeSetValue('args.shift()', '0', 'parseInt(text, 16)', 'i32') }}}
break;
- }
- case 's': {
+ case 's':
var array = intArrayFromString(text);
+ var buf = args.shift();
for (var j = 0; j < array.length; j++) {
- {{{ makeSetValue('args[argsi]', 'j', 'array[j]', 'i8') }}}
+ {{{ makeSetValue('buf', 'j', 'array[j]', 'i8') }}}
}
break;
- }
}
- argsi++;
fields++;
- } else { // not '%'
- if (fmt[fmti] === str[stri]) {
- fmti++;
- stri++;
- } else {
- break;
+ } else {
+ // Not a specifier.
+ if (format[formatIndex].charCodeAt(0) !== next) {
+ unget(next);
+ return fields;
}
}
}
- return { fields: fields, bytes: stri };
- },
- sscanf__deps: ['_scanString'],
- sscanf: function() {
- return __scanString.apply(null, arguments).fields;
- },
-
- _formatString__deps: ['$STDIO', 'isdigit'],
- _formatString: function() {
- var cStyle = false;
- var textIndex = arguments[0];
- var argIndex = 1;
- if (textIndex < 0) {
- cStyle = true;
- textIndex = -textIndex;
- argIndex = arguments[1];
- } else {
- var _arguments = arguments;
- }
- function getNextArg(isFloat, size) {
- var ret;
- if (!cStyle) {
- ret = _arguments[argIndex];
- argIndex++;
- } else {
- if (isFloat) {
- ret = {{{ makeGetValue(0, 'argIndex', 'double') }}};
+ return fields;
+ },
+ // Performs prtinf-style formatting.
+ // isVarArgs: Whether the arguments are passed in varargs style, i.e. the
+ // third parameter is an address of the start of the argument list.
+ // format: A pointer to the format string.
+ // Returns the resulting string string as a character array.
+ _formatString: function(isVarArgs, format/*, ...*/) {
+ var textIndex = format;
+ var argIndex = 0;
+ var getNextArg;
+ if (isVarArgs) {
+ var varArgStart = arguments[2];
+ getNextArg = function(type) {
+ var ret;
+ if (type === 'double') {
+ ret = {{{ makeGetValue('varArgStart', 'argIndex', 'double') }}};
+ } else if (type === 'float') {
+ ret = {{{ makeGetValue('varArgStart', 'argIndex', 'float') }}};
+ } else if (type === 'i64') {
+ ret = {{{ makeGetValue('varArgStart', 'argIndex', 'i64') }}};
+ } else if (type === 'i32') {
+ ret = {{{ makeGetValue('varArgStart', 'argIndex', 'i32') }}};
+ } else if (type === 'i16') {
+ ret = {{{ makeGetValue('varArgStart', 'argIndex', 'i16') }}};
+ } else if (type === 'i8') {
+ ret = {{{ makeGetValue('varArgStart', 'argIndex', 'i8') }}};
+ } else if (type[type.length - 1] === '*') {
+ ret = {{{ makeGetValue('varArgStart', 'argIndex', 'void*') }}};
} else {
- ret = {{{ makeGetValue(0, 'argIndex', 'i32') }}};
+ throw new Error('Unknown formatString argument type: ' + type);
}
- argIndex += {{{ QUANTUM_SIZE === 1 ? 1 : 'Math.max(4, size || 0)' }}};
- }
- return +ret; // +: boolean=>int
+ argIndex += Runtime.getNativeFieldSize(type);
+ return Number(ret);
+ };
+ } else {
+ var args = arguments;
+ getNextArg = function() {
+ return Number(args[2 + argIndex++]);
+ };
}
var ret = [];
@@ -2043,11 +2218,11 @@ LibraryManager.library = {
// Handle width.
var width = 0;
if (next == '*'.charCodeAt(0)) {
- width = getNextArg();
+ width = getNextArg('i32');
textIndex++;
next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
} else {
- while (_isdigit(next)) {
+ while (next >= '0'.charCodeAt(0) && next <= '9'.charCodeAt(0)) {
width = width * 10 + (next - '0'.charCodeAt(0));
textIndex++;
next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
@@ -2062,12 +2237,13 @@ LibraryManager.library = {
textIndex++;
next = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
if (next == '*'.charCodeAt(0)) {
- precision = getNextArg();
+ precision = getNextArg('i32');
textIndex++;
} else {
while(1) {
var precisionChr = {{{ makeGetValue(0, 'textIndex+1', 'i8') }}};
- if (!_isdigit(precisionChr)) break;
+ if (precisionChr < '0'.charCodeAt(0) ||
+ precisionChr > '9'.charCodeAt(0)) break;
precision = precision * 10 + (precisionChr - '0'.charCodeAt(0));
textIndex++;
}
@@ -2118,9 +2294,9 @@ LibraryManager.library = {
if (['d', 'i', 'u', 'o', 'x', 'X', 'p'].indexOf(String.fromCharCode(next)) != -1) {
// Integer.
var signed = next == 'd'.charCodeAt(0) || next == 'i'.charCodeAt(0);
- var currArg = getNextArg(false, argSize);
- // Truncate to requested size.
argSize = argSize || 4;
+ var currArg = getNextArg('i' + (argSize * 8));
+ // Truncate to requested size.
if (argSize <= 4) {
var limit = Math.pow(256, argSize) - 1;
currArg = (signed ? reSign : unSign)(currArg & limit, argSize * 8);
@@ -2196,7 +2372,7 @@ LibraryManager.library = {
});
} else if (['f', 'F', 'e', 'E', 'g', 'G'].indexOf(String.fromCharCode(next)) != -1) {
// Float.
- var currArg = getNextArg(true, argSize);
+ var currArg = getNextArg(argSize === 4 ? 'float' : 'double');
var argText;
if (isNaN(currArg)) {
@@ -2281,7 +2457,7 @@ LibraryManager.library = {
});
} else if (next == 's'.charCodeAt(0)) {
// String.
- var arg = getNextArg();
+ var arg = getNextArg('i8*');
var copiedString;
if (arg) {
copiedString = String_copy(arg);
@@ -2304,14 +2480,15 @@ LibraryManager.library = {
}
} else if (next == 'c'.charCodeAt(0)) {
// Character.
- if (flagLeftAlign) ret.push(getNextArg());
+ if (flagLeftAlign) ret.push(getNextArg('i8'));
while (--width > 0) {
ret.push(' '.charCodeAt(0));
}
- if (!flagLeftAlign) ret.push(getNextArg());
+ if (!flagLeftAlign) ret.push(getNextArg('i8'));
} else if (next == 'n'.charCodeAt(0)) {
// Write the length written so far to the next parameter.
- {{{ makeSetValue('getNextArg()', '0', 'ret.length', 'i32') }}}
+ var ptr = getNextArg('i32*');
+ {{{ makeSetValue('ptr', '0', 'ret.length', 'i32') }}}
} else if (next == '%'.charCodeAt(0)) {
// Literal percent sign.
ret.push(curr);
@@ -2329,372 +2506,626 @@ LibraryManager.library = {
textIndex += 1;
}
}
- return allocate(ret.concat(0), 'i8', ALLOC_STACK); // NB: Stored on the stack
- //var len = ret.length+1;
- //var ret = allocate(ret.concat(0), 0, ALLOC_STACK); // NB: Stored on the stack
- //STACKTOP -= len; // XXX horrible hack. we rewind the stack, to 'undo' the alloc we just did.
- // // the point is that this works if nothing else allocs on the stack before
- // // the string is read, which should be true - it is very transient, see the *printf* functions below.
- //return ret;
- },
-
- printf__deps: ['_formatString'],
- printf: function() {
- __print__(Pointer_stringify(__formatString.apply(null, arguments)));
- },
-
- sprintf__deps: ['strcpy', '_formatString'],
- sprintf: function() {
- var str = arguments[0];
- var args = Array.prototype.slice.call(arguments, 1);
- _strcpy(str, __formatString.apply(null, args)); // not terribly efficient
- },
-
- snprintf__deps: ['strncpy', '_formatString'],
- snprintf: function() {
- var str = arguments[0];
- var num = arguments[1];
- var args = Array.prototype.slice.call(arguments, 2);
- _strncpy(str, __formatString.apply(null, args), num); // not terribly efficient
- },
-
- puts: function(p) {
- __print__(Pointer_stringify(p) + '\n');
- },
-
- putc: 'fputc',
- _IO_putc: 'fputc',
-
- putchar: function(p) {
- __print__(String.fromCharCode(p));
- },
- _ZNSo3putEc: 'putchar',
-
- _ZNSo5flushEv: function() {
- __print__('\n');
+ return ret;
},
-
- vsprintf__deps: ['strcpy', '_formatString'],
- vsprintf: function(dst, src, ptr) {
- _strcpy(dst, __formatString(-src, ptr));
+ // NOTE: Invalid stream pointers passed to these functions would cause a crash
+ // in native code. We, on the other hand, just ignore them, since it's
+ // easier.
+ clearerr__deps: ['$FS'],
+ clearerr: function(stream) {
+ // void clearerr(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/clearerr.html
+ if (stream in FS.streams) FS.streams[stream].error = false;
},
-
- vsnprintf__deps: ['_formatString'],
- vsnprintf: function(dst, num, src, ptr) {
- var text = __formatString(-src, ptr); // |-|src tells formatstring to use C-style params (typically they are from varargs)
- var i;
- for (i = 0; i < num; i++) {
- {{{ makeCopyValues('dst+i', 'text+i', 1, 'i8') }}}
- if ({{{ makeGetValue('dst', 'i', 'i8') }}} == 0) break;
+ fclose__deps: ['close', 'fsync'],
+ fclose: function(stream) {
+ // int fclose(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fclose.html
+ _fsync(stream);
+ return _close(stream);
+ },
+ fdopen__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
+ fdopen: function(fildes, mode) {
+ // FILE *fdopen(int fildes, const char *mode);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fdopen.html
+ if (fildes in FS.streams) {
+ var stream = FS.streams[fildes];
+ mode = Pointer_stringify(mode);
+ if ((mode.indexOf('w') != -1 && !stream.isWrite) ||
+ (mode.indexOf('r') != -1 && !stream.isRead) ||
+ (mode.indexOf('a') != -1 && !stream.isAppend) ||
+ (mode.indexOf('+') != -1 && (!stream.isRead || !stream.isWrite))) {
+ ___setErrNo(ERRNO_CODES.EINVAL);
+ return 0;
+ } else {
+ stream.error = false;
+ stream.eof = false;
+ return fildes;
+ }
+ } else {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return 0;
}
- return i; // Actually, should return how many *would* have been written, if the |num| had not stopped us.
},
-
- fileno: function(file) {
- return file;
+ feof__deps: ['$FS'],
+ feof: function(stream) {
+ // int feof(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/feof.html
+ return Number(stream in FS.streams && FS.streams[stream].eof);
},
-
- clearerr: function(stream) {
+ ferror__deps: ['$FS'],
+ ferror: function(stream) {
+ // int ferror(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/ferror.html
+ return Number(stream in FS.streams && FS.streams[stream].error);
},
-
- flockfile: function(file) {
+ fflush__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
+ fflush: function(stream) {
+ // int fflush(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fflush.html
+ var flush = function(filedes) {
+ // Right now we write all data directly, except for output devices.
+ if (filedes in FS.streams && FS.streams[filedes].object.output) {
+ FS.streams[filedes].object.output(null);
+ }
+ };
+ try {
+ if (stream === 0) {
+ for (var i in FS.streams) flush(i);
+ } else {
+ flush(stream);
+ }
+ return 0;
+ } catch (e) {
+ ___setErrNo(ERRNO_CODES.EIO);
+ return -1;
+ }
},
-
- funlockfile: function(file) {
+ fgetc__deps: ['$FS', 'read'],
+ fgetc: function(stream) {
+ // int fgetc(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetc.html
+ if (!(stream in FS.streams)) return -1;
+ if (!_fgetc.buffer) _fgetc.buffer = _malloc(1);
+ var streamObj = FS.streams[stream];
+ if (streamObj.eof || streamObj.error) return -1;
+ var ret = _read(stream, _fgetc.buffer, 1);
+ if (ret == 0) {
+ streamObj.eof = true;
+ return -1;
+ } else if (ret == -1) {
+ streamObj.error = true;
+ return -1;
+ } else {
+ return {{{ makeGetValue('_fgetc.buffer', '0', 'i8') }}};
+ }
},
-
- stdin: 0,
- stdout: 0,
- stderr: 0,
-
- $STDIO__postset: 'STDIO.init()',
- $STDIO__deps: ['stdin', 'stdout', 'stderr'],
- $STDIO: {
- streams: {},
- filenames: {},
- counter: 1,
- SEEK_SET: 0, /* Beginning of file. */
- SEEK_CUR: 1, /* Current position. */
- SEEK_END: 2, /* End of file. */
- init: function() {
- _stdin = allocate([0], 'void*', ALLOC_STATIC);
- {{{ makeSetValue('_stdin', '0', "STDIO.prepare('<<stdin>>', null, null, true)", 'i32') }}};
- if (Module.stdin) {
- // Make sure stdin returns a newline
- var orig = Module.stdin;
- Module.stdin = function stdinFixed(prompt) {
- var ret = orig(prompt);
- if (ret[ret.length-1] !== '\n') ret = ret + '\n';
- return ret;
- }
+ getc: 'fgetc',
+ getc_unlocked: 'fgetc',
+ getchar__deps: ['fgetc', 'stdin'],
+ getchar: function() {
+ // int getchar(void);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/getchar.html
+ return _fgetc({{{ makeGetValue('_stdin', '0', 'void*') }}});
+ },
+ fgetpos__deps: ['$FS', '__setErrNo', '$ERRNO_CODES'],
+ fgetpos: function(stream, pos) {
+ // int fgetpos(FILE *restrict stream, fpos_t *restrict pos);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgetpos.html
+ if (stream in FS.streams) {
+ stream = FS.streams[stream];
+ if (stream.object.isDevice) {
+ ___setErrNo(ERRNO_CODES.ESPIPE);
+ return -1;
} else {
- Module.stdin = function stdin(prompt) {
- return window.prompt(prompt) || '';
- };
+ {{{ makeSetValue('pos', '0', 'stream.position', 'i32') }}}
+ var state = (stream.eof ? 1 : 0) + (stream.error ? 2 : 0);
+ {{{ makeSetValue('pos', Runtime.getNativeFieldSize('i32'), 'state', 'i32') }}}
+ return 0;
}
-
- _stdout = allocate([0], 'void*', ALLOC_STATIC);
- {{{ makeSetValue('_stdout', '0', "STDIO.prepare('<<stdout>>', null, true)", 'i32') }}};
-
- _stderr = allocate([0], 'void*', ALLOC_STATIC);
- {{{ makeSetValue('_stderr', '0', "STDIO.prepare('<<stderr>>', null, true)", 'i32') }}};
- },
- cleanFilename: function(filename) {
- return filename.replace('./', '');
- },
- prepare: function(filename, data, print_, interactiveInput) {
- filename = STDIO.cleanFilename(filename);
- var stream = STDIO.counter++;
- STDIO.streams[stream] = {
- filename: filename,
- data: data ? data : [],
- position: 0,
- eof: 0,
- error: 0,
- interactiveInput: interactiveInput, // true for stdin - on the web, we allow interactive input
- print: print_ // true for stdout and stderr - we print when receiving data for them
- };
- STDIO.filenames[filename] = stream;
- return stream;
- },
- open: function(filename) {
- filename = STDIO.cleanFilename(filename);
- var stream = STDIO.filenames[filename];
- if (!stream) {
- // Not already cached; try to load it right now
- try {
- return STDIO.prepare(filename, readBinary(filename));
- } catch(e) {
- return 0;
- }
+ } else {
+ ___setErrNo(ERRNO_CODES.EBADF);
+ return -1;
+ }
+ },
+ fgets__deps: ['fgetc'],
+ fgets: function(s, n, stream) {
+ // char *fgets(char *restrict s, int n, FILE *restrict stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fgets.html
+ if (!(stream in FS.streams)) return 0;
+ var streamObj = FS.streams[stream];
+ if (streamObj.error || streamObj.eof) return 0;
+ for (var i = 0; i < n - 1; i++) {
+ var byte = _fgetc(stream);
+ if (byte == -1) {
+ if (streamObj.error) return 0;
+ else if (streamObj.eof) break;
+ } else if (byte == '\n'.charCodeAt(0)) {
+ break;
}
- var info = STDIO.streams[stream];
- info.position = info.error = info.eof = 0;
- return stream;
- },
- read: function(stream, ptr, size) {
- var info = STDIO.streams[stream];
- if (!info) return -1;
- if (info.interactiveInput) {
- for (var i = 0; i < size; i++) {
- if (info.data.length === 0) {
- info.data = intArrayFromString(Module.stdin(PRINTBUFFER.length > 0 ? PRINTBUFFER : '?')).map(function(x) { return x === 0 ? 10 : x }); // change 0 to newline
- PRINTBUFFER = '';
- if (info.data.length === 0) return i;
- }
- {{{ makeSetValue('ptr', '0', 'info.data.shift()', 'i8') }}}
- ptr++;
- }
- return size;
+ {{{ makeSetValue('s', 'i', 'byte', 'i8') }}}
+ }
+ {{{ makeSetValue('s', 'i', '0', 'i8') }}}
+ return s;
+ },
+ gets__deps: ['fgets'],
+ gets: function(s) {
+ // char *gets(char *s);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/gets.html
+ return _fgets(s, 1e6, {{{ makeGetValue('_stdin', '0', 'void*') }}});
+ },
+ fileno: function(stream) {
+ // int fileno(FILE *stream);
+ // http://pubs.opengroup.org/onlinepubs/000095399/functions/fileno.html
+ // We use file descriptor numbers and FILE* streams interchangeably.
+ return stream;