aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile24
-rw-r--r--config.mk2
-rwxr-xr-xdmenu_run6
-rw-r--r--lsx.111
-rw-r--r--lsx.c43
-rw-r--r--stest.187
-rw-r--r--stest.c85
7 files changed, 189 insertions, 69 deletions
diff --git a/Makefile b/Makefile
index 929b108..c127f6a 100644
--- a/Makefile
+++ b/Makefile
@@ -3,10 +3,10 @@
3 3
4include config.mk 4include config.mk
5 5
6SRC = dmenu.c draw.c lsx.c 6SRC = dmenu.c draw.c stest.c
7OBJ = ${SRC:.c=.o} 7OBJ = ${SRC:.c=.o}
8 8
9all: options dmenu lsx 9all: options dmenu stest
10 10
11options: 11options:
12 @echo dmenu build options: 12 @echo dmenu build options:
@@ -24,18 +24,18 @@ dmenu: dmenu.o draw.o
24 @echo CC -o $@ 24 @echo CC -o $@
25 @${CC} -o $@ dmenu.o draw.o ${LDFLAGS} 25 @${CC} -o $@ dmenu.o draw.o ${LDFLAGS}
26 26
27lsx: lsx.o 27stest: stest.o
28 @echo CC -o $@ 28 @echo CC -o $@
29 @${CC} -o $@ lsx.o ${LDFLAGS} 29 @${CC} -o $@ stest.o ${LDFLAGS}
30 30
31clean: 31clean:
32 @echo cleaning 32 @echo cleaning
33 @rm -f dmenu lsx ${OBJ} dmenu-${VERSION}.tar.gz 33 @rm -f dmenu stest ${OBJ} dmenu-${VERSION}.tar.gz
34 34
35dist: clean 35dist: clean
36 @echo creating dist tarball 36 @echo creating dist tarball
37 @mkdir -p dmenu-${VERSION} 37 @mkdir -p dmenu-${VERSION}
38 @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_run lsx.1 ${SRC} dmenu-${VERSION} 38 @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_run stest.1 ${SRC} dmenu-${VERSION}
39 @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} 39 @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
40 @gzip dmenu-${VERSION}.tar 40 @gzip dmenu-${VERSION}.tar
41 @rm -rf dmenu-${VERSION} 41 @rm -rf dmenu-${VERSION}
@@ -43,24 +43,24 @@ dist: clean
43install: all 43install: all
44 @echo installing executables to ${DESTDIR}${PREFIX}/bin 44 @echo installing executables to ${DESTDIR}${PREFIX}/bin
45 @mkdir -p ${DESTDIR}${PREFIX}/bin 45 @mkdir -p ${DESTDIR}${PREFIX}/bin
46 @cp -f dmenu dmenu_run lsx ${DESTDIR}${PREFIX}/bin 46 @cp -f dmenu dmenu_run stest ${DESTDIR}${PREFIX}/bin
47 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu 47 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
48 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run 48 @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
49 @chmod 755 ${DESTDIR}${PREFIX}/bin/lsx 49 @chmod 755 ${DESTDIR}${PREFIX}/bin/stest
50 @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 50 @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1
51 @mkdir -p ${DESTDIR}${MANPREFIX}/man1 51 @mkdir -p ${DESTDIR}${MANPREFIX}/man1
52 @sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1 52 @sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1
53 @sed "s/VERSION/${VERSION}/g" < lsx.1 > ${DESTDIR}${MANPREFIX}/man1/lsx.1 53 @sed "s/VERSION/${VERSION}/g" < stest.1 > ${DESTDIR}${MANPREFIX}/man1/stest.1
54 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1 54 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
55 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/lsx.1 55 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/stest.1
56 56
57uninstall: 57uninstall:
58 @echo removing executables from ${DESTDIR}${PREFIX}/bin 58 @echo removing executables from ${DESTDIR}${PREFIX}/bin
59 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu 59 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu
60 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run 60 @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run
61 @rm -f ${DESTDIR}${PREFIX}/bin/lsx 61 @rm -f ${DESTDIR}${PREFIX}/bin/stest
62 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 62 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
63 @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 63 @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
64 @rm -f ${DESTDIR}${MANPREFIX}/man1/lsx.1 64 @rm -f ${DESTDIR}${MANPREFIX}/man1/stest.1
65 65
66.PHONY: all options clean dist install uninstall 66.PHONY: all options clean dist install uninstall
diff --git a/config.mk b/config.mk
index f66389d..87a87f9 100644
--- a/config.mk
+++ b/config.mk
@@ -17,7 +17,7 @@ INCS = -I${X11INC}
17LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} 17LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
18 18
19# flags 19# flags
20CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} 20CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
21CFLAGS = -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS} 21CFLAGS = -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
22LDFLAGS = -s ${LIBS} 22LDFLAGS = -s ${LIBS}
23 23
diff --git a/dmenu_run b/dmenu_run
index 2747919..a15df0f 100755
--- a/dmenu_run
+++ b/dmenu_run
@@ -5,8 +5,10 @@ if [ ! -d "`dirname "$CACHE"`" ]; then
5fi 5fi
6( 6(
7 IFS=: 7 IFS=:
8 if [ "`ls -dt $PATH "$CACHE" | head -n 1`" != "$CACHE" ]; then 8 if ls -d $PATH | stest -q -n "$CACHE"; then
9 lsx $PATH | sort -u > "$CACHE" 9 for dir in $PATH; do
10 ls $dir | stest -C $dir -fx
11 done | sort -u > "$CACHE"
10 fi 12 fi
11) 13)
12cmd=`dmenu "$@" < "$CACHE"` && exec sh -c "$cmd" 14cmd=`dmenu "$@" < "$CACHE"` && exec sh -c "$cmd"
diff --git a/lsx.1 b/lsx.1
deleted file mode 100644
index 1b2a15e..0000000
--- a/lsx.1
+++ /dev/null
@@ -1,11 +0,0 @@
1.TH LSX 1 dmenu\-VERSION
2.SH NAME
3lsx \- list executables
4.SH SYNOPSIS
5.B lsx
6.RI [ directory ...]
7.SH DESCRIPTION
8.B lsx
9lists the executables in each
10.IR directory .
11If none are given the current working directory is used.
diff --git a/lsx.c b/lsx.c
deleted file mode 100644
index cb016cf..0000000
--- a/lsx.c
+++ /dev/null
@@ -1,43 +0,0 @@
1/* See LICENSE file for copyright and license details. */
2#include <dirent.h>
3#include <errno.h>
4#include <limits.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <unistd.h>
8#include <sys/stat.h>
9
10static void lsx(const char *dir);
11
12static int status = EXIT_SUCCESS;
13
14int
15main(int argc, char *argv[]) {
16 int i;
17
18 if(argc < 2)
19 lsx(".");
20 else for(i = 1; i < argc; i++)
21 lsx(argv[i]);
22 return status;
23}
24
25void
26lsx(const char *dir) {
27 char buf[PATH_MAX];
28 struct dirent *d;
29 struct stat st;
30 DIR *dp;
31
32 for(dp = opendir(dir); dp && (d = readdir(dp)); errno = 0)
33 if(snprintf(buf, sizeof buf, "%s/%s", dir, d->d_name) < (int)sizeof buf
34 && access(buf, X_OK) == 0 && stat(buf, &st) == 0 && S_ISREG(st.st_mode))
35 puts(d->d_name);
36
37 if(errno != 0) {
38 status = EXIT_FAILURE;
39 perror(dir);
40 }
41 if(dp)
42 closedir(dp);
43}
diff --git a/stest.1 b/stest.1
new file mode 100644
index 0000000..cafd4bb
--- /dev/null
+++ b/stest.1
@@ -0,0 +1,87 @@
1.TH STEST 1 dmenu\-VERSION
2.SH NAME
3stest \- filter a list of files by properties
4.SH SYNOPSIS
5.B stest
6.RB [ -bcdefghpqrsuwx ]
7.RB [ -C
8.IR dir ]
9.RB [ -n
10.IR file ]
11.RB [ -o
12.IR file ]
13.RI [ file ...]
14.SH DESCRIPTION
15.B stest
16takes a list of files and filters by the files' properties, analogous to
17.IR test (1).
18Files which pass all tests are printed to stdout. If no files are given as
19arguments, stest will read a list of files from stdin, one path per line.
20.SH OPTIONS
21.TP
22.BI \-C " dir"
23Tests files relative to directory
24.IR dir .
25.TP
26.B \-b
27Test that files are block specials.
28.TP
29.B \-c
30Test that files are character specials.
31.TP
32.B \-d
33Test that files are directories.
34.TP
35.B \-e
36Test that files exist.
37.TP
38.B \-f
39Test that files are regular files.
40.TP
41.B \-g
42Test that files have their set-group-ID flag set.
43.TP
44.B \-h
45Test that files are symbolic links.
46.TP
47.BI \-n " file"
48Test that files are newer than
49.IR file .
50.TP
51.BI \-o " file"
52Test that files are older than
53.IR file .
54.TP
55.B \-p
56Test that files are named pipes.
57.TP
58.B \-q
59No files are printed, only the exit status is returned.
60.TP
61.B \-r
62Test that files are readable.
63.TP
64.B \-s
65Test that files are not empty.
66.TP
67.B \-u
68Test that files have their set-user-ID flag set.
69.TP
70.B \-w
71Test that files are writable.
72.TP
73.B \-x
74Test that files are executable.
75.SH EXIT STATUS
76.TP
77.B 0
78At least one file passed all tests.
79.TP
80.B 1
81No files passed all tests.
82.TP
83.B 2
84An error occurred.
85.SH SEE ALSO
86.IR dmenu (1),
87.IR test (1)
diff --git a/stest.c b/stest.c
new file mode 100644
index 0000000..a5596ea
--- /dev/null
+++ b/stest.c
@@ -0,0 +1,85 @@
1/* See LICENSE file for copyright and license details. */
2#include <stdbool.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 OPER(x) (oper[(x)-'a'])
10
11static bool test(const char *);
12
13static bool quiet = false;
14static bool oper[26];
15static struct stat old, new;
16
17int
18main(int argc, char *argv[]) {
19 char buf[BUFSIZ], *p;
20 bool match = false;
21 int opt;
22
23 while((opt = getopt(argc, argv, "C:bcdefghn:o:pqrsuwx")) != -1)
24 switch(opt) {
25 case 'C': /* tests relative to directory */
26 if(chdir(optarg) == -1) {
27 perror(optarg);
28 exit(2);
29 }
30 break;
31 case 'n': /* newer than file */
32 case 'o': /* older than file */
33 if(!(OPER(opt) = stat(optarg, (opt == 'n' ? &new : &old)) == 0))
34 perror(optarg);
35 break;
36 case 'q': /* quiet (no output, just status) */
37 quiet = true;
38 break;
39 default: /* miscellaneous operators */
40 OPER(opt) = true;
41 break;
42 case '?': /* error: unknown flag */
43 fprintf(stderr, "usage: %s [-bcdefghpqrsuwx] [-C dir] [-n file] [-o file] [file...]\n", argv[0]);
44 exit(2);
45 }
46 if(optind == argc)
47 while(fgets(buf, sizeof buf, stdin)) {
48 if(*(p = &buf[strlen(buf)-1]) == '\n')
49 *p = '\0';
50 match |= test(buf);
51 }
52 else
53 while(optind < argc)
54 match |= test(argv[optind++]);
55
56 return match ? 0 : 1;
57}
58
59bool
60test(const char *path) {
61 struct stat st;
62
63 if((!OPER('b') || (stat(path, &st) == 0 && S_ISBLK(st.st_mode))) /* block special */
64 && (!OPER('c') || (stat(path, &st) == 0 && S_ISCHR(st.st_mode))) /* character special */
65 && (!OPER('d') || (stat(path, &st) == 0 && S_ISDIR(st.st_mode))) /* directory */
66 && (!OPER('e') || (access(path, F_OK) == 0)) /* exists */
67 && (!OPER('f') || (stat(path, &st) == 0 && S_ISREG(st.st_mode))) /* regular file */
68 && (!OPER('g') || (stat(path, &st) == 0 && (st.st_mode & S_ISGID))) /* set-group-id flag */
69 && (!OPER('h') || (lstat(path, &st) == 0 && S_ISLNK(st.st_mode))) /* symbolic link */
70 && (!OPER('n') || (stat(path, &st) == 0 && st.st_mtime > new.st_mtime)) /* newer than file */
71 && (!OPER('o') || (stat(path, &st) == 0 && st.st_mtime < old.st_mtime)) /* older than file */
72 && (!OPER('p') || (stat(path, &st) == 0 && S_ISFIFO(st.st_mode))) /* named pipe */
73 && (!OPER('r') || (access(path, R_OK) == 0)) /* readable */
74 && (!OPER('s') || (stat(path, &st) == 0 && st.st_size > 0)) /* not empty */
75 && (!OPER('u') || (stat(path, &st) == 0 && (st.st_mode & S_ISUID))) /* set-user-id flag */
76 && (!OPER('w') || (access(path, W_OK) == 0)) /* writable */
77 && (!OPER('x') || (access(path, X_OK) == 0))) { /* executable */
78 if(quiet)
79 exit(0);
80 puts(path);
81 return true;
82 }
83 else
84 return false;
85}