diff --git a/include/cli.h b/include/cli.h index 8ef002f..15ca29f 100644 --- a/include/cli.h +++ b/include/cli.h @@ -4,7 +4,7 @@ #include #include -#define LASER_VERSION "1.7.3" +#define LASER_VERSION "1.7.4" typedef struct laser_opts { diff --git a/include/laser.h b/include/laser.h index 681d89e..7f3fb84 100644 --- a/include/laser.h +++ b/include/laser.h @@ -18,12 +18,15 @@ struct laser_dirent { struct dirent *d; + + // NOTE THIS WILL NOT ALWAYS BE SET!! struct stat s; + uint8_t stat_loaded : 1; char git_status; }; void laser_start(laser_opts opts); -void laser_process_single_file(laser_opts opts); +void laser_process_single_file(laser_opts opts, struct stat st); #endif diff --git a/include/lua_filters.h b/include/lua_filters.h index 7d39fd1..666e210 100644 --- a/include/lua_filters.h +++ b/include/lua_filters.h @@ -4,6 +4,7 @@ #include "init_lua.h" #include "laser.h" -int lua_filters_apply(laser_opts opts, struct laser_dirent *entry); +int lua_filters_apply(laser_opts opts, struct laser_dirent *entry, + const char *full_path); #endif diff --git a/src/git/lgit.c b/src/git/lgit.c index cc0297c..30b0c39 100644 --- a/src/git/lgit.c +++ b/src/git/lgit.c @@ -53,7 +53,7 @@ void lgit_getGitStatus(laser_opts opts, struct laser_dirent *entry, full_path += 2; // so... git dosent track dirs, only files - if (S_ISDIR(entry->s.st_mode)) + if (entry->d->d_type == DT_DIR) { lgit_getDirsStatus(opts, entry, full_path); return; diff --git a/src/laser.c b/src/laser.c index 257e182..45c0ed3 100644 --- a/src/laser.c +++ b/src/laser.c @@ -34,6 +34,8 @@ static void laser_print_entry(struct laser_dirent *entry, const char *color, char *indent, int depth, laser_opts opts, int is_last); static laser_color_type laser_color_for_format(const char *filename); +static laser_color_type laser_color_for_entry(struct laser_dirent *entry, + const char *full_path); // returns size of directory if able. else returns -1 static off_t laser_get_dir_size(struct laser_dirent *entry, char *fp); @@ -72,18 +74,13 @@ void laser_start(laser_opts opts) } } -void laser_process_single_file(laser_opts opts) +void laser_process_single_file(laser_opts opts, struct stat st) { - struct stat file_stat; - if (lstat(opts.dir, &file_stat) == -1) - { - laser_logger_error("Couldn't stat file %s, %s\n", opts.dir, - strerror(errno)); - return; - } - - struct laser_dirent entry; - entry.s = file_stat; + struct laser_dirent entry = {0}; + // always need to stat single files + // plus it ain't to bad cuz it just be only one stat call + entry.s = st; + entry.stat_loaded = 1; entry.d = malloc(sizeof(struct dirent) + strlen(opts.dir) + 1); if (entry.d == NULL) laser_logger_fatal(1, "Failed to allocate entry struct: %s", @@ -91,6 +88,24 @@ void laser_process_single_file(laser_opts opts) strcpy(entry.d->d_name, opts.dir); + // setup d_type cuz we ain't getting it + if (S_ISREG(st.st_mode)) + entry.d->d_type = DT_REG; + else if (S_ISDIR(st.st_mode)) + entry.d->d_type = DT_DIR; + else if (S_ISLNK(st.st_mode)) + entry.d->d_type = DT_LNK; + else if (S_ISCHR(st.st_mode)) + entry.d->d_type = DT_CHR; + else if (S_ISBLK(st.st_mode)) + entry.d->d_type = DT_BLK; + else if (S_ISFIFO(st.st_mode)) + entry.d->d_type = DT_FIFO; + else if (S_ISSOCK(st.st_mode)) + entry.d->d_type = DT_SOCK; + else + entry.d->d_type = DT_UNKNOWN; + // default status to ' ' entry.git_status = ' '; if (opts.show_git->show_git_status) @@ -168,12 +183,6 @@ static void laser_process_entries(laser_opts opts, int depth, char *indent) snprintf(full_path, sizeof(full_path), "%s/%s", opts.dir, entry->d->d_name); - if (lstat(full_path, &entry->s) == -1) - { - laser_logger_error("%s\n", strerror(errno)); - continue; - } - if (opts.show_git->hide_git_ignored) { if (strcmp(entry->d->d_name, ".git") == 0) @@ -196,17 +205,31 @@ static void laser_process_entries(laser_opts opts, int depth, char *indent) if (opts.show_git->show_git_status) lgit_getGitStatus(opts, entry, full_path); - if (!lua_filters_apply(opts, entry)) + if (!lua_filters_apply(opts, entry, full_path)) continue; if (!opts.show_all && entry->d->d_name[0] == '.') continue; - if ((S_ISDIR(entry->s.st_mode) && opts.show_directories) || - (S_ISLNK(entry->s.st_mode) && opts.show_symlinks) || - (S_ISREG(entry->s.st_mode) && opts.show_files)) + // stat will be needed + if (entry->d->d_type == DT_UNKNOWN || opts.show_long || + opts.show_directory_size) + { + if (lstat(full_path, &entry->s) == -1) + continue; + entry->stat_loaded = 1; + + char *ownername = laser_getpwuid(entry->s.st_uid)->name; + ssize_t ownername_len = strlen(ownername); + if (ownername_len > longest_ownername) + longest_ownername = ownername_len; + } + + if ((entry->d->d_type == DT_DIR && opts.show_directories) || + (entry->d->d_type == DT_LNK && opts.show_symlinks) || + (entry->d->d_type == DT_REG && opts.show_files)) { - if ((S_ISDIR(entry->s.st_mode) && + if ((entry->d->d_type == DT_DIR && (opts.show_recursive || opts.show_directory_size)) && (strcmp(entry->d->d_name, ".") == 0 || strcmp(entry->d->d_name, "..") == 0)) @@ -214,7 +237,7 @@ static void laser_process_entries(laser_opts opts, int depth, char *indent) if (opts.show_directory_size) { - if (S_ISDIR(entry->s.st_mode)) + if (entry->d->d_type == DT_DIR) { off_t dirsize = laser_get_dir_size(entry, full_path); if (dirsize == -1) @@ -227,11 +250,6 @@ static void laser_process_entries(laser_opts opts, int depth, char *indent) current_dir_total_size += entry->s.st_size; } - char *ownername = laser_getpwuid(entry->s.st_uid)->name; - ssize_t ownername_len = strlen(ownername); - if (ownername_len > longest_ownername) - longest_ownername = ownername_len; - if (!opts.sort) { laser_handle_entry(entry, full_path, indent, depth, opts, 0); @@ -306,7 +324,7 @@ static void laser_handle_entry(struct laser_dirent *entry, const char *full_path, char *indent, int depth, laser_opts opts, int is_last) { - if (S_ISDIR(entry->s.st_mode)) + if (entry->d->d_type == DT_DIR) { laser_print_entry(entry, LASER_COLORS[LASER_COLOR_DIR].value, indent, depth, opts, is_last); @@ -320,7 +338,7 @@ static void laser_handle_entry(struct laser_dirent *entry, return; } - if (S_ISLNK(entry->s.st_mode) && opts.show_symlinks) + if (entry->d->d_type == DT_LNK && opts.show_symlinks) { char symlink_target[LASER_PATH_MAX]; int len = @@ -338,9 +356,10 @@ static void laser_handle_entry(struct laser_dirent *entry, if (ent == NULL) laser_logger_fatal(1, "Failed to allocate entry struct: %s", strerror(errno)); - - ent->s = entry->s; - ent->d = malloc(sizeof(struct dirent)); + // +1 for the null + ent->d = malloc(offsetof(struct dirent, d_name) + + strlen(res_string) + 1); // allocating enough size + // for the name too if (ent->d == NULL) laser_logger_fatal(1, "Failed to allocate entry struct: %s", strerror(errno)); @@ -349,6 +368,9 @@ static void laser_handle_entry(struct laser_dirent *entry, ent->git_status = entry->git_status; + ent->s = entry->s; + ent->stat_loaded = 1; + laser_print_entry(ent, LASER_COLORS[LASER_COLOR_SYMLINK].value, indent, depth, opts, is_last); @@ -358,23 +380,9 @@ static void laser_handle_entry(struct laser_dirent *entry, return; } - if (laser_is_filestat_exec(&entry->s)) - laser_print_entry(entry, LASER_COLORS[LASER_COLOR_EXEC].value, indent, - depth, opts, is_last); - else if (entry->d->d_name[0] == '.') - laser_print_entry(entry, LASER_COLORS[LASER_COLOR_HIDDEN].value, indent, - depth, opts, is_last); - else - { - // coloring which depends on formats - laser_color_type color_type = laser_color_for_format(full_path); - if (color_type != LASER_COLOR_FILE) - laser_print_entry(entry, LASER_COLORS[color_type].value, indent, - depth, opts, is_last); - else if (S_ISREG(entry->s.st_mode)) - laser_print_entry(entry, LASER_COLORS[LASER_COLOR_FILE].value, - indent, depth, opts, is_last); - } + laser_color_type ctype = laser_color_for_entry(entry, full_path); + laser_print_entry(entry, LASER_COLORS[ctype].value, indent, depth, opts, + is_last); } // last parameter is only to match the signature for laser_sort @@ -387,8 +395,8 @@ static int laser_cmp_dirent(const void *a, const void *b, const void *_) lua_pushstring(L, dirent_a->d->d_name); lua_pushstring(L, dirent_b->d->d_name); - lua_pushboolean(L, S_ISDIR(dirent_a->s.st_mode)); - lua_pushboolean(L, S_ISDIR(dirent_b->s.st_mode)); + lua_pushboolean(L, dirent_a->d->d_type == DT_DIR); + lua_pushboolean(L, dirent_b->d->d_type == DT_DIR); if (lua_pcall(L, 4, 1, 0) != LUA_OK) { @@ -419,7 +427,7 @@ static void laser_print_long_entry(struct laser_dirent *entry, lua_setfield(L, -2, "mode"); off_t size = entry->s.st_size; - if (S_ISDIR(entry->s.st_mode) && !opts.show_directory_size) + if (entry->d->d_type == DT_DIR && !opts.show_directory_size) size = -1; lua_pushinteger(L, size); lua_setfield(L, -2, "size"); @@ -429,13 +437,13 @@ static void laser_print_long_entry(struct laser_dirent *entry, lua_pushstring(L, laser_getpwuid(entry->s.st_uid)->name); lua_setfield(L, -2, "owner"); - lua_pushstring(L, S_ISDIR(entry->s.st_mode) ? "d" - : S_ISLNK(entry->s.st_mode) ? "l" - : S_ISCHR(entry->s.st_mode) ? "c" - : S_ISBLK(entry->s.st_mode) ? "b" - : S_ISFIFO(entry->s.st_mode) ? "p" - : S_ISSOCK(entry->s.st_mode) ? "s" - : "-"); + lua_pushstring(L, entry->d->d_type == DT_DIR ? "d" + : entry->d->d_type == DT_LNK ? "l" + : entry->d->d_type == DT_CHR ? "c" + : entry->d->d_type == DT_BLK ? "b" + : entry->d->d_type == DT_FIFO ? "p" + : entry->d->d_type == DT_SOCK ? "s" + : "-"); lua_setfield(L, -2, "type"); lua_pushstring( @@ -496,9 +504,37 @@ static laser_color_type laser_color_for_format(const char *filename) return LASER_COLOR_FILE; } +static laser_color_type laser_color_for_entry(struct laser_dirent *entry, + const char *full_path) +{ + if (entry->d->d_name[0] == '.') + return LASER_COLOR_HIDDEN; + + laser_color_type fmt_color = laser_color_for_format(full_path); + if (fmt_color != LASER_COLOR_FILE) + return fmt_color; + + if (entry->d->d_type == DT_REG) + { + if (!entry->stat_loaded) + { + if (lstat(full_path, &entry->s) == -1) + return LASER_COLOR_FILE; + entry->stat_loaded = 1; + } + + if (laser_is_filestat_exec(&entry->s)) + return LASER_COLOR_EXEC; + + return LASER_COLOR_FILE; + } + + return LASER_COLOR_FILE; +} + static off_t laser_get_dir_size(struct laser_dirent *ent, char *fp) { - if (!S_ISDIR(ent->s.st_mode)) + if (ent->d->d_type != DT_DIR) return -1; DIR *dir = opendir(fp); @@ -535,8 +571,9 @@ static off_t laser_get_dir_size(struct laser_dirent *ent, char *fp) strerror(errno)); continue; } + e.stat_loaded = 1; - if (S_ISDIR(e.s.st_mode)) + if (e.d->d_type == DT_DIR) { off_t sub_s = laser_get_dir_size(&e, full_path); if (sub_s == -1) // unable to calculate the size diff --git a/src/lua_filters.c b/src/lua_filters.c index 28ef22f..5171f54 100644 --- a/src/lua_filters.c +++ b/src/lua_filters.c @@ -1,29 +1,55 @@ #include "lua_filters.h" #include "laser_pwuid.h" #include "logger.h" +#include +#include -int lua_filters_apply(laser_opts opts, struct laser_dirent *entry) -{ - lua_getglobal(L, "L_filters"); +static void ensure_stat(struct laser_dirent *entry, const char *full_path); - lua_newtable(L); - - lua_pushstring(L, entry->d->d_name); - lua_setfield(L, -2, "name"); +// lazy getters +static int lua_get_mode(lua_State *L) +{ + struct laser_dirent *entry = lua_touserdata(L, lua_upvalueindex(1)); + const char *full_path = lua_tostring(L, lua_upvalueindex(2)); + ensure_stat(entry, full_path); lua_pushinteger(L, entry->s.st_mode); - lua_setfield(L, -2, "mode"); + return 1; +} +static int lua_get_size(lua_State *L) +{ + struct laser_dirent *entry = lua_touserdata(L, lua_upvalueindex(1)); + const char *full_path = lua_tostring(L, lua_upvalueindex(2)); + ensure_stat(entry, full_path); off_t size = (S_ISDIR(entry->s.st_mode)) ? -1 : entry->s.st_size; lua_pushinteger(L, size); - lua_setfield(L, -2, "size"); + return 1; +} +static int lua_get_mtime(lua_State *L) +{ + struct laser_dirent *entry = lua_touserdata(L, lua_upvalueindex(1)); + const char *full_path = lua_tostring(L, lua_upvalueindex(2)); + ensure_stat(entry, full_path); lua_pushinteger(L, entry->s.st_mtime); - lua_setfield(L, -2, "mtime"); + return 1; +} +static int lua_get_owner(lua_State *L) +{ + struct laser_dirent *entry = lua_touserdata(L, lua_upvalueindex(1)); + const char *full_path = lua_tostring(L, lua_upvalueindex(2)); + ensure_stat(entry, full_path); lua_pushstring(L, laser_getpwuid(entry->s.st_uid)->name); - lua_setfield(L, -2, "owner"); + return 1; +} +static int lua_get_type(lua_State *L) +{ + struct laser_dirent *entry = lua_touserdata(L, lua_upvalueindex(1)); + const char *full_path = lua_tostring(L, lua_upvalueindex(2)); + ensure_stat(entry, full_path); lua_pushstring(L, S_ISDIR(entry->s.st_mode) ? "d" : S_ISLNK(entry->s.st_mode) ? "l" : S_ISCHR(entry->s.st_mode) ? "c" @@ -31,45 +57,95 @@ int lua_filters_apply(laser_opts opts, struct laser_dirent *entry) : S_ISFIFO(entry->s.st_mode) ? "p" : S_ISSOCK(entry->s.st_mode) ? "s" : "-"); - lua_setfield(L, -2, "type"); + return 1; +} + +// --- Stat loader --- +static void ensure_stat(struct laser_dirent *entry, const char *full_path) +{ + if (!entry->stat_loaded) + { + if (lstat(full_path, &entry->s) == -1) + luaL_error(L, "stat failed for %s", full_path); + entry->stat_loaded = 1; + } +} + +// --- Main filter application --- +int lua_filters_apply(laser_opts opts, struct laser_dirent *entry, + const char *full_path) +{ + lua_getglobal(L, "L_filters"); + + lua_newtable(L); + + lua_pushstring(L, entry->d->d_name); + lua_setfield(L, -2, "name"); lua_pushstring( L, (char[]){entry->git_status == ' ' ? 0 : entry->git_status, 0}); lua_setfield(L, -2, "git_status"); + // lazy fields + lua_pushlightuserdata(L, entry); + lua_pushstring(L, full_path); + lua_pushcclosure(L, lua_get_mode, 2); + lua_setfield(L, -2, "mode"); + + lua_pushlightuserdata(L, entry); + lua_pushstring(L, full_path); + lua_pushcclosure(L, lua_get_size, 2); + lua_setfield(L, -2, "size"); + + lua_pushlightuserdata(L, entry); + lua_pushstring(L, full_path); + lua_pushcclosure(L, lua_get_mtime, 2); + lua_setfield(L, -2, "mtime"); + + lua_pushlightuserdata(L, entry); + lua_pushstring(L, full_path); + lua_pushcclosure(L, lua_get_owner, 2); + lua_setfield(L, -2, "owner"); + + lua_pushlightuserdata(L, entry); + lua_pushstring(L, full_path); + lua_pushcclosure(L, lua_get_type, 2); + lua_setfield(L, -2, "type"); + + // run the filters for (int i = 0; i < opts.filter_count; i++) { lua_getfield(L, -2, opts.filters[i]); + lua_pushvalue(L, -2); // push the file table - lua_pushvalue(L, -2); if (lua_pcall(L, 1, 1, 0) != LUA_OK) { - laser_logger_error("error calling filter with name '%s': %s\n", + laser_logger_error("error calling filter '%s': %s\n", opts.filters[i], lua_tostring(L, -1)); - lua_pop(L, 1); // pop the error message + lua_pop(L, 1); exit(1); } + if (!lua_isboolean(L, -1)) { - laser_logger_error( - "filter with name '%s' did not return a boolean!\n", - opts.filters[i]); - lua_pop(L, 1); // pop the error message + laser_logger_error("filter '%s' did not return a boolean!\n", + opts.filters[i]); + lua_pop(L, 1); exit(1); } if (lua_toboolean(L, -1)) { - lua_pop(L, 1); // pop the boolean - continue; // check the next filter + lua_pop(L, 1); // pop boolean + continue; } - lua_pop(L, 1); // pop the boolean - lua_pop(L, 1); // pop the table - return 0; // filtered out + lua_pop(L, 1); // pop boolean + lua_pop(L, 1); // pop table + return 0; } - lua_pop(L, 1); // pop the boolean - lua_pop(L, 1); // pop the table + lua_pop(L, 1); // pop last boolean + lua_pop(L, 1); // pop table return 1; } diff --git a/src/main.c b/src/main.c index b219a5a..7b75952 100644 --- a/src/main.c +++ b/src/main.c @@ -61,16 +61,16 @@ int main(int argc, char **argv) laser_colors_init(opts); struct stat path_stat; - if (stat(opts.dir, &path_stat) != 0) + if (lstat(opts.dir, &path_stat) != 0) { laser_logger_error("Error checking %s: %s\n", opts.dir, strerror(errno)); goto clean; } - if (S_ISREG(path_stat.st_mode)) + if (!S_ISDIR(path_stat.st_mode)) { - laser_process_single_file(opts); + laser_process_single_file(opts, path_stat); goto clean; } laser_start(opts);