Browse Source

fs watcher binding

v0.7.4-release
Igor Zinkovsky 13 years ago
committed by Ryan Dahl
parent
commit
8fe5712477
  1. 71
      lib/fs.js
  2. 1
      node.gyp
  3. 132
      src/fs_event_wrap.cc
  4. 1
      src/node_extensions.h
  5. 143
      test/simple/test-fs-watch.js
  6. 1
      wscript

71
lib/fs.js

@ -589,6 +589,73 @@ fs.writeFileSync = function(path, data, encoding) {
fs.closeSync(fd); fs.closeSync(fd);
}; };
function errnoException(errorno, syscall) {
// TODO make this more compatible with ErrnoException from src/node.cc
// Once all of Node is using this function the ErrnoException from
// src/node.cc should be removed.
var e = new Error(syscall + ' ' + errorno);
e.errno = e.code = errorno;
e.syscall = syscall;
return e;
}
function FSWather() {
var self = this;
var FSEvent = process.binding('fs_event_wrap').FSEvent;
this._handle = new FSEvent();
this._handle.onchange = function(status, event, filename) {
if (status) {
self.emit('error', errnoException(errno, 'watch'));
} else {
self.emit('change', event, filename);
}
};
}
util.inherits(FSWather, EventEmitter);
FSWather.prototype.start = function(filename, persistent) {
var r = this._handle.start(filename, persistent);
if (r) {
this._handle.close();
throw errnoException(errno, "watch")
}
};
FSWather.prototype.close = function() {
this._handle.close();
};
fs.watch = function(filename) {
var watcher;
var options;
var listener;
if ('object' == typeof arguments[1]) {
options = arguments[1];
listener = arguments[2];
} else {
options = {};
listener = arguments[1];
}
if (!listener) {
throw new Error('watch requires a listener function');
}
if (options.persistent === undefined) options.persistent = true;
watcher = new FSWather();
watcher.start(filename, options.persistent);
watcher.addListener('change', listener);
return watcher;
};
// Stat Change Watchers // Stat Change Watchers
function StatWatcher() { function StatWatcher() {
@ -623,6 +690,10 @@ function inStatWatchers(filename) {
fs.watchFile = function(filename) { fs.watchFile = function(filename) {
if (isWindows) {
throw new Error('use fs.watch api instead');
}
var stat; var stat;
var options; var options;
var listener; var listener;

1
node.gyp

@ -73,6 +73,7 @@
], ],
'sources': [ 'sources': [
'src/fs_event_wrap.cc',
'src/cares_wrap.cc', 'src/cares_wrap.cc',
'src/handle_wrap.cc', 'src/handle_wrap.cc',
'src/node.cc', 'src/node.cc',

132
src/fs_event_wrap.cc

@ -0,0 +1,132 @@
#include <node.h>
#include <handle_wrap.h>
#include <stdlib.h>
using namespace v8;
namespace node {
#define UNWRAP \
assert(!args.Holder().IsEmpty()); \
assert(args.Holder()->InternalFieldCount() > 0); \
FSEventWrap* wrap = \
static_cast<FSEventWrap*>(args.Holder()->GetPointerFromInternalField(0)); \
if (!wrap) { \
SetErrno(UV_EBADF); \
return scope.Close(Integer::New(-1)); \
}
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);
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_;
};
FSEventWrap::FSEventWrap(Handle<Object> object): HandleWrap(object,
(uv_handle_t*)&handle_) {
handle_.data = reinterpret_cast<void*>(this);
}
FSEventWrap::~FSEventWrap() {
}
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
if (args.Length() < 1 || !args[0]->IsString()) {
return ThrowException(Exception::TypeError(String::New("Bad arguments")));
}
String::Utf8Value path(args[0]->ToString());
int r = uv_fs_event_init(uv_default_loop(), &wrap->handle_, *path, OnEvent);
if (r == 0) {
// Check for persistent argument
if (!args[1]->IsTrue()) {
uv_unref(uv_default_loop());
}
} else {
SetErrno(uv_last_error(uv_default_loop()).code);
}
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 = reinterpret_cast<FSEventWrap*>(handle->data);
assert(wrap->object_.IsEmpty() == false);
if (status) {
SetErrno(uv_last_error(uv_default_loop()).code);
eventStr = String::Empty();
} else {
switch (events) {
case UV_RENAME:
eventStr = String::New("rename");
break;
case UV_CHANGE:
eventStr = String::New("change");
break;
}
}
Local<Value> argv[3] = {
Integer::New(status),
eventStr,
filename ? (Local<Value>)String::New(filename) : Local<Value>::New(v8::Null())
};
MakeCallback(wrap->object_, "onchange", 3, argv);
}
} // namespace node
NODE_MODULE(node_fs_event_wrap, node::FSEventWrap::Initialize);

1
src/node_extensions.h

@ -51,6 +51,7 @@ NODE_EXT_LIST_ITEM(node_cares_wrap)
NODE_EXT_LIST_ITEM(node_stdio_wrap) NODE_EXT_LIST_ITEM(node_stdio_wrap)
NODE_EXT_LIST_ITEM(node_tty_wrap) NODE_EXT_LIST_ITEM(node_tty_wrap)
NODE_EXT_LIST_ITEM(node_process_wrap) NODE_EXT_LIST_ITEM(node_process_wrap)
NODE_EXT_LIST_ITEM(node_fs_event_wrap)
NODE_EXT_LIST_END NODE_EXT_LIST_END

143
test/simple/test-fs-watch.js

@ -0,0 +1,143 @@
// 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.
var common = require('../common');
var assert = require('assert');
var path = require('path');
var fs = require('fs');
var expectFilePath = process.platform == 'win32' || process.platform == 'linux';
var watchSeenOne = 0;
var watchSeenTwo = 0;
var watchSeenThree = 0;
var startDir = process.cwd();
var testDir = common.fixturesDir;
var filenameOne = 'watch.txt';
var filepathOne = path.join(testDir, filenameOne);
var filenameTwo = 'hasOwnProperty';
var filepathTwo = filenameTwo;
var filepathTwoAbs = path.join(testDir, filenameTwo);
var filenameThree = 'newfile.txt';
var testsubdir = path.join(testDir, 'testsubdir');
var filepathThree = path.join(testsubdir, filenameThree);
process.addListener('exit', function() {
fs.unlinkSync(filepathOne);
fs.unlinkSync(filepathTwoAbs);
fs.unlinkSync(filepathThree);
fs.rmdirSync(testsubdir);
assert.ok(watchSeenOne > 0);
assert.ok(watchSeenTwo > 0);
assert.ok(watchSeenThree > 0);
});
fs.writeFileSync(filepathOne, "hello");
assert.throws(
function() {
fs.watch(filepathOne);
},
function(e) {
return e.message === 'watch requires a listener function';
}
);
assert.doesNotThrow(
function() {
var watcher = fs.watch(filepathOne, function(event, filename) {
assert.equal('change', event);
if (expectFilePath) {
assert.equal('watch.txt', filename);
} else {
assert.equal(null, filename);
}
watcher.close();
++watchSeenOne;
});
}
);
setTimeout(function() {
fs.writeFileSync(filepathOne, "world");
}, 1000);
process.chdir(testDir);
fs.writeFileSync(filepathTwoAbs, "howdy");
assert.throws(
function() {
fs.watch(filepathTwo);
},
function(e) {
return e.message === 'watch requires a listener function';
}
);
assert.doesNotThrow(
function() {
var watcher = fs.watch(filepathTwo, function(event, filename) {
assert.equal('change', event);
if (expectFilePath) {
assert.equal('hasOwnProperty', filename);
} else {
assert.equal(null, filename);
}
watcher.close();
++watchSeenTwo;
});
}
);
setTimeout(function() {
fs.writeFileSync(filepathTwoAbs, "pardner");
}, 1000);
try { fs.unlinkSync(filepathThree); } catch(e) {}
try { fs.mkdirSync(testsubdir, 0700); } catch(e) {}
assert.doesNotThrow(
function() {
var watcher = fs.watch(testsubdir, function(event, filename) {
assert.equal('rename', event);
if (expectFilePath) {
assert.equal('newfile.txt', filename);
} else {
assert.equal(null, filename);
}
watcher.close();
++watchSeenThree;
});
}
);
setTimeout(function() {
var fd = fs.openSync(filepathThree, 'w');
fs.closeSync(fd);
}, 1000);

1
wscript

@ -890,6 +890,7 @@ def build(bld):
src/cares_wrap.cc src/cares_wrap.cc
src/stdio_wrap.cc src/stdio_wrap.cc
src/tty_wrap.cc src/tty_wrap.cc
src/fs_event_wrap.cc
src/process_wrap.cc src/process_wrap.cc
src/v8_typed_array.cc src/v8_typed_array.cc
""" """

Loading…
Cancel
Save