dmenu_path.c (shell is a bottleneck)
bf7b8e37
3 file(s) · +108 −34
| 3 | 3 | ||
| 4 | 4 | include config.mk |
|
| 5 | 5 | ||
| 6 | - | all: options dmenu |
|
| 6 | + | all: options dmenu dmenu_path |
|
| 7 | 7 | ||
| 8 | 8 | options: |
|
| 9 | 9 | @echo dmenu build options: |
|
| 11 | 11 | @echo "LDFLAGS = ${LDFLAGS}" |
|
| 12 | 12 | @echo "CC = ${CC}" |
|
| 13 | 13 | ||
| 14 | - | dmenu.o: dmenu.c config.mk |
|
| 15 | - | @echo CC $< |
|
| 16 | - | @${CC} -c ${CFLAGS} $< |
|
| 14 | + | dmenu: dmenu.c config.mk |
|
| 15 | + | dmenu_path: dmenu_path.c |
|
| 17 | 16 | ||
| 18 | - | dmenu: dmenu.o |
|
| 17 | + | dmenu dmenu_path: |
|
| 19 | 18 | @echo CC -o $@ |
|
| 20 | - | @${CC} -o $@ $+ ${LDFLAGS} |
|
| 19 | + | @${CC} -o $@ $< ${CFLAGS} ${LDFLAGS} |
|
| 21 | 20 | ||
| 22 | 21 | clean: |
|
| 23 | 22 | @echo cleaning |
|
| 24 | - | @rm -f dmenu dmenu.o dmenu-${VERSION}.tar.gz |
|
| 23 | + | @rm -f dmenu dmenu_path dmenu-${VERSION}.tar.gz |
|
| 25 | 24 | ||
| 26 | 25 | dist: clean |
|
| 27 | 26 | @echo creating dist tarball |
|
| 28 | 27 | @mkdir -p dmenu-${VERSION} |
|
| 29 | - | @cp LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path dmenu_run dmenu-${VERSION} |
|
| 28 | + | @cp LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path.c dmenu_run dmenu-${VERSION} |
|
| 30 | 29 | @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} |
|
| 31 | 30 | @gzip dmenu-${VERSION}.tar |
|
| 32 | 31 | @rm -rf dmenu-${VERSION} |
|
| 1 | - | #!/bin/sh |
|
| 2 | - | CACHE=$HOME/.dmenu_cache |
|
| 3 | - | IFS=: |
|
| 4 | - | ||
| 5 | - | uptodate() { |
|
| 6 | - | test -f "$CACHE" && |
|
| 7 | - | for dir in $PATH |
|
| 8 | - | do |
|
| 9 | - | test ! $dir -nt "$CACHE" || return 1 |
|
| 10 | - | done |
|
| 11 | - | } |
|
| 12 | - | ||
| 13 | - | if ! uptodate |
|
| 14 | - | then |
|
| 15 | - | for dir in $PATH |
|
| 16 | - | do |
|
| 17 | - | cd "$dir" && |
|
| 18 | - | for file in * |
|
| 19 | - | do |
|
| 20 | - | test -x "$file" && echo "$file" |
|
| 21 | - | done |
|
| 22 | - | done | sort -u > "$CACHE".$$ && |
|
| 23 | - | mv "$CACHE".$$ "$CACHE" |
|
| 24 | - | fi |
|
| 25 | - | ||
| 26 | - | cat "$CACHE" |
| 1 | + | /* See LICENSE file for copyright and license details. */ |
|
| 2 | + | #include <dirent.h> |
|
| 3 | + | #include <stdio.h> |
|
| 4 | + | #include <stdlib.h> |
|
| 5 | + | #include <string.h> |
|
| 6 | + | #include <unistd.h> |
|
| 7 | + | #include <sys/stat.h> |
|
| 8 | + | ||
| 9 | + | #define CACHE ".dmenu_cache" |
|
| 10 | + | ||
| 11 | + | static int qstrcmp(const void *a, const void *b); |
|
| 12 | + | static void die(const char *s); |
|
| 13 | + | static void scan(void); |
|
| 14 | + | static int uptodate(void); |
|
| 15 | + | ||
| 16 | + | static char **items = NULL; |
|
| 17 | + | static const char *Home, *Path; |
|
| 18 | + | static size_t count = 0; |
|
| 19 | + | ||
| 20 | + | int |
|
| 21 | + | main(void) { |
|
| 22 | + | if(!(Home = getenv("HOME"))) |
|
| 23 | + | die("no $HOME"); |
|
| 24 | + | if(!(Path = getenv("PATH"))) |
|
| 25 | + | die("no $PATH"); |
|
| 26 | + | if(chdir(Home) < 0) |
|
| 27 | + | die("chdir failed"); |
|
| 28 | + | if(uptodate()) { |
|
| 29 | + | execlp("cat", "cat", CACHE, NULL); |
|
| 30 | + | die("exec failed"); |
|
| 31 | + | } |
|
| 32 | + | scan(); |
|
| 33 | + | return EXIT_SUCCESS; |
|
| 34 | + | } |
|
| 35 | + | ||
| 36 | + | void |
|
| 37 | + | die(const char *s) { |
|
| 38 | + | fprintf(stderr, "dmenu_path: %s\n", s); |
|
| 39 | + | exit(EXIT_FAILURE); |
|
| 40 | + | } |
|
| 41 | + | ||
| 42 | + | int |
|
| 43 | + | qstrcmp(const void *a, const void *b) { |
|
| 44 | + | return strcmp(*(const char **)a, *(const char **)b); |
|
| 45 | + | } |
|
| 46 | + | ||
| 47 | + | void |
|
| 48 | + | scan(void) { |
|
| 49 | + | char buf[PATH_MAX]; |
|
| 50 | + | char *dir, *path; |
|
| 51 | + | size_t i; |
|
| 52 | + | struct dirent *ent; |
|
| 53 | + | DIR *dp; |
|
| 54 | + | FILE *cache; |
|
| 55 | + | ||
| 56 | + | if(!(path = strdup(Path))) |
|
| 57 | + | die("strdup failed"); |
|
| 58 | + | for(dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) { |
|
| 59 | + | if(!(dp = opendir(dir))) |
|
| 60 | + | continue; |
|
| 61 | + | while((ent = readdir(dp))) { |
|
| 62 | + | snprintf(buf, sizeof buf, "%s/%s", dir, ent->d_name); |
|
| 63 | + | if(ent->d_name[0] == '.' || access(buf, X_OK) < 0) |
|
| 64 | + | continue; |
|
| 65 | + | if(!(items = realloc(items, ++count * sizeof *items))) |
|
| 66 | + | die("malloc failed"); |
|
| 67 | + | if(!(items[count-1] = strdup(ent->d_name))) |
|
| 68 | + | die("strdup failed"); |
|
| 69 | + | } |
|
| 70 | + | closedir(dp); |
|
| 71 | + | } |
|
| 72 | + | qsort(items, count, sizeof *items, qstrcmp); |
|
| 73 | + | if(!(cache = fopen(CACHE, "w"))) |
|
| 74 | + | die("open failed"); |
|
| 75 | + | for(i = 0; i < count; i++) { |
|
| 76 | + | if(i > 0 && !strcmp(items[i], items[i-1])) |
|
| 77 | + | continue; |
|
| 78 | + | fprintf(cache, "%s\n", items[i]); |
|
| 79 | + | fprintf(stdout, "%s\n", items[i]); |
|
| 80 | + | } |
|
| 81 | + | fclose(cache); |
|
| 82 | + | free(path); |
|
| 83 | + | } |
|
| 84 | + | ||
| 85 | + | int |
|
| 86 | + | uptodate(void) { |
|
| 87 | + | char *dir, *path; |
|
| 88 | + | time_t mtime; |
|
| 89 | + | struct stat st; |
|
| 90 | + | ||
| 91 | + | if(stat(CACHE, &st) < 0) |
|
| 92 | + | return 0; |
|
| 93 | + | mtime = st.st_mtime; |
|
| 94 | + | if(!(path = strdup(Path))) |
|
| 95 | + | die("strdup failed"); |
|
| 96 | + | for(dir = strtok(path, ":"); dir; dir = strtok(NULL, ":")) |
|
| 97 | + | if(!stat(dir, &st) && st.st_mtime > mtime) |
|
| 98 | + | return 0; |
|
| 99 | + | free(path); |
|
| 100 | + | return 1; |
|
| 101 | + | } |