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