dmenu_path.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/* See LICENSE file for copyright and license details. */ #include <dirent.h> #include <limits.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/stat.h> static void die(const char *s); static int qstrcmp(const void *a, const void *b); static void scan(void); static int uptodate(void); static char **items = NULL; static const char *home, *path; 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()) { execl("/bin/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, *p; size_t i, count; struct dirent *ent; DIR *dp; FILE *cache; count = 0; if(!(p = strdup(path))) die("strdup failed"); for(dir = strtok(p, ":"); 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(p); } int uptodate(void) { char *dir, *p; time_t mtime; struct stat st; if(stat(CACHE, &st) < 0) return 0; mtime = st.st_mtime; if(!(p = strdup(path))) die("strdup failed"); for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":")) if(!stat(dir, &st) && st.st_mtime > mtime) return 0; free(p); return 1; }