diff --git a/Makefile b/Makefile index 0f7dfbd..e07e87f 100644 --- a/Makefile +++ b/Makefile @@ -39,7 +39,7 @@ clean: dist: clean @echo creating dist tarball @mkdir -p dmenu-${VERSION} - @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_path dmenu_run stest.1 ${SRC} dmenu-${VERSION} + @cp LICENSE Makefile README config.mk dmenu.1 draw.h stest.1 ${SRC} dmenu-${VERSION} @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} @gzip dmenu-${VERSION}.tar @rm -rf dmenu-${VERSION} @@ -47,10 +47,9 @@ dist: clean install: all @echo installing executables to ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin - @cp -f dmenu dmenu_path dmenu_run stest ${DESTDIR}${PREFIX}/bin + @cp -f dmenu stest ${DESTDIR}${PREFIX}/bin @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu - @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path - @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run + @(cd ${DESTDIR}${PREFIX}/bin && ln -sf dmenu dmenu_run) @chmod 755 ${DESTDIR}${PREFIX}/bin/stest @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 @mkdir -p ${DESTDIR}${MANPREFIX}/man1 @@ -62,7 +61,6 @@ install: all uninstall: @echo removing executables from ${DESTDIR}${PREFIX}/bin @rm -f ${DESTDIR}${PREFIX}/bin/dmenu - @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run @rm -f ${DESTDIR}${PREFIX}/bin/stest @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 diff --git a/config.def.h b/config.def.h index c2a23fa..98a0524 100644 --- a/config.def.h +++ b/config.def.h @@ -3,6 +3,8 @@ */ /* Default settings; can be overrided by command line. */ +#define CACHE ".cache/dmenu_run" + static Bool topbar = True; /* -b option; if False, dmenu appears at bottom */ static const char *font = NULL; /* -fn option; default X11 font or font set */ static const char *prompt = NULL; /* -p option; prompt to the elft of input field */ diff --git a/dmenu.c b/dmenu.c index 94c70de..8a97eda 100644 --- a/dmenu.c +++ b/dmenu.c @@ -1,9 +1,11 @@ /* See LICENSE file for copyright and license details. */ #include +#include #include #include #include #include +#include #include #include #include @@ -35,9 +37,13 @@ static void keypress(XKeyEvent *ev); static void match(void); static size_t nextrune(int inc); static void paste(void); +static int qstrcmp(const void *a, const void *b); static void readstdin(void); static void run(void); +static void scan(void); static void setup(void); +static void updatecache(void); +static int uptodate(void); static void usage(void); static char text[BUFSIZ] = ""; @@ -52,6 +58,8 @@ static DC *dc; static Item *items = NULL; static Item *matches, *matchend; static Item *prev, *curr, *next, *sel; +static Bool dmenurun = False; +static const char *HOME, *PATH; static Window win; static XIC xic; static int mon = -1; @@ -66,6 +74,11 @@ main(int argc, char *argv[]) { Bool fast = False; int i; + if(strcmp(argv[0], "dmenu_run") == 0) { + dmenurun = True; + updatecache(); + } + for(i = 1; i < argc; i++) /* these options take no arguments */ if(!strcmp(argv[i], "-v")) { /* prints version information */ @@ -367,6 +380,8 @@ keypress(XKeyEvent *ev) { break; case XK_Return: case XK_KP_Enter: + if(dmenurun) + execlp(sel->text, sel->text, NULL); /* dmenu_run */ puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); if(!(ev->state & ControlMask)) exit(EXIT_SUCCESS); @@ -477,13 +492,23 @@ paste(void) { drawmenu(); } +int +qstrcmp(const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + void readstdin(void) { char buf[sizeof text], *p, *maxstr = NULL; size_t i, max = 0, size = 0; + FILE *cache; + + if(dmenurun) + if(!(cache = fopen(CACHE, "r"))) + eprintf("open failed"); - /* read each line from stdin and add it to the item list */ - for(i = 0; fgets(buf, sizeof buf, stdin); i++) { + /* read each line from stdin or cache and add it to the item list */ + for(i = 0; fgets(buf, sizeof buf, (dmenurun) ? cache : stdin); i++) { if(i+1 >= size / sizeof *items) if(!(items = realloc(items, (size += BUFSIZ)))) eprintf("cannot realloc %u bytes:", size); @@ -499,6 +524,9 @@ readstdin(void) { items[i].text = NULL; inputw = maxstr ? textw(dc, maxstr) : 0; lines = MIN(lines, i); + + if(dmenurun) + fclose(cache); } void @@ -529,6 +557,43 @@ run(void) { } void +scan(void) { + char buf[PATH_MAX]; + char *dir, *path, **bins = NULL; + size_t i, count = 0; + struct dirent *ent; + DIR *dp; + FILE *cache; + + if(!(path = strdup(PATH))) + eprintf("strdup failed"); + for(dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) { + if(!(dp = opendir(dir))) + continue; + while((ent = readdir(dp))) { + snprintf(buf, sizeof buf, "%s/%s", dir, ent->d_name); + if(ent->d_name[0] == '.' || access(buf, X_OK) < 0) + continue; + if(!(bins = realloc(bins, ++count * sizeof *bins))) + eprintf("malloc failed"); + if(!(bins[count-1] = strdup(ent->d_name))) + eprintf("strdup failed"); + } + closedir(dp); + } + qsort(bins, count, sizeof *bins, qstrcmp); + if(!(cache = fopen(CACHE, "w"))) + eprintf("open failed"); + for(i = 0; i < count; i++) { + if(i > 0 && !strcmp(bins[i], bins[i-1])) + continue; + fprintf(cache, "%s\n", bins[i]); + } + fclose(cache); + free(path); +} + +void setup(void) { int x, y, screen = DefaultScreen(dc->dpy); Window root = RootWindow(dc->dpy, screen); @@ -619,6 +684,36 @@ setup(void) { } void +updatecache(void) { + if(!(HOME = getenv("HOME"))) + eprintf("no $HOME"); + if(!(PATH = getenv("PATH"))) + eprintf("no $PATH"); + if(chdir(HOME) < 0) + eprintf("chdir failed"); + if(!(uptodate())) + scan(); +} + +int +uptodate(void) { + char *dir, *path; + time_t mtime; + struct stat st; + + if(stat(CACHE, &st) < 0) + return 0; + mtime = st.st_mtime; + if(!(path = strdup(PATH))) + eprintf("strdup failed"); + for(dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) + if(!stat(dir, &st) && st.st_mtime > mtime) + return 0; + free(path); + return 1; +} + +void usage(void) { fputs("usage: dmenu [-b] [-f] [-i] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" " [-nb color] [-nf color] [-sb color] [-sf color] [-v]\n", stderr); diff --git a/dmenu_path b/dmenu_path deleted file mode 100644 index 338bac4..0000000 --- a/dmenu_path +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/sh -cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"} -if [ -d "$cachedir" ]; then - cache=$cachedir/dmenu_run -else - cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~ -fi -IFS=: -if stest -dqr -n "$cache" $PATH; then - stest -flx $PATH | sort -u | tee "$cache" -else - cat "$cache" -fi diff --git a/dmenu_run b/dmenu_run deleted file mode 100755 index 834ede5..0000000 --- a/dmenu_run +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &