diff --git a/src/node.cc b/src/node.cc index c66172cb46..d0931a7777 100644 --- a/src/node.cc +++ b/src/node.cc @@ -1530,6 +1530,69 @@ static void ProcessTitleSetter(Local property, } +static Handle EnvGetter(Local property, + const AccessorInfo& info) { + String::Utf8Value key(property); + const char* val = getenv(*key); + if (val) { + HandleScope scope; + return scope.Close(String::New(val)); + } + return Undefined(); +} + + +static Handle EnvSetter(Local property, + Local value, + const AccessorInfo& info) { + String::Utf8Value key(property); + String::Utf8Value val(value); + setenv(*key, *val, 1); + return value; +} + + +static Handle EnvQuery(Local property, + const AccessorInfo& info) { + String::Utf8Value key(property); + if (getenv(*key)) { + HandleScope scope; + return scope.Close(Integer::New(None)); + } + return Handle(); +} + + +static Handle EnvDeleter(Local property, + const AccessorInfo& info) { + String::Utf8Value key(property); + if (getenv(*key)) { + unsetenv(*key); // prototyped as `void unsetenv(const char*)` on some platforms + return True(); + } + return False(); +} + + +static Handle EnvEnumerator(const AccessorInfo& info) { + HandleScope scope; + + int size = 0; + while (environ[size]) size++; + + Local env = Array::New(size); + + for (int i = 0; i < size; ++i) { + const char* var = environ[i]; + const char* s = strchr(var, '='); + const int length = s ? s - var : strlen(var); + env->Set(i, String::New(var, length)); + } + + return scope.Close(env); +} + + static void Load(int argc, char *argv[]) { HandleScope scope; @@ -1577,20 +1640,11 @@ static void Load(int argc, char *argv[]) { process->Set(String::NewSymbol("argv"), arguments); // create process.env - Local env = Object::New(); - for (i = 0; environ[i]; i++) { - // skip entries without a '=' character - for (j = 0; environ[i][j] && environ[i][j] != '='; j++) { ; } - // create the v8 objects - Local field = String::New(environ[i], j); - Local value = Local(); - if (environ[i][j] == '=') { - value = String::New(environ[i]+j+1); - } - // assign them - env->Set(field, value); - } + Local envTemplate = ObjectTemplate::New(); + envTemplate->SetNamedPropertyHandler(EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, Undefined()); + // assign process.ENV + Local env = envTemplate->NewInstance(); process->Set(String::NewSymbol("ENV"), env); process->Set(String::NewSymbol("env"), env); diff --git a/test/simple/test-process-env.js b/test/simple/test-process-env.js new file mode 100644 index 0000000000..b193e46248 --- /dev/null +++ b/test/simple/test-process-env.js @@ -0,0 +1,36 @@ +// first things first, set the timezone; see tzset(3) +process.env.TZ = 'Europe/Amsterdam'; + +assert = require('assert'); +spawn = require('child_process').spawn; + +// time difference between Greenwich and Amsterdam is +2 hours in the summer +date = new Date('Fri, 10 Sep 1982 03:15:00 GMT'); +assert.equal(3, date.getUTCHours()); +assert.equal(5, date.getHours()); + +// changes in environment should be visible to child processes +if (process.argv[2] == 'you-are-the-child') { + // failed assertion results in process exiting with status code 1 + assert.equal(false, 'NODE_PROCESS_ENV_DELETED' in process.env); + assert.equal(42, process.env.NODE_PROCESS_ENV); + process.exit(0); +} else { + process.env.NODE_PROCESS_ENV = 42; + assert.equal(42, process.env.NODE_PROCESS_ENV); + + process.env.NODE_PROCESS_ENV_DELETED = 42; + assert.equal(true, 'NODE_PROCESS_ENV_DELETED' in process.env); + + delete process.env.NODE_PROCESS_ENV_DELETED; + assert.equal(false, 'NODE_PROCESS_ENV_DELETED' in process.env); + + child = spawn(process.argv[0], [process.argv[1], 'you-are-the-child']); + child.stdout.on('data', function(data) { console.log(data.toString()); }); + child.stderr.on('data', function(data) { console.log(data.toString()); }); + child.on('exit', function(statusCode) { + if (statusCode != 0) { + process.exit(statusCode); // failed assertion in child process + } + }); +}