From b6b5430d73d74671bbae160715eed397b4cecd28 Mon Sep 17 00:00:00 2001 From: max99x Date: Fri, 29 Jul 2011 22:32:29 +0300 Subject: Implemented support for environment variables. --- src/library.js | 137 +++++++++++++++++++++++++++++++++++++++++++++++++-- tests/env/output.txt | 29 +++++++++++ tests/env/src.c | 43 ++++++++++++++++ tests/runner.py | 5 ++ 4 files changed, 210 insertions(+), 4 deletions(-) create mode 100644 tests/env/output.txt create mode 100644 tests/env/src.c diff --git a/src/library.js b/src/library.js index 8d649131..232a5581 100644 --- a/src/library.js +++ b/src/library.js @@ -3184,10 +3184,6 @@ LibraryManager.library = { return ret; }, - getenv: function(name_) { - return 0; // TODO - }, - strtod__deps: ['isspace', 'isdigit'], strtod: function(str, endptr) { // Skip space. @@ -3269,6 +3265,139 @@ LibraryManager.library = { _free(temp); }, + // NOTE: The global environment array is rebuilt from scratch after every + // change, which is inefficient, but should not be a bottleneck unless + // no malloc is used. + __environ: null, + __buildEnvironment__deps: ['__environ'], + __buildEnvironment: function(env) { + if (___environ === null) ___environ = allocate([0], "i8**", ALLOC_STATIC); + var ptrSize = {{{ Runtime.getNativeFieldSize('i8*') }}}; + var envPtr = {{{ makeGetValue('___environ', '0', 'i8**') }}}; + // Clear old. + if (envPtr !== 0) { + var cur = envPtr; + while ({{{ makeGetValue('cur', '0', 'i8*') }}} !== 0) { + _free({{{ makeGetValue('cur', '0', 'i8*') }}}); + cur += ptrSize; + } + _free(envPtr); + } + // Collect key=value lines. + var strings = []; + for (var key in env) { + if (typeof env[key] === 'string') { + strings.push(key + '=' + env[key]); + } + } + // Make new. + envPtr = _malloc(ptrSize * (strings.length + 1)); + {{{ makeSetValue('___environ', '0', 'envPtr', 'i8**') }}} + for (var i = 0; i < strings.length; i++) { + var line = strings[i]; + var ptr = _malloc(line.length + 1); + for (var j = 0; j < line.length; j++) { + {{{ makeSetValue('ptr', 'j', 'line.charCodeAt(j)', 'i8') }}} + } + {{{ makeSetValue('ptr', 'j', '0', 'i8') }}} + {{{ makeSetValue('envPtr', 'i * ptrSize', 'ptr', 'i8*') }}} + } + {{{ makeSetValue('envPtr', 'strings.length * ptrSize', '0', 'i8*') }}} + }, + $ENV__deps: ['__buildEnvironment'], + $ENV__postset: '___buildEnvironment(ENV);', + $ENV: { + 'USER': 'root', + 'PATH': '/', + 'PWD': '/', + 'HOME': '/', + 'LANG': 'en_US.UTF-8', + '_': './this.program' + }, + 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; + }, + // ========================================================================== // string.h // ========================================================================== diff --git a/tests/env/output.txt b/tests/env/output.txt new file mode 100644 index 00000000..fa342d48 --- /dev/null +++ b/tests/env/output.txt @@ -0,0 +1,29 @@ +List: +USER=root +PATH=/ +PWD=/ +HOME=/ +LANG=en_US.UTF-8 +_=./this.program + +getenv(PATH): / +getenv(NONEXISTENT): (null) +setenv/0(PATH) ret: 0 +getenv(PATH) after setenv/0: / +setenv/1(PATH) ret: 0 +getenv(PATH) after setenv/1: test2 +setenv(SET_VALUE) ret: 0 +setenv(SET_VALUE2) ret: 0 +getenv(SET_VALUE): test3 +getenv(SET_VALUE2): test4 +putenv(PUT_VALUE) ret: 0 +getenv(PUT_VALUE): test5 +getenv(PUT_VALUE) after alteration: test5 +unsetenv(PUT_VALUE) ret: 0 +getenv(PUT_VALUE) after unsetenv: (null) +setenv(0) ret: -1 +setenv('') ret: -1 +setenv(X=Y) ret: -1 +unsetenv(0) ret: -1 +unsetenv('') ret: -1 +unsetenv(X=Y) ret: -1 diff --git a/tests/env/src.c b/tests/env/src.c new file mode 100644 index 00000000..46419495 --- /dev/null +++ b/tests/env/src.c @@ -0,0 +1,43 @@ +#include +#include +#include + +int main() { + printf("List:\n"); + for (char** str = __environ; *str; str++) { + printf("%s\n", *str); + } + printf("\n"); + + printf("getenv(PATH): %s\n", getenv("PATH")); + printf("getenv(NONEXISTENT): %s\n", getenv("NONEXISTENT")); + + printf("setenv/0(PATH) ret: %d\n", setenv("PATH", "test", 0)); + printf("getenv(PATH) after setenv/0: %s\n", getenv("PATH")); + printf("setenv/1(PATH) ret: %d\n", setenv("PATH", "test2", 1)); + printf("getenv(PATH) after setenv/1: %s\n", getenv("PATH")); + + printf("setenv(SET_VALUE) ret: %d\n", setenv("SET_VALUE", "test3", 0)); + printf("setenv(SET_VALUE2) ret: %d\n", setenv("SET_VALUE2", "test4", 1)); + printf("getenv(SET_VALUE): %s\n", getenv("SET_VALUE")); + printf("getenv(SET_VALUE2): %s\n", getenv("SET_VALUE2")); + + char buffer[] = "PUT_VALUE=test5"; + printf("putenv(PUT_VALUE) ret: %d\n", putenv(buffer)); + printf("getenv(PUT_VALUE): %s\n", getenv("PUT_VALUE")); + // NOTE: This should change the value. In our implementation, it doesn't. + buffer[10] = 'Q'; + printf("getenv(PUT_VALUE) after alteration: %s\n", getenv("PUT_VALUE")); + + printf("unsetenv(PUT_VALUE) ret: %d\n", unsetenv("PUT_VALUE")); + printf("getenv(PUT_VALUE) after unsetenv: %s\n", getenv("PUT_VALUE")); + + printf("setenv(0) ret: %d\n", setenv(0, "foo", 1)); + printf("setenv('') ret: %d\n", setenv("", "foo", 1)); + printf("setenv(X=Y) ret: %d\n", setenv("X=Y", "foo", 1)); + printf("unsetenv(0) ret: %d\n", unsetenv(0)); + printf("unsetenv('') ret: %d\n", unsetenv("")); + printf("unsetenv(X=Y) ret: %d\n", unsetenv("X=Y")); + + return 0; +} diff --git a/tests/runner.py b/tests/runner.py index 06ad2d80..39ce4c84 100644 --- a/tests/runner.py +++ b/tests/runner.py @@ -2605,6 +2605,11 @@ if 'benchmark' not in sys.argv: ''' self.do_test(src, re.sub('(^|\n)\s+', '\\1', expected)) + def test_env(self): + src = open(path_from_root('tests', 'env', 'src.c'), 'r').read() + expected = open(path_from_root('tests', 'env', 'output.txt'), 'r').read() + self.do_test(src, expected) + ### 'Big' tests def test_fannkuch(self): -- cgit v1.2.3-18-g5258