aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile24
-rw-r--r--config.mk2
-rwxr-xr-xdmenu_run4
-rw-r--r--lsx.111
-rw-r--r--lsx.c43
-rw-r--r--stest.187
-rw-r--r--stest.c84
7 files changed, 186 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 976da46..052e51b 100755
--- a/dmenu_run
+++ b/dmenu_run
@@ -7,8 +7,8 @@ else
7fi 7fi
8( 8(
9 IFS=: 9 IFS=:
10 if [ "`ls -dt $PATH "$cache" | head -n 1`" != "$cache" ]; then 10 if stest -dqr -n "$cache" $PATH; then
11 lsx $PATH | sort -u | tee "$cache" | dmenu "$@" 11 stest -flx $PATH | sort -u | tee "$cache" | dmenu "$@"
12 else 12 else
13 dmenu "$@" < "$cache" 13 dmenu "$@" < "$cache"
14 fi 14 fi
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..bb48f45
--- /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 [ -abcdefghlpqrsuwx ]
7.RB [ -n
8.IR file ]
9.RB [ -o
10.IR file ]
11.RI [ file ...]
12.SH DESCRIPTION
13.B stest
14takes a list of files and filters by the files' properties, analogous to
15.IR test (1).
16Files which pass all tests are printed to stdout. If no files are given, stest
17reads files from stdin.
18.SH OPTIONS
19.TP
20.B \-a
21Test hidden files.
22.TP
23.B \-b
24Test that files are block specials.
25.TP
26.B \-c
27Test that files are character specials.
28.TP
29.B \-d
30Test that files are directories.
31.TP
32.B \-e
33Test that files exist.
34.TP
35.B \-f
36Test that files are regular files.
37.TP
38.B \-g
39Test that files have their set-group-ID flag set.
40.TP
41.B \-h
42Test that files are symbolic links.
43.TP
44.B \-l
45Test the contents of a directory given as an argument.
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..e1dcf36
--- /dev/null
+++ b/stest.c
@@ -0,0 +1,84 @@
1/* See LICENSE file for copyright and license details. */
2#include <dirent.h>
3#include <stdbool.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <unistd.h>
8#include <sys/stat.h>
9
10#define FLAG(x) (flag[(x)-'a'])
11
12static void test(const char *, const char *);
13
14static bool match = false;
15static bool flag[26];
16static struct stat old, new;
17
18int
19main(int argc, char *argv[]) {
20 struct dirent *d;
21 char buf[BUFSIZ], *p;
22 DIR *dir;
23 int opt;
24
25 while((opt = getopt(argc, argv, "abcdefghln:o:pqrsuwx")) != -1)
26 switch(opt) {
27 case 'n': /* newer than file */
28 case 'o': /* older than file */
29 if(!(FLAG(opt) = !stat(optarg, (opt == 'n' ? &new : &old))))
30 perror(optarg);
31 break;
32 default: /* miscellaneous operators */
33 FLAG(opt) = true;
34 break;
35 case '?': /* error: unknown flag */
36 fprintf(stderr, "usage: %s [-abcdefghlpqrsuwx] [-n file] [-o file] [file...]\n", argv[0]);
37 exit(2);
38 }
39 if(optind == argc)
40 while(fgets(buf, sizeof buf, stdin)) {
41 if((p = strchr(buf, '\n')))
42 *p = '\0';
43 test(buf, buf);
44 }
45 for(; optind < argc; optind++)
46 if(FLAG('l') && (dir = opendir(argv[optind]))) {
47 /* test directory contents */
48 while((d = readdir(dir)))
49 if(snprintf(buf, sizeof buf, "%s/%s", argv[optind], d->d_name) < sizeof buf)
50 test(buf, d->d_name);
51 closedir(dir);
52 }
53 else
54 test(argv[optind], argv[optind]);
55
56 return match ? 0 : 1;
57}
58
59void
60test(const char *path, const char *name) {
61 struct stat st, ln;
62
63 if(!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */
64 && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */
65 && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */
66 && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */
67 && (!FLAG('e') || access(path, F_OK) == 0) /* exists */
68 && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */
69 && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */
70 && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */
71 && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */
72 && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */
73 && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */
74 && (!FLAG('r') || access(path, R_OK) == 0) /* readable */
75 && (!FLAG('s') || st.st_size > 0) /* not empty */
76 && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */
77 && (!FLAG('w') || access(path, W_OK) == 0) /* writable */
78 && (!FLAG('x') || access(path, X_OK) == 0)) { /* executable */
79 if(FLAG('q'))
80 exit(0);
81 match = true;
82 puts(name);
83 }
84}