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