io: implement pre- and post-order directory traversal for windows
This commit is contained in:
32
io-test/tree.c
Normal file
32
io-test/tree.c
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#include <blue/io/directory.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define NRAND_NUMBERS 12
|
||||||
|
#define NRAND_BYTES 128
|
||||||
|
#define NRAND_DOUBLES 8
|
||||||
|
|
||||||
|
int main(int argc, const char **argv)
|
||||||
|
{
|
||||||
|
if (argc < 2) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_directory *dir = NULL;
|
||||||
|
b_path *path = b_path_create_from_cstr(argv[1]);
|
||||||
|
b_status status = b_directory_open(B_DIRECTORY_ROOT, path, &dir);
|
||||||
|
|
||||||
|
if (!B_OK(status)) {
|
||||||
|
printf("cannot open %s\n", b_path_ptr(path));
|
||||||
|
printf("%s\n", b_status_to_string(status));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_directory_iterator it = {0};
|
||||||
|
b_directory_iterator_begin(dir, &it, B_DIRECTORY_ITERATE_PARENT_FIRST);
|
||||||
|
while (b_directory_iterator_is_valid(&it)) {
|
||||||
|
printf("%s\n", it.filename);
|
||||||
|
b_directory_iterator_next(&it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
#include <blue/core/iterator.h>
|
#include <blue/core/iterator.h>
|
||||||
#include <blue/core/misc.h>
|
#include <blue/core/misc.h>
|
||||||
#include <blue/core/status.h>
|
#include <blue/core/status.h>
|
||||||
|
#include <blue/io/path.h>
|
||||||
|
|
||||||
#define B_DIRECTORY_ROOT ((b_directory *)NULL)
|
#define B_DIRECTORY_ROOT ((b_directory *)NULL)
|
||||||
|
|
||||||
@@ -11,8 +12,14 @@ typedef struct b_directory b_directory;
|
|||||||
|
|
||||||
struct z__b_directory_iterator;
|
struct z__b_directory_iterator;
|
||||||
|
|
||||||
|
typedef enum b_directory_iterator_flags {
|
||||||
|
B_DIRECTORY_ITERATE_PARENT_FIRST = 0x01u,
|
||||||
|
B_DIRECTORY_ITERATE_PARENT_LAST = 0x02u,
|
||||||
|
} b_directory_iterator_flags;
|
||||||
|
|
||||||
typedef struct b_directory_iterator {
|
typedef struct b_directory_iterator {
|
||||||
b_iterator _base;
|
b_iterator _base;
|
||||||
|
b_directory_iterator_flags flags;
|
||||||
char *filename;
|
char *filename;
|
||||||
char *filepath;
|
char *filepath;
|
||||||
struct z__b_directory_iterator *_z;
|
struct z__b_directory_iterator *_z;
|
||||||
@@ -23,10 +30,10 @@ typedef struct b_directory_iterator {
|
|||||||
b_directory_iterator_is_valid(it); b_directory_iterator_next(it))
|
b_directory_iterator_is_valid(it); b_directory_iterator_next(it))
|
||||||
|
|
||||||
BLUE_API b_status b_directory_open(
|
BLUE_API b_status b_directory_open(
|
||||||
b_directory *root, const char *path, b_directory **out);
|
b_directory *root, const b_path *path, b_directory **out);
|
||||||
|
|
||||||
BLUE_API int b_directory_iterator_begin(
|
BLUE_API int b_directory_iterator_begin(
|
||||||
b_directory *directory, b_directory_iterator *it);
|
b_directory *directory, b_directory_iterator *it, b_directory_iterator_flags flags);
|
||||||
BLUE_API bool b_directory_iterator_next(b_directory_iterator *it);
|
BLUE_API bool b_directory_iterator_next(b_directory_iterator *it);
|
||||||
BLUE_API b_status b_directory_iterator_erase(b_directory_iterator *it);
|
BLUE_API b_status b_directory_iterator_erase(b_directory_iterator *it);
|
||||||
BLUE_API bool b_directory_iterator_is_valid(const b_directory_iterator *it);
|
BLUE_API bool b_directory_iterator_is_valid(const b_directory_iterator *it);
|
||||||
|
|||||||
@@ -24,6 +24,8 @@ BLUE_API bool b_path_exists(const b_path *path);
|
|||||||
BLUE_API bool b_path_is_file(const b_path *path);
|
BLUE_API bool b_path_is_file(const b_path *path);
|
||||||
BLUE_API bool b_path_is_directory(const b_path *path);
|
BLUE_API bool b_path_is_directory(const b_path *path);
|
||||||
|
|
||||||
|
BLUE_API const char *b_path_ptr(const b_path *path);
|
||||||
|
|
||||||
BLUE_API b_path *b_path_retain(b_path *path);
|
BLUE_API b_path *b_path_retain(b_path *path);
|
||||||
BLUE_API void b_path_release(b_path *path);
|
BLUE_API void b_path_release(b_path *path);
|
||||||
|
|
||||||
|
|||||||
@@ -1,30 +1,326 @@
|
|||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
|
||||||
|
#include <Windows.h>
|
||||||
#include <blue/io/directory.h>
|
#include <blue/io/directory.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
struct b_directory {
|
struct b_directory {
|
||||||
char *abs_path;
|
struct b_object base;
|
||||||
|
HANDLE handle;
|
||||||
|
struct b_path *abs_path;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct z__b_directory_iterator {
|
struct z__b_directory_iterator {
|
||||||
int x;
|
b_queue state_stack;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum b_status b_directory_open(
|
struct iteration_state {
|
||||||
struct b_directory *root, const char *path, struct b_directory **out)
|
const b_path *search_path;
|
||||||
|
b_queue_entry entry;
|
||||||
|
HANDLE search;
|
||||||
|
WIN32_FIND_DATAA data;
|
||||||
|
bool child_search_complete;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void directory_release(struct b_object *obj);
|
||||||
|
|
||||||
|
static struct b_object_type directory_type = {
|
||||||
|
.t_name = "corelib::directory",
|
||||||
|
.t_flags = B_OBJECT_FUNDAMENTAL,
|
||||||
|
.t_id = B_OBJECT_TYPE_PATH,
|
||||||
|
.t_instance_size = sizeof(struct b_directory),
|
||||||
|
.t_release = directory_release,
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum b_status status_from_win32_error(int error, enum b_status default_value)
|
||||||
{
|
{
|
||||||
|
switch (error) {
|
||||||
|
case ERROR_FILE_NOT_FOUND:
|
||||||
|
return B_ERR_NO_ENTRY;
|
||||||
|
default:
|
||||||
|
return default_value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum b_status b_directory_open(
|
||||||
|
struct b_directory *root, const struct b_path *path, struct b_directory **out)
|
||||||
|
{
|
||||||
|
enum b_status status = B_SUCCESS;
|
||||||
|
|
||||||
|
const b_path *parts[] = {
|
||||||
|
root ? root->abs_path : NULL,
|
||||||
|
path,
|
||||||
|
};
|
||||||
|
|
||||||
|
b_path *new_path = b_path_join(parts, sizeof parts / sizeof *parts);
|
||||||
|
if (!new_path) {
|
||||||
|
return B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
HANDLE dir_handle = CreateFileA(
|
||||||
|
b_path_ptr(new_path), GENERIC_READ,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
||||||
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, INVALID_HANDLE_VALUE);
|
||||||
|
|
||||||
|
if (dir_handle == INVALID_HANDLE_VALUE) {
|
||||||
|
status = status_from_win32_error(GetLastError(), B_ERR_IO_FAILURE);
|
||||||
|
|
||||||
|
b_path_release(new_path);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
BY_HANDLE_FILE_INFORMATION dir_info;
|
||||||
|
if (!GetFileInformationByHandle(dir_handle, &dir_info)) {
|
||||||
|
status = status_from_win32_error(GetLastError(), B_ERR_IO_FAILURE);
|
||||||
|
|
||||||
|
CloseHandle(dir_handle);
|
||||||
|
b_path_release(new_path);
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!(dir_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
CloseHandle(dir_handle);
|
||||||
|
b_path_release(new_path);
|
||||||
|
return B_ERR_NOT_DIRECTORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct b_directory *dir
|
||||||
|
= (struct b_directory *)b_object_type_instantiate(&directory_type);
|
||||||
|
if (!path) {
|
||||||
|
CloseHandle(dir_handle);
|
||||||
|
b_path_release(new_path);
|
||||||
|
return B_ERR_NO_MEMORY;
|
||||||
|
}
|
||||||
|
|
||||||
|
dir->abs_path = new_path;
|
||||||
|
dir->handle = dir_handle;
|
||||||
|
|
||||||
|
*out = dir;
|
||||||
|
|
||||||
return B_SUCCESS;
|
return B_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
int b_directory_iterator_begin(
|
static struct iteration_state *get_iteration_state(struct z__b_directory_iterator *it)
|
||||||
struct b_directory *directory, struct b_directory_iterator *it)
|
|
||||||
{
|
{
|
||||||
|
b_queue_entry *last = b_queue_last(&it->state_stack);
|
||||||
|
|
||||||
|
if (!last) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return b_unbox(struct iteration_state, last, entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct iteration_state *push_iteration_state(struct z__b_directory_iterator *it)
|
||||||
|
{
|
||||||
|
struct iteration_state *state = malloc(sizeof *state);
|
||||||
|
if (!state) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(state, 0x0, sizeof *state);
|
||||||
|
|
||||||
|
b_queue_push_back(&it->state_stack, &state->entry);
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pop_iteration_state(struct z__b_directory_iterator *it)
|
||||||
|
{
|
||||||
|
struct iteration_state *state = get_iteration_state(it);
|
||||||
|
|
||||||
|
if (!state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
b_queue_pop_back(&it->state_stack);
|
||||||
|
FindClose(state->search);
|
||||||
|
free(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void cleanup_iterator(struct b_directory_iterator *it)
|
||||||
|
{
|
||||||
|
while (!b_queue_empty(&it->_z->state_stack)) {
|
||||||
|
pop_iteration_state(it->_z);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(it->_z);
|
||||||
|
it->_z = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void update_iterator_data(struct b_directory_iterator *it)
|
||||||
|
{
|
||||||
|
struct iteration_state *state = get_iteration_state(it->_z);
|
||||||
|
it->filename = state->data.cFileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool move_into_directory(struct b_directory_iterator *it, const char *dir_name)
|
||||||
|
{
|
||||||
|
struct iteration_state *state = get_iteration_state(it->_z);
|
||||||
|
|
||||||
|
struct b_path *dir_name_p = b_path_create_from_cstr(dir_name);
|
||||||
|
struct b_path *wildcard = b_path_create_from_cstr("*");
|
||||||
|
|
||||||
|
const struct b_path *parts[] = {
|
||||||
|
state ? state->search_path : NULL,
|
||||||
|
dir_name_p,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct b_path *dir_path
|
||||||
|
= b_path_join(parts, sizeof parts / sizeof *parts);
|
||||||
|
|
||||||
|
parts[0] = dir_path;
|
||||||
|
parts[1] = wildcard;
|
||||||
|
|
||||||
|
struct b_path *search_path
|
||||||
|
= b_path_join(parts, sizeof parts / sizeof *parts);
|
||||||
|
|
||||||
|
state = push_iteration_state(it->_z);
|
||||||
|
state->search_path = dir_path;
|
||||||
|
state->search = FindFirstFileA(b_path_ptr(search_path), &state->data);
|
||||||
|
|
||||||
|
b_path_release(search_path);
|
||||||
|
b_path_release(wildcard);
|
||||||
|
b_path_release(dir_name_p);
|
||||||
|
|
||||||
|
if (state->search == INVALID_HANDLE_VALUE) {
|
||||||
|
pop_iteration_state(it->_z);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!strcmp(state->data.cFileName, ".")
|
||||||
|
|| !strcmp(state->data.cFileName, "..")) {
|
||||||
|
BOOL ok = FindNextFileA(state->search, &state->data);
|
||||||
|
|
||||||
|
if (!ok) {
|
||||||
|
pop_iteration_state(it->_z);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool move_to_first_item(struct b_directory_iterator *it, const struct b_path *root_dir)
|
||||||
|
{
|
||||||
|
bool has_results = move_into_directory(it, b_path_ptr(root_dir));
|
||||||
|
|
||||||
|
if (!has_results) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
struct iteration_state *state = get_iteration_state(it->_z);
|
||||||
|
|
||||||
|
if (!(state->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
has_results = move_into_directory(it, state->data.cFileName);
|
||||||
|
|
||||||
|
if (!has_results) {
|
||||||
|
pop_iteration_state(it->_z);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool move_to_next_item(struct b_directory_iterator *it)
|
||||||
|
{
|
||||||
|
while (true) {
|
||||||
|
struct iteration_state *state = get_iteration_state(it->_z);
|
||||||
|
|
||||||
|
if (!state->child_search_complete && (state->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST)) {
|
||||||
|
if (move_into_directory(it, state->data.cFileName)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool has_items = FindNextFileA(state->search, &state->data);
|
||||||
|
|
||||||
|
if (!has_items) {
|
||||||
|
pop_iteration_state(it->_z);
|
||||||
|
state = get_iteration_state(it->_z);
|
||||||
|
|
||||||
|
if (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST && state != NULL) {
|
||||||
|
state->child_search_complete = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return state != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
state->child_search_complete = false;
|
||||||
|
|
||||||
|
if (!(state->data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
state = get_iteration_state(it->_z);
|
||||||
|
has_items = move_into_directory(it, state->data.cFileName);
|
||||||
|
|
||||||
|
if (!has_items) {
|
||||||
|
pop_iteration_state(it->_z);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
state = get_iteration_state(it->_z);
|
||||||
|
|
||||||
|
if (!(state->data.dwFileAttributes
|
||||||
|
& FILE_ATTRIBUTE_DIRECTORY)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int b_directory_iterator_begin(
|
||||||
|
struct b_directory *directory, struct b_directory_iterator *it, enum b_directory_iterator_flags flags)
|
||||||
|
{
|
||||||
|
if (b_directory_iterator_is_valid(it)) {
|
||||||
|
cleanup_iterator(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
it->flags = flags;
|
||||||
|
|
||||||
|
struct z__b_directory_iterator *it_data = malloc(sizeof *it_data);
|
||||||
|
memset(it_data, 0x0, sizeof *it_data);
|
||||||
|
|
||||||
|
it->_z = it_data;
|
||||||
|
move_to_first_item(it, directory->abs_path);
|
||||||
|
|
||||||
|
update_iterator_data(it);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool b_directory_iterator_next(struct b_directory_iterator *it)
|
bool b_directory_iterator_next(struct b_directory_iterator *it)
|
||||||
{
|
{
|
||||||
|
if (!it->_z) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool result = move_to_next_item(it);
|
||||||
|
update_iterator_data(it);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
enum b_status b_directory_iterator_erase(struct b_directory_iterator *it)
|
enum b_status b_directory_iterator_erase(struct b_directory_iterator *it)
|
||||||
{
|
{
|
||||||
return B_SUCCESS;
|
return B_SUCCESS;
|
||||||
@@ -32,5 +328,18 @@ enum b_status b_directory_iterator_erase(struct b_directory_iterator *it)
|
|||||||
|
|
||||||
bool b_directory_iterator_is_valid(const struct b_directory_iterator *it)
|
bool b_directory_iterator_is_valid(const struct b_directory_iterator *it)
|
||||||
{
|
{
|
||||||
|
if (!it->_z) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!get_iteration_state(it->_z)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void directory_release(struct b_object *obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -156,8 +156,10 @@ struct b_path *b_path_join(
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < nr_paths; i++) {
|
for (size_t i = 0; i < nr_paths; i++) {
|
||||||
|
if (paths[i]) {
|
||||||
append_path(result, paths[i]);
|
append_path(result, paths[i]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -213,6 +215,11 @@ bool b_path_is_directory(const struct b_path *path)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *b_path_ptr(const struct b_path *path)
|
||||||
|
{
|
||||||
|
return b_string_ptr(path->pathstr);
|
||||||
|
}
|
||||||
|
|
||||||
struct b_path *b_path_retain(struct b_path *path)
|
struct b_path *b_path_retain(struct b_path *path)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user