replace lsx with stest

This commit is contained in:
Connor Lane Smith 2011-11-19 19:54:55 +01:00
parent 8ac44eb75a
commit bb4424df07
7 changed files with 189 additions and 69 deletions

View file

@ -3,10 +3,10 @@
include config.mk include config.mk
SRC = dmenu.c draw.c lsx.c SRC = dmenu.c draw.c stest.c
OBJ = ${SRC:.c=.o} OBJ = ${SRC:.c=.o}
all: options dmenu lsx all: options dmenu stest
options: options:
@echo dmenu build options: @echo dmenu build options:
@ -24,18 +24,18 @@ dmenu: dmenu.o draw.o
@echo CC -o $@ @echo CC -o $@
@${CC} -o $@ dmenu.o draw.o ${LDFLAGS} @${CC} -o $@ dmenu.o draw.o ${LDFLAGS}
lsx: lsx.o stest: stest.o
@echo CC -o $@ @echo CC -o $@
@${CC} -o $@ lsx.o ${LDFLAGS} @${CC} -o $@ stest.o ${LDFLAGS}
clean: clean:
@echo cleaning @echo cleaning
@rm -f dmenu lsx ${OBJ} dmenu-${VERSION}.tar.gz @rm -f dmenu stest ${OBJ} dmenu-${VERSION}.tar.gz
dist: clean dist: clean
@echo creating dist tarball @echo creating dist tarball
@mkdir -p dmenu-${VERSION} @mkdir -p dmenu-${VERSION}
@cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_run lsx.1 ${SRC} dmenu-${VERSION} @cp LICENSE Makefile README config.mk dmenu.1 draw.h dmenu_run stest.1 ${SRC} dmenu-${VERSION}
@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION} @tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
@gzip dmenu-${VERSION}.tar @gzip dmenu-${VERSION}.tar
@rm -rf dmenu-${VERSION} @rm -rf dmenu-${VERSION}
@ -43,24 +43,24 @@ dist: clean
install: all install: all
@echo installing executables to ${DESTDIR}${PREFIX}/bin @echo installing executables to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin @mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f dmenu dmenu_run lsx ${DESTDIR}${PREFIX}/bin @cp -f dmenu dmenu_run stest ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run @chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
@chmod 755 ${DESTDIR}${PREFIX}/bin/lsx @chmod 755 ${DESTDIR}${PREFIX}/bin/stest
@echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1 @echo installing manual pages to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1 @mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1 @sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@sed "s/VERSION/${VERSION}/g" < lsx.1 > ${DESTDIR}${MANPREFIX}/man1/lsx.1 @sed "s/VERSION/${VERSION}/g" < stest.1 > ${DESTDIR}${MANPREFIX}/man1/stest.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/lsx.1 @chmod 644 ${DESTDIR}${MANPREFIX}/man1/stest.1
uninstall: uninstall:
@echo removing executables from ${DESTDIR}${PREFIX}/bin @echo removing executables from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu @rm -f ${DESTDIR}${PREFIX}/bin/dmenu
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run @rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run
@rm -f ${DESTDIR}${PREFIX}/bin/lsx @rm -f ${DESTDIR}${PREFIX}/bin/stest
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1 @echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1 @rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@rm -f ${DESTDIR}${MANPREFIX}/man1/lsx.1 @rm -f ${DESTDIR}${MANPREFIX}/man1/stest.1
.PHONY: all options clean dist install uninstall .PHONY: all options clean dist install uninstall

View file

@ -17,7 +17,7 @@ INCS = -I${X11INC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
# flags # flags
CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CFLAGS = -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS} CFLAGS = -ansi -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS} LDFLAGS = -s ${LIBS}

View file

@ -5,8 +5,10 @@ if [ ! -d "`dirname "$CACHE"`" ]; then
fi fi
( (
IFS=: IFS=:
if [ "`ls -dt $PATH "$CACHE" | head -n 1`" != "$CACHE" ]; then if ls -d $PATH | stest -q -n "$CACHE"; then
lsx $PATH | sort -u > "$CACHE" for dir in $PATH; do
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"

11
lsx.1
View file

@ -1,11 +0,0 @@
.TH LSX 1 dmenu\-VERSION
.SH NAME
lsx \- list executables
.SH SYNOPSIS
.B lsx
.RI [ directory ...]
.SH DESCRIPTION
.B lsx
lists the executables in each
.IR directory .
If none are given the current working directory is used.

43
lsx.c
View file

@ -1,43 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <errno.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
static void lsx(const char *dir);
static int status = EXIT_SUCCESS;
int
main(int argc, char *argv[]) {
int i;
if(argc < 2)
lsx(".");
else for(i = 1; i < argc; i++)
lsx(argv[i]);
return status;
}
void
lsx(const char *dir) {
char buf[PATH_MAX];
struct dirent *d;
struct stat st;
DIR *dp;
for(dp = opendir(dir); dp && (d = readdir(dp)); errno = 0)
if(snprintf(buf, sizeof buf, "%s/%s", dir, d->d_name) < (int)sizeof buf
&& access(buf, X_OK) == 0 && stat(buf, &st) == 0 && S_ISREG(st.st_mode))
puts(d->d_name);
if(errno != 0) {
status = EXIT_FAILURE;
perror(dir);
}
if(dp)
closedir(dp);
}

87
stest.1 Normal file
View file

@ -0,0 +1,87 @@
.TH STEST 1 dmenu\-VERSION
.SH NAME
stest \- filter a list of files by properties
.SH SYNOPSIS
.B stest
.RB [ -bcdefghpqrsuwx ]
.RB [ -C
.IR dir ]
.RB [ -n
.IR file ]
.RB [ -o
.IR file ]
.RI [ file ...]
.SH DESCRIPTION
.B stest
takes a list of files and filters by the files' properties, analogous to
.IR test (1).
Files which pass all tests are printed to stdout. If no files are given as
arguments, stest will read a list of files from stdin, one path per line.
.SH OPTIONS
.TP
.BI \-C " dir"
Tests files relative to directory
.IR dir .
.TP
.B \-b
Test that files are block specials.
.TP
.B \-c
Test that files are character specials.
.TP
.B \-d
Test that files are directories.
.TP
.B \-e
Test that files exist.
.TP
.B \-f
Test that files are regular files.
.TP
.B \-g
Test that files have their set-group-ID flag set.
.TP
.B \-h
Test that files are symbolic links.
.TP
.BI \-n " file"
Test that files are newer than
.IR file .
.TP
.BI \-o " file"
Test that files are older than
.IR file .
.TP
.B \-p
Test that files are named pipes.
.TP
.B \-q
No files are printed, only the exit status is returned.
.TP
.B \-r
Test that files are readable.
.TP
.B \-s
Test that files are not empty.
.TP
.B \-u
Test that files have their set-user-ID flag set.
.TP
.B \-w
Test that files are writable.
.TP
.B \-x
Test that files are executable.
.SH EXIT STATUS
.TP
.B 0
At least one file passed all tests.
.TP
.B 1
No files passed all tests.
.TP
.B 2
An error occurred.
.SH SEE ALSO
.IR dmenu (1),
.IR test (1)

85
stest.c Normal file
View file

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