From 69789c042460e012ec3054cc2e6ceeff25e394b9 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Tue, 13 Apr 2021 12:29:32 -0400 Subject: change fs_list_directory to include entry types directly --- filesystem-posix.c | 70 +++++++++++++++++++++++++++++++++++------------------- filesystem-win.c | 26 +++++++++++++------- filesystem.h | 13 ++++++---- main.c | 2 +- ui.c | 13 +++++----- 5 files changed, 79 insertions(+), 45 deletions(-) diff --git a/filesystem-posix.c b/filesystem-posix.c index 8af0bf2..6a52732 100644 --- a/filesystem-posix.c +++ b/filesystem-posix.c @@ -4,16 +4,21 @@ #include #include #include +#include + +static FsType statbuf_path_type(const struct stat *statbuf) { + if (S_ISREG(statbuf->st_mode)) + return FS_FILE; + if (S_ISDIR(statbuf->st_mode)) + return FS_DIRECTORY; + return FS_OTHER; +} FsType fs_path_type(char const *path) { struct stat statbuf = {0}; if (stat(path, &statbuf) != 0) return FS_NON_EXISTENT; - if (S_ISREG(statbuf.st_mode)) - return FS_FILE; - if (S_ISDIR(statbuf.st_mode)) - return FS_DIRECTORY; - return FS_OTHER; + return statbuf_path_type(&statbuf); } FsPermission fs_path_permission(char const *path) { @@ -28,32 +33,49 @@ bool fs_file_exists(char const *path) { return fs_path_type(path) == FS_FILE; } -char **fs_list_directory(char const *dirname) { - char **ret = NULL; +FsDirectoryEntry **fs_list_directory(char const *dirname) { + FsDirectoryEntry **entries = NULL; DIR *dir = opendir(dirname); if (dir) { struct dirent *ent; - char **filenames = NULL; size_t nentries = 0; - size_t filename_idx = 0; - - while (readdir(dir)) ++nentries; - rewinddir(dir); - filenames = (char **)calloc(nentries+1, sizeof *filenames); - - while ((ent = readdir(dir))) { - char const *filename = ent->d_name; - size_t len = strlen(filename); - char *filename_copy = (char *)malloc(len+1); - if (!filename_copy) break; - strcpy(filename_copy, filename); - if (filename_idx < nentries) // this could actually fail if someone creates files between calculating nentries and here. - filenames[filename_idx++] = filename_copy; + int fd = dirfd(dir); + if (fd != -1) { + while (readdir(dir)) ++nentries; + rewinddir(dir); + entries = (FsDirectoryEntry **)calloc(nentries+1, sizeof *entries); + if (entries) { + size_t idx = 0; + while ((ent = readdir(dir))) { + char const *filename = ent->d_name; + size_t len = strlen(filename); + FsDirectoryEntry *entry = (FsDirectoryEntry *)calloc(1, sizeof *entry + len + 1); + if (!entry) break; + memcpy(entry->name, filename, len); + switch (ent->d_type) { + case DT_REG: + entry->type = FS_FILE; + break; + case DT_DIR: + entry->type = FS_DIRECTORY; + break; + case DT_LNK: // we need to dereference the link + case DT_UNKNOWN: { // information not available directly from dirent, we need to get it ourselves + struct stat statbuf = {0}; + fstatat(fd, filename, &statbuf, 0); + entry->type = statbuf_path_type(&statbuf); + } break; + default: + entry->type = FS_OTHER; + } + if (idx < nentries) // this could actually fail if someone creates files between calculating nentries and here. + entries[idx++] = entry; + } + } } - ret = filenames; closedir(dir); } - return ret; + return entries; } int fs_mkdir(char const *path) { diff --git a/filesystem-win.c b/filesystem-win.c index c6c64dd..d0aef37 100644 --- a/filesystem-win.c +++ b/filesystem-win.c @@ -25,9 +25,9 @@ bool fs_file_exists(char const *path) { return fs_path_type(path) == FS_FILE; } -char **fs_list_directory(char const *dirname) { +FsDirectoryEntry **fs_list_directory(char const *dirname) { char file_pattern[256] = {0}; - char **ret = NULL; + FsDirectoryEntry **files = NULL; WIN32_FIND_DATA find_data; HANDLE fhandle; assert(*dirname); @@ -43,25 +43,33 @@ char **fs_list_directory(char const *dirname) { } FindClose(fhandle); // now, fill out files array - files = malloc((nfiles + 1) * sizeof *files); + files = calloc(nfiles + 1, sizeof *files); if (files) { fhandle = FindFirstFileA(file_pattern, &find_data); if (fhandle != INVALID_HANDLE_VALUE) { do { if (idx < nfiles) { - char *dup = _strdup(find_data.cFileName); - if (dup) { - files[idx++] = dup; + const char *filename = find_data.cFileName; + size_t len = strlen(filename); + FsDirectoryEntry *entry = calloc(1, sizeof *entry + len + 1); + if (entry) { + DWORD attrs = find_data.dwFileAttributes; + if (attrs & FILE_ATTRIBUTE_NORMAL) + entry->type = FS_FILE; + else if (attrs & FILE_ATTRIBUTE_DIRECTORY) + entry->type = FS_DIRECTORY; + else + entry->type = FS_OTHER; + memcpy(entry->name, filename, len); + files[idx++] = entry; } else break; // stop now } } while (FindNextFile(fhandle, &find_data)); - files[idx] = NULL; FindClose(fhandle); - ret = files; } } } - return ret; + return files; } int fs_mkdir(char const *path) { diff --git a/filesystem.h b/filesystem.h index 6325bd7..be57323 100644 --- a/filesystem.h +++ b/filesystem.h @@ -14,15 +14,20 @@ enum { }; typedef u8 FsPermission; +typedef struct { + FsType type; + char name[]; +} FsDirectoryEntry; + // returns what kind of thing this is. FsType fs_path_type(char const *path); FsPermission fs_path_permission(char const *path); // Does this file exist? Returns false for directories. bool fs_file_exists(char const *path); -// Returns a NULL-terminated array of the files/directories in this directory, or NULL if the directory does not exist. -// When you're done with the file names, call free on each one, then on the array. -// NOTE: The files aren't returned in any particular order! -char **fs_list_directory(char const *dirname); +// Returns a NULL-terminated array of the files/directories in this directory, or NULL if the directory does not exist/out of memory. +// When you're done with the entries, call free on each one, then on the array. +// NOTE: The files/directories aren't returned in any particular order! +FsDirectoryEntry **fs_list_directory(char const *dirname); // Create the directory specified by `path` // Returns: // 1 if the directory was created successfully diff --git a/main.c b/main.c index 0b9aaef..1f8a13b 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,6 @@ // @TODO: // - terminate process not working on windows? -// - auto-regenerate tags (if no tag found/line number tag found) +// - auto-regenerate tags (if tag not found) // - comment/uncomment selection #include "base.h" no_warn_start diff --git a/ui.c b/ui.c index a01b99f..dc413c3 100644 --- a/ui.c +++ b/ui.c @@ -152,7 +152,7 @@ static void selector_render(Ted *ted, Selector *s) { // clear the entries in the file selector static void file_selector_clear_entries(FileSelector *fs) { for (u32 i = 0; i < fs->n_entries; ++i) { - free(fs->entries[i].name); + free(fs->entries[i].name - offsetof(FsDirectoryEntry, name)); // yes this is kinda hacky; oh well free(fs->entries[i].path); } free(fs->entries); @@ -400,7 +400,7 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { // free previous entries file_selector_clear_entries(fs); // get new entries - char **files; + FsDirectoryEntry **files; // if the directory we're in gets deleted, go back a directory. for (u32 i = 0; i < 100; ++i) { files = fs_list_directory(cwd); @@ -423,9 +423,9 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { bool increment = true; for (u32 i = 0; i < nfiles; i += increment, increment = true) { // remove if the file name does not contain the search term, - bool remove = search_term && *search_term && !stristr(files[i], search_term); + bool remove = search_term && *search_term && !stristr(files[i]->name, search_term); // or if this is just the current directory - remove |= streq(files[i], "."); + remove |= streq(files[i]->name, "."); if (remove) { // remove this one free(files[i]); @@ -444,8 +444,9 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { if (fs->sel.cursor >= fs->n_entries) fs->sel.cursor = nfiles - 1; fs->entries = entries; for (u32 i = 0; i < nfiles; ++i) { - char *name = files[i]; + char *name = files[i]->name; entries[i].name = name; + entries[i].type = files[i]->type; // add cwd to start of file name size_t path_size = strlen(name) + strlen(cwd) + 3; char *path = ted_calloc(ted, 1, path_size); @@ -454,10 +455,8 @@ static char *file_selector_update(Ted *ted, FileSelector *fs) { cwd[strlen(cwd) - 1] == PATH_SEPARATOR ? "" : PATH_SEPARATOR_STR, name); entries[i].path = path; - entries[i].type = fs_path_type(path); } else { entries[i].path = NULL; // what can we do? - entries[i].type = FS_NON_EXISTENT; } } } -- cgit v1.2.3