diff --git a/runtime/bin/builtin_in.cc b/runtime/bin/builtin_in.cc index 3710a963c7b..3a40dd1e907 100644 --- a/runtime/bin/builtin_in.cc +++ b/runtime/bin/builtin_in.cc @@ -25,7 +25,7 @@ static const char Builtin_source_[] = { #define BUILTIN_NATIVE_LIST(V) \ V(Logger_PrintString, 1) \ V(Directory_Close, 2) \ - V(Directory_List, 7) \ + V(Directory_List, 8) \ V(Directory_Open, 2) \ V(File_OpenFile, 3) \ V(File_Exists, 1) \ diff --git a/runtime/bin/directory.cc b/runtime/bin/directory.cc index 9fd3d07777c..ca9448a9240 100644 --- a/runtime/bin/directory.cc +++ b/runtime/bin/directory.cc @@ -48,14 +48,17 @@ static intptr_t GetHandlerPort(Dart_Handle handle) { void FUNCTION_NAME(Directory_List)(Dart_NativeArguments args) { Dart_EnterScope(); - intptr_t dir = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 1)); - bool recursive = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 2)); - Dart_Port dir_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 3)); - Dart_Port file_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 4)); - Dart_Port done_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 5)); + Dart_Handle path = Dart_GetNativeArgument(args, 1); + intptr_t dir = DartUtils::GetIntegerValue(Dart_GetNativeArgument(args, 2)); + bool recursive = DartUtils::GetBooleanValue(Dart_GetNativeArgument(args, 3)); + Dart_Port dir_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 4)); + Dart_Port file_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 5)); + Dart_Port done_handler_port = GetHandlerPort(Dart_GetNativeArgument(args, 6)); Dart_Port dir_error_handler_port = - GetHandlerPort(Dart_GetNativeArgument(args, 6)); - Directory::List(dir, + GetHandlerPort(Dart_GetNativeArgument(args, 7)); + ASSERT(Dart_IsString(path)); + Directory::List(DartUtils::GetStringValue(path), + dir, recursive, dir_handler_port, file_handler_port, diff --git a/runtime/bin/directory.dart b/runtime/bin/directory.dart index 68ea8703776..664db22ddb2 100644 --- a/runtime/bin/directory.dart +++ b/runtime/bin/directory.dart @@ -2,7 +2,7 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -interface Directory factory DirectoryImpl { +interface Directory factory _Directory { /** * Creates a directory object. The path is either a full path or * relative to the directory in which the Dart VM was diff --git a/runtime/bin/directory.h b/runtime/bin/directory.h index 6ae47c043a5..a187d69d80e 100644 --- a/runtime/bin/directory.h +++ b/runtime/bin/directory.h @@ -12,7 +12,8 @@ class Directory { public: static bool Open(const char* path, intptr_t* dir); static bool Close(intptr_t dir); - static void List(intptr_t dir, + static void List(const char* path, + intptr_t dir, bool recursive, Dart_Port dir_handler, Dart_Port file_handler, diff --git a/runtime/bin/directory_impl.dart b/runtime/bin/directory_impl.dart index 27815b8bba6..302927beeff 100644 --- a/runtime/bin/directory_impl.dart +++ b/runtime/bin/directory_impl.dart @@ -7,14 +7,13 @@ class DirectoryException { final String message; } +class _Directory implements Directory { -class DirectoryImpl implements Directory { - - DirectoryImpl.open(String dir) { + _Directory.open(String this._dir) { _id = 0; _closed = false; _listing = false; - if (!_open(dir)) { + if (!_open(_dir)) { _closed = true; throw new DirectoryException("Error: could not open directory"); } @@ -41,6 +40,7 @@ class DirectoryImpl implements Directory { } void list([bool recursive = false]) { + // TODO(ager): Spawn an isolate to make the listing an async operation. if (_closed) { throw new DirectoryException("Error: directory closed"); } @@ -48,7 +48,8 @@ class DirectoryImpl implements Directory { throw new DirectoryException("Error: listing already in progress"); } _listing = true; - _list(_id, + _list(_dir, + _id, recursive, _dirHandler, _fileHandler, @@ -112,7 +113,8 @@ class DirectoryImpl implements Directory { // Native code binding. bool _open(String dir) native "Directory_Open"; bool _close(int id) native "Directory_Close"; - void _list(int id, + void _list(String dir, + int id, bool recursive, ReceivePort dirHandler, ReceivePort fileHandler, @@ -124,6 +126,7 @@ class DirectoryImpl implements Directory { ReceivePort _doneHandler; ReceivePort _dirErrorHandler; + String _dir; int _id; bool _closed; bool _listing; diff --git a/runtime/bin/directory_linux.cc b/runtime/bin/directory_linux.cc index 77f6d88780b..74a4fbdcc18 100644 --- a/runtime/bin/directory_linux.cc +++ b/runtime/bin/directory_linux.cc @@ -2,11 +2,18 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -#include -#include #include +#include +#include +#include +#include +#include +#include +#include "bin/dartutils.h" #include "bin/directory.h" +#include "bin/file.h" + bool Directory::Open(const char* path, intptr_t* dir) { DIR* dir_pointer = opendir(path); @@ -17,56 +24,154 @@ bool Directory::Open(const char* path, intptr_t* dir) { return true; } + bool Directory::Close(intptr_t dir) { DIR* dir_pointer = reinterpret_cast(dir); int result = closedir(dir_pointer); return result == 0; } -void Directory::List(intptr_t dir, + +static void ComputeFullPath(const char* dir_name, + char* path, + int* path_length) { + size_t written = 0; + + if (!File::IsAbsolutePath(dir_name)) { + ASSERT(getcwd(path, PATH_MAX) != NULL); + *path_length = strlen(path); + written = snprintf(path + *path_length, + PATH_MAX - *path_length, + "%s", + File::PathSeparator()); + ASSERT(written == strlen(File::PathSeparator())); + *path_length += written; + } + + // Use dirname and basename to canonicalize the provided directory + // name. + char* dir_name_copy = strdup(dir_name); + char* dir = dirname(dir_name_copy); + if (strcmp(dir, ".") != 0) { + written = snprintf(path + *path_length, + PATH_MAX - *path_length, + "%s%s", + dir, + File::PathSeparator()); + ASSERT(written == (strlen(dir) + strlen(File::PathSeparator()))); + *path_length += written; + } + char* base = basename(dir_name_copy); + if (strcmp(base, ".") != 0) { + written = snprintf(path + *path_length, + PATH_MAX - *path_length, + "%s%s", + base, + File::PathSeparator()); + ASSERT(written == (strlen(base) + strlen(File::PathSeparator()))); + *path_length += written; + } + free(dir_name_copy); +} + + +static void HandleDir(char* dir_name, + char* path, + int path_length, + Dart_Port dir_handler) { + if (dir_handler != 0 && + strcmp(dir_name, ".") != 0 && + strcmp(dir_name, "..") != 0) { + size_t written = snprintf(path + path_length, + PATH_MAX - path_length, + "%s", + dir_name); + ASSERT(written == strlen(dir_name)); + Dart_Handle name = Dart_NewString(path); + Dart_Post(dir_handler, name); + } +} + + +static void HandleFile(char* file_name, + char* path, + int path_length, + Dart_Port file_handler) { + if (file_handler != 0) { + size_t written = snprintf(path + path_length, + PATH_MAX - path_length, + "%s", + file_name); + ASSERT(written == strlen(file_name)); + Dart_Handle name = Dart_NewString(path); + Dart_Post(file_handler, name); + } +} + + +void Directory::List(const char* dir_name, + intptr_t dir_handle, bool recursive, Dart_Port dir_handler, Dart_Port file_handler, Dart_Port done_handler, Dart_Port dir_error_handler) { + // Compute full path for the directory currently being listed. + char path[PATH_MAX]; + int path_length = 0; + ComputeFullPath(dir_name, path, &path_length); + + // Iterated the directory and post the directories and files to the + // handlers. + // // TODO(ager): Handle recursion and errors caused by recursion. - // TODO(ager): Make this async by using a thread to do this. - DIR* dir_pointer = reinterpret_cast(dir); + DIR* dir_pointer = reinterpret_cast(dir_handle); int success = 0; + bool lstat_error = false; dirent entry; dirent* result; while ((success = readdir_r(dir_pointer, &entry, &result)) == 0 && result != NULL) { switch (entry.d_type) { case DT_DIR: - if (dir_handler != 0 && - strcmp(entry.d_name, ".") != 0 && - strcmp(entry.d_name, "..") != 0) { - Dart_Handle name = Dart_NewString(entry.d_name); - Dart_Post(dir_handler, name); - } + HandleDir(entry.d_name, path, path_length, dir_handler); break; case DT_REG: - if (file_handler != 0) { - Dart_Handle name = Dart_NewString(entry.d_name); - Dart_Post(file_handler, name); + HandleFile(entry.d_name, path, path_length, file_handler); + break; + case DT_UNKNOWN: { + // On some file systems the entry type is not determined by + // readdir_r. For those we use lstat to determine the entry + // type. + struct stat entry_info; + size_t written = snprintf(path + path_length, + PATH_MAX - path_length, + "%s", + entry.d_name); + ASSERT(written == strlen(entry.d_name)); + int lstat_success = lstat(path, &entry_info); + if (lstat_success != 0) { + lstat_error = true; + break; + } + if ((entry_info.st_mode & S_IFMT) == S_IFDIR) { + HandleDir(entry.d_name, path, path_length, dir_handler); + } else if ((entry_info.st_mode & S_IFMT) == S_IFREG) { + HandleFile(entry.d_name, path, path_length, file_handler); } break; - case DT_UNKNOWN: - UNIMPLEMENTED(); - // TODO(ager): Handle this correctly. Use lstat or something? - break; + } default: - // TODO(ager): Handle symbolic links? break; } } - if (success != 0) { - // TODO(ager): Error listing directory. There should probably be - // a general error handler. Maybe collaps the dir_error_handler - // to just be a general error handler. - } else if (done_handler != 0) { - Dart_Handle value = Dart_NewBoolean(true); - Dart_Post(done_handler, value); + if (done_handler != 0) { + if (success != 0 || lstat_error) { + Dart_Handle value = Dart_NewBoolean(false); + Dart_Post(done_handler, value); + } else { + Dart_Handle value = Dart_NewBoolean(true); + Dart_Post(done_handler, value); + } } } diff --git a/runtime/bin/directory_macos.cc b/runtime/bin/directory_macos.cc index 77f6d88780b..f1f23dbd002 100644 --- a/runtime/bin/directory_macos.cc +++ b/runtime/bin/directory_macos.cc @@ -2,11 +2,18 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -#include -#include #include +#include +#include +#include +#include +#include +#include +#include "bin/dartutils.h" #include "bin/directory.h" +#include "bin/file.h" + bool Directory::Open(const char* path, intptr_t* dir) { DIR* dir_pointer = opendir(path); @@ -17,56 +24,151 @@ bool Directory::Open(const char* path, intptr_t* dir) { return true; } + bool Directory::Close(intptr_t dir) { DIR* dir_pointer = reinterpret_cast(dir); int result = closedir(dir_pointer); return result == 0; } -void Directory::List(intptr_t dir, + +static void ComputeFullPath(const char* dir_name, + char* path, + int* path_length) { + size_t written = 0; + + if (!File::IsAbsolutePath(dir_name)) { + ASSERT(getcwd(path, PATH_MAX) != NULL); + *path_length = strlen(path); + written = snprintf(path + *path_length, + PATH_MAX - *path_length, + "%s", + File::PathSeparator()); + ASSERT(written == strlen(File::PathSeparator())); + *path_length += written; + } + + // Use dirname and basename to canonicalize the provided directory + // name. + char* dir_name_copy = strdup(dir_name); + char* dir = dirname(dir_name_copy); + if (strcmp(dir, ".") != 0) { + written = snprintf(path + *path_length, + PATH_MAX - *path_length, + "%s%s", + dir, + File::PathSeparator()); + ASSERT(written == (strlen(dir) + strlen(File::PathSeparator()))); + *path_length += written; + } + char* base = basename(dir_name_copy); + if (strcmp(base, ".") != 0) { + written = snprintf(path + *path_length, + PATH_MAX - *path_length, + "%s%s", + base, + File::PathSeparator()); + ASSERT(written == (strlen(base) + strlen(File::PathSeparator()))); + *path_length += written; + } + free(dir_name_copy); +} + + +static void HandleDir(char* dir_name, + char* path, + int path_length, + Dart_Port dir_handler) { + if (dir_handler != 0 && + strcmp(dir_name, ".") != 0 && + strcmp(dir_name, "..") != 0) { + size_t written = snprintf(path + path_length, + PATH_MAX - path_length, + "%s", + dir_name); + ASSERT(written == strlen(dir_name)); + Dart_Handle name = Dart_NewString(path); + Dart_Post(dir_handler, name); + } +} + + +static void HandleFile(char* file_name, + char* path, + int path_length, + Dart_Port file_handler) { + if (file_handler != 0) { + size_t written = snprintf(path + path_length, + PATH_MAX - path_length, + "%s", + file_name); + ASSERT(written == strlen(file_name)); + Dart_Handle name = Dart_NewString(path); + Dart_Post(file_handler, name); + } +} + + +void Directory::List(const char* dir_name, + intptr_t dir_handle, bool recursive, Dart_Port dir_handler, Dart_Port file_handler, Dart_Port done_handler, Dart_Port dir_error_handler) { + // Compute full path for the directory currently being listed. + char path[PATH_MAX]; + int path_length = 0; + ComputeFullPath(dir_name, path, &path_length); + + // Iterated the directory and post the directories and files to the + // handlers. + // // TODO(ager): Handle recursion and errors caused by recursion. - // TODO(ager): Make this async by using a thread to do this. - DIR* dir_pointer = reinterpret_cast(dir); + DIR* dir_pointer = reinterpret_cast(dir_handle); int success = 0; + bool lstat_error = false; dirent entry; dirent* result; while ((success = readdir_r(dir_pointer, &entry, &result)) == 0 && result != NULL) { switch (entry.d_type) { case DT_DIR: - if (dir_handler != 0 && - strcmp(entry.d_name, ".") != 0 && - strcmp(entry.d_name, "..") != 0) { - Dart_Handle name = Dart_NewString(entry.d_name); - Dart_Post(dir_handler, name); - } + HandleDir(entry.d_name, path, path_length, dir_handler); break; case DT_REG: - if (file_handler != 0) { - Dart_Handle name = Dart_NewString(entry.d_name); - Dart_Post(file_handler, name); + HandleFile(entry.d_name, path, path_length, file_handler); + break; + case DT_UNKNOWN: { + struct stat entry_info; + size_t written = snprintf(path + path_length, + PATH_MAX - path_length, + "%s", + entry.d_name); + ASSERT(written == strlen(entry.d_name)); + int lstat_success = lstat(path, &entry_info); + if (lstat_success != 0) { + lstat_error = true; + break; + } + if ((entry_info.st_mode & S_IFMT) == S_IFDIR) { + HandleDir(entry.d_name, path, path_length, dir_handler); + } else if ((entry_info.st_mode & S_IFMT) == S_IFREG) { + HandleFile(entry.d_name, path, path_length, file_handler); } break; - case DT_UNKNOWN: - UNIMPLEMENTED(); - // TODO(ager): Handle this correctly. Use lstat or something? - break; + } default: - // TODO(ager): Handle symbolic links? break; } } - if (success != 0) { - // TODO(ager): Error listing directory. There should probably be - // a general error handler. Maybe collaps the dir_error_handler - // to just be a general error handler. - } else if (done_handler != 0) { - Dart_Handle value = Dart_NewBoolean(true); - Dart_Post(done_handler, value); + if (done_handler != 0) { + if (success != 0 || lstat_error) { + Dart_Handle value = Dart_NewBoolean(false); + Dart_Post(done_handler, value); + } else { + Dart_Handle value = Dart_NewBoolean(true); + Dart_Post(done_handler, value); + } } }