| // Copyright Joyent, Inc. and other Node contributors. |
| // |
| // Permission is hereby granted, free of charge, to any person obtaining a |
| // copy of this software and associated documentation files (the |
| // "Software"), to deal in the Software without restriction, including |
| // without limitation the rights to use, copy, modify, merge, publish, |
| // distribute, sublicense, and/or sell copies of the Software, and to permit |
| // persons to whom the Software is furnished to do so, subject to the |
| // following conditions: |
| // |
| // The above copyright notice and this permission notice shall be included |
| // in all copies or substantial portions of the Software. |
| // |
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
| // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN |
| // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, |
| // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR |
| // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE |
| // USE OR OTHER DEALINGS IN THE SOFTWARE. |
| |
| #include "node.h" |
| #include "handle_wrap.h" |
| |
| #include <stdlib.h> |
| |
| using namespace v8; |
| |
| namespace node { |
| |
| static Persistent<String> onchange_sym; |
| |
| class FSEventWrap: public HandleWrap { |
| public: |
| static void Initialize(Handle<Object> target); |
| static Handle<Value> New(const Arguments& args); |
| static Handle<Value> Start(const Arguments& args); |
| static Handle<Value> Close(const Arguments& args); |
| |
| private: |
| FSEventWrap(Handle<Object> object); |
| virtual ~FSEventWrap(); |
| |
| static void OnEvent(uv_fs_event_t* handle, const char* filename, int events, |
| int status); |
| |
| uv_fs_event_t handle_; |
| bool initialized_; |
| }; |
| |
| |
| FSEventWrap::FSEventWrap(Handle<Object> object): HandleWrap(object, |
| (uv_handle_t*)&handle_) { |
| handle_.data = static_cast<void*>(this); |
| initialized_ = false; |
| } |
| |
| |
| FSEventWrap::~FSEventWrap() { |
| assert(initialized_ == false); |
| } |
| |
| |
| void FSEventWrap::Initialize(Handle<Object> target) { |
| HandleWrap::Initialize(target); |
| |
| HandleScope scope; |
| |
| Local<FunctionTemplate> t = FunctionTemplate::New(New); |
| t->InstanceTemplate()->SetInternalFieldCount(1); |
| t->SetClassName(String::NewSymbol("FSEvent")); |
| |
| NODE_SET_PROTOTYPE_METHOD(t, "start", Start); |
| NODE_SET_PROTOTYPE_METHOD(t, "close", Close); |
| |
| target->Set(String::NewSymbol("FSEvent"), |
| Persistent<FunctionTemplate>::New(t)->GetFunction()); |
| } |
| |
| |
| Handle<Value> FSEventWrap::New(const Arguments& args) { |
| HandleScope scope; |
| |
| assert(args.IsConstructCall()); |
| new FSEventWrap(args.This()); |
| |
| return scope.Close(args.This()); |
| } |
| |
| |
| Handle<Value> FSEventWrap::Start(const Arguments& args) { |
| HandleScope scope; |
| |
| UNWRAP(FSEventWrap) |
| |
| if (args.Length() < 1 || !args[0]->IsString()) { |
| return ThrowException(Exception::TypeError(String::New("Bad arguments"))); |
| } |
| |
| String::Utf8Value path(args[0]); |
| |
| int r = uv_fs_event_init(uv_default_loop(), &wrap->handle_, *path, OnEvent, 0); |
| if (r == 0) { |
| // Check for persistent argument |
| if (!args[1]->IsTrue()) { |
| uv_unref(reinterpret_cast<uv_handle_t*>(&wrap->handle_)); |
| } |
| wrap->initialized_ = true; |
| } else { |
| SetErrno(uv_last_error(uv_default_loop())); |
| } |
| |
| return scope.Close(Integer::New(r)); |
| } |
| |
| |
| void FSEventWrap::OnEvent(uv_fs_event_t* handle, const char* filename, |
| int events, int status) { |
| HandleScope scope; |
| Local<String> eventStr; |
| |
| FSEventWrap* wrap = static_cast<FSEventWrap*>(handle->data); |
| |
| assert(wrap->object_.IsEmpty() == false); |
| |
| // We're in a bind here. libuv can set both UV_RENAME and UV_CHANGE but |
| // the Node API only lets us pass a single event to JS land. |
| // |
| // The obvious solution is to run the callback twice, once for each event. |
| // However, since the second event is not allowed to fire if the handle is |
| // closed after the first event, and since there is no good way to detect |
| // closed handles, that option is out. |
| // |
| // For now, ignore the UV_CHANGE event if UV_RENAME is also set. Make the |
| // assumption that a rename implicitly means an attribute change. Not too |
| // unreasonable, right? Still, we should revisit this before v1.0. |
| if (status) { |
| SetErrno(uv_last_error(uv_default_loop())); |
| eventStr = String::Empty(); |
| } |
| else if (events & UV_RENAME) { |
| eventStr = String::New("rename"); |
| } |
| else if (events & UV_CHANGE) { |
| eventStr = String::New("change"); |
| } |
| else { |
| assert(0 && "bad fs events flag"); |
| abort(); |
| } |
| |
| Local<Value> argv[3] = { |
| Integer::New(status), |
| eventStr, |
| filename ? static_cast<Local<Value> >(String::New(filename)) |
| : Local<Value>::New(v8::Null()) |
| }; |
| |
| if (onchange_sym.IsEmpty()) { |
| onchange_sym = NODE_PSYMBOL("onchange"); |
| } |
| |
| MakeCallback(wrap->object_, onchange_sym, ARRAY_SIZE(argv), argv); |
| } |
| |
| |
| Handle<Value> FSEventWrap::Close(const Arguments& args) { |
| HandleScope scope; |
| |
| // Unwrap manually here. The UNWRAP() macro asserts that wrap != NULL. |
| // That usually indicates an error but not here: double closes are possible |
| // and legal, HandleWrap::Close() deals with them the same way. |
| assert(!args.Holder().IsEmpty()); |
| assert(args.Holder()->InternalFieldCount() > 0); |
| void* ptr = args.Holder()->GetPointerFromInternalField(0); |
| FSEventWrap* wrap = static_cast<FSEventWrap*>(ptr); |
| |
| if (wrap == NULL || wrap->initialized_ == false) { |
| return Undefined(); |
| } |
| wrap->initialized_ = false; |
| |
| return HandleWrap::Close(args); |
| } |
| |
| |
| } // namespace node |
| |
| NODE_MODULE(node_fs_event_wrap, node::FSEventWrap::Initialize) |