// 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 using namespace v8; namespace node { static Persistent onchange_sym; class FSEventWrap: public HandleWrap { public: static void Initialize(Handle target); static Handle New(const Arguments& args); static Handle Start(const Arguments& args); static Handle Close(const Arguments& args); private: FSEventWrap(Handle 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): HandleWrap(object, (uv_handle_t*)&handle_) { handle_.data = reinterpret_cast(this); initialized_ = false; } FSEventWrap::~FSEventWrap() { assert(initialized_ == false); } void FSEventWrap::Initialize(Handle target) { HandleWrap::Initialize(target); HandleScope scope; Local 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::New(t)->GetFunction()); } Handle FSEventWrap::New(const Arguments& args) { HandleScope scope; assert(args.IsConstructCall()); new FSEventWrap(args.This()); return scope.Close(args.This()); } Handle 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(&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 eventStr; FSEventWrap* wrap = reinterpret_cast(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 argv[3] = { Integer::New(status), eventStr, filename ? (Local)String::New(filename) : Local::New(v8::Null()) }; if (onchange_sym.IsEmpty()) { onchange_sym = NODE_PSYMBOL("onchange"); } MakeCallback(wrap->object_, onchange_sym, ARRAY_SIZE(argv), argv); } Handle 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(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)