diff --git a/src/file.cc b/src/file.cc index c8f6fae6ae..d5f261dbd1 100644 --- a/src/file.cc +++ b/src/file.cc @@ -12,490 +12,405 @@ 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 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 *callback = NULL; \ + Local last_arg = args[args.Length()-1]; \ + if (last_arg->IsFunction()) { \ + Local l = Local::Cast(last_arg); \ + callback = new Persistent(); \ + *callback = Persistent::New(l); \ + } \ + node::eio_warmup(); \ + +#define CALL_CALLBACK_PTR(req, argc, argv) \ +do { \ + if (req->data) { \ + Persistent *callback = \ + reinterpret_cast*>(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 argv[] = { Integer::New(req->errorno) }; \ + CALL_CALLBACK_PTR(req, 1, argv); \ + return 0; \ +} \ + +DEFINE_SIMPLE_CB(Close) +static Handle 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 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 -File::GetEncoding (Local 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 property, Local value, const AccessorInfo& info) -{ - File *file = NODE_UNWRAP(File, info.This()); - - if (value->IsString()) { - Local 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 handle, const int argc, Handle argv[]) -{ - HandleScope scope; - - // poll_actions - Local poll_actions_value = handle->Get(POLL_ACTIONS_SYMBOL); - assert(poll_actions_value->IsFunction()); - Handle poll_actions = Handle::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 -FileSystem::Rename (const Arguments& args) +DEFINE_SIMPLE_CB(Rename) +static Handle 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 *callback = NULL; - if (args[2]->IsFunction()) { - Local l = Local::Cast(args[2]); - callback = new Persistent(); - *callback = Persistent::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 argv[argc]; argv[0] = Integer::New(req->errorno); - - if (req->data) { - Persistent *callback = reinterpret_cast*>(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 -FileSystem::Stat (const Arguments& args) +static Handle +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(args[2]->Int32Value()); - Persistent *callback = NULL; - if (args[1]->IsFunction()) { - Local l = Local::Cast(args[1]); - callback = new Persistent(); - *callback = Persistent::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 argv[argc]; argv[0] = Integer::New(req->errorno); + argv[1] = written >= 0 ? Integer::New(written) : Integer::New(0); - Local stats = Object::New(); - argv[1] = stats; - - if (req->result == 0) { - struct stat *s = reinterpret_cast(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(s->st_atime))); - /* time of last modification */ - stats->Set(NODE_SYMBOL("mtime"), Date::New(1000*static_cast(s->st_mtime))); - /* time of last status change */ - stats->Set(NODE_SYMBOL("ctime"), Date::New(1000*static_cast(s->st_ctime))); - } - - if (req->data) { - Persistent *callback = reinterpret_cast*>(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 -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 +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 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 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 = args[1]->ToString(); + len = string->Utf8Length(); + buf = reinterpret_cast(malloc(len)); + string->WriteUtf8(buf, len); + + } else if (args[1]->IsArray()) { + // raw encoding + Local array = Local::Cast(args[1]); + len = array->Length(); + buf = reinterpret_cast(malloc(len)); + for (unsigned int i = 0; i < len; i++) { + Local 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 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 -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 argv[argc]; + argv[0] = Integer::New(req->errorno); -int -File::AfterClose (eio_req *req) -{ - File *file = reinterpret_cast(req->data); + char *buf = reinterpret_cast(req->ptr2); - if (req->result == 0) { - file->handle_->Delete(FD_SYMBOL); + if (req->result == 0) { + // eof + argv[1] = Local::New(Null()); + } else { + size_t len = req->result; + argv[1] = String::New(buf, len); } - const int argc = 1; - Local argv[argc]; - argv[0] = Integer::New(req->errorno); - CallTopCallback(file->handle_, argc, argv); - file->Detach(); + CALL_CALLBACK_PTR(req, argc, argv); return 0; } -Handle -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 argv[argc]; + argv[0] = Integer::New(req->errorno); - String::Utf8Value path(args[0]->ToString()); + char *buf = reinterpret_cast(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::New(Null()); + } else { + // raw encoding + size_t len = req->result; + Local 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(req->data); - HandleScope scope; - - if(req->result >= 0) { - file->handle_->Set(FD_SYMBOL, Integer::New(req->result)); - } - - const int argc = 1; - Handle argv[argc]; - argv[0] = Integer::New(req->errorno); - CallTopCallback(file->handle_, argc, argv); - - file->Detach(); + CALL_CALLBACK_PTR(req, argc, argv); return 0; } -Handle -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 +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 = args[0]->ToString(); - length = string->Utf8Length(); - buf = reinterpret_cast(malloc(length)); - string->WriteUtf8(buf, length); - - } else if (args[0]->IsArray()) { - // raw encoding - Local array = Local::Cast(args[0]); - length = array->Length(); - buf = reinterpret_cast(malloc(length)); - for (unsigned int i = 0; i < length; i++) { - Local 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(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(req->data); - - //char *buf = reinterpret_cast(req->ptr2); - free(req->ptr2); - ssize_t written = req->result; - HandleScope scope; const int argc = 2; Local 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 stats = Object::New(); + argv[1] = stats; + + if (req->result == 0) { + struct stat *s = reinterpret_cast(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(s->st_atime))); + /* time of last modification */ + stats->Set(MTIME_SYMBOL, Date::New(1000*static_cast(s->st_mtime))); + /* time of last status change */ + stats->Set(CTIME_SYMBOL, Date::New(1000*static_cast(s->st_ctime))); + } + CALL_CALLBACK_PTR(req, argc, argv); \ return 0; } -Handle -File::Read (const Arguments& args) +static Handle +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 +StrError (const Arguments& args) { - File *file = reinterpret_cast(req->data); - HandleScope scope; - - const int argc = 2; - Local 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(req->ptr2); + HandleScope scope; - if (req->result == 0) { - // eof - argv[1] = Local::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::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 message = String::New(strerror(errorno)); - file->Detach(); - return 0; + return scope.Close(message); } -Handle -File::New(const Arguments& args) +void +File::Initialize (Handle 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); } - diff --git a/src/file.h b/src/file.h index b42d1ae22e..e48694c3b6 100644 --- a/src/file.h +++ b/src/file.h @@ -5,51 +5,9 @@ namespace node { -class FileSystem { -public: - static v8::Handle Rename (const v8::Arguments& args); - static int AfterRename (eio_req *req); - - static v8::Handle Stat (const v8::Arguments& args); - static int AfterStat (eio_req *req); - - static v8::Handle StrError (const v8::Arguments& args); -}; - -class File : ObjectWrap { +class File { public: static void Initialize (v8::Handle target); - - virtual size_t size (void) { return sizeof(File); } - -protected: - File (v8::Handle handle); - ~File (); - - static v8::Handle New (const v8::Arguments& args); - - static v8::Handle Open (const v8::Arguments& args); - static int AfterOpen (eio_req *req); - - static v8::Handle Close (const v8::Arguments& args); - static int AfterClose (eio_req *req); - - static v8::Handle Write (const v8::Arguments& args); - static int AfterWrite (eio_req *req); - - static v8::Handle Read (const v8::Arguments& args); - static int AfterRead (eio_req *req); - - static v8::Handle GetEncoding (v8::Local property, - const v8::AccessorInfo& info); - static void SetEncoding (v8::Local property, - v8::Local value, - const v8::AccessorInfo& info); - - bool HasUtf8Encoding (void); - int GetFD (void); - - enum encoding encoding_; }; } // namespace node diff --git a/src/file.js b/src/file.js index a4caafc856..66879b26f2 100644 --- a/src/file.js +++ b/src/file.js @@ -5,14 +5,13 @@ node.fs.exists = function (path, callback) { } 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; @@ -38,112 +37,144 @@ node.fs.cat = function (path, encoding, callback) { }); } -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); diff --git a/src/node.h b/src/node.h index d6db49da12..7d1d296078 100644 --- a/src/node.h +++ b/src/node.h @@ -12,6 +12,8 @@ namespace node { #define NODE_SET_METHOD(obj, name, callback) \ obj->Set(NODE_SYMBOL(name), v8::FunctionTemplate::New(callback)->GetFunction()) #define NODE_UNWRAP(type, value) static_cast(node::ObjectWrap::Unwrap(value)) +#define NODE_DEFINE_CONSTANT(target, constant) \ + (target)->Set(v8::String::NewSymbol(#constant), v8::Integer::New(constant)) #define NODE_SET_PROTOTYPE_METHOD(templ, name, callback) \ do { \ diff --git a/src/node.js b/src/node.js index e3d9d1688c..6bdef379b2 100644 --- a/src/node.js +++ b/src/node.js @@ -134,7 +134,7 @@ clearInterval = clearTimeout; } function loadScript (filename, target, callback) { - node.fs.cat(filename, "utf8", function (status, content) { + node.fs.cat(filename, node.fs.UTF8, function (status, content) { if (status != 0) { stderr.puts("Error reading " + filename + ": " + node.fs.strerror(status)); node.exit(1);