Finish non-recursive synchronous directory listing on Linux and Mac.

Next steps: Testing, Windows support and using isolates to make
listing async.

We should probably change the API to not tie a Directory object
to a given OS directory structure. When spawning an isolate to
perform a listing operation, that will open the directory,
list its contents and close the directory. That way we can
do multiple listing operations with the same Directory object
without having interference.

R=sgjesse@google.com,iposva@google.com
BUG=
TEST=

Review URL: https://chromereviews.googleplex.com/3511020

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart@43 260f80e4-7a28-3924-810f-c04153c831b5
This commit is contained in:
ager@google.com 2011-10-05 14:33:49 +00:00
parent a717c9b7c1
commit 023fa46e48
7 changed files with 282 additions and 68 deletions

View file

@ -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) \

View file

@ -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,

View file

@ -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

View file

@ -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,

View file

@ -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;

View file

@ -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 <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <libgen.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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*>(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* dir_pointer = reinterpret_cast<DIR*>(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);
}
}
}

View file

@ -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 <string.h>
#include <sys/types.h>
#include <dirent.h>
#include <libgen.h>
#include <string.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#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*>(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* dir_pointer = reinterpret_cast<DIR*>(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);
}
}
}