using namespace v8;
using namespace node;
-#define FD_SYMBOL String::NewSymbol("fd")
-#define ACTION_QUEUE_SYMBOL String::NewSymbol("_actionQueue")
-#define ENCODING_SYMBOL String::NewSymbol("encoding")
-#define CALLBACK_SYMBOL String::NewSymbol("callbaccallback")
-#define POLL_ACTIONS_SYMBOL String::NewSymbol("_pollActions")
-
-#define UTF8_SYMBOL String::NewSymbol("utf8")
-#define RAW_SYMBOL String::NewSymbol("raw")
-
-void
-File::Initialize (Handle<Object> target)
+#define DEV_SYMBOL String::NewSymbol("dev")
+#define INO_SYMBOL String::NewSymbol("ino")
+#define MODE_SYMBOL String::NewSymbol("mode")
+#define NLINK_SYMBOL String::NewSymbol("nlink")
+#define UID_SYMBOL String::NewSymbol("uid")
+#define GID_SYMBOL String::NewSymbol("gid")
+#define RDEV_SYMBOL String::NewSymbol("rdev")
+#define SIZE_SYMBOL String::NewSymbol("size")
+#define BLKSIZE_SYMBOL String::NewSymbol("blksize")
+#define BLOCKS_SYMBOL String::NewSymbol("blocks")
+#define ATIME_SYMBOL String::NewSymbol("atime")
+#define MTIME_SYMBOL String::NewSymbol("mtime")
+#define CTIME_SYMBOL String::NewSymbol("ctime")
+#define BAD_ARGUMENTS String::New("Bad argument")
+
+#define MAKE_CALLBACK_PTR \
+ Persistent<Function> *callback = NULL; \
+ Local<Value> last_arg = args[args.Length()-1]; \
+ if (last_arg->IsFunction()) { \
+ Local<Function> l = Local<Function>::Cast(last_arg); \
+ callback = new Persistent<Function>(); \
+ *callback = Persistent<Function>::New(l); \
+ } \
+ node::eio_warmup(); \
+
+#define CALL_CALLBACK_PTR(req, argc, argv) \
+do { \
+ if (req->data) { \
+ Persistent<Function> *callback = \
+ reinterpret_cast<Persistent<Function>*>(req->data); \
+ TryCatch try_catch; \
+ (*callback)->Call(Context::GetCurrent()->Global(), argc, argv); \
+ if(try_catch.HasCaught()) \
+ node::fatal_exception(try_catch); \
+ free(callback); \
+ } \
+} while(0)
+
+#define DEFINE_SIMPLE_CB(name) \
+static int After##name (eio_req *req) \
+{ \
+ HandleScope scope; \
+ Local<Value> argv[] = { Integer::New(req->errorno) }; \
+ CALL_CALLBACK_PTR(req, 1, argv); \
+ return 0; \
+} \
+
+DEFINE_SIMPLE_CB(Close)
+static Handle<Value> Close (const Arguments& args)
{
+ if (args.Length() < 1 || !args[0]->IsInt32())
+ return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
-
- // file system methods
- NODE_SET_METHOD(target, "rename", FileSystem::Rename);
- NODE_SET_METHOD(target, "stat", FileSystem::Stat);
- NODE_SET_METHOD(target, "strerror", FileSystem::StrError);
-
- target->Set(String::NewSymbol("STDIN_FILENO"), Integer::New(STDIN_FILENO));
- target->Set(String::NewSymbol("STDOUT_FILENO"), Integer::New(STDOUT_FILENO));
- target->Set(String::NewSymbol("STDERR_FILENO"), Integer::New(STDERR_FILENO));
-
-
- Local<FunctionTemplate> file_template = FunctionTemplate::New(File::New);
- file_template->InstanceTemplate()->SetInternalFieldCount(1);
-
- // file methods
- NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_open", File::Open);
- NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_close", File::Close);
- NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_write", File::Write);
- NODE_SET_PROTOTYPE_METHOD(file_template, "_ffi_read", File::Read);
- file_template->InstanceTemplate()->SetAccessor(ENCODING_SYMBOL, File::GetEncoding, File::SetEncoding);
- target->Set(String::NewSymbol("File"), file_template->GetFunction());
-}
-
-Handle<Value>
-File::GetEncoding (Local<String> property, const AccessorInfo& info)
-{
- File *file = NODE_UNWRAP(File, info.This());
-
- if (file->encoding_ == UTF8)
- return UTF8_SYMBOL;
- else
- return RAW_SYMBOL;
-}
-
-void
-File::SetEncoding (Local<String> property, Local<Value> value, const AccessorInfo& info)
-{
- File *file = NODE_UNWRAP(File, info.This());
-
- if (value->IsString()) {
- Local<String> encoding_string = value->ToString();
- char buf[5]; // need enough room for "utf8" or "raw"
- encoding_string->WriteAscii(buf, 0, 4);
- buf[4] = '\0';
- file->encoding_ = strcasecmp(buf, "utf8") == 0 ? UTF8 : RAW;
- } else {
- file->encoding_ = RAW;
- }
-}
-
-static void
-CallTopCallback (Handle<Object> handle, const int argc, Handle<Value> argv[])
-{
- HandleScope scope;
-
- // poll_actions
- Local<Value> poll_actions_value = handle->Get(POLL_ACTIONS_SYMBOL);
- assert(poll_actions_value->IsFunction());
- Handle<Function> poll_actions = Handle<Function>::Cast(poll_actions_value);
-
- TryCatch try_catch;
- poll_actions->Call(handle, argc, argv);
- if(try_catch.HasCaught())
- node::fatal_exception(try_catch);
+ int fd = args[0]->Int32Value();
+ MAKE_CALLBACK_PTR
+ eio_close(fd, EIO_PRI_DEFAULT, AfterClose, callback);
+ return Undefined();
}
-Handle<Value>
-FileSystem::Rename (const Arguments& args)
+DEFINE_SIMPLE_CB(Rename)
+static Handle<Value> Rename (const Arguments& args)
{
- if (args.Length() < 2)
- return Undefined();
-
+ if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString())
+ return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
-
String::Utf8Value path(args[0]->ToString());
String::Utf8Value new_path(args[1]->ToString());
-
- Persistent<Function> *callback = NULL;
- if (args[2]->IsFunction()) {
- Local<Function> l = Local<Function>::Cast(args[2]);
- callback = new Persistent<Function>();
- *callback = Persistent<Function>::New(l);
- }
-
- node::eio_warmup();
- eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, NULL);
-
+ MAKE_CALLBACK_PTR
+ eio_rename(*path, *new_path, EIO_PRI_DEFAULT, AfterRename, callback);
return Undefined();
}
-int
-FileSystem::AfterRename (eio_req *req)
+static int
+AfterOpen (eio_req *req)
{
HandleScope scope;
- const int argc = 1;
+ const int argc = 2;
Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
-
- if (req->data) {
- Persistent<Function> *callback = reinterpret_cast<Persistent<Function>*>(req->data);
-
- TryCatch try_catch;
- (*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
- if(try_catch.HasCaught())
- node::fatal_exception(try_catch);
-
- free(callback);
- }
-
+ argv[1] = Integer::New(req->result);
+ CALL_CALLBACK_PTR(req, argc, argv);
return 0;
}
-Handle<Value>
-FileSystem::Stat (const Arguments& args)
+static Handle<Value>
+Open (const Arguments& args)
{
- if (args.Length() < 1)
- return v8::Undefined();
+ if ( args.Length() < 3
+ || !args[0]->IsString()
+ || !args[1]->IsInt32()
+ || !args[2]->IsInt32()
+ ) return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
-
String::Utf8Value path(args[0]->ToString());
+ int flags = args[1]->Int32Value();
+ mode_t mode = static_cast<mode_t>(args[2]->Int32Value());
- Persistent<Function> *callback = NULL;
- if (args[1]->IsFunction()) {
- Local<Function> l = Local<Function>::Cast(args[1]);
- callback = new Persistent<Function>();
- *callback = Persistent<Function>::New(l);
- }
-
- node::eio_warmup();
- eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback);
+ MAKE_CALLBACK_PTR
+ eio_open(*path, flags, mode, EIO_PRI_DEFAULT, AfterOpen, callback);
return Undefined();
}
-int
-FileSystem::AfterStat (eio_req *req)
+static int
+AfterWrite (eio_req *req)
{
HandleScope scope;
+ free(req->ptr2);
+
+ ssize_t written = req->result;
+
const int argc = 2;
Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
+ argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
- Local<Object> stats = Object::New();
- argv[1] = stats;
-
- if (req->result == 0) {
- struct stat *s = reinterpret_cast<struct stat*>(req->ptr2);
-
- /* ID of device containing file */
- stats->Set(NODE_SYMBOL("dev"), Integer::New(s->st_dev));
- /* inode number */
- stats->Set(NODE_SYMBOL("ino"), Integer::New(s->st_ino));
- /* protection */
- stats->Set(NODE_SYMBOL("mode"), Integer::New(s->st_mode));
- /* number of hard links */
- stats->Set(NODE_SYMBOL("nlink"), Integer::New(s->st_nlink));
- /* user ID of owner */
- stats->Set(NODE_SYMBOL("uid"), Integer::New(s->st_uid));
- /* group ID of owner */
- stats->Set(NODE_SYMBOL("gid"), Integer::New(s->st_gid));
- /* device ID (if special file) */
- stats->Set(NODE_SYMBOL("rdev"), Integer::New(s->st_rdev));
- /* total size, in bytes */
- stats->Set(NODE_SYMBOL("size"), Integer::New(s->st_size));
- /* blocksize for filesystem I/O */
- stats->Set(NODE_SYMBOL("blksize"), Integer::New(s->st_blksize));
- /* number of blocks allocated */
- stats->Set(NODE_SYMBOL("blocks"), Integer::New(s->st_blocks));
- /* time of last access */
- stats->Set(NODE_SYMBOL("atime"), Date::New(1000*static_cast<double>(s->st_atime)));
- /* time of last modification */
- stats->Set(NODE_SYMBOL("mtime"), Date::New(1000*static_cast<double>(s->st_mtime)));
- /* time of last status change */
- stats->Set(NODE_SYMBOL("ctime"), Date::New(1000*static_cast<double>(s->st_ctime)));
- }
-
- if (req->data) {
- Persistent<Function> *callback = reinterpret_cast<Persistent<Function>*>(req->data);
-
- TryCatch try_catch;
- (*callback)->Call(Context::GetCurrent()->Global(), argc, argv);
- if(try_catch.HasCaught())
- node::fatal_exception(try_catch);
-
- free(callback);
- }
-
+ CALL_CALLBACK_PTR(req, argc, argv);
return 0;
}
-Handle<Value>
-FileSystem::StrError (const Arguments& args)
+
+/* node.fs.write(fd, data, position, callback)
+ * Wrapper for write(2).
+ *
+ * 0 fd integer. file descriptor
+ * 1 data the data to write
+ * 2 position if integer, position to write at in the file.
+ * if null, write from the current position
+ *
+ * 3 callback(errorno, written)
+ */
+static Handle<Value>
+Write (const Arguments& args)
{
- if (args.Length() < 1) return v8::Undefined();
- if (!args[0]->IsNumber()) return v8::Undefined();
+ if ( args.Length() < 3
+ || !args[0]->IsInt32()
+ ) return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
- int errorno = args[0]->IntegerValue();
-
- Local<String> message = String::New(strerror(errorno));
-
- return scope.Close(message);
-}
-
-///////////////////// FILE /////////////////////
+ int fd = args[0]->Int32Value();
+ off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
-File::File (Handle<Object> handle)
- : ObjectWrap(handle)
-{
- HandleScope scope;
- encoding_ = RAW;
- handle->Set(ACTION_QUEUE_SYMBOL, Array::New());
-}
+ char *buf = NULL;
+ size_t len = 0;
-File::~File ()
-{
- ; // XXX call close?
-}
+ if (args[1]->IsString()) {
+ // utf8 encoding
+ Local<String> string = args[1]->ToString();
+ len = string->Utf8Length();
+ buf = reinterpret_cast<char*>(malloc(len));
+ string->WriteUtf8(buf, len);
+
+ } else if (args[1]->IsArray()) {
+ // raw encoding
+ Local<Array> array = Local<Array>::Cast(args[1]);
+ len = array->Length();
+ buf = reinterpret_cast<char*>(malloc(len));
+ for (unsigned int i = 0; i < len; i++) {
+ Local<Value> int_value = array->Get(Integer::New(i));
+ buf[i] = int_value->Int32Value();
+ }
-bool
-File::HasUtf8Encoding (void)
-{
- return false;
-}
+ } else {
+ return ThrowException(BAD_ARGUMENTS);
+ }
-int
-File::GetFD (void)
-{
- Handle<Value> fd_value = handle_->Get(FD_SYMBOL);
- int fd = fd_value->IntegerValue();
- return fd;
+ MAKE_CALLBACK_PTR
+ eio_write(fd, buf, len, pos, EIO_PRI_DEFAULT, AfterWrite, callback);
+ return Undefined();
}
-Handle<Value>
-File::Close (const Arguments& args)
+static int
+AfterUtf8Read (eio_req *req)
{
HandleScope scope;
- File *file = NODE_UNWRAP(File, args.Holder());
-
- int fd = file->GetFD();
-
- node::eio_warmup();
- eio_close (fd, EIO_PRI_DEFAULT, File::AfterClose, file);
- file->Attach();
- return Undefined();
-}
+ const int argc = 2;
+ Local<Value> argv[argc];
+ argv[0] = Integer::New(req->errorno);
-int
-File::AfterClose (eio_req *req)
-{
- File *file = reinterpret_cast<File*>(req->data);
+ char *buf = reinterpret_cast<char*>(req->ptr2);
- if (req->result == 0) {
- file->handle_->Delete(FD_SYMBOL);
+ if (req->result == 0) {
+ // eof
+ argv[1] = Local<Value>::New(Null());
+ } else {
+ size_t len = req->result;
+ argv[1] = String::New(buf, len);
}
- const int argc = 1;
- Local<Value> argv[argc];
- argv[0] = Integer::New(req->errorno);
- CallTopCallback(file->handle_, argc, argv);
- file->Detach();
+ CALL_CALLBACK_PTR(req, argc, argv);
return 0;
}
-Handle<Value>
-File::Open (const Arguments& args)
+static int
+AfterRawRead(eio_req *req)
{
- /* check arguments */
- if (args.Length() < 1) return Undefined();
- if (!args[0]->IsString()) return Undefined();
-
HandleScope scope;
- File *file = NODE_UNWRAP(File, args.Holder());
-
- // make sure that we don't already have a pending open
- if (file->handle_->Has(FD_SYMBOL)) {
- return ThrowException(String::New("File object is opened."));
- }
+ const int argc = 2;
+ Local<Value> argv[argc];
+ argv[0] = Integer::New(req->errorno);
- String::Utf8Value path(args[0]->ToString());
+ char *buf = reinterpret_cast<char*>(req->ptr2);
- int flags = O_RDONLY; // default
- if (args[1]->IsString()) {
- String::AsciiValue mode_v(args[1]->ToString());
- char *mode = *mode_v;
- // XXX is this interpretation of the mode correct?
- // I don't want to to use fopen() directly because eio doesn't support it.
- switch(mode[0]) {
- case 'r':
- flags = (mode[1] == '+' ? O_RDWR : O_RDONLY);
- break;
- case 'w':
- flags = O_CREAT | O_TRUNC | (mode[1] == '+' ? O_RDWR : O_WRONLY);
- break;
- case 'a':
- flags = O_APPEND | O_CREAT | (mode[1] == '+' ? O_RDWR : O_WRONLY);
- break;
+ if (req->result == 0) {
+ // eof
+ argv[1] = Local<Value>::New(Null());
+ } else {
+ // raw encoding
+ size_t len = req->result;
+ Local<Array> array = Array::New(len);
+ for (unsigned int i = 0; i < len; i++) {
+ array->Set(Integer::New(i), Integer::New(buf[i]));
}
+ argv[1] = array;
}
- // TODO how should the mode be set?
- node::eio_warmup();
- eio_open (*path, flags, 0666, EIO_PRI_DEFAULT, File::AfterOpen, file);
- file->Attach();
- return Undefined();
-}
-
-int
-File::AfterOpen (eio_req *req)
-{
- File *file = reinterpret_cast<File*>(req->data);
- HandleScope scope;
-
- if(req->result >= 0) {
- file->handle_->Set(FD_SYMBOL, Integer::New(req->result));
- }
-
- const int argc = 1;
- Handle<Value> argv[argc];
- argv[0] = Integer::New(req->errorno);
- CallTopCallback(file->handle_, argc, argv);
-
- file->Detach();
+ CALL_CALLBACK_PTR(req, argc, argv);
return 0;
}
-Handle<Value>
-File::Write (const Arguments& args)
+/* node.fs.read(fd, length, position, encoding, callback)
+ * Wrapper for read(2).
+ *
+ * 0 fd integer. file descriptor
+ * 1 length integer. length to read
+ * 2 position if integer, position to read from in the file.
+ * if null, read from the current position
+ * 3 encoding either node.fs.UTF8 or node.fs.RAW
+ *
+ * 4 callback(errorno, data)
+ */
+static Handle<Value>
+Read (const Arguments& args)
{
- if (args.Length() < 2) return Undefined();
- if (!args[1]->IsNumber()) return Undefined();
+ if ( args.Length() < 3
+ || !args[0]->IsInt32() // fd
+ || !args[1]->IsNumber() // len
+ ) return ThrowException(BAD_ARGUMENTS);
HandleScope scope;
- File *file = NODE_UNWRAP(File, args.Holder());
+ int fd = args[0]->Int32Value();
+ size_t len = args[1]->IntegerValue();
+ off_t pos = args[2]->IsNumber() ? args[2]->IntegerValue() : -1;
- off_t pos = args[1]->IntegerValue();
-
- char *buf = NULL;
- size_t length = 0;
-
- if (args[0]->IsString()) {
- // utf8 encoding
- Local<String> string = args[0]->ToString();
- length = string->Utf8Length();
- buf = reinterpret_cast<char*>(malloc(length));
- string->WriteUtf8(buf, length);
-
- } else if (args[0]->IsArray()) {
- // raw encoding
- Local<Array> array = Local<Array>::Cast(args[0]);
- length = array->Length();
- buf = reinterpret_cast<char*>(malloc(length));
- for (unsigned int i = 0; i < length; i++) {
- Local<Value> int_value = array->Get(Integer::New(i));
- buf[i] = int_value->Int32Value();
- }
-
- } else {
- // bad arguments. raise error?
- return Undefined();
+ enum encoding encoding = RAW;
+ if (args[3]->IsInt32()) {
+ encoding = static_cast<enum encoding>(args[3]->Int32Value());
}
- if (file->handle_->Has(FD_SYMBOL) == false) {
- printf("trying to write to a bad fd!\n");
- return Undefined();
- }
-
- int fd = file->GetFD();
-
- node::eio_warmup();
- eio_write(fd, buf, length, pos, EIO_PRI_DEFAULT, File::AfterWrite, file);
-
- file->Attach();
+ MAKE_CALLBACK_PTR
+ // NOTE: 2nd param: NULL pointer tells eio to allocate it itself
+ eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT,
+ encoding == UTF8 ? AfterUtf8Read : AfterRawRead, callback);
return Undefined();
}
-int
-File::AfterWrite (eio_req *req)
+static int
+AfterStat (eio_req *req)
{
- File *file = reinterpret_cast<File*>(req->data);
-
- //char *buf = reinterpret_cast<char*>(req->ptr2);
- free(req->ptr2);
- ssize_t written = req->result;
-
HandleScope scope;
const int argc = 2;
Local<Value> argv[argc];
argv[0] = Integer::New(req->errorno);
- argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0);
- CallTopCallback(file->handle_, argc, argv);
- file->Detach();
+ Local<Object> stats = Object::New();
+ argv[1] = stats;
+
+ if (req->result == 0) {
+ struct stat *s = reinterpret_cast<struct stat*>(req->ptr2);
+
+ /* ID of device containing file */
+ stats->Set(DEV_SYMBOL, Integer::New(s->st_dev));
+ /* inode number */
+ stats->Set(INO_SYMBOL, Integer::New(s->st_ino));
+ /* protection */
+ stats->Set(MODE_SYMBOL, Integer::New(s->st_mode));
+ /* number of hard links */
+ stats->Set(NLINK_SYMBOL, Integer::New(s->st_nlink));
+ /* user ID of owner */
+ stats->Set(UID_SYMBOL, Integer::New(s->st_uid));
+ /* group ID of owner */
+ stats->Set(GID_SYMBOL, Integer::New(s->st_gid));
+ /* device ID (if special file) */
+ stats->Set(RDEV_SYMBOL, Integer::New(s->st_rdev));
+ /* total size, in bytes */
+ stats->Set(SIZE_SYMBOL, Integer::New(s->st_size));
+ /* blocksize for filesystem I/O */
+ stats->Set(BLKSIZE_SYMBOL, Integer::New(s->st_blksize));
+ /* number of blocks allocated */
+ stats->Set(BLOCKS_SYMBOL, Integer::New(s->st_blocks));
+ /* time of last access */
+ stats->Set(ATIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_atime)));
+ /* time of last modification */
+ stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_mtime)));
+ /* time of last status change */
+ stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast<double>(s->st_ctime)));
+ }
+ CALL_CALLBACK_PTR(req, argc, argv); \
return 0;
}
-Handle<Value>
-File::Read (const Arguments& args)
+static Handle<Value>
+Stat (const Arguments& args)
{
- HandleScope scope;
+ if (args.Length() < 1 || !args[0]->IsString())
+ return ThrowException(BAD_ARGUMENTS);
- if (args.Length() < 1 || !args[0]->IsNumber() || !args[1]->IsNumber()) {
- return ThrowException(String::New("Bad parameter for _ffi_read()"));
- }
+ HandleScope scope;
- File *file = NODE_UNWRAP(File, args.Holder());
- size_t len = args[0]->IntegerValue();
- off_t pos = args[1]->IntegerValue();
+ String::Utf8Value path(args[0]->ToString());
- int fd = file->GetFD();
- assert(fd >= 0);
+ MAKE_CALLBACK_PTR
- // NOTE: 2nd param: NULL pointer tells eio to allocate it itself
- node::eio_warmup();
- eio_read(fd, NULL, len, pos, EIO_PRI_DEFAULT, File::AfterRead, file);
+ eio_stat(*path, EIO_PRI_DEFAULT, AfterStat, callback);
- file->Attach();
return Undefined();
}
-int
-File::AfterRead (eio_req *req)
+static Handle<Value>
+StrError (const Arguments& args)
{
- File *file = reinterpret_cast<File*>(req->data);
- HandleScope scope;
-
- const int argc = 2;
- Local<Value> argv[argc];
- argv[0] = Integer::New(req->errorno);
+ if (args.Length() < 1) return v8::Undefined();
+ if (!args[0]->IsNumber()) return v8::Undefined();
- char *buf = reinterpret_cast<char*>(req->ptr2);
+ HandleScope scope;
- if (req->result == 0) {
- // eof
- argv[1] = Local<Value>::New(Null());
- } else {
- size_t len = req->result;
- if (file->encoding_ == UTF8) {
- // utf8 encoding
- argv[1] = String::New(buf, req->result);
- } else {
- // raw encoding
- Local<Array> array = Array::New(len);
- for (unsigned int i = 0; i < len; i++) {
- array->Set(Integer::New(i), Integer::New(buf[i]));
- }
- argv[1] = array;
- }
- }
+ int errorno = args[0]->IntegerValue();
- CallTopCallback(file->handle_, argc, argv);
+ Local<String> message = String::New(strerror(errorno));
- file->Detach();
- return 0;
+ return scope.Close(message);
}
-Handle<Value>
-File::New(const Arguments& args)
+void
+File::Initialize (Handle<Object> target)
{
HandleScope scope;
- File *f = new File(args.Holder());
- ObjectWrap::InformV8ofAllocation(f);
-
- return args.This();
+ // file system methods
+ NODE_SET_METHOD(target, "rename", Rename);
+ NODE_SET_METHOD(target, "stat", Stat);
+ NODE_SET_METHOD(target, "close", Close);
+ NODE_SET_METHOD(target, "open", Open);
+ NODE_SET_METHOD(target, "write", Write);
+ NODE_SET_METHOD(target, "read", Read);
+
+ NODE_SET_METHOD(target, "strerror", StrError);
+
+ NODE_DEFINE_CONSTANT(target, RAW);
+ NODE_DEFINE_CONSTANT(target, UTF8);
+
+ NODE_DEFINE_CONSTANT(target, STDIN_FILENO);
+ NODE_DEFINE_CONSTANT(target, STDOUT_FILENO);
+ NODE_DEFINE_CONSTANT(target, STDERR_FILENO);
+ // file access modes
+ NODE_DEFINE_CONSTANT(target, O_RDONLY);
+ NODE_DEFINE_CONSTANT(target, O_WRONLY);
+ NODE_DEFINE_CONSTANT(target, O_RDWR);
+ // file creation flags
+ NODE_DEFINE_CONSTANT(target, O_CREAT);
+ NODE_DEFINE_CONSTANT(target, O_EXCL);
+ NODE_DEFINE_CONSTANT(target, O_NOCTTY);
+ NODE_DEFINE_CONSTANT(target, O_TRUNC);
+ // file status flags
+ NODE_DEFINE_CONSTANT(target, O_APPEND);
+// NODE_DEFINE_CONSTANT(target, O_ASYNC);
+ NODE_DEFINE_CONSTANT(target, O_CLOEXEC);
+ NODE_DEFINE_CONSTANT(target, O_DIRECT);
+ NODE_DEFINE_CONSTANT(target, O_DIRECTORY);
+ NODE_DEFINE_CONSTANT(target, O_EXCL);
+ NODE_DEFINE_CONSTANT(target, O_LARGEFILE);
+ NODE_DEFINE_CONSTANT(target, O_NOATIME);
+ NODE_DEFINE_CONSTANT(target, O_NOFOLLOW);
+// NODE_DEFINE_CONSTANT(target, O_NONBLOCK);
+// NODE_DEFINE_CONSTANT(target, O_SYNC);
+ NODE_DEFINE_CONSTANT(target, O_SYNC);
+
+ NODE_DEFINE_CONSTANT(target, S_IRWXU);
+
+ NODE_DEFINE_CONSTANT(target, S_IRUSR);
+ NODE_DEFINE_CONSTANT(target, S_IWUSR);
+ NODE_DEFINE_CONSTANT(target, S_IXUSR);
+
+ NODE_DEFINE_CONSTANT(target, S_IRWXG);
+
+ NODE_DEFINE_CONSTANT(target, S_IRGRP);
+ NODE_DEFINE_CONSTANT(target, S_IWGRP);
+ NODE_DEFINE_CONSTANT(target, S_IXGRP);
+
+ NODE_DEFINE_CONSTANT(target, S_IRWXO);
+
+ NODE_DEFINE_CONSTANT(target, S_IROTH);
+ NODE_DEFINE_CONSTANT(target, S_IWOTH);
+ NODE_DEFINE_CONSTANT(target, S_IXOTH);
}
-
}
node.fs.cat = function (path, encoding, callback) {
- var file = new node.fs.File();
- file.encoding = encoding;
+ var file = new node.fs.File({encoding: encoding});
file.onError = function (method, errno, msg) {
callback(-1);
};
- var content = (file.encoding == "raw" ? "" : []);
+ var content = (encoding == node.fs.UTF8 ? "" : []);
var pos = 0;
var chunkSize = 10*1024;
});
}
-node.fs.File.prototype.open = function (path, mode, callback) {
- this._addAction("open", [path, mode], callback);
-};
+node.fs.File = function (options) {
+ var self = this;
+ options = options || {};
+
+ if (options.encoding === undefined)
+ self.encoding = node.fs.RAW;
+ else
+ self.encoding = options.encoding
+
+ //node.debug("encoding: opts=" + options.encoding + " self=" + self.encoding);
+ self.fd = options.fd || null;
+
+ var actionQueue = [];
+
+ // Adds a method to the queue.
+ function addAction (method, args, callback) {
+ var action = { method: method
+ , callback: callback
+ , args: args
+ };
+ //node.debug("add action: " + JSON.stringify(action));
+ actionQueue.push(action);
+
+ // If the queue was empty, immediately call the method.
+ if (actionQueue.length == 1) act();
+ }
-node.fs.File.prototype.close = function (callback) {
- this._addAction("close", [], callback);
-};
+ // called after each action finishes (when it returns from the thread pool)
+ function poll () {
+ var action = actionQueue[0];
-node.fs.File.prototype.read = function (length, pos, callback) {
- this._addAction("read", [length, pos], callback);
-};
+ var errno = arguments[0];
-node.fs.File.prototype.write = function (buf, pos, callback) {
- this._addAction("write", [buf, pos], callback);
-};
+ if (errno < 0) {
+ if (self.onError)
+ self.onError(action.method, errno, node.fs.strerror(errno));
+ actionQueue = []; // empty the queue.
+ return;
+ }
-node.fs.File.prototype.print = function (data, callback) {
- this.write(data, -1, callback);
-};
+ var rest = [];
+ for (var i = 1; i < arguments.length; i++)
+ rest.push(arguments[i]);
-node.fs.File.prototype.puts = function (data, callback) {
- this.write(data + "\n", -1, callback);
-};
+ //node.debug("poll action: " + JSON.stringify(action));
+ //node.debug("poll rest: " + JSON.stringify(rest));
-// Some explanation of the File binding.
-//
-// All file operations are blocking. To get around this they are executed
-// in a thread pool in C++ (libeio).
-//
-// The ordering of method calls to a file should be preserved, so they are
-// only executed one at a time. A queue, called _actionQueue is employed.
-//
-// The constructor File() is implemented in C++. It initlizes
-// the member _actionQueue = []
-//
-// Any of the methods called on a file are put into this queue. When they
-// reach the head of the queue they will be executed. C++ calls the
-// method _pollActions each time it becomes idle. If there is no action
-// currently being executed then _pollActions will not be called. When
-// actions are added to an empty _actionQueue, they will be immediately
-// executed.
-//
-// When an action has completed, the C++ side is looks at the first
-// element of _actionQueue in order to get a handle on the callback
-// function. Only after that completion callback has been made can the
-// action be shifted out of the queue.
-//
-// See File::CallTopCallback() in file.cc for the other side of the binding.
-
-
-node.fs.File.prototype._addAction = function (method, args, callback) {
- // This adds a method to the queue.
- var action = { method: method
- , callback: callback
- , args: args
- };
- this._actionQueue.push(action);
-
- // If the queue was empty, immediately call the method.
- if (this._actionQueue.length == 1) this._act();
-};
+ if (action.callback)
+ action.callback.apply(this, rest);
-node.fs.File.prototype._act = function () {
- // peek at the head of the queue
- var action = this._actionQueue[0];
- if (action) {
- // execute the c++ version of the method. the c++ version
- // is gotten by appending "_ffi_" to the method name.
- this["_ffi_" + action.method].apply(this, action.args);
+ actionQueue.shift();
+ act();
}
-};
-
-// called from C++ after each action finishes
-// (i.e. when it returns from the thread pool)
-node.fs.File.prototype._pollActions = function () {
- var action = this._actionQueue[0];
-
- var errno = arguments[0];
- if (errno < 0) {
- if (this.onError)
- this.onError(action.method, errno, node.fs.strerror(errno));
- this._actionQueue = []; // empty the queue.
- return;
+ function act () {
+ var action = actionQueue[0]; // peek at the head of the queue
+ if (action) {
+ internal_methods[action.method].apply(this, action.args);
+ }
}
- var rest = [];
- for (var i = 1; i < arguments.length; i++)
- rest.push(arguments[i]);
+ var internal_methods = {
+ open: function (path, mode) {
+ var m = node.fs.O_RDONLY;
+ switch (mode) {
+ case "r":
+ m = node.fs.O_RDONLY;
+ break;
+ case "r+":
+ m = node.fs.O_RDWR;
+ break;
+ case "w":
+ m = O_CREAT | O_TRUNC | O_WRONLY;
+ break;
+ case "w+":
+ m = O_CREAT | O_TRUNC | O_RDWR;
+ break;
+ case "a":
+ m = O_APPEND | O_CREAT | O_WRONLY;
+ break;
+ case "a+":
+ m = O_APPEND | O_CREAT | O_RDWR;
+ break;
+ default:
+ throw "Unknown mode";
+ }
- if (action.callback)
- action.callback.apply(this, rest);
+ node.fs.open(path, m, 0666, function (status, fd) {
+ self.fd = fd;
+ poll(status, fd);
+ });
+ },
+
+ close: function ( ) {
+ node.fs.close(self.fd, function (status) {
+ self.fd = null;
+ poll(status);
+ });
+ },
+
+ read: function (length, position) {
+ //node.debug("encoding: " + self.encoding);
+ node.fs.read(self.fd, length, position, self.encoding, poll);
+ },
+
+ write: function (data, position) {
+ node.fs.write(self.fd, data, position, poll);
+ }
+ };
- this._actionQueue.shift();
+ self.open = function (path, mode, callback) {
+ addAction("open", [path, mode], callback);
+ };
+
+ self.close = function (callback) {
+ addAction("close", [], callback);
+ };
- this._act();
-};
+ self.read = function (length, pos, callback) {
+ addAction("read", [length, pos], callback);
+ };
-stdout = new node.fs.File();
-stdout.fd = node.fs.File.STDOUT_FILENO;
+ self.write = function (buf, pos, callback) {
+ addAction("write", [buf, pos], callback);
+ };
+
+ self.print = function (data, callback) {
+ return self.write(data, null, callback);
+ };
-stderr = new node.fs.File();
-stderr.fd = node.fs.File.STDERR_FILENO;
+ self.puts = function (data, callback) {
+ return self.write(data + "\n", null, callback);
+ };
+};
-stdin = new node.fs.File();
-stdin.fd = node.fs.File.STDIN_FILENO;
+stdout = new node.fs.File({ fd: node.fs.STDOUT_FILENO });
+stderr = new node.fs.File({ fd: node.fs.STDERR_FILENO });
+stdin = new node.fs.File({ fd: node.fs.STDIN_FILENO });
puts = function (data, callback) {
stdout.puts(data, callback);