#include #include #include #include #include #include #include struct b_directory { struct b_object base; int fd; struct b_path *abs_path; }; struct z__b_directory_iterator { FTS *fts; FTSENT *ent; }; 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_errno(int error, enum b_status default_value) { switch (error) { case ENOENT: 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; } int root_fd = root ? root->fd : AT_FDCWD; int fd = openat(root_fd, b_path_ptr(path), O_DIRECTORY); if (fd == -1) { status = status_from_errno(errno, B_ERR_IO_FAILURE); b_path_release(new_path); return status; } struct b_directory *dir = (struct b_directory *)b_object_type_instantiate(&directory_type); dir->abs_path = new_path; dir->fd = fd; *out = dir; return B_SUCCESS; } static int ftsent_compare(const FTSENT **one, const FTSENT **two) { return (strcmp((*one)->fts_name, (*two)->fts_name)); } int b_directory_iterator_begin( struct b_directory *directory, struct b_directory_iterator *it, enum b_directory_iterator_flags flags) { it->flags = flags; it->root = directory; struct z__b_directory_iterator *it_data = malloc(sizeof *it_data); memset(it_data, 0x0, sizeof *it_data); it->_z = it_data; int fts_flags = FTS_COMFOLLOW | FTS_NOCHDIR; const char *path_list[] = { b_path_ptr(directory->abs_path), NULL, }; it_data->fts = fts_open((char *const *)path_list, fts_flags, ftsent_compare); if (!it_data) { return -1; } bool done = false; while (!done) { it_data->ent = fts_read(it_data->fts); if (!it_data->ent) { return -1; } if (it_data->ent->fts_level == 0) { continue; } switch (it_data->ent->fts_info) { case FTS_DOT: continue; case FTS_F: done = true; break; case FTS_D: if (it->flags & B_DIRECTORY_ITERATE_PARENT_LAST) { continue; } done = true; break; case FTS_DP: if (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST) { continue; } done = true; break; default: done = true; break; } } it->filename = it_data->ent->fts_name; it->filepath = it_data->ent->fts_path + b_path_length(it->root->abs_path) + 1; return 0; } bool b_directory_iterator_next(struct b_directory_iterator *it) { struct z__b_directory_iterator *it_data = it->_z; if (!it_data || !it_data->fts) { return false; } bool done = false; while (!done) { it_data->ent = fts_read(it_data->fts); if (!it_data->ent) { return false; } if (it_data->ent->fts_level == 0) { continue; } switch (it_data->ent->fts_info) { case FTS_DOT: continue; case FTS_F: done = true; break; case FTS_D: if (it->flags & B_DIRECTORY_ITERATE_PARENT_LAST) { continue; } done = true; break; case FTS_DP: if (it->flags & B_DIRECTORY_ITERATE_PARENT_FIRST) { continue; } done = true; break; default: done = true; break; } } it->filename = it_data->ent->fts_name; it->filepath = it_data->ent->fts_path + b_path_length(it->root->abs_path) + 1; return true; } enum b_status b_directory_iterator_erase(struct b_directory_iterator *it) { return B_SUCCESS; } bool b_directory_iterator_is_valid(const struct b_directory_iterator *it) { if (!it->_z) { return false; } if (!it->_z->ent) { return false; } return true; } static void directory_release(struct b_object *obj) { }