From: Connor Lane Smith Date: Fri, 8 Oct 2010 22:24:22 +0000 (+0100) Subject: dmenu_path.c (shell is a bottleneck) X-Git-Url: https://git.danieliu.xyz/?a=commitdiff_plain;h=bf7b8e37ee2a53f0f1bed75dd84e5214269cfac8;p=dmenu.git dmenu_path.c (shell is a bottleneck) --- diff --git a/Makefile b/Makefile index 1082697..c183779 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ include config.mk -all: options dmenu +all: options dmenu dmenu_path options: @echo dmenu build options: @@ -11,22 +11,21 @@ options: @echo "LDFLAGS = ${LDFLAGS}" @echo "CC = ${CC}" -dmenu.o: dmenu.c config.mk - @echo CC $< - @${CC} -c ${CFLAGS} $< +dmenu: dmenu.c config.mk +dmenu_path: dmenu_path.c -dmenu: dmenu.o +dmenu dmenu_path: @echo CC -o $@ - @${CC} -o $@ $+ ${LDFLAGS} + @${CC} -o $@ $< ${CFLAGS} ${LDFLAGS} clean: @echo cleaning - @rm -f dmenu dmenu.o dmenu-${VERSION}.tar.gz + @rm -f dmenu dmenu_path dmenu-${VERSION}.tar.gz dist: clean @echo creating dist tarball @mkdir -p dmenu-${VERSION} - @cp LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path dmenu_run dmenu-${VERSION} + @cp LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path.c dmenu_run dmenu-${VERSION} @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} @gzip dmenu-${VERSION}.tar @rm -rf dmenu-${VERSION} diff --git a/dmenu_path b/dmenu_path deleted file mode 100755 index a9ddd47..0000000 --- a/dmenu_path +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh -CACHE=$HOME/.dmenu_cache -IFS=: - -uptodate() { - test -f "$CACHE" && - for dir in $PATH - do - test ! $dir -nt "$CACHE" || return 1 - done -} - -if ! uptodate -then - for dir in $PATH - do - cd "$dir" && - for file in * - do - test -x "$file" && echo "$file" - done - done | sort -u > "$CACHE".$$ && - mv "$CACHE".$$ "$CACHE" -fi - -cat "$CACHE" diff --git a/dmenu_path.c b/dmenu_path.c new file mode 100644 index 0000000..1575f1d --- /dev/null +++ b/dmenu_path.c @@ -0,0 +1,101 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include + +#define CACHE ".dmenu_cache" + +static int qstrcmp(const void *a, const void *b); +static void die(const char *s); +static void scan(void); +static int uptodate(void); + +static char **items = NULL; +static const char *Home, *Path; +static size_t count = 0; + +int +main(void) { + if(!(Home = getenv("HOME"))) + die("no $HOME"); + if(!(Path = getenv("PATH"))) + die("no $PATH"); + if(chdir(Home) < 0) + die("chdir failed"); + if(uptodate()) { + execlp("cat", "cat", CACHE, NULL); + die("exec failed"); + } + scan(); + return EXIT_SUCCESS; +} + +void +die(const char *s) { + fprintf(stderr, "dmenu_path: %s\n", s); + exit(EXIT_FAILURE); +} + +int +qstrcmp(const void *a, const void *b) { + return strcmp(*(const char **)a, *(const char **)b); +} + +void +scan(void) { + char buf[PATH_MAX]; + char *dir, *path; + size_t i; + struct dirent *ent; + DIR *dp; + FILE *cache; + + if(!(path = strdup(Path))) + die("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(!(items = realloc(items, ++count * sizeof *items))) + die("malloc failed"); + if(!(items[count-1] = strdup(ent->d_name))) + die("strdup failed"); + } + closedir(dp); + } + qsort(items, count, sizeof *items, qstrcmp); + if(!(cache = fopen(CACHE, "w"))) + die("open failed"); + for(i = 0; i < count; i++) { + if(i > 0 && !strcmp(items[i], items[i-1])) + continue; + fprintf(cache, "%s\n", items[i]); + fprintf(stdout, "%s\n", items[i]); + } + fclose(cache); + free(path); +} + +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))) + die("strdup failed"); + for(dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) + if(!stat(dir, &st) && st.st_mtime > mtime) + return 0; + free(path); + return 1; +}