simplify stest

This commit is contained in:
Connor Lane Smith 2011-11-27 23:35:09 +01:00
parent bb4424df07
commit 8cc28cb426
3 changed files with 53 additions and 62 deletions

View File

@ -5,10 +5,8 @@ if [ ! -d "`dirname "$CACHE"`" ]; then
fi fi
( (
IFS=: IFS=:
if ls -d $PATH | stest -q -n "$CACHE"; then if stest -dqr -n "$CACHE" $PATH; then
for dir in $PATH; do stest -flx $PATH | sort -u > "$CACHE"
ls $dir | stest -C $dir -fx
done | sort -u > "$CACHE"
fi fi
) )
cmd=`dmenu "$@" < "$CACHE"` && exec sh -c "$cmd" cmd=`dmenu "$@" < "$CACHE"` && exec sh -c "$cmd"

15
stest.1
View File

@ -3,9 +3,7 @@
stest \- filter a list of files by properties stest \- filter a list of files by properties
.SH SYNOPSIS .SH SYNOPSIS
.B stest .B stest
.RB [ -bcdefghpqrsuwx ] .RB [ -abcdefghlpqrsuwx ]
.RB [ -C
.IR dir ]
.RB [ -n .RB [ -n
.IR file ] .IR file ]
.RB [ -o .RB [ -o
@ -15,13 +13,11 @@ stest \- filter a list of files by properties
.B stest .B stest
takes a list of files and filters by the files' properties, analogous to takes a list of files and filters by the files' properties, analogous to
.IR test (1). .IR test (1).
Files which pass all tests are printed to stdout. If no files are given as Files which pass all tests are printed to stdout.
arguments, stest will read a list of files from stdin, one path per line.
.SH OPTIONS .SH OPTIONS
.TP .TP
.BI \-C " dir" .B \-a
Tests files relative to directory Test hidden files.
.IR dir .
.TP .TP
.B \-b .B \-b
Test that files are block specials. Test that files are block specials.
@ -44,6 +40,9 @@ Test that files have their set-group-ID flag set.
.B \-h .B \-h
Test that files are symbolic links. Test that files are symbolic links.
.TP .TP
.B \-l
Test the contents of a directory given as an argument.
.TP
.BI \-n " file" .BI \-n " file"
Test that files are newer than Test that files are newer than
.IR file . .IR file .

94
stest.c
View File

@ -1,4 +1,5 @@
/* See LICENSE file for copyright and license details. */ /* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -6,80 +7,73 @@
#include <unistd.h> #include <unistd.h>
#include <sys/stat.h> #include <sys/stat.h>
#define OPER(x) (oper[(x)-'a']) #define FLAG(x) (flag[(x)-'a'])
static bool test(const char *); static void test(const char *, const char *);
static bool quiet = false; static bool match = false;
static bool oper[26]; static bool flag[26];
static struct stat old, new; static struct stat old, new;
int int
main(int argc, char *argv[]) { main(int argc, char *argv[]) {
char buf[BUFSIZ], *p; struct dirent *d;
bool match = false; char buf[BUFSIZ];
DIR *dir;
int opt; int opt;
while((opt = getopt(argc, argv, "C:bcdefghn:o:pqrsuwx")) != -1) while((opt = getopt(argc, argv, "abcdefghln:o:pqrsuwx")) != -1)
switch(opt) { switch(opt) {
case 'C': /* tests relative to directory */
if(chdir(optarg) == -1) {
perror(optarg);
exit(2);
}
break;
case 'n': /* newer than file */ case 'n': /* newer than file */
case 'o': /* older than file */ case 'o': /* older than file */
if(!(OPER(opt) = stat(optarg, (opt == 'n' ? &new : &old)) == 0)) if(!(FLAG(opt) = !stat(optarg, (opt == 'n' ? &new : &old))))
perror(optarg); perror(optarg);
break; break;
case 'q': /* quiet (no output, just status) */
quiet = true;
break;
default: /* miscellaneous operators */ default: /* miscellaneous operators */
OPER(opt) = true; FLAG(opt) = true;
break; break;
case '?': /* error: unknown flag */ case '?': /* error: unknown flag */
fprintf(stderr, "usage: %s [-bcdefghpqrsuwx] [-C dir] [-n file] [-o file] [file...]\n", argv[0]); fprintf(stderr, "usage: %s [-abcdefghlpqrsuwx] [-n file] [-o file] [file...]\n", argv[0]);
exit(2); exit(2);
} }
if(optind == argc) for(; optind < argc; optind++)
while(fgets(buf, sizeof buf, stdin)) { if(FLAG('l') && (dir = opendir(argv[optind]))) {
if(*(p = &buf[strlen(buf)-1]) == '\n') /* test directory contents */
*p = '\0'; while((d = readdir(dir)))
match |= test(buf); if(snprintf(buf, sizeof buf, "%s/%s", argv[optind], d->d_name) < sizeof buf)
test(buf, d->d_name);
closedir(dir);
} }
else else
while(optind < argc) test(argv[optind], argv[optind]);
match |= test(argv[optind++]);
return match ? 0 : 1; return match ? 0 : 1;
} }
bool void
test(const char *path) { test(const char *path, const char *name) {
struct stat st; struct stat st, ln;
if((!OPER('b') || (stat(path, &st) == 0 && S_ISBLK(st.st_mode))) /* block special */ if(!stat(path, &st) && !lstat(path, &ln)
&& (!OPER('c') || (stat(path, &st) == 0 && S_ISCHR(st.st_mode))) /* character special */ && ( FLAG('a') || name[0] != '.') /* hidden */
&& (!OPER('d') || (stat(path, &st) == 0 && S_ISDIR(st.st_mode))) /* directory */ && (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */
&& (!OPER('e') || (access(path, F_OK) == 0)) /* exists */ && (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */
&& (!OPER('f') || (stat(path, &st) == 0 && S_ISREG(st.st_mode))) /* regular file */ && (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */
&& (!OPER('g') || (stat(path, &st) == 0 && (st.st_mode & S_ISGID))) /* set-group-id flag */ && (!FLAG('e') || access(path, F_OK) == 0) /* exists */
&& (!OPER('h') || (lstat(path, &st) == 0 && S_ISLNK(st.st_mode))) /* symbolic link */ && (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */
&& (!OPER('n') || (stat(path, &st) == 0 && st.st_mtime > new.st_mtime)) /* newer than file */ && (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */
&& (!OPER('o') || (stat(path, &st) == 0 && st.st_mtime < old.st_mtime)) /* older than file */ && (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */
&& (!OPER('p') || (stat(path, &st) == 0 && S_ISFIFO(st.st_mode))) /* named pipe */ && (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */
&& (!OPER('r') || (access(path, R_OK) == 0)) /* readable */ && (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */
&& (!OPER('s') || (stat(path, &st) == 0 && st.st_size > 0)) /* not empty */ && (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */
&& (!OPER('u') || (stat(path, &st) == 0 && (st.st_mode & S_ISUID))) /* set-user-id flag */ && (!FLAG('r') || access(path, R_OK) == 0) /* readable */
&& (!OPER('w') || (access(path, W_OK) == 0)) /* writable */ && (!FLAG('s') || st.st_size > 0) /* not empty */
&& (!OPER('x') || (access(path, X_OK) == 0))) { /* executable */ && (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */
if(quiet) && (!FLAG('w') || access(path, W_OK) == 0) /* writable */
&& (!FLAG('x') || access(path, X_OK) == 0)) { /* executable */
if(FLAG('q'))
exit(0); exit(0);
puts(path); match = true;
return true; puts(name);
} }
else
return false;
} }