#define WIN32_LEAN_AND_MEAN #include #include #include #include struct fx_directory { struct fx_dsref base; HANDLE handle; struct fx_path *abs_path; }; struct z__fx_directory_iterator { fx_queue state_stack; }; struct iteration_state { const fx_path *search_path; fx_queue_entry entry; HANDLE search; WIN32_FIND_DATAA data; bool child_search_complete; }; static void directory_release(struct fx_dsref *obj); static struct fx_dsref_type directory_type = { .t_name = "corelib::directory", .t_flags = FX_DSREF_FUNDAMENTAL, .t_id = FX_DSREF_TYPE_PATH, .t_instance_size = sizeof(struct fx_directory), .t_release = directory_release, }; static enum fx_status status_from_win32_error(int error, enum fx_status default_value) { switch (error) { case ERROR_FILE_NOT_FOUND: return FX_ERR_NO_ENTRY; default: return default_value; } } enum fx_status fx_directory_open( struct fx_directory *root, const struct fx_path *path, struct fx_directory **out) { enum fx_status status = FX_SUCCESS; const fx_path *parts[] = { root ? root->abs_path : NULL, path, }; fx_path *new_path = fx_path_join(parts, sizeof parts / sizeof *parts); if (!new_path) { return FX_ERR_NO_MEMORY; } HANDLE dir_handle = CreateFileA( fx_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(), FX_ERR_IO_FAILURE); fx_path_release(new_path); return status; } BY_HANDLE_FILE_INFORMATION dir_info; if (!GetFileInformationByHandle(dir_handle, &dir_info)) { status = status_from_win32_error(GetLastError(), FX_ERR_IO_FAILURE); CloseHandle(dir_handle); fx_path_release(new_path); return status; } if (!(dir_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { CloseHandle(dir_handle); fx_path_release(new_path); return FX_ERR_NOT_DIRECTORY; } struct fx_directory *dir = (struct fx_directory *)fx_dsref_type_instantiate(&directory_type); if (!path) { CloseHandle(dir_handle); fx_path_release(new_path); return FX_ERR_NO_MEMORY; } dir->abs_path = new_path; dir->handle = dir_handle; *out = dir; return FX_SUCCESS; } static struct iteration_state *get_iteration_state( struct z__fx_directory_iterator *it) { fx_queue_entry *last = fx_queue_last(&it->state_stack); if (!last) { return NULL; } return fx_unbox(struct iteration_state, last, entry); } static struct iteration_state *push_iteration_state( struct z__fx_directory_iterator *it) { struct iteration_state *state = malloc(sizeof *state); if (!state) { return NULL; } memset(state, 0x0, sizeof *state); fx_queue_push_back(&it->state_stack, &state->entry); return state; } static void pop_iteration_state(struct z__fx_directory_iterator *it) { struct iteration_state *state = get_iteration_state(it); if (!state) { return; } fx_queue_pop_back(&it->state_stack); FindClose(state->search); free(state); } static void cleanup_iterator(struct fx_directory_iterator *it) { while (!fx_queue_empty(&it->_z->state_stack)) { pop_iteration_state(it->_z); } free(it->_z); it->_z = NULL; } static void update_iterator_data(struct fx_directory_iterator *it) { if (it->filepath) { fx_path_release(FX_PATH(it->filepath)); it->filepath = NULL; } struct iteration_state *state = get_iteration_state(it->_z); if (state) { it->filename = state->data.cFileName; struct fx_path *filename = fx_path_create_from_cstr(it->filename); const struct fx_path *parts[] = { state->search_path, filename, }; it->filepath = fx_path_join(parts, sizeof parts / sizeof parts[0]); } } static bool move_into_directory(struct fx_directory_iterator *it, const char *dir_name) { struct iteration_state *state = get_iteration_state(it->_z); struct fx_path *dir_name_p = fx_path_create_from_cstr(dir_name); struct fx_path *wildcard = fx_path_create_from_cstr("*"); const struct fx_path *parts[] = { state ? state->search_path : NULL, dir_name_p, }; struct fx_path *dir_path = fx_path_join(parts, sizeof parts / sizeof *parts); parts[0] = dir_path; parts[1] = wildcard; struct fx_path *search_path = fx_path_join(parts, sizeof parts / sizeof *parts); state = push_iteration_state(it->_z); state->search_path = dir_path; state->search = FindFirstFileA(fx_path_ptr(search_path), &state->data); fx_path_release(search_path); fx_path_release(wildcard); fx_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 fx_directory_iterator *it, const struct fx_path *root_dir) { bool has_results = move_into_directory(it, fx_path_ptr(root_dir)); if (!has_results) { return false; } if (it->flags & FX_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 fx_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 & FX_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 & FX_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 & FX_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 fx_directory_iterator_begin( struct fx_directory *directory, struct fx_directory_iterator *it, enum fx_directory_iterator_flags flags) { if (fx_directory_iterator_is_valid(it)) { cleanup_iterator(it); } it->flags = flags; struct z__fx_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; } bool fx_directory_iterator_next(struct fx_directory_iterator *it) { if (!it->_z) { return false; } bool result = move_to_next_item(it); update_iterator_data(it); return result; } enum fx_status fx_directory_iterator_erase(struct fx_directory_iterator *it) { return FX_SUCCESS; } bool fx_directory_iterator_is_valid(const struct fx_directory_iterator *it) { if (!it->_z) { return false; } if (!get_iteration_state(it->_z)) { return false; } return true; } static void directory_release(struct fx_dsref *obj) { }