summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLouis Burda <quent.burda@gmail.com>2023-06-20 15:04:04 +0200
committerLouis Burda <quent.burda@gmail.com>2023-06-20 15:04:04 +0200
commit15c8e7fb77a4e9211618c0b5471d1b3e79da1f07 (patch)
tree2a6a70c94388d194897af513921f65899ceed48a
parent20e32bf4ca0737affcf3035aa2300b7c9cb5a377 (diff)
downloadtmenu-15c8e7fb77a4e9211618c0b5471d1b3e79da1f07.tar.gz
tmenu-15c8e7fb77a4e9211618c0b5471d1b3e79da1f07.zip
Load entries into memory instead of seeking file
-rw-r--r--.gitignore1
-rw-r--r--tmenu.c374
2 files changed, 181 insertions, 194 deletions
diff --git a/.gitignore b/.gitignore
index 400d1be..3ea3251 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
tmenu
.cache
compile_commands.json
+.gdb_history
diff --git a/tmenu.c b/tmenu.c
index ec7c90b..28e6238 100644
--- a/tmenu.c
+++ b/tmenu.c
@@ -1,19 +1,17 @@
-#include <stddef.h>
+#include <sys/ioctl.h>
#include <unistd.h>
#include <termios.h>
-#include <sys/ioctl.h>
-#include <err.h>
-
+#include <fcntl.h>
#include <stdbool.h>
-#include <stdlib.h>
-#include <stdio.h>
#include <stdarg.h>
#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
#define ARRLEN(x) (sizeof(x)/sizeof((x)[0]))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
-#define eprintf(...) fprintf(stderr, __VA_ARGS__)
+#define EPRINTF(...) fprintf(stderr, __VA_ARGS__)
#define KEY_CTRL(c) (((int) (c)) & 0b11111)
@@ -113,14 +111,16 @@ static const struct mode modes[] = {
}
};
-static FILE *infile = NULL;
+static char *input = NULL;
+static size_t input_len = 0;
+static size_t input_cap = 0;
-static size_t *entries = NULL;
-static size_t entries_cap = 0;
-static size_t entries_cnt = 0;
+static size_t *delims = NULL;
+static size_t delims_cnt = 0;
+static size_t delims_cap = 0;
static ssize_t selected = -1;
-static char *entry = NULL;
+static const char *entry = NULL;
static char searchbuf[1024];
static size_t searchlen = 0;
@@ -137,47 +137,58 @@ static bool multiout = false;
static bool verbose = false;
static bool prompt = true;
-static char
-lower(char c)
+static char delim = '\n';
+
+static void
+die(const char *fmt, ...)
{
- if (c >= 'A' && c <= 'Z')
- c += 'a' - 'A';
- return c;
+ va_list ap;
+
+ va_start(ap, fmt);
+ fputs("tmenu: ", stderr);
+ vfprintf(stderr, fmt, ap);
+ if (*fmt && fmt[strlen(fmt) - 1] == ':') {
+ fputc(' ', stderr);
+ perror(NULL);
+ } else {
+ fputc('\n', stderr);
+ }
+ va_end(ap);
+
+ exit(1);
}
-static int
-search_cmp(const char *a, const char *b, size_t size)
+static void *
+addcap(void *alloc, size_t dsize, size_t min, size_t *cap)
{
- size_t i;
-
- for (i = 0; i < size; i++) {
- if (searchcase == CASE_SENSITIVE) {
- if (a[i] != b[i])
- return a[i] - b[i];
- } else {
- if (lower(a[i]) != lower(b[i]))
- return lower(a[i]) - lower(b[i]);
- }
+ if (min > *cap) {
+ *cap = *cap * 2;
+ if (*cap < min) *cap = min;
+ alloc = realloc(alloc, dsize * *cap);
+ if (!alloc) die("realloc:");
}
- return 0;
+ return alloc;
}
-static const char*
-search_find(const char *a, char c, size_t size)
+static inline char
+lower(char c)
{
- size_t i;
+ if (c >= 'A' && c <= 'Z')
+ c += 'a' - 'A';
+ return c;
+}
- for (i = 0; i < size; i++) {
- if (searchcase == CASE_SENSITIVE) {
- if (a[i] == c) return a + i;
- } else if (searchcase == CASE_INSENSITIVE) {
- if (lower(a[i]) == lower(c))
- return a + i;
- }
- }
+static size_t
+entry_len(size_t index)
+{
+ return delims[index] - (index > 0 ? delims[index-1] + 1 : 0);
+}
- return NULL;
+static inline char *
+get_entry(size_t index)
+{
+ return input + (index > 0 ? delims[index-1] + 1 : 0);
}
static int
@@ -210,49 +221,39 @@ readkey(FILE *f)
return KEY_NONE;
}
-static size_t
-entry_len(size_t index)
+static int
+search_cmp(const char *a, const char *b, size_t size)
{
- return entries[index + 1] - entries[index];
-}
+ size_t i;
-static char *
-read_entry(char *buf, size_t index)
-{
- size_t nleft, nread;
- char *pos, *tok;
-
- fseek(infile, (ssize_t) entries[index], SEEK_SET);
-
- nleft = entry_len(index);
- buf = realloc(buf, nleft);
- if (!buf) err(1, "realloc");
-
- pos = buf;
- while (nleft > 0) {
- nread = fread(pos, 1, nleft, infile);
- if (!nread) break;
- if (nread < 0) err(1, "fread");
- nleft -= nread;
- pos += nread;
+ for (i = 0; i < size; i++) {
+ if (searchcase == CASE_SENSITIVE) {
+ if (a[i] != b[i])
+ return a[i] - b[i];
+ } else {
+ if (lower(a[i]) != lower(b[i]))
+ return lower(a[i]) - lower(b[i]);
+ }
}
- tok = memchr(buf, '\n', entry_len(index));
- if (tok) *tok = '\0';
-
- return buf;
+ return 0;
}
-static void
-add_entry(size_t pos)
+static const char*
+search_find(const char *a, char c, size_t size)
{
- if (entries_cnt >= entries_cap) {
- entries_cap *= 2;
- entries = realloc(entries,
- entries_cap * sizeof(size_t));
- if (!entries) err(1, "realloc");
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (searchcase == CASE_SENSITIVE) {
+ if (a[i] == c) return a + i;
+ } else if (searchcase == CASE_INSENSITIVE) {
+ if (lower(a[i]) == lower(c))
+ return a + i;
+ }
}
- entries[entries_cnt] = pos;
+
+ return NULL;
}
static void
@@ -269,38 +270,38 @@ browse_prompt(void)
i = (ssize_t) selected - (ssize_t) bwdctx;
for (; i <= (ssize_t) selected + (ssize_t) fwdctx; i++) {
- eprintf(CSI_CLEAR_LINE);
+ EPRINTF(CSI_CLEAR_LINE);
if (i == selected)
- eprintf(CSI_STYLE_BOLD);
+ EPRINTF(CSI_STYLE_BOLD);
if (prompt) {
if (i == selected) {
- eprintf("(browse): ");
+ EPRINTF("(browse): ");
} else {
- eprintf("%*.s", 10, " ");
+ EPRINTF("%*.s", 10, " ");
}
}
- if (selected >= 0 && i >= 0 && i < entries_cnt) {
- entry = read_entry(entry, (size_t) i);
+ if (selected >= 0 && i >= 0 && i < delims_cnt) {
+ entry = get_entry((size_t) i);
entlen = entry_len((size_t) i);
if (entlen > linew) {
- eprintf(" ..%.*s\n", (int) (linew - 3),
+ EPRINTF(" ..%.*s\n", (int) (linew - 3),
entry + entlen - (linew - 3));
} else {
- eprintf("%.*s\n", (int) linew, entry);
+ EPRINTF("%.*s\n", (int) linew, entry);
}
} else {
- eprintf("\n");
+ EPRINTF("\n");
}
if (i == selected)
- eprintf(CSI_STYLE_RESET);
+ EPRINTF(CSI_STYLE_RESET);
}
for (i = 0; i < bwdctx + fwdctx + 1; i++)
- eprintf(CSI_CUR_UP);
+ EPRINTF(CSI_CUR_UP);
}
static bool
@@ -313,7 +314,7 @@ browse_handlekey(int c)
selected = 0;
break;
case 'G':
- selected = (ssize_t) entries_cnt - 1;
+ selected = (ssize_t) delims_cnt - 1;
break;
case 'q':
return true;
@@ -326,17 +327,17 @@ browse_handlekey(int c)
break;
case KEY_PGDN:
cnt = fwdctx + bwdctx + 1;
- if (selected < entries_cnt - cnt)
+ if (selected < delims_cnt - cnt)
selected += (ssize_t) cnt;
else
- selected = (ssize_t) entries_cnt - 1;
+ selected = (ssize_t) delims_cnt - 1;
break;
case KEY_UP:
if (selected != 0)
selected--;
break;
case KEY_DOWN:
- if (selected != entries_cnt - 1)
+ if (selected != delims_cnt - 1)
selected++;
break;
}
@@ -350,9 +351,9 @@ browse_cleanup(void)
size_t i;
for (i = 0; i < bwdctx + 1 + fwdctx; i++)
- eprintf(CSI_CLEAR_LINE "\n");
+ EPRINTF(CSI_CLEAR_LINE "\n");
for (i = 0; i < bwdctx + 1 + fwdctx; i++)
- eprintf(CSI_CUR_UP);
+ EPRINTF(CSI_CUR_UP);
}
static void
@@ -375,7 +376,7 @@ search_prompt(void)
len = snprintf(promptbuf, sizeof(promptbuf), "(search[%c:%s]) %.*s",
(searchcase == CASE_SENSITIVE) ? 'I' : 'i',
searchmodes[searchmode].sh, (int) searchlen, searchbuf);
- if (len < 0) err(1, "snprintf");
+ if (len < 0) die("snprintf:");
linew = termw;
if (prompt) linew -= (size_t) len + 3;
@@ -395,36 +396,36 @@ search_prompt(void)
index = -1;
}
- eprintf(CSI_CLEAR_LINE);
+ EPRINTF(CSI_CLEAR_LINE);
- if (i == 0) eprintf(CSI_STYLE_BOLD);
+ if (i == 0) EPRINTF(CSI_STYLE_BOLD);
if (prompt) {
if (i == 0) {
- eprintf("%s : ", promptbuf);
+ EPRINTF("%s : ", promptbuf);
} else {
- eprintf("%*.s", (int) (len + 3), " ");
+ EPRINTF("%*.s", (int) (len + 3), " ");
}
}
if (index < 0) {
- eprintf("\n");
+ EPRINTF("\n");
} else {
entlen = (ssize_t) entry_len((size_t) index);
- entry = read_entry(entry, (size_t) index);
+ entry = get_entry((size_t) index);
if (entlen > linew) {
- eprintf(" ..%.*s\n", (int) (linew - 3), entry
+ EPRINTF(" ..%.*s\n", (int) (linew - 3), entry
+ MAX(0, (size_t) entlen - (linew - 3)));
} else {
- eprintf("%.*s\n", (int) linew, entry);
+ EPRINTF("%.*s\n", (int) linew, entry);
}
}
- if (i == 0) eprintf(CSI_STYLE_RESET);
+ if (i == 0) EPRINTF(CSI_STYLE_RESET);
}
for (i = 0; i < bwdctx + fwdctx + 1; i++)
- eprintf(CSI_CUR_UP);
+ EPRINTF(CSI_CUR_UP);
}
static bool
@@ -470,9 +471,9 @@ search_cleanup(void)
size_t i;
for (i = 0; i < bwdctx + 1 + fwdctx; i++)
- eprintf(CSI_CLEAR_LINE "\n");
+ EPRINTF(CSI_CLEAR_LINE "\n");
for (i = 0; i < bwdctx + 1 + fwdctx; i++)
- eprintf(CSI_CUR_UP);
+ EPRINTF(CSI_CUR_UP);
}
static ssize_t
@@ -491,18 +492,18 @@ search_match_substr(ssize_t start, int dir,
if (!searchlen) {
index = start + dir * (ssize_t) (new + cnt - 1);
- if (index < 0 || index >= entries_cnt)
+ if (index < 0 || index >= delims_cnt)
return fallback;
return index;
}
found = 0;
- for (i = new; i < entries_cnt; i++) {
+ for (i = new; i < delims_cnt; i++) {
index = start + dir * (ssize_t) i;
- if (index < 0 || index >= entries_cnt)
+ if (index < 0 || index >= delims_cnt)
break;
- entry = read_entry(entry, (size_t) index);
+ entry = get_entry((size_t) index);
end = entry + entry_len((size_t) index);
for (bp = entry; *bp; bp++) {
@@ -528,18 +529,18 @@ search_match_fuzzy(ssize_t start, int dir,
if (!searchlen) {
index = start + dir * (ssize_t) (new + cnt - 1);
- if (index < 0 || index >= entries_cnt)
+ if (index < 0 || index >= delims_cnt)
return fallback;
return index;
}
found = 0;
- for (i = new; i < entries_cnt; i++) {
+ for (i = new; i < delims_cnt; i++) {
index = start + dir * (ssize_t) i;
- if (index < 0 || index >= entries_cnt)
+ if (index < 0 || index >= delims_cnt)
break;
- entry = read_entry(entry, (size_t) index);
+ entry = get_entry((size_t) index);
end = entry + entry_len((size_t) index);
pos = entry;
@@ -558,88 +559,64 @@ search_match_fuzzy(ssize_t start, int dir,
}
static void
-load_entries(const char *filepath)
+load(int fd)
{
- size_t pos, start;
- size_t nread;
- char iobuf[1024];
-
- entries_cap = 100;
- entries = calloc(entries_cap, sizeof(size_t));
- if (!entries) err(1, "alloc");
-
- if (!filepath) {
- infile = tmpfile();
- if (!infile) err(1, "tmpfile");
-
- start = pos = 0;
- while (fgets(iobuf, sizeof(iobuf), stdin)) {
- nread = strlen(iobuf);
- pos += nread;
- if (fwrite(iobuf, 1, nread, infile) != nread)
- errx(1, "fwrite to tmpfile truncated");
- if (iobuf[nread - 1] == '\n') {
- add_entry(start);
- entries_cnt++;
- start = pos;
+ ssize_t nread;
+ size_t i;
+ char *c;
+
+ while (1) {
+ input = addcap(input, 1, input_len + BUFSIZ, &input_cap);
+ nread = read(fd, input + input_len, BUFSIZ);
+ if (nread <= 0) break;
+
+ c = input + input_len;
+ for (i = 0; i < (size_t) nread; i++, c++) {
+ if (*c == delim) {
+ *c = '\0';
+ delims = addcap(delims, sizeof(size_t),
+ delims_cnt + 1, &delims_cap);
+ delims[delims_cnt++] = input_len + i;
}
}
- add_entry(pos);
- fseek(infile, 0, SEEK_SET);
-
- if (!freopen("/dev/tty", "r", stdin))
- err(1, "freopen tty");
- if (fread(NULL, 0, 0, infile) < 0)
- err(1, "fread stdin");
- } else {
- infile = fopen(filepath, "r");
- if (!infile) err(1, "fopen %s", filepath);
-
- start = pos = 0;
- while (fgets(iobuf, sizeof(iobuf), infile)) {
- nread = strlen(iobuf);
- pos += nread;
- if (iobuf[nread - 1] == '\n') {
- add_entry(start);
- entries_cnt++;
- start = pos;
- }
- }
- add_entry(pos);
+ input_len += (size_t) nread;
+ }
- fseek(infile, 0, SEEK_SET);
+ if (!delims_cnt && input_len
+ || delims_cnt && delims[delims_cnt-1] != input_len-1) {
+ delims = addcap(delims, sizeof(size_t),
+ delims_cnt + 1, &delims_cap);
+ delims[delims_cnt++] = input_len;
}
+
+ if (verbose)
+ EPRINTF("Loaded %lu entries\n", delims_cnt);
}
static void
-run(const char *filepath)
+run(void)
{
struct termios prevterm, newterm = { 0 };
struct winsize ws = { 0 };
int c;
- load_entries(filepath);
-
- if (verbose)
- eprintf("Loaded %lu entries\n", entries_cnt);
-
- if (!entries_cnt) return;
+ if (!delims_cnt) return;
- if (tcgetattr(fileno(stderr), &prevterm))
- err(1, "tcgetattr");
+ if (tcgetattr(0, &prevterm))
+ die("tcgetattr:");
cfmakeraw(&newterm);
newterm.c_oflag |= ONLCR | OPOST;
- if (tcsetattr(fileno(stdin), TCSANOW, &newterm))
- err(1, "tcsetattr");
+ if (tcsetattr(0, TCSANOW, &newterm))
+ die("tcsetattr:");
- eprintf(CSI_CUR_HIDE);
+ EPRINTF(CSI_CUR_HIDE);
selected = 0;
searchlen = 0;
do {
- if (ioctl(1, TIOCGWINSZ, &ws) != -1)
+ if (ioctl(2, TIOCGWINSZ, &ws) != -1)
termw = ws.ws_col;
modes[mode].prompt();
@@ -663,7 +640,7 @@ run(const char *filepath)
mode = MODE_BROWSE;
break;
case KEY_CTRL('L'):
- eprintf(CSI_CLEAR_SCREEN CSI_CUR_GOTO, 0, 0);
+ EPRINTF(CSI_CLEAR_SCREEN CSI_CUR_GOTO, 0, 0);
break;
case KEY_CTRL('W'):
searchlen = 0;
@@ -671,7 +648,7 @@ run(const char *filepath)
case KEY_CTRL('J'):
case '\r':
if (selected < 0) break;
- entry = read_entry(entry, (size_t) selected);
+ entry = get_entry((size_t) selected);
modes[mode].cleanup();
printf("%.*s\n", (int) entry_len((size_t) selected), entry);
if (!multiout) goto exit;
@@ -686,11 +663,9 @@ run(const char *filepath)
exit:
modes[mode].cleanup();
- eprintf(CSI_CUR_SHOW);
+ EPRINTF(CSI_CUR_SHOW);
tcsetattr(fileno(stdin), TCSANOW, &prevterm);
-
- fclose(infile);
}
static int
@@ -699,7 +674,7 @@ parseopt(const char *flag, const char **args)
char *end;
if (flag[0] && flag[1]) {
- eprintf("Invalid flag: -%s\n", flag);
+ EPRINTF("Invalid flag: -%s\n", flag);
exit(1);
}
@@ -716,48 +691,59 @@ parseopt(const char *flag, const char **args)
prompt = false;
return 0;
case 'a':
+ if (!*args) die("missing -a arg");
fwdctx = strtoull(*args, &end, 10);
if (end && *end) goto badint;
return 1;
case 'b':
+ if (!*args) die("missing -b arg");
bwdctx = strtoull(*args, &end, 10);
if (end && *end) goto badint;
return 1;
+ case 'd':
+ if (!*args) die("missing -d arg");
+ if (args[0][0] && args[0][1]) die("bad -d arg");
+ delim = **args;
+ return 1;
case 'h':
printf("%s\n", usage);
exit(0);
+ default:
+ die("unknown opt '%s'", *flag);
}
return 0;
badint:
- eprintf("Invalid int: %s\n", *args);
+ EPRINTF("Invalid int: %s\n", *args);
exit(1);
}
int
main(int argc, const char **argv)
{
- const char *filepath;
- int i;
+ const char **arg;
+ int fd;
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
- filepath = NULL;
- for (i = 1; i < argc; i++) {
- if (*argv[i] == '-') {
- i += parseopt(argv[i] + 1, &argv[i+1]);
- } else if (!filepath) {
- filepath = argv[i];
+ for (arg = argv + 1; *arg; arg++) {
+ if (**arg == '-') {
+ arg += parseopt(*arg + 1, arg + 1);
} else {
- eprintf("Unexpected argument: %s\n", argv[i]);
- return 0;
+ fd = open(*arg, O_RDONLY);
+ if (fd < 0) die("open '%s':", *arg);
+ load(fd);
+ close(fd);
}
}
- run(filepath);
+ if (!input) {
+ load(0);
+ if (!freopen("/dev/tty", "r", stdin))
+ die("freopen tty:");
+ }
- free(entries);
- free(entry);
+ run();
}