From 63e5be288418b9addda5681ed12be6c6ee9cc8a7 Mon Sep 17 00:00:00 2001
From: speedie
Date: Sun, 7 May 2023 01:41:30 +0200
Subject: [PATCH] add the ability to read from file, been wanting this for a
very long time
---
README.html | 2 +-
README.md | 2 +-
docs/docs.md | 6 +++-
docs/spmenu.conf | 1 +
libs/argv.c | 5 +++-
libs/conf/config.c | 3 ++
libs/event.c | 10 +++++++
libs/stream.c | 74 +++++++++++++++++++++++++++++++++++++++++++++-
libs/stream.h | 1 +
options.h | 1 +
spmenu.1 | 5 +++-
spmenu.c | 24 +++++++++++----
spmenu.html | 6 +++-
13 files changed, 128 insertions(+), 12 deletions(-)
diff --git a/README.html b/README.html
index fe049d0..65b7215 100644
--- a/README.html
+++ b/README.html
@@ -188,6 +188,7 @@ href="https://git.speedie.site/speedwm">speedwm.
Vim-like modes, including indicator.
The ability to move around items with keybinds.
Customizable/dynamic line/column size.
+Ability to update entries dynamically by reading from file
IME support
- Was removed from suckless dmenu years ago due to issues I’ve
@@ -330,7 +331,6 @@ easy to have LibreSSL compatibility.
Matching: Add support for contextual completions similar to
xprompt
-Matching: FIFO, used to dynamically refresh entries.
Matching: Regex matching
- Probably use some minimal public domain library for this, I’d like
diff --git a/README.md b/README.md
index 6135ad3..eeed881 100644
--- a/README.md
+++ b/README.md
@@ -22,6 +22,7 @@ It is designed to integrate well with my [dwm](https://dwm.suckless.org) fork, [
- Vim-like modes, including indicator.
- The ability to move around items with keybinds.
- Customizable/dynamic line/column size.
+- Ability to update entries dynamically by reading from file
- IME support
- Was removed from suckless dmenu years ago due to issues I've resolved
- Powerlines
@@ -130,7 +131,6 @@ have LibreSSL compatibility.
- Just need to `XMoveResizeWindow()` as well as `mh += bh` and `y += bh`
for each added line.
- Matching: Add support for contextual completions similar to xprompt
-- Matching: FIFO, used to dynamically refresh entries.
- Matching: Regex matching
- Probably use some minimal public domain library for this, I'd
like to avoid adding more external dependencies unless it's a
diff --git a/docs/docs.md b/docs/docs.md
index 55d9a18..5fc0e59 100644
--- a/docs/docs.md
+++ b/docs/docs.md
@@ -265,7 +265,11 @@ You may use long, descriptive arguments or the shorter arguments.
: Embed spmenu inside window id
`-H, --hist-file hist file`
-: Specify a path to save the history to
+: Specify a file to save the history to
+
+`-lf, --list-file list file`
+: Specify a file to load entries from
+
`-ig, --image-gaps gaps`
: Set image gaps to gaps
diff --git a/docs/spmenu.conf b/docs/spmenu.conf
index 98e1563..e87a0ed 100644
--- a/docs/spmenu.conf
+++ b/docs/spmenu.conf
@@ -160,6 +160,7 @@ spmenu = {
preselected = 0; // Preselect an item, 0 is the first item (number)
accuratewidth = 0; // Enable accurate width, could be noticeably slower in some cases (0/1)
delimiters = " "; // Word delimiter, used to delete words (text)
+ listfile = "NULL"; // File to read entries from. If set to NULL standard input is read. This is read every time a key is pressed. (text)
} );
/* Line options */
diff --git a/libs/argv.c b/libs/argv.c
index 21be3ec..3bf5d4f 100644
--- a/libs/argv.c
+++ b/libs/argv.c
@@ -276,6 +276,8 @@ void readargs(int argc, char *argv[]) {
input = argv[++i];
else if (!strcmp(argv[i], "-fn") || (!strcmp(argv[i], "--font"))) // font or font set
fonts[0] = argv[++i];
+ else if (!strcmp(argv[i], "-lf") || (!strcmp(argv[i], "--lf"))) // list file
+ listfile = argv[++i];
else if (!strcmp(argv[i], "-nmt") || (!strcmp(argv[i], "--normal-mode-text"))) // normal mode text
strcpy(normtext, argv[++i]);
else if (!strcmp(argv[i], "-imt") || (!strcmp(argv[i], "--insert-mode-text"))) // insert mode text
@@ -531,7 +533,8 @@ void usage(void) {
"spmenu -ngbc, --no-global-colors Don't recognize global colors (such as *.color1) on runtime\n"
"spmenu -m, --monitor Specify a monitor to run spmenu on\n"
"spmenu -w, --embed Embed spmenu inside \n"
- "spmenu -H, --hist-file Specify a path to save the history to\n"
+ "spmenu -H, --hist-file Specify a file to save the history to\n"
+ "spmenu -lf, --list-file
Specify a file to load entries from\n"
"spmenu -ig, --image-gaps Set image gaps to \n"
"spmenu -txp, --text-padding Set text padding to \n"
"spmenu -lp, --vertical-padding Set the vertical padding\n"
diff --git a/libs/conf/config.c b/libs/conf/config.c
index cda5040..b255f71 100644
--- a/libs/conf/config.c
+++ b/libs/conf/config.c
@@ -443,6 +443,9 @@ void conf_init(void) {
config_setting_lookup_int(conf, "accuratewidth", &accuratewidth); // spmenu.match.accuratewidth
config_setting_lookup_string(conf, "delimiters", &dest); // spmenu.match.delimiters
worddelimiters = strdup(dest);
+ if (config_setting_lookup_string(conf, "listfile", &dest)) // spmenu.match.listfile
+ if (dest && strcmp(strdup(dest), "NULL"))
+ listfile = strdup(dest);
}
}
diff --git a/libs/event.c b/libs/event.c
index 330f5c8..e41b1e3 100644
--- a/libs/event.c
+++ b/libs/event.c
@@ -7,6 +7,7 @@ void eventloop(void) {
while (!XNextEvent(dpy, &ev)) {
if (XFilterEvent(&ev, None))
continue;
+
switch(ev.type) {
case DestroyNotify:
if (ev.xdestroywindow.window != win)
@@ -31,6 +32,15 @@ void eventloop(void) {
grabfocus();
break;
case KeyPress: // read key array and call functions
+ if (listfile) {
+ readfile();
+
+ if (listchanged) {
+ match();
+ drawmenu();
+ }
+ }
+
if (incremental) {
puts(text);
fflush(stdout);
diff --git a/libs/stream.c b/libs/stream.c
index 25c912e..b0397f4 100644
--- a/libs/stream.c
+++ b/libs/stream.c
@@ -7,7 +7,7 @@ void readstdin(void) {
char *limg = NULL;
#endif
- if (passwd){
+ if (passwd) {
inputw = lines = 0;
return;
}
@@ -112,3 +112,75 @@ void readstdin(void) {
inputw = items ? TEXTWM(items[imax].text) : 0;
lines = MIN(lines, i);
}
+
+void readfile(void) {
+ if (passwd){
+ inputw = lines = 0;
+ return;
+ }
+
+ size_t len;
+ static size_t cap = 0;
+ char *l;
+
+ FILE *ef = fopen(listfile, "r");
+
+ if (!ef) return;
+
+ items = NULL;
+ //list = NULL;
+ listsize = 0;
+
+ for (;;) {
+ l = NULL;
+ len = 0;
+
+ if (-1 == getline(&l, &len, ef)) {
+ if (ferror(ef)) die("spmenu: failed to read file\n");
+ free(l);
+ break;
+ }
+
+ if (cap == listsize) {
+ cap += 64 * sizeof(char*);
+ list = realloc(list, cap);
+ if (!list) die("spmenu: failed to realloc memory");
+ }
+
+ strtok(l, "\n");
+ list[listsize] = l;
+ listsize++;
+ }
+
+ if (fclose(ef)) {
+ die("spmenu: failed to close file %s\n", listfile);
+ }
+
+ if (!list_items) {
+ list_items = items;
+ items = calloc(listsize + 1, sizeof(struct item));
+ if (!items) die("spmenu: cannot alloc memory\n");
+
+ int i = 0;
+
+ for (i = 0; i < listsize; i++) {
+ items[i].text = list[i];
+ }
+
+ if (i == olistcount) {
+ listcount = i;
+ listchanged = 0;
+ } else {
+ olistcount = listcount;
+ listcount = i;
+ listchanged = 1;
+ }
+ } else {
+ free(items);
+ items = list_items;
+ list_items = NULL;
+ }
+
+ //match();
+ //drawmenu();
+}
diff --git a/libs/stream.h b/libs/stream.h
index 2fca8bb..dbcc038 100644
--- a/libs/stream.h
+++ b/libs/stream.h
@@ -1 +1,2 @@
static void readstdin(void);
+static void readfile(void);
diff --git a/options.h b/options.h
index 1a67249..fe05f34 100644
--- a/options.h
+++ b/options.h
@@ -79,6 +79,7 @@ static int casesensitive = 0; /* Case-sensitive by default? (0/1)
static int preselected = 0; /* Which line should spmenu preselect? */
static int accuratewidth = 0; /* Enable accurate width. May have a performance hit if you are matching a lot of items at once */
static int fuzzy = 1; /* Whether or not to enable fuzzy matching by default */
+static char *listfile = NULL; /* File to read entries from instead of stdin. NULL means read from stdin instead. */
/* Line options */
static int lineheight = 1; /* Line height (0: Calculate automatically) */
diff --git a/spmenu.1 b/spmenu.1
index a0c7672..85d268c 100644
--- a/spmenu.1
+++ b/spmenu.1
@@ -286,7 +286,10 @@ Specify a monitor to run spmenu on
Embed spmenu inside window id
.TP
\f[V]-H, --hist-file hist file\f[R]
-Specify a path to save the history to
+Specify a file to save the history to
+.TP
+\f[V]-lf, --list-file list file\f[R]
+Specify a file to load entries from
.TP
\f[V]-ig, --image-gaps gaps\f[R]
Set image gaps to gaps
diff --git a/spmenu.c b/spmenu.c
index 3b17ba4..b1356db 100644
--- a/spmenu.c
+++ b/spmenu.c
@@ -149,7 +149,7 @@ static int vp; // vertical padding for bar
static int sp; // side padding for bar
static int cursorstate = 1; // cursor state
static size_t cursor;
-static struct item *items = NULL, *backup_items;
+static struct item *items = NULL, *backup_items, *list_items;
static struct item *matches, *matchend;
static struct item *prev, *curr, *next, *sel;
static int screen;
@@ -229,6 +229,12 @@ static void setupdisplay(void);
static int max_textw(void);
static size_t nextrune(int inc);
+static char **list;
+static size_t listsize;
+static int listcount;
+static int olistcount;
+static int listchanged = 0;
+
// user configuration
#include "options.h"
#include "keybinds.h"
@@ -256,8 +262,6 @@ static char *(*fstrstr)(const char *, const char *) = cistrstr;
#include "libs/img.c"
#include "libs/rtl.h"
#include "libs/rtl.c"
-#include "libs/event.h"
-#include "libs/event.c"
#include "libs/key.c"
#include "libs/mouse.c"
#include "libs/sort.c"
@@ -273,6 +277,8 @@ static char *(*fstrstr)(const char *, const char *) = cistrstr;
#include "libs/history.c"
#include "libs/arg.c"
#include "libs/stream.c"
+#include "libs/event.h"
+#include "libs/event.c"
void appenditem(struct item *item, struct item **list, struct item **last) {
if (*last)
@@ -736,9 +742,17 @@ int main(int argc, char *argv[]) {
// fast (-f) means we grab keyboard before reading standard input
if (fast && !isatty(0)) {
grabkeyboard();
- readstdin();
+
+ if (!listfile)
+ readstdin();
+ else
+ readfile();
} else {
- readstdin();
+ if (listfile)
+ readfile();
+ else
+ readstdin();
+
grabkeyboard();
}
diff --git a/spmenu.html b/spmenu.html
index 2055f38..b884f0e 100644
--- a/spmenu.html
+++ b/spmenu.html
@@ -505,7 +505,11 @@ Embed spmenu inside window id
-H, --hist-file hist file
-
-Specify a path to save the history to
+Specify a file to save the history to
+
+-lf, --list-file list file
+-
+Specify a file to load entries from
-ig, --image-gaps gaps
-