Compare commits

..

No commits in common. "master" and "4.2" have entirely different histories.
master ... 4.2

19 changed files with 829 additions and 1644 deletions

44
.hgtags Normal file
View File

@ -0,0 +1,44 @@
fcc8a282cb52c6a9343b461026b386825590cd31 0.1
656be0f47df545dfdd2e1e0663663b8b1b26f031 0.2
d352e9dc112ee96aa5cad961a0ed880ae9ce7276 0.3
7acf0dde1120542917bae12e0e42293f9d2cc899 0.4
4a0ecd881c4fc15de4a0bebd79308b064be020ef 0.5
25f679fb19686140a907684ffcb423b9e9d44b53 0.6
5fc20d7158bd16b4d5f8d1c25e177680b6d54252 0.7
409667a57221f7e50ba8b5248f638915cd61b366 0.8
d046c818ea467555cc338751c9bf3024609f1f12 0.9
9e11140d4cc3eecac3b0ab752f91528fd5e04be8 1.0
e8c1e9733752db12f2dbd1fa93c46f5806242ba9 1.1
bee7fe6d1189174d0204ca3195b83cdc1bb4f82e 1.2
2eb9997be51cb1b11a8900728ccc0904f9371157 1.3
df3fbb050004c544d14e43c36f6a94cca6ed4a69 1.4
e071fb045bd9e8574947acff7196360bc0270e68 1.5
dcc5427f99f51a978386a0dd770467cd911ac84b 1.6
58dbef4aef3d45c7a3da6945e53c9667c0f02d5b 1.7
3696d77aaf02f5d15728dde3b9e35abcaf291496 1.7.1
d3e6fa22ae45b38b1bdb0d813390365e5930360b 1.8
c7f5f4d543170f03d70468e98a3a0ec8d2c4161b 1.9
1fce5c464fcd870b9f024aa1853d5cf3a3eb371b 2.0
7656557298c954469a6a9564e6649b1fb5db663e 2.1
90f0e34e7f118c9ad3227a1606211ee825942b1c 2.2
b6e09682c8adcb6569656bee73c311f9ab457715 2.3
9e9036cbfb4b7306c6fb366249e81dc0e65bdfde 2.4
03e83e2788c83ddd63b45a667939d7ec783c98cb 2.4.1
1ca5d430524e838c52ede912533cb90108c5cd66 2.4.2
041143e9fc544c62edc58af52cae9ac5237e5945 2.5
775f761a5647a05038e091d1c99fc35d3034cd68 2.6
fbd9e9d63f202afe6834ccfdf890904f1897ec0b 2.7
dd3d02b07cac44fbafc074a361c1002cebe7aae4 2.8
59b3024854db49739c6d237fa9077f04a2da847a 3.0
8f0f917ac988164e1b4446236e3a6ab6cfcb8c67 3.1
e4c81a78ffbad6ba4d1ad119cc654da6eca63a4c 3.2
709df5a4bad7015a346b2b44b1b3b573ea3088ff 3.3
9ab649b3b3e5bfccf1c8f352c59e5361e070a25f 3.4
05e5bd706b3b3e61399d57c4bb43df296a20112d 3.5
0bc2751d06e8b95e0138854c7815e154c5c3d990 3.6
0508a3a6ee106f36d9b8ff07bb5b28584edfa89c 3.7
644b0798fcccd570fd519899e1601c6857496b91 3.8
21a1ed9a69b9541a355758a57103e294fb722c33 3.9
78f9f72cc9c6bdb022ff8908486b61ef5e242aad 4.0
844587572673cf6326c3f61737264a46b728fc0a 4.1
72749a826cab0baa805620e44a22e54486c97a4e 4.1.1

11
LICENSE
View File

@ -1,15 +1,12 @@
MIT/X Consortium License
© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
© 2006-2008 Sander van Dijk <a.h.vandijk@gmail.com>
© 2006-2007 Michał Janeczek <janeczek@gmail.com>
© 2007 Kris Maglione <jg@suckless.org>
© 2010 Connor Lane Smith <cls@lubutu.com>
© 2006-2010 Anselm R Garbe <anselm@garbe.us>
© 2009 Gottox <gottox@s01.de>
© 2009 Markus Schnalke <meillo@marmaro.de>
© 2009 Evan Gates <evan.gates@gmail.com>
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 2014-2022 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2015-2019 Quentin Rameau <quinq@fifth.space>
© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),

View File

@ -3,56 +3,55 @@
include config.mk
SRC = drw.c dmenu.c stest.c util.c
OBJ = $(SRC:.c=.o)
all: options dmenu dmenu_path
all: dmenu stest
options:
@echo dmenu build options:
@echo "CFLAGS = ${CFLAGS}"
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
.c.o:
$(CC) -c $(CFLAGS) $<
dmenu: dmenu.o draw.o
dmenu_path: dmenu_path.o
config.h:
cp config.def.h $@
.c.o: config.mk
@echo CC -c $<
@${CC} -c $< ${CFLAGS}
$(OBJ): arg.h config.h config.mk drw.h
dmenu: dmenu.o drw.o util.o
$(CC) -o $@ dmenu.o drw.o util.o $(LDFLAGS)
stest: stest.o
$(CC) -o $@ stest.o $(LDFLAGS)
dmenu dmenu_path:
@echo CC -o $@
@${CC} -o $@ $+ ${LDFLAGS}
clean:
rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz
@echo cleaning
@rm -f dmenu dmenu.o draw.o dmenu_path dmenu_path.o dmenu-${VERSION}.tar.gz
dist: clean
mkdir -p dmenu-$(VERSION)
cp LICENSE Makefile README arg.h config.def.h config.mk dmenu.1\
drw.h util.h dmenu_path dmenu_run stest.1 $(SRC)\
dmenu-$(VERSION)
tar -cf dmenu-$(VERSION).tar dmenu-$(VERSION)
gzip dmenu-$(VERSION).tar
rm -rf dmenu-$(VERSION)
@echo creating dist tarball
@mkdir -p dmenu-${VERSION}
@cp LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path.c dmenu_run dmenu-${VERSION}
@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
@gzip dmenu-${VERSION}.tar
@rm -rf dmenu-${VERSION}
install: all
mkdir -p $(DESTDIR)$(PREFIX)/bin
cp -f dmenu dmenu_path dmenu_run stest $(DESTDIR)$(PREFIX)/bin
chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu
chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_path
chmod 755 $(DESTDIR)$(PREFIX)/bin/dmenu_run
chmod 755 $(DESTDIR)$(PREFIX)/bin/stest
mkdir -p $(DESTDIR)$(MANPREFIX)/man1
sed "s/VERSION/$(VERSION)/g" < dmenu.1 > $(DESTDIR)$(MANPREFIX)/man1/dmenu.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/stest.1
@echo installing executables to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_path
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu_run
@echo installing manual page to ${DESTDIR}${MANPREFIX}/man1
@mkdir -p ${DESTDIR}${MANPREFIX}/man1
@sed "s/VERSION/${VERSION}/g" < dmenu.1 > ${DESTDIR}${MANPREFIX}/man1/dmenu.1
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
uninstall:
rm -f $(DESTDIR)$(PREFIX)/bin/dmenu\
$(DESTDIR)$(PREFIX)/bin/dmenu_path\
$(DESTDIR)$(PREFIX)/bin/dmenu_run\
$(DESTDIR)$(PREFIX)/bin/stest\
$(DESTDIR)$(MANPREFIX)/man1/dmenu.1\
$(DESTDIR)$(MANPREFIX)/man1/stest.1
@echo removing executables from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1
.PHONY: all clean dist install uninstall
.PHONY: all options clean dist install uninstall

49
arg.h
View File

@ -1,49 +0,0 @@
/*
* Copy me if you can.
* by 20h
*/
#ifndef ARG_H__
#define ARG_H__
extern char *argv0;
/* use main(int argc, char *argv[]) */
#define ARGBEGIN for (argv0 = *argv, argv++, argc--;\
argv[0] && argv[0][0] == '-'\
&& argv[0][1];\
argc--, argv++) {\
char argc_;\
char **argv_;\
int brk_;\
if (argv[0][1] == '-' && argv[0][2] == '\0') {\
argv++;\
argc--;\
break;\
}\
for (brk_ = 0, argv[0]++, argv_ = argv;\
argv[0][0] && !brk_;\
argv[0]++) {\
if (argv_ != argv)\
break;\
argc_ = argv[0][0];\
switch (argc_)
#define ARGEND }\
}
#define ARGC() argc_
#define EARGF(x) ((argv[0][1] == '\0' && argv[1] == NULL)?\
((x), abort(), (char *)0) :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#define ARGF() ((argv[0][1] == '\0' && argv[1] == NULL)?\
(char *)0 :\
(brk_ = 1, (argv[0][1] != '\0')?\
(&argv[0][1]) :\
(argc--, argv++, argv[0])))
#endif

View File

@ -1,23 +0,0 @@
/* See LICENSE file for copyright and license details. */
/* Default settings; can be overriden by command line. */
static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */
/* -fn option overrides fonts[0]; default X11 font or font set */
static const char *fonts[] = {
"monospace:size=10"
};
static const char *prompt = NULL; /* -p option; prompt to the left of input field */
static const char *colors[SchemeLast][2] = {
/* fg bg */
[SchemeNorm] = { "#bbbbbb", "#222222" },
[SchemeSel] = { "#eeeeee", "#005577" },
[SchemeOut] = { "#000000", "#00ffff" },
};
/* -l option; if nonzero, dmenu uses vertical list with given number of lines */
static unsigned int lines = 0;
/*
* Characters not considered part of a word while deleting words
* for example: " /?\"&[]"
*/
static const char worddelimiters[] = " ";

View File

@ -1,9 +1,11 @@
# dmenu version
VERSION = 5.2
VERSION = 4.2
# Customize below to fit your system
# paths
PREFIX = /usr/local
MANPREFIX = $(PREFIX)/share/man
MANPREFIX = ${PREFIX}/share/man
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
@ -12,21 +14,14 @@ X11LIB = /usr/X11R6/lib
XINERAMALIBS = -lXinerama
XINERAMAFLAGS = -DXINERAMA
# freetype
FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = $(X11INC)/freetype2
#MANPREFIX = ${PREFIX}/man
# includes and libs
INCS = -I$(X11INC) -I$(FREETYPEINC)
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
INCS = -I${X11INC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XINERAMAFLAGS)
CFLAGS = -std=c99 -pedantic -Wall -Os $(INCS) $(CPPFLAGS)
LDFLAGS = $(LIBS)
CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS}
# compiler and linker
CC = cc

158
dmenu.1
View File

@ -3,7 +3,8 @@
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-bfiv ]
.RB [ \-b ]
.RB [ \-i ]
.RB [ \-l
.IR lines ]
.RB [ \-m
@ -20,30 +21,32 @@ dmenu \- dynamic menu
.IR color ]
.RB [ \-sf
.IR color ]
.RB [ \-w
.IR windowid ]
.RB [ \-v ]
.P
.BR dmenu_run " ..."
.P
.B dmenu_path
.SH DESCRIPTION
.B dmenu
is a dynamic menu for X, which reads a list of newline\-separated items from
stdin. When the user selects an item and presses Return, their choice is printed
to stdout and dmenu terminates. Entering text will narrow the items to those
matching the tokens in the input.
is a dynamic menu for X, originally designed for
.BR dwm (1).
It manages huge numbers of user-defined menu items efficiently.
.P
dmenu reads a list of newline-separated items from standard input and creates a
menu. When the user selects an item or enters any text and presses Return,
their choice is printed to standard output and dmenu terminates.
.P
.B dmenu_run
is a script used by
.IR dwm (1)
which lists programs in the user's $PATH and runs the result in their $SHELL.
is a dmenu script used by dwm which lists programs in the user's PATH and
executes the selected item.
.P
.B dmenu_path
is a program used by dmenu_run to find and cache a list of executables.
.SH OPTIONS
.TP
.B \-b
dmenu appears at the bottom of the screen.
.TP
.B \-f
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
is faster, but will lock up X until stdin reaches end\-of\-file.
.TP
.B \-i
dmenu matches menu items case insensitively.
.TP
@ -51,8 +54,7 @@ dmenu matches menu items case insensitively.
dmenu lists items vertically, with the given number of lines.
.TP
.BI \-m " monitor"
dmenu is displayed on the monitor number supplied. Monitor numbers are starting
from 0.
dmenu appears on the given Xinerama screen.
.TP
.BI \-p " prompt"
defines the prompt to be displayed to the left of the input field.
@ -64,7 +66,7 @@ defines the font or font set used.
defines the normal background color.
.IR #RGB ,
.IR #RRGGBB ,
and X color names are supported.
and color names are supported.
.TP
.BI \-nf " color"
defines the normal foreground color.
@ -76,119 +78,27 @@ defines the selected background color.
defines the selected foreground color.
.TP
.B \-v
prints version information to stdout, then exits.
.TP
.BI \-w " windowid"
embed into windowid.
prints version information to standard output, then exits.
.SH USAGE
dmenu is completely controlled by the keyboard. Items are selected using the
arrow keys, page up, page down, home, and end.
dmenu is completely controlled by the keyboard. Besides standard Unix line
editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the
following keys are recognized:
.TP
.B Tab
.B Tab (Control\-i)
Copy the selected item to the input field.
.TP
.B Return
Confirm selection. Prints the selected item to stdout and exits, returning
.B Return (Control\-j)
Confirm selection. Prints the selected item to standard output and exits,
returning success.
.TP
.B Shift\-Return (Control\-Shift\-j)
Confirm input. Prints the input text to standard output and exits, returning
success.
.TP
.B Ctrl-Return
Confirm selection. Prints the selected item to stdout and continues.
.TP
.B Shift\-Return
Confirm input. Prints the input text to stdout and exits, returning success.
.TP
.B Escape
.B Escape (Control\-c)
Exit without selecting an item, returning failure.
.TP
.B Ctrl-Left
Move cursor to the start of the current word
.TP
.B Ctrl-Right
Move cursor to the end of the current word
.TP
.B C\-a
Home
.TP
.B C\-b
Left
.TP
.B C\-c
Escape
.TP
.B C\-d
Delete
.TP
.B C\-e
End
.TP
.B C\-f
Right
.TP
.B C\-g
Escape
.TP
.B C\-h
Backspace
.TP
.B C\-i
Tab
.TP
.B C\-j
Return
.TP
.B C\-J
Shift-Return
.TP
.B C\-k
Delete line right
.TP
.B C\-m
Return
.TP
.B C\-M
Shift-Return
.TP
.B C\-n
Down
.TP
.B C\-p
Up
.TP
.B C\-u
Delete line left
.TP
.B C\-w
Delete word left
.TP
.B C\-y
Paste from primary X selection
.TP
.B C\-Y
Paste from X clipboard
.TP
.B M\-b
Move cursor to the start of the current word
.TP
.B M\-f
Move cursor to the end of the current word
.TP
.B M\-g
Home
.TP
.B M\-G
End
.TP
.B M\-h
Up
.TP
.B M\-j
Page down
.TP
.B M\-k
Page up
.TP
.B M\-l
Down
.B Control\-y
Paste the current X selection into the input field.
.SH SEE ALSO
.IR dwm (1),
.IR stest (1)
.BR dwm (1)

986
dmenu.c

File diff suppressed because it is too large Load Diff

View File

@ -1,13 +0,0 @@
#!/bin/sh
cachedir="${XDG_CACHE_HOME:-"$HOME/.cache"}"
cache="$cachedir/dmenu_run"
[ ! -e "$cachedir" ] && mkdir -p "$cachedir"
IFS=:
if stest -dqr -n "$cache" $PATH; then
stest -flx $PATH | sort -u | tee "$cache"
else
cat "$cache"
fi

101
dmenu_path.c Normal file
View File

@ -0,0 +1,101 @@
/* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#define CACHE ".dmenu_cache"
static void die(const char *s);
static int qstrcmp(const void *a, const void *b);
static void scan(void);
static int uptodate(void);
static char **items = NULL;
static const char *home, *path;
int
main(void) {
if(!(home = getenv("HOME")))
die("no $HOME");
if(!(path = getenv("PATH")))
die("no $PATH");
if(chdir(home) < 0)
die("chdir failed");
if(uptodate()) {
execlp("cat", "cat", CACHE, NULL);
die("exec failed");
}
scan();
return EXIT_SUCCESS;
}
void
die(const char *s) {
fprintf(stderr, "dmenu_path: %s\n", s);
exit(EXIT_FAILURE);
}
int
qstrcmp(const void *a, const void *b) {
return strcmp(*(const char **)a, *(const char **)b);
}
void
scan(void) {
char buf[PATH_MAX];
char *dir, *p;
size_t i, count;
struct dirent *ent;
DIR *dp;
FILE *cache;
count = 0;
if(!(p = strdup(path)))
die("strdup failed");
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":")) {
if(!(dp = opendir(dir)))
continue;
while((ent = readdir(dp))) {
snprintf(buf, sizeof buf, "%s/%s", dir, ent->d_name);
if(ent->d_name[0] == '.' || access(buf, X_OK) < 0)
continue;
if(!(items = realloc(items, ++count * sizeof *items)))
die("malloc failed");
if(!(items[count-1] = strdup(ent->d_name)))
die("strdup failed");
}
closedir(dp);
}
qsort(items, count, sizeof *items, qstrcmp);
if(!(cache = fopen(CACHE, "w")))
die("open failed");
for(i = 0; i < count; i++) {
if(i > 0 && !strcmp(items[i], items[i-1]))
continue;
fprintf(cache, "%s\n", items[i]);
fprintf(stdout, "%s\n", items[i]);
}
fclose(cache);
free(p);
}
int
uptodate(void) {
char *dir, *p;
time_t mtime;
struct stat st;
if(stat(CACHE, &st) < 0)
return 0;
mtime = st.st_mtime;
if(!(p = strdup(path)))
die("strdup failed");
for(dir = strtok(p, ":"); dir; dir = strtok(NULL, ":"))
if(!stat(dir, &st) && st.st_mtime > mtime)
return 0;
free(p);
return 1;
}

View File

@ -1,2 +1,2 @@
#!/bin/sh
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &
exe=`dmenu_path | dmenu ${1+"$@"}` && exec $exe

194
draw.c Normal file
View File

@ -0,0 +1,194 @@
/* See LICENSE file for copyright and license details. */
#include <locale.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include "draw.h"
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define DEFFONT "fixed"
static Bool loadfont(DC *dc, const char *fontstr);
void
drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color) {
XRectangle r = { dc->x + x, dc->y + y, w, h };
if(!fill) {
r.width -= 1;
r.height -= 1;
}
XSetForeground(dc->dpy, dc->gc, color);
(fill ? XFillRectangles : XDrawRectangles)(dc->dpy, dc->canvas, dc->gc, &r, 1);
}
void
drawtext(DC *dc, const char *text, unsigned long col[ColLast]) {
char buf[256];
size_t n, mn;
/* shorten text if necessary */
n = strlen(text);
for(mn = MIN(n, sizeof buf); textnw(dc, text, mn) > dc->w - dc->font.height/2; mn--)
if(mn == 0)
return;
memcpy(buf, text, mn);
if(mn < n)
for(n = MAX(mn-3, 0); n < mn; buf[n++] = '.');
drawrect(dc, 0, 0, dc->w, dc->h, True, BG(dc, col));
drawtextn(dc, buf, mn, col);
}
void
drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]) {
int x, y;
x = dc->x + dc->font.height/2;
y = dc->y + dc->font.ascent+1;
XSetForeground(dc->dpy, dc->gc, FG(dc, col));
if(dc->font.set)
XmbDrawString(dc->dpy, dc->canvas, dc->font.set, dc->gc, x, y, text, n);
else {
XSetFont(dc->dpy, dc->gc, dc->font.xfont->fid);
XDrawString(dc->dpy, dc->canvas, dc->gc, x, y, text, n);
}
}
void
eprintf(const char *fmt, ...) {
va_list ap;
fprintf(stderr, "%s: ", progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
exit(EXIT_FAILURE);
}
void
freedc(DC *dc) {
if(dc->font.set)
XFreeFontSet(dc->dpy, dc->font.set);
if(dc->font.xfont)
XFreeFont(dc->dpy, dc->font.xfont);
if(dc->canvas)
XFreePixmap(dc->dpy, dc->canvas);
XFreeGC(dc->dpy, dc->gc);
XCloseDisplay(dc->dpy);
free(dc);
}
unsigned long
getcolor(DC *dc, const char *colstr) {
Colormap cmap = DefaultColormap(dc->dpy, DefaultScreen(dc->dpy));
XColor color;
if(!XAllocNamedColor(dc->dpy, cmap, colstr, &color, &color))
eprintf("cannot allocate color '%s'\n", colstr);
return color.pixel;
}
DC *
initdc(void) {
DC *dc;
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
weprintf("no locale support\n");
if(!(dc = malloc(sizeof *dc)))
eprintf("cannot malloc %u bytes\n", sizeof *dc);
if(!(dc->dpy = XOpenDisplay(NULL)))
eprintf("cannot open display\n");
dc->gc = XCreateGC(dc->dpy, DefaultRootWindow(dc->dpy), 0, NULL);
XSetLineAttributes(dc->dpy, dc->gc, 1, LineSolid, CapButt, JoinMiter);
dc->font.xfont = NULL;
dc->font.set = NULL;
dc->canvas = None;
return dc;
}
void
initfont(DC *dc, const char *fontstr) {
if(!loadfont(dc, fontstr ? fontstr : DEFFONT)) {
if(fontstr != NULL)
weprintf("cannot load font '%s'\n", fontstr);
if(fontstr == NULL || !loadfont(dc, DEFFONT))
eprintf("cannot load font '%s'\n", DEFFONT);
}
dc->font.height = dc->font.ascent + dc->font.descent;
}
Bool
loadfont(DC *dc, const char *fontstr) {
char *def, **missing;
int i, n;
if(!*fontstr)
return False;
if((dc->font.set = XCreateFontSet(dc->dpy, fontstr, &missing, &n, &def))) {
char **names;
XFontStruct **xfonts;
n = XFontsOfFontSet(dc->font.set, &xfonts, &names);
for(i = dc->font.ascent = dc->font.descent = 0; i < n; i++) {
dc->font.ascent = MAX(dc->font.ascent, xfonts[i]->ascent);
dc->font.descent = MAX(dc->font.descent, xfonts[i]->descent);
}
}
else if((dc->font.xfont = XLoadQueryFont(dc->dpy, fontstr))) {
dc->font.ascent = dc->font.xfont->ascent;
dc->font.descent = dc->font.xfont->descent;
}
if(missing)
XFreeStringList(missing);
return (dc->font.set || dc->font.xfont);
}
void
mapdc(DC *dc, Window win, unsigned int w, unsigned int h) {
XCopyArea(dc->dpy, dc->canvas, win, dc->gc, 0, 0, w, h, 0, 0);
}
void
resizedc(DC *dc, unsigned int w, unsigned int h) {
if(dc->canvas)
XFreePixmap(dc->dpy, dc->canvas);
dc->canvas = XCreatePixmap(dc->dpy, DefaultRootWindow(dc->dpy), w, h,
DefaultDepth(dc->dpy, DefaultScreen(dc->dpy)));
dc->x = dc->y = 0;
dc->w = w;
dc->h = h;
dc->invert = False;
}
int
textnw(DC *dc, const char *text, size_t len) {
if(dc->font.set) {
XRectangle r;
XmbTextExtents(dc->font.set, text, len, NULL, &r);
return r.width;
}
return XTextWidth(dc->font.xfont, text, len);
}
int
textw(DC *dc, const char *text) {
return textnw(dc, text, strlen(text)) + dc->font.height;
}
void
weprintf(const char *fmt, ...) {
va_list ap;
fprintf(stderr, "%s: warning: ", progname);
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
}

37
draw.h Normal file
View File

@ -0,0 +1,37 @@
/* See LICENSE file for copyright and license details. */
#define FG(dc, col) ((col)[(dc)->invert ? ColBG : ColFG])
#define BG(dc, col) ((col)[(dc)->invert ? ColFG : ColBG])
enum { ColBG, ColFG, ColBorder, ColLast };
typedef struct {
int x, y, w, h;
Bool invert;
Display *dpy;
GC gc;
Pixmap canvas;
struct {
int ascent;
int descent;
int height;
XFontSet set;
XFontStruct *xfont;
} font;
} DC; /* draw context */
unsigned long getcolor(DC *dc, const char *colstr);
void drawrect(DC *dc, int x, int y, unsigned int w, unsigned int h, Bool fill, unsigned long color);
void drawtext(DC *dc, const char *text, unsigned long col[ColLast]);
void drawtextn(DC *dc, const char *text, size_t n, unsigned long col[ColLast]);
void initfont(DC *dc, const char *fontstr);
void freedc(DC *dc);
DC *initdc(void);
void mapdc(DC *dc, Window win, unsigned int w, unsigned int h);
void resizedc(DC *dc, unsigned int w, unsigned int h);
int textnw(DC *dc, const char *text, size_t len);
int textw(DC *dc, const char *text);
void eprintf(const char *fmt, ...);
void weprintf(const char *fmt, ...);
const char *progname;

451
drw.c
View File

@ -1,451 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <X11/Xlib.h>
#include <X11/Xft/Xft.h>
#include "drw.h"
#include "util.h"
#define UTF_INVALID 0xFFFD
#define UTF_SIZ 4
static const unsigned char utfbyte[UTF_SIZ + 1] = {0x80, 0, 0xC0, 0xE0, 0xF0};
static const unsigned char utfmask[UTF_SIZ + 1] = {0xC0, 0x80, 0xE0, 0xF0, 0xF8};
static const long utfmin[UTF_SIZ + 1] = { 0, 0, 0x80, 0x800, 0x10000};
static const long utfmax[UTF_SIZ + 1] = {0x10FFFF, 0x7F, 0x7FF, 0xFFFF, 0x10FFFF};
static long
utf8decodebyte(const char c, size_t *i)
{
for (*i = 0; *i < (UTF_SIZ + 1); ++(*i))
if (((unsigned char)c & utfmask[*i]) == utfbyte[*i])
return (unsigned char)c & ~utfmask[*i];
return 0;
}
static size_t
utf8validate(long *u, size_t i)
{
if (!BETWEEN(*u, utfmin[i], utfmax[i]) || BETWEEN(*u, 0xD800, 0xDFFF))
*u = UTF_INVALID;
for (i = 1; *u > utfmax[i]; ++i)
;
return i;
}
static size_t
utf8decode(const char *c, long *u, size_t clen)
{
size_t i, j, len, type;
long udecoded;
*u = UTF_INVALID;
if (!clen)
return 0;
udecoded = utf8decodebyte(c[0], &len);
if (!BETWEEN(len, 1, UTF_SIZ))
return 1;
for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
if (type)
return j;
}
if (j < len)
return 0;
*u = udecoded;
utf8validate(u, len);
return len;
}
Drw *
drw_create(Display *dpy, int screen, Window root, unsigned int w, unsigned int h)
{
Drw *drw = ecalloc(1, sizeof(Drw));
drw->dpy = dpy;
drw->screen = screen;
drw->root = root;
drw->w = w;
drw->h = h;
drw->drawable = XCreatePixmap(dpy, root, w, h, DefaultDepth(dpy, screen));
drw->gc = XCreateGC(dpy, root, 0, NULL);
XSetLineAttributes(dpy, drw->gc, 1, LineSolid, CapButt, JoinMiter);
return drw;
}
void
drw_resize(Drw *drw, unsigned int w, unsigned int h)
{
if (!drw)
return;
drw->w = w;
drw->h = h;
if (drw->drawable)
XFreePixmap(drw->dpy, drw->drawable);
drw->drawable = XCreatePixmap(drw->dpy, drw->root, w, h, DefaultDepth(drw->dpy, drw->screen));
}
void
drw_free(Drw *drw)
{
XFreePixmap(drw->dpy, drw->drawable);
XFreeGC(drw->dpy, drw->gc);
drw_fontset_free(drw->fonts);
free(drw);
}
/* This function is an implementation detail. Library users should use
* drw_fontset_create instead.
*/
static Fnt *
xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
{
Fnt *font;
XftFont *xfont = NULL;
FcPattern *pattern = NULL;
if (fontname) {
/* Using the pattern found at font->xfont->pattern does not yield the
* same substitution results as using the pattern returned by
* FcNameParse; using the latter results in the desired fallback
* behaviour whereas the former just results in missing-character
* rectangles being drawn, at least with some fonts. */
if (!(xfont = XftFontOpenName(drw->dpy, drw->screen, fontname))) {
fprintf(stderr, "error, cannot load font from name: '%s'\n", fontname);
return NULL;
}
if (!(pattern = FcNameParse((FcChar8 *) fontname))) {
fprintf(stderr, "error, cannot parse font name to pattern: '%s'\n", fontname);
XftFontClose(drw->dpy, xfont);
return NULL;
}
} else if (fontpattern) {
if (!(xfont = XftFontOpenPattern(drw->dpy, fontpattern))) {
fprintf(stderr, "error, cannot load font from pattern.\n");
return NULL;
}
} else {
die("no font specified.");
}
font = ecalloc(1, sizeof(Fnt));
font->xfont = xfont;
font->pattern = pattern;
font->h = xfont->ascent + xfont->descent;
font->dpy = drw->dpy;
return font;
}
static void
xfont_free(Fnt *font)
{
if (!font)
return;
if (font->pattern)
FcPatternDestroy(font->pattern);
XftFontClose(font->dpy, font->xfont);
free(font);
}
Fnt*
drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount)
{
Fnt *cur, *ret = NULL;
size_t i;
if (!drw || !fonts)
return NULL;
for (i = 1; i <= fontcount; i++) {
if ((cur = xfont_create(drw, fonts[fontcount - i], NULL))) {
cur->next = ret;
ret = cur;
}
}
return (drw->fonts = ret);
}
void
drw_fontset_free(Fnt *font)
{
if (font) {
drw_fontset_free(font->next);
xfont_free(font);
}
}
void
drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
{
if (!drw || !dest || !clrname)
return;
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen),
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
}
/* Wrapper to create color schemes. The caller has to call free(3) on the
* returned color scheme when done using it. */
Clr *
drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount)
{
size_t i;
Clr *ret;
/* need at least two colors for a scheme */
if (!drw || !clrnames || clrcount < 2 || !(ret = ecalloc(clrcount, sizeof(XftColor))))
return NULL;
for (i = 0; i < clrcount; i++)
drw_clr_create(drw, &ret[i], clrnames[i]);
return ret;
}
void
drw_setfontset(Drw *drw, Fnt *set)
{
if (drw)
drw->fonts = set;
}
void
drw_setscheme(Drw *drw, Clr *scm)
{
if (drw)
drw->scheme = scm;
}
void
drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert)
{
if (!drw || !drw->scheme)
return;
XSetForeground(drw->dpy, drw->gc, invert ? drw->scheme[ColBg].pixel : drw->scheme[ColFg].pixel);
if (filled)
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
else
XDrawRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w - 1, h - 1);
}
int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert)
{
int ty, ellipsis_x = 0;
unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
int utf8strlen, utf8charlen, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
FcPattern *fcpattern;
FcPattern *match;
XftResult result;
int charexists = 0, overflow = 0;
/* keep track of a couple codepoints for which we have no match. */
static unsigned int nomatches[128], ellipsis_width;
if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
if (!render) {
w = invert ? invert : ~invert;
} else {
XSetForeground(drw->dpy, drw->gc, drw->scheme[invert ? ColFg : ColBg].pixel);
XFillRectangle(drw->dpy, drw->drawable, drw->gc, x, y, w, h);
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
x += lpad;
w -= lpad;
}
usedfont = drw->fonts;
if (!ellipsis_width && render)
ellipsis_width = drw_fontset_getwidth(drw, "...");
while (1) {
ew = ellipsis_len = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
drw_font_getexts(curfont, text, utf8charlen, &tmpw, NULL);
if (ew + ellipsis_width <= w) {
/* keep track where the ellipsis still fits */
ellipsis_x = x + ew;
ellipsis_w = w - ew;
ellipsis_len = utf8strlen;
}
if (ew + tmpw > w) {
overflow = 1;
/* called from drw_fontset_getwidth_clamp():
* it wants the width AFTER the overflow
*/
if (!render)
x += tmpw;
else
utf8strlen = ellipsis_len;
} else if (curfont == usedfont) {
utf8strlen += utf8charlen;
text += utf8charlen;
ew += tmpw;
} else {
nextfont = curfont;
}
break;
}
}
if (overflow || !charexists || nextfont)
break;
else
charexists = 0;
}
if (utf8strlen) {
if (render) {
ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent;
XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg],
usedfont->xfont, x, ty, (XftChar8 *)utf8str, utf8strlen);
}
x += ew;
w -= ew;
}
if (render && overflow)
drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
if (!*text || overflow) {
break;
} else if (nextfont) {
charexists = 0;
usedfont = nextfont;
} else {
/* Regardless of whether or not a fallback font is found, the
* character must be drawn. */
charexists = 1;
hash = (unsigned int)utf8codepoint;
hash = ((hash >> 16) ^ hash) * 0x21F0AAAD;
hash = ((hash >> 15) ^ hash) * 0xD35A2D97;
h0 = ((hash >> 15) ^ hash) % LENGTH(nomatches);
h1 = (hash >> 17) % LENGTH(nomatches);
/* avoid expensive XftFontMatch call when we know we won't find a match */
if (nomatches[h0] == utf8codepoint || nomatches[h1] == utf8codepoint)
goto no_match;
fccharset = FcCharSetCreate();
FcCharSetAddChar(fccharset, utf8codepoint);
if (!drw->fonts->pattern) {
/* Refer to the comment in xfont_create for more information. */
die("the first font in the cache must be loaded from a font string.");
}
fcpattern = FcPatternDuplicate(drw->fonts->pattern);
FcPatternAddCharSet(fcpattern, FC_CHARSET, fccharset);
FcPatternAddBool(fcpattern, FC_SCALABLE, FcTrue);
FcConfigSubstitute(NULL, fcpattern, FcMatchPattern);
FcDefaultSubstitute(fcpattern);
match = XftFontMatch(drw->dpy, drw->screen, fcpattern, &result);
FcCharSetDestroy(fccharset);
FcPatternDestroy(fcpattern);
if (match) {
usedfont = xfont_create(drw, NULL, match);
if (usedfont && XftCharExists(drw->dpy, usedfont->xfont, utf8codepoint)) {
for (curfont = drw->fonts; curfont->next; curfont = curfont->next)
; /* NOP */
curfont->next = usedfont;
} else {
xfont_free(usedfont);
nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
no_match:
usedfont = drw->fonts;
}
}
}
}
if (d)
XftDrawDestroy(d);
return x + (render ? w : 0);
}
void
drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h)
{
if (!drw)
return;
XCopyArea(drw->dpy, drw->drawable, win, drw->gc, x, y, w, h, x, y);
XSync(drw->dpy, False);
}
unsigned int
drw_fontset_getwidth(Drw *drw, const char *text)
{
if (!drw || !drw->fonts || !text)
return 0;
return drw_text(drw, 0, 0, 0, 0, 0, text, 0);
}
unsigned int
drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n)
{
unsigned int tmp = 0;
if (drw && drw->fonts && text && n)
tmp = drw_text(drw, 0, 0, 0, 0, 0, text, n);
return MIN(n, tmp);
}
void
drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h)
{
XGlyphInfo ext;
if (!font || !text)
return;
XftTextExtentsUtf8(font->dpy, font->xfont, (XftChar8 *)text, len, &ext);
if (w)
*w = ext.xOff;
if (h)
*h = font->h;
}
Cur *
drw_cur_create(Drw *drw, int shape)
{
Cur *cur;
if (!drw || !(cur = ecalloc(1, sizeof(Cur))))
return NULL;
cur->cursor = XCreateFontCursor(drw->dpy, shape);
return cur;
}
void
drw_cur_free(Drw *drw, Cur *cursor)
{
if (!cursor)
return;
XFreeCursor(drw->dpy, cursor->cursor);
free(cursor);
}

58
drw.h
View File

@ -1,58 +0,0 @@
/* See LICENSE file for copyright and license details. */
typedef struct {
Cursor cursor;
} Cur;
typedef struct Fnt {
Display *dpy;
unsigned int h;
XftFont *xfont;
FcPattern *pattern;
struct Fnt *next;
} Fnt;
enum { ColFg, ColBg }; /* Clr scheme index */
typedef XftColor Clr;
typedef struct {
unsigned int w, h;
Display *dpy;
int screen;
Window root;
Drawable drawable;
GC gc;
Clr *scheme;
Fnt *fonts;
} Drw;
/* Drawable abstraction */
Drw *drw_create(Display *dpy, int screen, Window win, unsigned int w, unsigned int h);
void drw_resize(Drw *drw, unsigned int w, unsigned int h);
void drw_free(Drw *drw);
/* Fnt abstraction */
Fnt *drw_fontset_create(Drw* drw, const char *fonts[], size_t fontcount);
void drw_fontset_free(Fnt* set);
unsigned int drw_fontset_getwidth(Drw *drw, const char *text);
unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);
void drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w, unsigned int *h);
/* Colorscheme abstraction */
void drw_clr_create(Drw *drw, Clr *dest, const char *clrname);
Clr *drw_scm_create(Drw *drw, const char *clrnames[], size_t clrcount);
/* Cursor abstraction */
Cur *drw_cur_create(Drw *drw, int shape);
void drw_cur_free(Drw *drw, Cur *cursor);
/* Drawing context manipulation */
void drw_setfontset(Drw *drw, Fnt *set);
void drw_setscheme(Drw *drw, Clr *scm);
/* Drawing functions */
void drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int invert);
int drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert);
/* Map functions */
void drw_map(Drw *drw, Window win, int x, int y, unsigned int w, unsigned int h);

90
stest.1
View File

@ -1,90 +0,0 @@
.TH STEST 1 dmenu\-VERSION
.SH NAME
stest \- filter a list of files by properties
.SH SYNOPSIS
.B stest
.RB [ -abcdefghlpqrsuwx ]
.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, stest
reads files from stdin.
.SH OPTIONS
.TP
.B \-a
Test hidden files.
.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
.B \-l
Test the contents of a directory given as an argument.
.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 \-v
Invert the sense of tests, only failing files pass.
.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)

109
stest.c
View File

@ -1,109 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "arg.h"
char *argv0;
#define FLAG(x) (flag[(x)-'a'])
static void test(const char *, const char *);
static void usage(void);
static int match = 0;
static int flag[26];
static struct stat old, new;
static void
test(const char *path, const char *name)
{
struct stat st, ln;
if ((!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */
&& (!FLAG('b') || S_ISBLK(st.st_mode)) /* block special */
&& (!FLAG('c') || S_ISCHR(st.st_mode)) /* character special */
&& (!FLAG('d') || S_ISDIR(st.st_mode)) /* directory */
&& (!FLAG('e') || access(path, F_OK) == 0) /* exists */
&& (!FLAG('f') || S_ISREG(st.st_mode)) /* regular file */
&& (!FLAG('g') || st.st_mode & S_ISGID) /* set-group-id flag */
&& (!FLAG('h') || (!lstat(path, &ln) && S_ISLNK(ln.st_mode))) /* symbolic link */
&& (!FLAG('n') || st.st_mtime > new.st_mtime) /* newer than file */
&& (!FLAG('o') || st.st_mtime < old.st_mtime) /* older than file */
&& (!FLAG('p') || S_ISFIFO(st.st_mode)) /* named pipe */
&& (!FLAG('r') || access(path, R_OK) == 0) /* readable */
&& (!FLAG('s') || st.st_size > 0) /* not empty */
&& (!FLAG('u') || st.st_mode & S_ISUID) /* set-user-id flag */
&& (!FLAG('w') || access(path, W_OK) == 0) /* writable */
&& (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */
if (FLAG('q'))
exit(0);
match = 1;
puts(name);
}
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] "
"[-n file] [-o file] [file...]\n", argv0);
exit(2); /* like test(1) return > 1 on error */
}
int
main(int argc, char *argv[])
{
struct dirent *d;
char path[PATH_MAX], *line = NULL, *file;
size_t linesiz = 0;
ssize_t n;
DIR *dir;
int r;
ARGBEGIN {
case 'n': /* newer than file */
case 'o': /* older than file */
file = EARGF(usage());
if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old))))
perror(file);
break;
default:
/* miscellaneous operators */
if (strchr("abcdefghlpqrsuvwx", ARGC()))
FLAG(ARGC()) = 1;
else
usage(); /* unknown flag */
} ARGEND;
if (!argc) {
/* read list from stdin */
while ((n = getline(&line, &linesiz, stdin)) > 0) {
if (line[n - 1] == '\n')
line[n - 1] = '\0';
test(line, line);
}
free(line);
} else {
for (; argc; argc--, argv++) {
if (FLAG('l') && (dir = opendir(*argv))) {
/* test directory contents */
while ((d = readdir(dir))) {
r = snprintf(path, sizeof path, "%s/%s",
*argv, d->d_name);
if (r >= 0 && (size_t)r < sizeof path)
test(path, d->d_name);
}
closedir(dir);
} else {
test(*argv, *argv);
}
}
}
return match ? 0 : 1;
}

36
util.c
View File

@ -1,36 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "util.h"
void
die(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vfprintf(stderr, fmt, ap);
va_end(ap);
if (fmt[0] && fmt[strlen(fmt)-1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
exit(1);
}
void *
ecalloc(size_t nmemb, size_t size)
{
void *p;
if (!(p = calloc(nmemb, size)))
die("calloc:");
return p;
}

9
util.h
View File

@ -1,9 +0,0 @@
/* See LICENSE file for copyright and license details. */
#define MAX(A, B) ((A) > (B) ? (A) : (B))
#define MIN(A, B) ((A) < (B) ? (A) : (B))
#define BETWEEN(X, A, B) ((A) <= (X) && (X) <= (B))
#define LENGTH(X) (sizeof (X) / sizeof (X)[0])
void die(const char *fmt, ...);
void *ecalloc(size_t nmemb, size_t size);