aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormax99x <max99x@gmail.com>2011-07-29 22:32:29 +0300
committermax99x <max99x@gmail.com>2011-07-29 22:32:29 +0300
commitb6b5430d73d74671bbae160715eed397b4cecd28 (patch)
tree69187dcd51a826e5fd8b50a3da0b6678b4b5e718
parent1ac86ad03da43b50092ca18b5dcdc6359028d7a5 (diff)
Implemented support for environment variables.
-rw-r--r--src/library.js137
-rw-r--r--tests/env/output.txt29
-rw-r--r--tests/env/src.c43
-rw-r--r--tests/runner.py5
4 files changed, 210 insertions, 4 deletions
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 <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+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):