summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Tenenbaum <pommicket@gmail.com>2021-01-21 13:35:18 -0500
committerLeo Tenenbaum <pommicket@gmail.com>2021-01-21 13:36:36 -0500
commit8fca7beaf35cfc438d5d29f352f80dd18efe7d2e (patch)
tree1fee5e822db6292be2d1629edf20d6239524f365
parentdd91d6c72625cc7ed2ec5954a5cbca35fd7655d4 (diff)
file selector now actually working
also made stristr work with UTF-8
-rw-r--r--buffer.c8
-rw-r--r--filesystem.h3
-rw-r--r--main.c2
-rw-r--r--menu.c37
-rw-r--r--ted.cfg2
-rw-r--r--util.c62
6 files changed, 84 insertions, 30 deletions
diff --git a/buffer.c b/buffer.c
index 162547a..520d7d2 100644
--- a/buffer.c
+++ b/buffer.c
@@ -483,9 +483,13 @@ Status buffer_load_file(TextBuffer *buffer, char const *filename) {
bool success = true;
if (fp) {
fseek(fp, 0, SEEK_END);
- size_t file_size = (size_t)ftell(fp);
+ long file_pos = ftell(fp);
+ size_t file_size = (size_t)file_pos;
fseek(fp, 0, SEEK_SET);
- if (file_size > 10L<<20) {
+ if (file_pos == -1 || file_pos == LONG_MAX) {
+ buffer_seterr(buffer, "Couldn't get file position. There is something wrong with the file '%s'.", filename);
+ success = false;
+ } else if (file_size > 10L<<20) {
buffer_seterr(buffer, "File too big (size: %zu).", file_size);
success = false;
} else {
diff --git a/filesystem.h b/filesystem.h
index 424b824..238ebb1 100644
--- a/filesystem.h
+++ b/filesystem.h
@@ -23,4 +23,5 @@ char **fs_list_directory(char const *dirname);
// -1 if the path already exists, but it's not a directory, or if there's another error (e.g. don't have permission to create directory).
int fs_mkdir(char const *path);
-#endif // FILESYSTEM_H_ \ No newline at end of file
+#endif // FILESYSTEM_H_
+
diff --git a/main.c b/main.c
index f014d89..39e582b 100644
--- a/main.c
+++ b/main.c
@@ -81,6 +81,8 @@ int main(int argc, char **argv) {
#endif
setlocale(LC_ALL, ""); // allow unicode
+ printf("%d\n", !!stristr("foo", "x"));
+
{ // get local data directory
#if _WIN32
wchar_t *appdata = NULL;
diff --git a/menu.c b/menu.c
index 9c194c1..4d7e965 100644
--- a/menu.c
+++ b/menu.c
@@ -57,6 +57,18 @@ static void file_selector_free(FileSelector *fs) {
file_selector_clear_entries(fs);
}
+static int qsort_file_entry_cmp(void const *av, void const *bv) {
+ FileEntry const *a = av, *b = bv;
+ // put directories first
+ if (a->type > b->type) {
+ return -1;
+ }
+ if (a->type < b->type) {
+ return +1;
+ }
+ return strcmp_case_insensitive(a->name, b->name);
+}
+
// returns the entry of the selected file, or a NULL entry (check .name == NULL)
// if none was selected
static FileEntry file_selector_update(Ted *ted, FileSelector *fs, String32 const search_term32) {
@@ -83,16 +95,23 @@ static FileEntry file_selector_update(Ted *ted, FileSelector *fs, String32 const
if (files) {
u32 nfiles;
for (nfiles = 0; files[nfiles]; ++nfiles);
- if (search_term && *search_term) {
- // filter entries based on search term
- u32 in, out = 0;
- for (in = 0; in < nfiles; ++in) {
- if (stristr(files[in], search_term)) {
- free(files[out]);
- files[out++] = files[in];
+
+ // filter entries
+ 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);
+ // or if this is just the current directory
+ remove |= streq(files[i], ".");
+ if (remove) {
+ // remove this one
+ free(files[i]);
+ --nfiles;
+ if (nfiles) {
+ files[i] = files[nfiles];
}
+ increment = false;
}
- nfiles = out;
}
if (nfiles) {
@@ -105,6 +124,7 @@ static FileEntry file_selector_update(Ted *ted, FileSelector *fs, String32 const
entries[i].type = fs_path_type(files[i]);
}
}
+ qsort(entries, nfiles, sizeof *entries, qsort_file_entry_cmp);
}
} else {
#if DEBUG
@@ -156,6 +176,7 @@ static void file_selector_render(Ted const *ted, FileSelector const *fs) {
break;
case FS_DIRECTORY:
gl_color_rgba(colors[COLOR_TEXT_FOLDER]);
+ break;
default:
gl_color_rgba(colors[COLOR_TEXT_OTHER]);
break;
diff --git a/ted.cfg b/ted.cfg
index c4cf97c..05ea53f 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -80,4 +80,4 @@ bg = #001
# By making it transparent, we can dim everything else while the menu is open.
menu-backdrop = #0004
menu-bg = #222
-menu-hl = #888 \ No newline at end of file
+menu-hl = #666
diff --git a/util.c b/util.c
index 8c1239c..9dcb5ea 100644
--- a/util.c
+++ b/util.c
@@ -105,29 +105,50 @@ static void str_cpy(char *dst, size_t dst_sz, char const *src) {
dst[n] = 0;
}
+// advances str to the start of the next UTF8 character
+static void utf8_next_char_const(char const **str) {
+ if (**str) {
+ do {
+ ++*str;
+ } while (((u8)(**str) & 0xC0) == 0x80); // while we are on a continuation byte
+ }
+}
+
/*
-returns the first instance of needle in haystack, ignoring the case of the characters,
+returns the first instance of needle in haystack, where both are UTF-8 strings, ignoring the case of the characters,
or NULL if the haystack does not contain needle
WARNING: O(strlen(haystack) * strlen(needle))
*/
static char *stristr(char const *haystack, char const *needle) {
- size_t needle_len = strlen(needle), haystack_len = strlen(haystack), i, j;
+ size_t needle_bytes = strlen(needle), haystack_bytes = strlen(haystack);
+
+ if (needle_bytes > haystack_bytes) return NULL;
- if (needle_len > haystack_len) return NULL; // a larger string can't fit in a smaller string
+ char const *haystack_end = haystack + haystack_bytes;
+ char const *needle_end = needle + needle_bytes;
- for (i = 0; i <= haystack_len - needle_len; ++i) {
- char const *p = haystack + i, *q = needle;
+ for (char const *haystack_start = haystack; haystack_start + needle_bytes <= haystack_end; utf8_next_char_const(&haystack_start)) {
+ char const *p = haystack_start, *q = needle;
+ mbstate_t pstate = {0}, qstate = {0};
bool match = true;
- for (j = 0; j < needle_len; ++j) {
- if (tolower(*p) != tolower(*q)) {
- match = false;
- break;
- }
- ++p;
- ++q;
+
+ // check if p matches q
+ while (q < needle_end) {
+ char32_t pchar = 0, qchar = 0;
+ size_t bytes_p = mbrtoc32(&pchar, p, (size_t)(haystack_end - p), &pstate);
+ size_t bytes_q = mbrtoc32(&qchar, q, (size_t)(needle_end - q), &qstate);
+ if (bytes_p == (size_t)-3) bytes_p = 0;
+ if (bytes_q == (size_t)-3) bytes_q = 0;
+ if (bytes_p > (size_t)-3 || bytes_q > (size_t)-3) return NULL; // invalid UTF-8
+ bool same = pchar == qchar;
+ if (pchar < WINT_MAX && qchar < WINT_MAX) // on Windows, there is no way of finding the lower-case version of a codepoint outside the BMP. ):
+ same = towlower((wint_t)pchar) == towlower((wint_t)qchar);
+ if (!same) match = false;
+ p += bytes_p;
+ q += bytes_q;
}
if (match)
- return (char *)haystack + i;
+ return (char *)haystack_start;
}
return NULL;
}
@@ -151,12 +172,17 @@ static bool str_satisfies(char const *s, int (*predicate)(int)) {
return true;
}
-// function to be passed into qsort for case insensitive sorting
-static int str_qsort_case_insensitive_cmp(const void *av, const void *bv) {
- char const *const *a = av, *const *b = bv;
+
+static int strcmp_case_insensitive(char const *a, char const *b) {
#if _WIN32
- return _stricmp(*a, *b);
+ return _stricmp(a, b);
#else
- return strcasecmp(*a, *b);
+ return strcasecmp(a, b);
#endif
}
+
+// function to be passed into qsort for case insensitive sorting
+static int str_qsort_case_insensitive_cmp(const void *av, const void *bv) {
+ char const *const *a = av, *const *b = bv;
+ return strcmp_case_insensitive(*a, *b);
+}