summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arr.c3
-rw-r--r--filesystem-posix.c7
-rw-r--r--filesystem.h1
-rw-r--r--ui.c63
4 files changed, 49 insertions, 25 deletions
diff --git a/arr.c b/arr.c
index 9d2220b..2fa9b6b 100644
--- a/arr.c
+++ b/arr.c
@@ -223,7 +223,8 @@ static void arrcstr_append_strn_(char **a, char const *s, size_t s_len) {
size_t new_len = curr_len + s_len;
arr_reserve(*a, new_len + 1);
arr_set_len(*a, new_len);
- memcpy(*a + curr_len, s, s_len + 1);
+ memcpy(*a + curr_len, s, s_len);
+ (*a)[curr_len + s_len] = '\0';
}
static void arrcstr_shrink_(char **a, u32 new_len) {
diff --git a/filesystem-posix.c b/filesystem-posix.c
index cf4a04b..ebf1407 100644
--- a/filesystem-posix.c
+++ b/filesystem-posix.c
@@ -7,15 +7,8 @@
FsType fs_path_type(char const *path) {
struct stat statbuf = {0};
- char linkbuf[8];
- if (readlink(path, linkbuf, sizeof linkbuf) != -1) {
- // unfortunately there is no way of telling from stat alone whether a directory is a symbolic link >:(
- return FS_LINK;
- }
if (stat(path, &statbuf) != 0)
return FS_NON_EXISTENT;
- if (S_ISLNK(statbuf.st_mode))
- return FS_LINK;
if (S_ISREG(statbuf.st_mode))
return FS_FILE;
if (S_ISDIR(statbuf.st_mode))
diff --git a/filesystem.h b/filesystem.h
index 725b231..2e561cd 100644
--- a/filesystem.h
+++ b/filesystem.h
@@ -4,7 +4,6 @@
typedef enum {
FS_NON_EXISTENT,
FS_FILE,
- FS_LINK,
FS_DIRECTORY,
FS_OTHER
} FsType;
diff --git a/ui.c b/ui.c
index 852428e..75057db 100644
--- a/ui.c
+++ b/ui.c
@@ -1,3 +1,7 @@
+#if __unix__
+#include <fcntl.h>
+#endif
+
// where is the ith entry in the file selector on the screen?
// returns false if it's completely offscreen
static bool file_selector_entry_pos(Ted const *ted, FileSelector const *fs,
@@ -42,8 +46,10 @@ static int qsort_file_entry_cmp(void const *av, void const *bv) {
return strcmp_case_insensitive(a->name, b->name);
}
+static void file_selector_cd_(FileSelector *fs, char const *path, int symlink_depth);
+
// cd to the directory `name`. `name` cannot include any path separators.
-static void file_selector_cd1(FileSelector *fs, char const *name, size_t name_len) {
+static void file_selector_cd1(FileSelector *fs, char const *name, size_t name_len, int symlink_depth) {
if (name_len == 0 || (name_len == 1 && name[0] == '.')) {
// no name, or .
return;
@@ -64,19 +70,36 @@ static void file_selector_cd1(FileSelector *fs, char const *name, size_t name_le
}
}
} else {
+ #if __unix__
+ if (symlink_depth < 32) { // on my system, MAXSYMLINKS is 20, so this should be plenty
+ char path[TED_PATH_MAX], link_to[TED_PATH_MAX];
+ // join fs->cwd with name to get full path
+ str_printf(path, TED_PATH_MAX, "%s%s%*s", fs->cwd,
+ fs->cwd[strlen(fs->cwd) - 1] == PATH_SEPARATOR ?
+ "" : PATH_SEPARATOR_STR,
+ (int)name_len, name);
+ ssize_t bytes = readlink(path, link_to, sizeof link_to);
+ if (bytes != -1) {
+ // this is a symlink
+ link_to[bytes] = '\0';
+ file_selector_cd_(fs, link_to, symlink_depth + 1);
+ return;
+ }
+ }
+ #else
+ (void)symlink_depth;
+ #endif
// add path separator to end if not already there (which could happen in the case of /)
if (fs->cwd[strlen(fs->cwd) - 1] != PATH_SEPARATOR)
arrcstr_append_str(fs->cwd, PATH_SEPARATOR_STR);
// add name itself
arrcstr_append_strn(fs->cwd, name, name_len);
}
+
}
-// go to the directory `path`. make sure `path` only contains path separators like PATH_SEPARATOR, not any
-// other members of ALL_PATH_SEPARATORS
-static void file_selector_cd(FileSelector *fs, char const *path) {
- size_t path_len = strlen(path);
- if (path_len == 0) return;
+static void file_selector_cd_(FileSelector *fs, char const *path, int symlink_depth) {
+ if (path[0] == '\0') return;
if (path[0] == PATH_SEPARATOR
#if _WIN32
@@ -84,29 +107,37 @@ static void file_selector_cd(FileSelector *fs, char const *path) {
#endif
) {
// absolute path (e.g. /foo, c:\foo)
+ // start out by replacing cwd with the start of the absolute path
arr_clear(fs->cwd);
- if (path_len > 1 &&
-#if _WIN32
- !(path_len == 3 && path[1] == ':')
-#endif
- path[path_len - 1] == PATH_SEPARATOR) {
- // path ends with path separator
- --path_len;
+ if (path[0] == PATH_SEPARATOR) {
+ arrcstr_append_str(fs->cwd, PATH_SEPARATOR_STR);
+ ++path;
}
- arrcstr_append_strn(fs->cwd, path, path_len);
- return;
+ #if _WIN32
+ else {
+ char s[] = {path[0], path[1], path[2], 0};
+ arrcstr_append_str(fs->cwd, s);
+ path += 3;
+ }
+ #endif
}
char const *p = path;
while (*p) {
size_t len = strcspn(p, PATH_SEPARATOR_STR);
- file_selector_cd1(fs, p, len);
+ file_selector_cd1(fs, p, len, symlink_depth);
p += len;
p += strspn(p, PATH_SEPARATOR_STR);
}
}
+// go to the directory `path`. make sure `path` only contains path separators like PATH_SEPARATOR, not any
+// other members of ALL_PATH_SEPARATORS
+static void file_selector_cd(FileSelector *fs, char const *path) {
+ file_selector_cd_(fs, path, 0);
+}
+
// returns the name of the selected file, or NULL
// if none was selected. the returned pointer should be freed.
static char *file_selector_update(Ted *ted, FileSelector *fs) {