diff --git a/doc/api.txt b/doc/api.txt index 18a5eb350e..2801452636 100644 --- a/doc/api.txt +++ b/doc/api.txt @@ -124,6 +124,13 @@ Send a signal to a process. +pid+ is the process id and +signal+ is the signal to send; for example, "SIGINT" or "SIGUSR1". See kill(2) for more information. ++process.watchFile(filename, listener)+:: +Watch for changes on +filename+. The callback +listener+ will be called each +time the file changes. + ++process.unwatchFile(filename)+:: +Stop watching for changes on +filename+. + +process.compile(source, scriptOrigin)+:: Just like +eval()+ except that you can specify a +scriptOrigin+ for better error reporting. diff --git a/src/node.cc b/src/node.cc index 04f55f5d6b..5185fc50b2 100644 --- a/src/node.cc +++ b/src/node.cc @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -695,6 +696,7 @@ static Local Load(int argc, char *argv[]) { Stdio::Initialize(process); // stdio.cc Timer::Initialize(process); // timer.cc SignalHandler::Initialize(process); // signal_handler.cc + Stat::Initialize(process); // stat.cc ChildProcess::Initialize(process); // child_process.cc DefineConstants(process); // constants.cc // Create node.dns diff --git a/src/node.js b/src/node.js index 6876a228e8..a471160122 100644 --- a/src/node.js +++ b/src/node.js @@ -332,6 +332,32 @@ process.addListener("newListener", function (event) { }); +// Stat Change Watchers + +var statWatchers = {}; + +process.watchFile = function (filename, listener) { + var stat; + if (filename in statWatchers) { + stat = statWatchers[filename]; + } else { + statWatchers[filename] = new process.Stat(); + stat = statWatchers[filename]; + stat.start(filename, true); + } + stat.addListener("change", listener); + return stat; +}; + +process.unwatchFile = function (filename) { + if (filename in statWatchers) { + stat = statWatchers[filename]; + stat.stop(); + statWatchers[filename] = undefined; + } +}; + + // Timers diff --git a/src/node_stat.cc b/src/node_stat.cc new file mode 100644 index 0000000000..282252fcff --- /dev/null +++ b/src/node_stat.cc @@ -0,0 +1,95 @@ +// Copyright 2009 Ryan Dahl +#include + +#include +#include +#include + +namespace node { + +using namespace v8; + +Persistent Stat::constructor_template; + +void Stat::Initialize(Handle target) { + HandleScope scope; + + Local t = FunctionTemplate::New(Stat::New); + constructor_template = Persistent::New(t); + constructor_template->Inherit(EventEmitter::constructor_template); + constructor_template->InstanceTemplate()->SetInternalFieldCount(1); + constructor_template->SetClassName(String::NewSymbol("Stat")); + + NODE_SET_PROTOTYPE_METHOD(constructor_template, "start", Stat::Start); + NODE_SET_PROTOTYPE_METHOD(constructor_template, "stop", Stat::Stop); + + target->Set(String::NewSymbol("Stat"), constructor_template->GetFunction()); +} + + +void Stat::Callback(EV_P_ ev_stat *watcher, int revents) { + assert(revents == EV_STAT); + Stat *handler = static_cast(watcher->data); + assert(watcher == &handler->watcher_); + HandleScope scope; + handler->Emit("change", 0, NULL); +} + + +Handle Stat::New(const Arguments& args) { + HandleScope scope; + Stat *s = new Stat(); + s->Wrap(args.Holder()); + return args.This(); +} + + +Handle Stat::Start(const Arguments& args) { + HandleScope scope; + + if (args.Length() < 1 || !args[0]->IsString()) { + return ThrowException(Exception::TypeError(String::New("Bad arguments"))); + } + + Stat *handler = ObjectWrap::Unwrap(args.Holder()); + String::Utf8Value path(args[0]->ToString()); + + assert(handler->path_ == NULL); + handler->path_ = strdup(*path); + + ev_stat_set(&handler->watcher_, handler->path_, 0.); + ev_stat_start(EV_DEFAULT_UC_ &handler->watcher_); + + handler->persistent_ = args[1]->IsTrue(); + + if (!handler->persistent_) { + ev_unref(EV_DEFAULT_UC); + } + + handler->Attach(); + + return Undefined(); +} + + +Handle Stat::Stop(const Arguments& args) { + HandleScope scope; + Stat *handler = ObjectWrap::Unwrap(args.Holder()); + handler->Emit("stop", 0, NULL); + handler->Stop(); + return Undefined(); +} + + +void Stat::Stop () { + if (watcher_.active) { + if (!persistent_) ev_ref(EV_DEFAULT_UC); + ev_stat_stop(EV_DEFAULT_UC_ &watcher_); + free(path_); + path_ = NULL; + Detach(); + } +} + + +} // namespace node diff --git a/src/node_stat.h b/src/node_stat.h new file mode 100644 index 0000000000..bb6a20de83 --- /dev/null +++ b/src/node_stat.h @@ -0,0 +1,46 @@ +// Copyright 2009 Ryan Dahl +#ifndef NODE_STAT_H_ +#define NODE_STAT_H_ + +#include +#include +#include + +namespace node { + +class Stat : EventEmitter { + public: + static void Initialize(v8::Handle target); + + protected: + static v8::Persistent constructor_template; + + Stat() : EventEmitter() { + persistent_ = false; + path_ = NULL; + ev_init(&watcher_, Stat::Callback); + watcher_.data = this; + } + + ~Stat() { + Stop(); + assert(path_ == NULL); + } + + static v8::Handle New(const v8::Arguments& args); + static v8::Handle Start(const v8::Arguments& args); + static v8::Handle Stop(const v8::Arguments& args); + + private: + static void Callback(EV_P_ ev_stat *watcher, int revents); + + void Stop(); + + ev_stat watcher_; + bool persistent_; + char *path_; +}; + +} // namespace node +#endif // NODE_STAT_H_ + diff --git a/test/mjsunit/test-stat-handler.js b/test/mjsunit/test-stat-handler.js new file mode 100644 index 0000000000..32b1fe8cfc --- /dev/null +++ b/test/mjsunit/test-stat-handler.js @@ -0,0 +1,25 @@ +process.mixin(require("./common")); + +var posix = require("posix"); +var path = require("path"); + +var f = path.join(fixturesDir, "x.txt"); +var f2 = path.join(fixturesDir, "x2.txt"); + +var changes = 0; +process.watchFile(f, function () { + puts(f + " change"); + changes++; +}); + + +setTimeout(function () { + posix.rename(f, f2).wait(); + posix.rename(f2, f).wait(); + process.unwatchFile(f); +}, 10); + + +process.addListener("exit", function () { + assertTrue(changes > 0); +}); diff --git a/wscript b/wscript index a78e69eef4..3bc403bd53 100644 --- a/wscript +++ b/wscript @@ -321,6 +321,7 @@ def build(bld): src/node_http.cc src/node_net.cc src/node_signal_handler.cc + src/node_stat.cc src/node_stdio.cc src/node_timer.cc """