From 98667d6b9139d20f0a9c1a29984398749ab1e5d1 Mon Sep 17 00:00:00 2001 From: Jean-Marie Mineau Date: Sun, 11 Oct 2020 11:44:58 +0200 Subject: [PATCH] center patch --- LICENSE | 30 + Makefile | 64 ++ README | 24 + arg.h | 49 ++ config.def.h | 25 + config.h | 27 + config.mk | 31 + dmenu | Bin 0 -> 44048 bytes dmenu-5.0.tar.gz | Bin 0 -> 16028 bytes dmenu-borderoption-20200217-bf60a1e.diff | 46 ++ dmenu-center-20200111-8cd37e1.diff | 120 ++++ dmenu.1 | 197 ++++++ dmenu.c | 796 +++++++++++++++++++++++ dmenu.o | Bin 0 -> 33632 bytes dmenu_path | 13 + dmenu_run | 2 + drw.c | 436 +++++++++++++ drw.h | 57 ++ drw.o | Bin 0 -> 10624 bytes stest | Bin 0 -> 17744 bytes stest.1 | 90 +++ stest.c | 109 ++++ stest.o | Bin 0 -> 5280 bytes util.c | 35 + util.h | 8 + util.o | Bin 0 -> 2264 bytes 26 files changed, 2159 insertions(+) create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 README create mode 100644 arg.h create mode 100644 config.def.h create mode 100644 config.h create mode 100644 config.mk create mode 100755 dmenu create mode 100644 dmenu-5.0.tar.gz create mode 100644 dmenu-borderoption-20200217-bf60a1e.diff create mode 100644 dmenu-center-20200111-8cd37e1.diff create mode 100644 dmenu.1 create mode 100644 dmenu.c create mode 100644 dmenu.o create mode 100755 dmenu_path create mode 100755 dmenu_run create mode 100644 drw.c create mode 100644 drw.h create mode 100644 drw.o create mode 100755 stest create mode 100644 stest.1 create mode 100644 stest.c create mode 100644 stest.o create mode 100644 util.c create mode 100644 util.h create mode 100644 util.o diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..3afd28e --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +MIT/X Consortium License + +© 2006-2019 Anselm R Garbe +© 2006-2008 Sander van Dijk +© 2006-2007 Michał Janeczek +© 2007 Kris Maglione +© 2009 Gottox +© 2009 Markus Schnalke +© 2009 Evan Gates +© 2010-2012 Connor Lane Smith +© 2014-2020 Hiltjo Posthuma +© 2015-2019 Quentin Rameau + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a03a95c --- /dev/null +++ b/Makefile @@ -0,0 +1,64 @@ +# dmenu - dynamic menu +# See LICENSE file for copyright and license details. + +include config.mk + +SRC = drw.c dmenu.c stest.c util.c +OBJ = $(SRC:.c=.o) + +all: options dmenu stest + +options: + @echo dmenu build options: + @echo "CFLAGS = $(CFLAGS)" + @echo "LDFLAGS = $(LDFLAGS)" + @echo "CC = $(CC)" + +.c.o: + $(CC) -c $(CFLAGS) $< + +config.h: + cp config.def.h $@ + +$(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) + +clean: + rm -f dmenu stest $(OBJ) 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) + +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 + +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 + +.PHONY: all options clean dist install uninstall diff --git a/README b/README new file mode 100644 index 0000000..a8fcdfe --- /dev/null +++ b/README @@ -0,0 +1,24 @@ +dmenu - dynamic menu +==================== +dmenu is an efficient dynamic menu for X. + + +Requirements +------------ +In order to build dmenu you need the Xlib header files. + + +Installation +------------ +Edit config.mk to match your local setup (dmenu is installed into +the /usr/local namespace by default). + +Afterwards enter the following command to build and install dmenu +(if necessary as root): + + make clean install + + +Running dmenu +------------- +See the man page for details. diff --git a/arg.h b/arg.h new file mode 100644 index 0000000..e94e02b --- /dev/null +++ b/arg.h @@ -0,0 +1,49 @@ +/* + * 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 diff --git a/config.def.h b/config.def.h new file mode 100644 index 0000000..88ef264 --- /dev/null +++ b/config.def.h @@ -0,0 +1,25 @@ +/* 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 */ +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ +/* -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[] = " "; diff --git a/config.h b/config.h new file mode 100644 index 0000000..6a2bf95 --- /dev/null +++ b/config.h @@ -0,0 +1,27 @@ +/* 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 */ + +static int centered = 0; /* -c option; centers dmenu on screen */ +static int min_width = 500; /* minimum width when centered */ + +/* -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[] = " "; diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..05d5a3e --- /dev/null +++ b/config.mk @@ -0,0 +1,31 @@ +# dmenu version +VERSION = 5.0 + +# paths +PREFIX = /usr/local +MANPREFIX = $(PREFIX)/share/man + +X11INC = /usr/X11R6/include +X11LIB = /usr/X11R6/lib + +# Xinerama, comment if you don't want it +XINERAMALIBS = -lXinerama +XINERAMAFLAGS = -DXINERAMA + +# freetype +FREETYPELIBS = -lfontconfig -lXft +FREETYPEINC = /usr/include/freetype2 +# OpenBSD (uncomment) +#FREETYPEINC = $(X11INC)/freetype2 + +# includes and libs +INCS = -I$(X11INC) -I$(FREETYPEINC) +LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) + +# 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) + +# compiler and linker +CC = cc diff --git a/dmenu b/dmenu new file mode 100755 index 0000000000000000000000000000000000000000..02b8de6e6c6c87893f282323a67f5bdd77b25149 GIT binary patch literal 44048 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 iLHeaAwkQlAJ{7Y40Oj?07R`@@`trboH literal 0 HcmV?d00001 diff --git a/dmenu-5.0.tar.gz b/dmenu-5.0.tar.gz new file mode 100644 index 0000000000000000000000000000000000000000..66fd77bc456e935f225309cfb8c4a948366f82a5 GIT binary patch literal 16028 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 WpNG%G=i&3!KK~Ca+;`0Y@BjeI7YVlj literal 0 HcmV?d00001 diff --git a/dmenu-borderoption-20200217-bf60a1e.diff b/dmenu-borderoption-20200217-bf60a1e.diff new file mode 100644 index 0000000..b9b5726 --- /dev/null +++ b/dmenu-borderoption-20200217-bf60a1e.diff @@ -0,0 +1,46 @@ +From bf60a1eaf98c7aebae51021914e35bc73dd8c23e Mon Sep 17 00:00:00 2001 +From: 0x1bi +Date: Mon, 17 Feb 2020 11:02:35 -0500 +Subject: [PATCH] added border with option + + +diff --git a/config.def.h b/config.def.h +index 1edb647..dd3eb31 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -21,3 +21,6 @@ static unsigned int lines = 0; + * for example: " /?\"&[]" + */ + static const char worddelimiters[] = " "; ++ ++/* Size of the window border */ ++static unsigned int border_width = 0; +diff --git a/dmenu.c b/dmenu.c +index 65f25ce..f0c3c6f 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -659,9 +659,11 @@ setup(void) + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; +- win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, ++ win = XCreateWindow(dpy, parentwin, x, y, mw, mh, border_width, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); ++ if (border_width) ++ XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel); + XSetClassHint(dpy, win, &ch); + + +@@ -733,6 +735,8 @@ main(int argc, char *argv[]) + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; ++ else if (!strcmp(argv[i], "-bw")) ++ border_width = atoi(argv[++i]); /* border width */ + else + usage(); + +-- +2.24.1 + diff --git a/dmenu-center-20200111-8cd37e1.diff b/dmenu-center-20200111-8cd37e1.diff new file mode 100644 index 0000000..af249a6 --- /dev/null +++ b/dmenu-center-20200111-8cd37e1.diff @@ -0,0 +1,120 @@ +From 8cd37e1ab9e7cb025224aeb3543f1a5be8bceb93 Mon Sep 17 00:00:00 2001 +From: Nihal Jere +Date: Sat, 11 Jan 2020 21:16:08 -0600 +Subject: [PATCH] center patch now has adjustable minimum width + +--- + config.def.h | 2 ++ + dmenu.1 | 3 +++ + dmenu.c | 39 ++++++++++++++++++++++++++++++++------- + 3 files changed, 37 insertions(+), 7 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 1edb647..88ef264 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -2,6 +2,8 @@ + /* Default settings; can be overriden by command line. */ + + static int topbar = 1; /* -b option; if 0, dmenu appears at bottom */ ++static int centered = 0; /* -c option; centers dmenu on screen */ ++static int min_width = 500; /* minimum width when centered */ + /* -fn option overrides fonts[0]; default X11 font or font set */ + static const char *fonts[] = { + "monospace:size=10" +diff --git a/dmenu.1 b/dmenu.1 +index 323f93c..c036baa 100644 +--- a/dmenu.1 ++++ b/dmenu.1 +@@ -40,6 +40,9 @@ which lists programs in the user's $PATH and runs the result in their $SHELL. + .B \-b + dmenu appears at the bottom of the screen. + .TP ++.B \-c ++dmenu appears centered on 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. +diff --git a/dmenu.c b/dmenu.c +index 65f25ce..041c7f8 100644 +--- a/dmenu.c ++++ b/dmenu.c +@@ -89,6 +89,15 @@ calcoffsets(void) + break; + } + ++static int ++max_textw(void) ++{ ++ int len = 0; ++ for (struct item *item = items; item && item->text; item++) ++ len = MAX(TEXTW(item->text), len); ++ return len; ++} ++ + static void + cleanup(void) + { +@@ -611,6 +620,7 @@ setup(void) + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; ++ promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + #ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { +@@ -637,9 +647,16 @@ setup(void) + if (INTERSECT(x, y, 1, 1, info[i])) + break; + +- x = info[i].x_org; +- y = info[i].y_org + (topbar ? 0 : info[i].height - mh); +- mw = info[i].width; ++ if (centered) { ++ mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); ++ x = info[i].x_org + ((info[i].width - mw) / 2); ++ y = info[i].y_org + ((info[i].height - mh) / 2); ++ } else { ++ x = info[i].x_org; ++ y = info[i].y_org + (topbar ? 0 : info[i].height - mh); ++ mw = info[i].width; ++ } ++ + XFree(info); + } else + #endif +@@ -647,11 +664,17 @@ setup(void) + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); +- x = 0; +- y = topbar ? 0 : wa.height - mh; +- mw = wa.width; ++ ++ if (centered) { ++ mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); ++ x = (wa.width - mw) / 2; ++ y = (wa.height - mh) / 2; ++ } else { ++ x = 0; ++ y = topbar ? 0 : wa.height - mh; ++ mw = wa.width; ++ } + } +- promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; + inputw = MIN(inputw, mw/3); + match(); + +@@ -709,6 +732,8 @@ main(int argc, char *argv[]) + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; ++ else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ ++ centered = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; +-- +2.24.1 + diff --git a/dmenu.1 b/dmenu.1 new file mode 100644 index 0000000..c036baa --- /dev/null +++ b/dmenu.1 @@ -0,0 +1,197 @@ +.TH DMENU 1 dmenu\-VERSION +.SH NAME +dmenu \- dynamic menu +.SH SYNOPSIS +.B dmenu +.RB [ \-bfiv ] +.RB [ \-l +.IR lines ] +.RB [ \-m +.IR monitor ] +.RB [ \-p +.IR prompt ] +.RB [ \-fn +.IR font ] +.RB [ \-nb +.IR color ] +.RB [ \-nf +.IR color ] +.RB [ \-sb +.IR color ] +.RB [ \-sf +.IR color ] +.RB [ \-w +.IR windowid ] +.P +.BR dmenu_run " ..." +.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. +.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. +.SH OPTIONS +.TP +.B \-b +dmenu appears at the bottom of the screen. +.TP +.B \-c +dmenu appears centered on 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 +.BI \-l " lines" +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. +.TP +.BI \-p " prompt" +defines the prompt to be displayed to the left of the input field. +.TP +.BI \-fn " font" +defines the font or font set used. +.TP +.BI \-nb " color" +defines the normal background color. +.IR #RGB , +.IR #RRGGBB , +and X color names are supported. +.TP +.BI \-nf " color" +defines the normal foreground color. +.TP +.BI \-sb " color" +defines the selected background color. +.TP +.BI \-sf " color" +defines the selected foreground color. +.TP +.B \-v +prints version information to stdout, then exits. +.TP +.BI \-w " windowid" +embed into windowid. +.SH USAGE +dmenu is completely controlled by the keyboard. Items are selected using the +arrow keys, page up, page down, home, and end. +.TP +.B Tab +Copy the selected item to the input field. +.TP +.B Return +Confirm selection. Prints the selected item to stdout 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 +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 +.SH SEE ALSO +.IR dwm (1), +.IR stest (1) diff --git a/dmenu.c b/dmenu.c new file mode 100644 index 0000000..041c7f8 --- /dev/null +++ b/dmenu.c @@ -0,0 +1,796 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef XINERAMA +#include +#endif +#include + +#include "drw.h" +#include "util.h" + +/* macros */ +#define INTERSECT(x,y,w,h,r) (MAX(0, MIN((x)+(w),(r).x_org+(r).width) - MAX((x),(r).x_org)) \ + * MAX(0, MIN((y)+(h),(r).y_org+(r).height) - MAX((y),(r).y_org))) +#define LENGTH(X) (sizeof X / sizeof X[0]) +#define TEXTW(X) (drw_fontset_getwidth(drw, (X)) + lrpad) + +/* enums */ +enum { SchemeNorm, SchemeSel, SchemeOut, SchemeLast }; /* color schemes */ + +struct item { + char *text; + struct item *left, *right; + int out; +}; + +static char text[BUFSIZ] = ""; +static char *embed; +static int bh, mw, mh; +static int inputw = 0, promptw; +static int lrpad; /* sum of left and right padding */ +static size_t cursor; +static struct item *items = NULL; +static struct item *matches, *matchend; +static struct item *prev, *curr, *next, *sel; +static int mon = -1, screen; + +static Atom clip, utf8; +static Display *dpy; +static Window root, parentwin, win; +static XIC xic; + +static Drw *drw; +static Clr *scheme[SchemeLast]; + +#include "config.h" + +static int (*fstrncmp)(const char *, const char *, size_t) = strncmp; +static char *(*fstrstr)(const char *, const char *) = strstr; + +static void +appenditem(struct item *item, struct item **list, struct item **last) +{ + if (*last) + (*last)->right = item; + else + *list = item; + + item->left = *last; + item->right = NULL; + *last = item; +} + +static void +calcoffsets(void) +{ + int i, n; + + if (lines > 0) + n = lines * bh; + else + n = mw - (promptw + inputw + TEXTW("<") + TEXTW(">")); + /* calculate which items will begin the next page and previous page */ + for (i = 0, next = curr; next; next = next->right) + if ((i += (lines > 0) ? bh : MIN(TEXTW(next->text), n)) > n) + break; + for (i = 0, prev = curr; prev && prev->left; prev = prev->left) + if ((i += (lines > 0) ? bh : MIN(TEXTW(prev->left->text), n)) > n) + break; +} + +static int +max_textw(void) +{ + int len = 0; + for (struct item *item = items; item && item->text; item++) + len = MAX(TEXTW(item->text), len); + return len; +} + +static void +cleanup(void) +{ + size_t i; + + XUngrabKey(dpy, AnyKey, AnyModifier, root); + for (i = 0; i < SchemeLast; i++) + free(scheme[i]); + drw_free(drw); + XSync(dpy, False); + XCloseDisplay(dpy); +} + +static char * +cistrstr(const char *s, const char *sub) +{ + size_t len; + + for (len = strlen(sub); *s; s++) + if (!strncasecmp(s, sub, len)) + return (char *)s; + return NULL; +} + +static int +drawitem(struct item *item, int x, int y, int w) +{ + if (item == sel) + drw_setscheme(drw, scheme[SchemeSel]); + else if (item->out) + drw_setscheme(drw, scheme[SchemeOut]); + else + drw_setscheme(drw, scheme[SchemeNorm]); + + return drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0); +} + +static void +drawmenu(void) +{ + unsigned int curpos; + struct item *item; + int x = 0, y = 0, w; + + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, 0, 0, mw, mh, 1, 1); + + if (prompt && *prompt) { + drw_setscheme(drw, scheme[SchemeSel]); + x = drw_text(drw, x, 0, promptw, bh, lrpad / 2, prompt, 0); + } + /* draw input field */ + w = (lines > 0 || !matches) ? mw - x : inputw; + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0); + + curpos = TEXTW(text) - TEXTW(&text[cursor]); + if ((curpos += lrpad / 2 - 1) < w) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0); + } + + if (lines > 0) { + /* draw vertical list */ + for (item = curr; item != next; item = item->right) + drawitem(item, x, y += bh, mw - x); + } else if (matches) { + /* draw horizontal list */ + x += inputw; + w = TEXTW("<"); + if (curr->left) { + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, x, 0, w, bh, lrpad / 2, "<", 0); + } + x += w; + for (item = curr; item != next; item = item->right) + x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">"))); + if (next) { + w = TEXTW(">"); + drw_setscheme(drw, scheme[SchemeNorm]); + drw_text(drw, mw - w, 0, w, bh, lrpad / 2, ">", 0); + } + } + drw_map(drw, win, 0, 0, mw, mh); +} + +static void +grabfocus(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 10000000 }; + Window focuswin; + int i, revertwin; + + for (i = 0; i < 100; ++i) { + XGetInputFocus(dpy, &focuswin, &revertwin); + if (focuswin == win) + return; + XSetInputFocus(dpy, win, RevertToParent, CurrentTime); + nanosleep(&ts, NULL); + } + die("cannot grab focus"); +} + +static void +grabkeyboard(void) +{ + struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 }; + int i; + + if (embed) + return; + /* try to grab keyboard, we may have to wait for another process to ungrab */ + for (i = 0; i < 1000; i++) { + if (XGrabKeyboard(dpy, DefaultRootWindow(dpy), True, GrabModeAsync, + GrabModeAsync, CurrentTime) == GrabSuccess) + return; + nanosleep(&ts, NULL); + } + die("cannot grab keyboard"); +} + +static void +match(void) +{ + static char **tokv = NULL; + static int tokn = 0; + + char buf[sizeof text], *s; + int i, tokc = 0; + size_t len, textsize; + struct item *item, *lprefix, *lsubstr, *prefixend, *substrend; + + strcpy(buf, text); + /* separate input text into tokens to be matched individually */ + for (s = strtok(buf, " "); s; tokv[tokc - 1] = s, s = strtok(NULL, " ")) + if (++tokc > tokn && !(tokv = realloc(tokv, ++tokn * sizeof *tokv))) + die("cannot realloc %u bytes:", tokn * sizeof *tokv); + len = tokc ? strlen(tokv[0]) : 0; + + matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL; + textsize = strlen(text) + 1; + for (item = items; item && item->text; item++) { + for (i = 0; i < tokc; i++) + if (!fstrstr(item->text, tokv[i])) + break; + if (i != tokc) /* not all tokens match */ + continue; + /* exact matches go first, then prefixes, then substrings */ + if (!tokc || !fstrncmp(text, item->text, textsize)) + appenditem(item, &matches, &matchend); + else if (!fstrncmp(tokv[0], item->text, len)) + appenditem(item, &lprefix, &prefixend); + else + appenditem(item, &lsubstr, &substrend); + } + if (lprefix) { + if (matches) { + matchend->right = lprefix; + lprefix->left = matchend; + } else + matches = lprefix; + matchend = prefixend; + } + if (lsubstr) { + if (matches) { + matchend->right = lsubstr; + lsubstr->left = matchend; + } else + matches = lsubstr; + matchend = substrend; + } + curr = sel = matches; + calcoffsets(); +} + +static void +insert(const char *str, ssize_t n) +{ + if (strlen(text) + n > sizeof text - 1) + return; + /* move existing text out of the way, insert new text, and update cursor */ + memmove(&text[cursor + n], &text[cursor], sizeof text - cursor - MAX(n, 0)); + if (n > 0) + memcpy(&text[cursor], str, n); + cursor += n; + match(); +} + +static size_t +nextrune(int inc) +{ + ssize_t n; + + /* return location of next utf8 rune in the given direction (+1 or -1) */ + for (n = cursor + inc; n + inc >= 0 && (text[n] & 0xc0) == 0x80; n += inc) + ; + return n; +} + +static void +movewordedge(int dir) +{ + if (dir < 0) { /* move cursor to the start of the word*/ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + cursor = nextrune(-1); + } else { /* move cursor to the end of the word */ + while (text[cursor] && strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + while (text[cursor] && !strchr(worddelimiters, text[cursor])) + cursor = nextrune(+1); + } +} + +static void +keypress(XKeyEvent *ev) +{ + char buf[32]; + int len; + KeySym ksym; + Status status; + + len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status); + switch (status) { + default: /* XLookupNone, XBufferOverflow */ + return; + case XLookupChars: + goto insert; + case XLookupKeySym: + case XLookupBoth: + break; + } + + if (ev->state & ControlMask) { + switch(ksym) { + case XK_a: ksym = XK_Home; break; + case XK_b: ksym = XK_Left; break; + case XK_c: ksym = XK_Escape; break; + case XK_d: ksym = XK_Delete; break; + case XK_e: ksym = XK_End; break; + case XK_f: ksym = XK_Right; break; + case XK_g: ksym = XK_Escape; break; + case XK_h: ksym = XK_BackSpace; break; + case XK_i: ksym = XK_Tab; break; + case XK_j: /* fallthrough */ + case XK_J: /* fallthrough */ + case XK_m: /* fallthrough */ + case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break; + case XK_n: ksym = XK_Down; break; + case XK_p: ksym = XK_Up; break; + + case XK_k: /* delete right */ + text[cursor] = '\0'; + match(); + break; + case XK_u: /* delete left */ + insert(NULL, 0 - cursor); + break; + case XK_w: /* delete word */ + while (cursor > 0 && strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + while (cursor > 0 && !strchr(worddelimiters, text[nextrune(-1)])) + insert(NULL, nextrune(-1) - cursor); + break; + case XK_y: /* paste selection */ + case XK_Y: + XConvertSelection(dpy, (ev->state & ShiftMask) ? clip : XA_PRIMARY, + utf8, utf8, win, CurrentTime); + return; + case XK_Left: + movewordedge(-1); + goto draw; + case XK_Right: + movewordedge(+1); + goto draw; + case XK_Return: + case XK_KP_Enter: + break; + case XK_bracketleft: + cleanup(); + exit(1); + default: + return; + } + } else if (ev->state & Mod1Mask) { + switch(ksym) { + case XK_b: + movewordedge(-1); + goto draw; + case XK_f: + movewordedge(+1); + goto draw; + case XK_g: ksym = XK_Home; break; + case XK_G: ksym = XK_End; break; + case XK_h: ksym = XK_Up; break; + case XK_j: ksym = XK_Next; break; + case XK_k: ksym = XK_Prior; break; + case XK_l: ksym = XK_Down; break; + default: + return; + } + } + + switch(ksym) { + default: +insert: + if (!iscntrl(*buf)) + insert(buf, len); + break; + case XK_Delete: + if (text[cursor] == '\0') + return; + cursor = nextrune(+1); + /* fallthrough */ + case XK_BackSpace: + if (cursor == 0) + return; + insert(NULL, nextrune(-1) - cursor); + break; + case XK_End: + if (text[cursor] != '\0') { + cursor = strlen(text); + break; + } + if (next) { + /* jump to end of list and position items in reverse */ + curr = matchend; + calcoffsets(); + curr = prev; + calcoffsets(); + while (next && (curr = curr->right)) + calcoffsets(); + } + sel = matchend; + break; + case XK_Escape: + cleanup(); + exit(1); + case XK_Home: + if (sel == matches) { + cursor = 0; + break; + } + sel = curr = matches; + calcoffsets(); + break; + case XK_Left: + if (cursor > 0 && (!sel || !sel->left || lines > 0)) { + cursor = nextrune(-1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Up: + if (sel && sel->left && (sel = sel->left)->right == curr) { + curr = prev; + calcoffsets(); + } + break; + case XK_Next: + if (!next) + return; + sel = curr = next; + calcoffsets(); + break; + case XK_Prior: + if (!prev) + return; + sel = curr = prev; + calcoffsets(); + break; + case XK_Return: + case XK_KP_Enter: + puts((sel && !(ev->state & ShiftMask)) ? sel->text : text); + if (!(ev->state & ControlMask)) { + cleanup(); + exit(0); + } + if (sel) + sel->out = 1; + break; + case XK_Right: + if (text[cursor] != '\0') { + cursor = nextrune(+1); + break; + } + if (lines > 0) + return; + /* fallthrough */ + case XK_Down: + if (sel && sel->right && (sel = sel->right) == next) { + curr = next; + calcoffsets(); + } + break; + case XK_Tab: + if (!sel) + return; + strncpy(text, sel->text, sizeof text - 1); + text[sizeof text - 1] = '\0'; + cursor = strlen(text); + match(); + break; + } + +draw: + drawmenu(); +} + +static void +paste(void) +{ + char *p, *q; + int di; + unsigned long dl; + Atom da; + + /* we have been given the current selection, now insert it into input */ + if (XGetWindowProperty(dpy, win, utf8, 0, (sizeof text / 4) + 1, False, + utf8, &da, &di, &dl, &dl, (unsigned char **)&p) + == Success && p) { + insert(p, (q = strchr(p, '\n')) ? q - p : (ssize_t)strlen(p)); + XFree(p); + } + drawmenu(); +} + +static void +readstdin(void) +{ + char buf[sizeof text], *p; + size_t i, imax = 0, size = 0; + unsigned int tmpmax = 0; + + /* read each line from stdin and add it to the item list */ + for (i = 0; fgets(buf, sizeof buf, stdin); i++) { + if (i + 1 >= size / sizeof *items) + if (!(items = realloc(items, (size += BUFSIZ)))) + die("cannot realloc %u bytes:", size); + if ((p = strchr(buf, '\n'))) + *p = '\0'; + if (!(items[i].text = strdup(buf))) + die("cannot strdup %u bytes:", strlen(buf) + 1); + items[i].out = 0; + drw_font_getexts(drw->fonts, buf, strlen(buf), &tmpmax, NULL); + if (tmpmax > inputw) { + inputw = tmpmax; + imax = i; + } + } + if (items) + items[i].text = NULL; + inputw = items ? TEXTW(items[imax].text) : 0; + lines = MIN(lines, i); +} + +static void +run(void) +{ + XEvent ev; + + while (!XNextEvent(dpy, &ev)) { + if (XFilterEvent(&ev, win)) + continue; + switch(ev.type) { + case DestroyNotify: + if (ev.xdestroywindow.window != win) + break; + cleanup(); + exit(1); + case Expose: + if (ev.xexpose.count == 0) + drw_map(drw, win, 0, 0, mw, mh); + break; + case FocusIn: + /* regrab focus from parent window */ + if (ev.xfocus.window != win) + grabfocus(); + break; + case KeyPress: + keypress(&ev.xkey); + break; + case SelectionNotify: + if (ev.xselection.property == utf8) + paste(); + break; + case VisibilityNotify: + if (ev.xvisibility.state != VisibilityUnobscured) + XRaiseWindow(dpy, win); + break; + } + } +} + +static void +setup(void) +{ + int x, y, i, j; + unsigned int du; + XSetWindowAttributes swa; + XIM xim; + Window w, dw, *dws; + XWindowAttributes wa; + XClassHint ch = {"dmenu", "dmenu"}; +#ifdef XINERAMA + XineramaScreenInfo *info; + Window pw; + int a, di, n, area = 0; +#endif + /* init appearance */ + for (j = 0; j < SchemeLast; j++) + scheme[j] = drw_scm_create(drw, colors[j], 2); + + clip = XInternAtom(dpy, "CLIPBOARD", False); + utf8 = XInternAtom(dpy, "UTF8_STRING", False); + + /* calculate menu geometry */ + bh = drw->fonts->h + 2; + lines = MAX(lines, 0); + mh = (lines + 1) * bh; + promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0; +#ifdef XINERAMA + i = 0; + if (parentwin == root && (info = XineramaQueryScreens(dpy, &n))) { + XGetInputFocus(dpy, &w, &di); + if (mon >= 0 && mon < n) + i = mon; + else if (w != root && w != PointerRoot && w != None) { + /* find top-level window containing current input focus */ + do { + if (XQueryTree(dpy, (pw = w), &dw, &w, &dws, &du) && dws) + XFree(dws); + } while (w != root && w != pw); + /* find xinerama screen with which the window intersects most */ + if (XGetWindowAttributes(dpy, pw, &wa)) + for (j = 0; j < n; j++) + if ((a = INTERSECT(wa.x, wa.y, wa.width, wa.height, info[j])) > area) { + area = a; + i = j; + } + } + /* no focused window is on screen, so use pointer location instead */ + if (mon < 0 && !area && XQueryPointer(dpy, root, &dw, &dw, &x, &y, &di, &di, &du)) + for (i = 0; i < n; i++) + if (INTERSECT(x, y, 1, 1, info[i])) + break; + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width); + x = info[i].x_org + ((info[i].width - mw) / 2); + y = info[i].y_org + ((info[i].height - mh) / 2); + } else { + x = info[i].x_org; + y = info[i].y_org + (topbar ? 0 : info[i].height - mh); + mw = info[i].width; + } + + XFree(info); + } else +#endif + { + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + + if (centered) { + mw = MIN(MAX(max_textw() + promptw, min_width), wa.width); + x = (wa.width - mw) / 2; + y = (wa.height - mh) / 2; + } else { + x = 0; + y = topbar ? 0 : wa.height - mh; + mw = wa.width; + } + } + inputw = MIN(inputw, mw/3); + match(); + + /* create menu window */ + swa.override_redirect = True; + swa.background_pixel = scheme[SchemeNorm][ColBg].pixel; + swa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask; + win = XCreateWindow(dpy, parentwin, x, y, mw, mh, 0, + CopyFromParent, CopyFromParent, CopyFromParent, + CWOverrideRedirect | CWBackPixel | CWEventMask, &swa); + XSetClassHint(dpy, win, &ch); + + + /* input methods */ + if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL) + die("XOpenIM failed: could not open input device"); + + xic = XCreateIC(xim, XNInputStyle, XIMPreeditNothing | XIMStatusNothing, + XNClientWindow, win, XNFocusWindow, win, NULL); + + XMapRaised(dpy, win); + if (embed) { + XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask); + if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) { + for (i = 0; i < du && dws[i] != win; ++i) + XSelectInput(dpy, dws[i], FocusChangeMask); + XFree(dws); + } + grabfocus(); + } + drw_resize(drw, mw, mh); + drawmenu(); +} + +static void +usage(void) +{ + fputs("usage: dmenu [-bfiv] [-l lines] [-p prompt] [-fn font] [-m monitor]\n" + " [-nb color] [-nf color] [-sb color] [-sf color] [-w windowid]\n", stderr); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + XWindowAttributes wa; + int i, fast = 0; + + for (i = 1; i < argc; i++) + /* these options take no arguments */ + if (!strcmp(argv[i], "-v")) { /* prints version information */ + puts("dmenu-"VERSION); + exit(0); + } else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */ + topbar = 0; + else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */ + fast = 1; + else if (!strcmp(argv[i], "-c")) /* centers dmenu on screen */ + centered = 1; + else if (!strcmp(argv[i], "-i")) { /* case-insensitive item matching */ + fstrncmp = strncasecmp; + fstrstr = cistrstr; + } else if (i + 1 == argc) + usage(); + /* these options take one argument */ + else if (!strcmp(argv[i], "-l")) /* number of lines in vertical list */ + lines = atoi(argv[++i]); + else if (!strcmp(argv[i], "-m")) + mon = atoi(argv[++i]); + else if (!strcmp(argv[i], "-p")) /* adds prompt to left of input field */ + prompt = argv[++i]; + else if (!strcmp(argv[i], "-fn")) /* font or font set */ + fonts[0] = argv[++i]; + else if (!strcmp(argv[i], "-nb")) /* normal background color */ + colors[SchemeNorm][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-nf")) /* normal foreground color */ + colors[SchemeNorm][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-sb")) /* selected background color */ + colors[SchemeSel][ColBg] = argv[++i]; + else if (!strcmp(argv[i], "-sf")) /* selected foreground color */ + colors[SchemeSel][ColFg] = argv[++i]; + else if (!strcmp(argv[i], "-w")) /* embedding window id */ + embed = argv[++i]; + else + usage(); + + if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + fputs("warning: no locale support\n", stderr); + if (!(dpy = XOpenDisplay(NULL))) + die("cannot open display"); + screen = DefaultScreen(dpy); + root = RootWindow(dpy, screen); + if (!embed || !(parentwin = strtol(embed, NULL, 0))) + parentwin = root; + if (!XGetWindowAttributes(dpy, parentwin, &wa)) + die("could not get embedding window attributes: 0x%lx", + parentwin); + drw = drw_create(dpy, screen, root, wa.width, wa.height); + if (!drw_fontset_create(drw, fonts, LENGTH(fonts))) + die("no fonts could be loaded."); + lrpad = drw->fonts->h; + +#ifdef __OpenBSD__ + if (pledge("stdio rpath", NULL) == -1) + die("pledge"); +#endif + + if (fast && !isatty(0)) { + grabkeyboard(); + readstdin(); + } else { + readstdin(); + grabkeyboard(); + } + setup(); + run(); + + return 1; /* unreachable */ +} diff --git a/dmenu.o b/dmenu.o new file mode 100644 index 0000000000000000000000000000000000000000..254a18126948ad15772f74465a42530be101553c GIT binary patch literal 33632 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 +#include +#include +#include +#include + +#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."); + } + + /* Do not allow using color fonts. This is a workaround for a BadLength + * error from Xft with color glyphs. Modelled on the Xterm workaround. See + * https://bugzilla.redhat.com/show_bug.cgi?id=1498269 + * https://lists.suckless.org/dev/1701/30932.html + * https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916349 + * and lots more all over the internet. + */ + FcBool iscol; + if(FcPatternGetBool(xfont->pattern, FC_COLOR, 0, &iscol) == FcResultMatch && iscol) { + XftFontClose(drw->dpy, xfont); + return NULL; + } + + 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) +{ + char buf[1024]; + int ty; + unsigned int ew; + XftDraw *d = NULL; + Fnt *usedfont, *curfont, *nextfont; + size_t i, len; + 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; + + if (!drw || (render && !drw->scheme) || !text || !drw->fonts) + return 0; + + if (!render) { + w = ~w; + } 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; + while (1) { + 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) { + if (curfont == usedfont) { + utf8strlen += utf8charlen; + text += utf8charlen; + } else { + nextfont = curfont; + } + break; + } + } + + if (!charexists || nextfont) + break; + else + charexists = 0; + } + + if (utf8strlen) { + drw_font_getexts(usedfont, utf8str, utf8strlen, &ew, NULL); + /* shorten text if necessary */ + for (len = MIN(utf8strlen, sizeof(buf) - 1); len && ew > w; len--) + drw_font_getexts(usedfont, utf8str, len, &ew, NULL); + + if (len) { + memcpy(buf, utf8str, len); + buf[len] = '\0'; + if (len < utf8strlen) + for (i = len; i && i > len - 3; buf[--i] = '.') + ; /* NOP */ + + if (render) { + ty = y + (h - usedfont->h) / 2 + usedfont->xfont->ascent; + XftDrawStringUtf8(d, &drw->scheme[invert ? ColBg : ColFg], + usedfont->xfont, x, ty, (XftChar8 *)buf, len); + } + x += ew; + w -= ew; + } + } + + if (!*text) { + 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; + + 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); + FcPatternAddBool(fcpattern, FC_COLOR, FcFalse); + + 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); + 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); +} + +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); +} diff --git a/drw.h b/drw.h new file mode 100644 index 0000000..4c67419 --- /dev/null +++ b/drw.h @@ -0,0 +1,57 @@ +/* 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); +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); diff --git a/drw.o b/drw.o new file mode 100644 index 0000000000000000000000000000000000000000..941460bd62bca873538fd2390d633e6978db77e9 GIT binary patch literal 10624 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 PX_G<`dDd=y*?<27)P!v+ literal 0 HcmV?d00001 diff --git a/stest b/stest new file mode 100755 index 0000000000000000000000000000000000000000..c93eed0aabd6b4ee4f747c9cb34b7bb157cf0469 GIT binary patch literal 17744 zcmeHPeQ;CPmA|$ym@ml?NJvPEnwl1xSYQgQDN!RCn>;5bIEkTb3J4*|wkERVNKarG zcH*q;w89bV$&e14Ng8*zyTf#|Gj1o7&~{23pd{PbOldlsX0~5!KCMEEHz9;&18;xl zzI*JaXQ|Wa&h#%`&(pc*cRucY=iU46yYJoWN19tVR#sF9P7d)EL0sQ;CgM|qny;t~ zh)*<#+4#Lh+$^SppC&O=?lTFbX6mXeq}2*{fRf!&CAZU?l^#>BAyKj`mCnpGB}_$) z$&+0bWfgCy-8bKU1q6#pY<8X?N6HNG~h9YFR1BRJ{l$yJO1k zn6hK)R(4D|o)i;0PAR#aHbF*-Dd|#bdsh!re!H8vskGgM1yi=W4|Y_~KQ(dK?^orO zipw@tf2I_FN-JW~-D_8`h=uE7(RjMIuD4-r-P+ZjRKl}Lp7#RiP@B5z-YsI!s-_=2 zIl6i7{2K=ze(PsfzPsQ1oI&-bI*>oop+x$sIGJAvp5%01-|)LD`h=K8*bO)+{qXH4 z4s2-tkMkW{f;DeGyzS<79}aT!G!-nruOUC*!gKZ(WZcYHXbAaY`Ws8&KPrJ!{oOe1 z=?wtI{NE1TZC3{W2##X;g|<~#<}0FQ+^m{tFJ5lQJ;yhz6i9mZwiDX z$w+53rALzOn>NG}@ko1ccPt_TfzIwkJdo0ZNj(rSRf{S{dZW5XMuOpRG%0!_$z&oa zLa{_DBKfqQf-86lJroFaJrw8&Mq?tShofFw5fKc9A}Ml-J{(fD>kh(ODy|GWk`c1%MVUJ3nHi{WtFNa%zE)5k zu9Ly;AOZCRbR^<>C=u_7c1}RsI&|}%Kt-9Bfk-&02jO*hDrFWf0yqhAS8Ge-hQKP% z>WSPc&l&`~QXZ9x63>5*i5wvn_@iIar~5{wSSGKfWW|DLbS~Xt?p9d(rYJ_HO3@CU z5|0ObU3=&ThBTt?_l&z0e#9rmf^p{Gywk+#o@Lg=K6tH48q+g11?4T)>I6&4N28P$70%aF+$|vfw=Sk+{c# z&m%ykUZ0tv`djapeHL6jl1asHSa8)uBwqe0N1z;mas=(%e|{7GPuPJNR`i@gYZi7s>Kx4Wy<2Njwed`7@G#o%l-P zMSf-ztz9R-@YZ6FdooyJFB%v&o9Hc9@Yk@EnNm>E!$ta2tfU)HaJ~-rKwgvL{UbL z{#v$*{Az;^MA+4T6&8IBb*_Pn$Q*2|=zrbSpC{}}U3y+$Z>31j)-ornjo0$OAm7Gl zOZL~z^_R4)W7FX6wf@Yd3je`o(Ld^X@`S(t4OjoSq1CeP*RFvh$heM==e9o4l-u)c z=1Rrhpug{LDn#0~X)sn>y=mZs^sKQ*adldZpPZX+yw-vXeMel&t-0tyIk~DFD%qO7 zpt+jI$L_;Lsej#{9c>An_8+G!+>E*(KH<+U_`1FvT4OI=GmLXfw4C=c;x~35IBGx7 zZVFx6@XE9orA7Uv7Gqj%zL`S%T1#$wZFT+==%F4nG;lTNKHHkpYZsl1X}P8uMqk6M zwBt0z=|F9@mc2H16;8Clg_r86b`tyT*i0%%Dh!N1^6J=AS5b+5+Ti-JVdPq}KcPBo zr|OM>2a{e*%tyq?nCh(=Zx7B>=xfit+M3&Soi;cdmo2xj31_h>d-gcJC}_=oGFAf< zui+A6@Aq1>?-3G5b~JmVhm8=_|LQ+z$^Lw&mVHahyffa`-kPgAjsBwL=5Ch6+Y1-Q z!c~~6UmQZP^T^;*y+vf*Wql#K?q?{E)1GOZbuH;5zb6%i{tu-7n(q@?eof1cYcK!d zPVME(4z1#}_Va7{0yud0Hq$}1aTZl;nzX0-^?9Fxtw?`qiLhV=_YN0=Z2GwMh4uK>*@m?=o&(3(~NsZ;_ zuA{zrVeDB{7`*`RuP90j%mjOm61@K6eV2@E~2nbxpOqT!${Bhh)4*q__DH zxsWqOx!*|9qf)fMQbjaR*TAS0#z;uj>6BWv=dM3aw`;1)@hkEK-^qWI`e5cjZG&(f zIw-9grLsJ+YFGariLI5MmS(*5lsSeI5qh1xlaRBAr89IJ*9)U4gDyG251M8CG`|Wy z)>U1=Y3a9Sysv`_TwY`B@BhazUa5K?JUML5zCX4Xh=$ludqCea@5tzTKO_9nQ`BzI z_0T2E_9yS@|MGddr?+I^@Na3!e(Z1eXFuJd4C z^{3YChppK^G-coS8w-Ai`$>hi?lpd5j61j-R8N1z;m|4#(yyG1$`?2LTby)@-hN?27d~oJ^(n?CsqFFz)V%#v+tUaE<_v$FpOWm|`K8rdBLXp;8r#9qZdbX>xw* zf??2%=hV1ioCmG_$S`7{w_G%g=Rli4M?nu?GK@R$WHjv)=z}f*eHwH%=<~!whf>A< z`$a`>O~v9lGpdIaP9I}OJ~RxPjftiic%(%+I9B6mK>6}{(OI+6x#(`!?7h`};?A4a zfALGTw~`(yZ^scuy}m$z-&sTR`W_qyfVI-&X`{2|@yaIWqQ@L}JI_=)8Y-QOpw{Tb zT)!3Kr*WJ?IbT(}e^=S!Ty)6M=yd0%H9D6)UgdYLd~AA?vmrC%9_LxNqps4~0LbrL z29-wVBDj~n;6{F@KM&gWv0+3A*yyZL{+h{OGx=*Ge;WC_%jt_3bw=@8jrjD!54~jJ zM0hLk9^f2Pl2IJOO5ci;tenabC`X_ifpP@O5hzEX9D#BK$`L3>;4c#aULVHm!SIMK z6U`AQF@M=)C-|94!19}IJiRifG{+?71OWNDivNyU+{J6Tc-`7!C6}K$k-tgtync-r z#5=CHu~fyk3dw8A9$#f7&vD1JSMoJV zj@SBO{qVFksNzv(Q z5#06G#c(M1h>uFgTI~%RzU*Fx72C_*^(#HAJS$E3kN!H)`guQrRgubgDsrHAyKV6%xkESw_xD15mk@3%V&)wvbbC>Jo){^L&!1TLYW3z2*G;T@ zEyNdyV(rr*77AYXTBv{hME@_uZxBO1D`Msymi5?HsYCGHZW5(_JWc$!F^QNtx^Gq> za@>C0N&z(~s((ufd@1lsT$g?7M)d{dhrW6eKCE#09ts%sg9_B&*JvtsnMBYEX2ONv z1cns8PT|8g+^g`T$`5_%rL+M@vGctbIMrvwx)>E$o5Ii9@Q}iJ+yX1pKKxQWU-g+h z#h20mj*24f;W^+`E{}txOX(Pn3apsraY=&sDOLYpO8t4leCx;BCM|Zp?@H0sc79p< zncCidj1!UdR09l;J5G6m^QW})kkTKr=`T_G_Vc3s1r-?0?ET_J+`lS|+y@o|cVo1( zxBvCfNn3H*>g?+aU@RA>>*{K~n)UjHa?x6-ka-V*)~mcVJB z#r(>t_a%BKY+WbKW&B9$qn+5|c>;K``urC7JTZBH6rWL5AFh9U z34L0rSj_(c;B42lY^w2qpEDp2oVWkeJ*D6wtc^*ufKz&_0`RBG4I2><-2P zVLg#d1%l~b5lVFT#3Fhmj7O-V8nj<08VDwn!F|{tqbK)?4(zsx1j6a=?tL(^Z~>}s zfodSI@qYiN=0NkkO|3$Kot95guzcsLV$546-SqJq6pJ=oM#s7QOD3Xv%r zlD@gLiO)v_5|jIdEEkj7pr1TYe@`5@z>Te5IXuuVyAP*U5hY_{h9C_8A~YE>vx z8@viZS-nCi5Wp_7iAxtwB?4W+cv$Wyn}|4FClO3`?pdieec4@Dc306!ShkK$YOdxT zVv~@%WOBn-AKpk+6kt1s};)Rel+1p?d#TqyFvA&NxL|Q z4Q`QSkMPL8+Zj)LcBiA*jT#Lr$lusfM_t)NvaVpNOL)Tj;_zouJ!wkzM3Sj!B3{4* zAWKGKK{8Oeo|rB?GUgsV(u+KGPRJ5+FPkUQrMhER7`Dur7G}RREjSOy!R}}Xr6%AD zKOR|t=?qs+cyN2@Mz81F^`A}Yy#Q|Acos1e&%>DVdjTbux|}EaIUF=aW_zBmF{SUh z*2LpAV{4JI+VgWcQ#UIrYPW9!j!)1DE}!RrOdFIPKQHlk!fxsLnBG;eJ&iwb6yp!o_Wy|$vJ*E#qPVX|9=lLm9 zns-pL*WW~i*pCdwlqOi*>gWKWMOcKbuXNQK`gVbC^{ zqWs=Up|Y-=Cz{1!wRbB!rc!7zu(=n9?e=P(&GYU4iHey{*q&WJWwYn`I8%Nv$NKj6 zJFM(E{=%&^6@5^#OzieYA)xbTd!FAjZD&J8?e;I&?D;(aQ+oclCcb`*jUwa3p}Nh) zypI20fL)(?rvDCGJk3wo^ZUpVTB(i1dL}o?GyWz7bcwipo@ep@V{og$@MUB>zK;I| zTRKIy=l2wEstMCF$gGL&nZ5_1)n53N_DYiuDV7P_F}(nR)qcolY7QxTuBY9e`Hzty zUtIrTpD7t;du|veEZmARxq=Lpuhbb9Y;S+ICS6KaYAZs@k12=8)QM6*{wo}O-hBV0 lwMJZbX{dgB=miMUGd3ZYYf5RZP~3hly(l4>&A`Tr{{u#bx1az3 literal 0 HcmV?d00001 diff --git a/stest.1 b/stest.1 new file mode 100644 index 0000000..2667d8a --- /dev/null +++ b/stest.1 @@ -0,0 +1,90 @@ +.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) diff --git a/stest.c b/stest.c new file mode 100644 index 0000000..7a7b0bc --- /dev/null +++ b/stest.c @@ -0,0 +1,109 @@ +/* See LICENSE file for copyright and license details. */ +#include + +#include +#include +#include +#include +#include +#include + +#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 (n && 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; +} diff --git a/stest.o b/stest.o new file mode 100644 index 0000000000000000000000000000000000000000..9408acc32b22a0b093552dc7cacd070a1e9a4780 GIT binary patch literal 5280 zcmbW4Z){sv6~K?}v@uQbOV?5|%9y*o3|CuBT)~u<73C#y`#c<4=${ESo#WciNmR$q z?B^zfp=OHR$#vaLV*><;!p4URCNVUie3&|GZP$XBR0e`C1NFniW_eSKG_`1KGv{32 z8{gdeec?pU_nzN9=iGDeJ?~GBg`+#&E*A)K!B?QI2?}tvx7{CR-C@wc2YbxQ4pPnP zm#7KV%*v;z^A7#I;ubw)U!?s0ezW|M`S`m-7(Z!Ny7~h+YL<(700BByZQaYob5C)i~~Orp?ieImd5(f z4YPbrvtKc%p15jQTFl~u^W+%~39YN!75aeJ!URt#1DAq{PiQp8izO11W2I~~2T@mv?h zUe3jStNuN-G?yOJhd?`ZqU~j~g?Y8&(Kaje^Hu+z%EKNwM@>7J_DoItT@B};BX48- zkM*q>V;ftReV5p{?ogBM@+HP8QndUwBPQ+*)kD&}w^bnasuq9z0@ckSW8yqox22Ge zPg&dj{dxbvf%xG>(n?KDr{^BYJ8u)uc+QW1XT8`%}ZmtmolRcLfVJcaaKHLsiqY_ zLbNzEpq1Xo&WSy);u~7APV9q$!n(x;HxzozrSsnA>-E2obF&sHzZs^I^o&>T(~YGY zF5^TPiZ$)oSB>J|wBpauDzfcOtwc4Zox5H=@bp;q=;@^oT=Q{b@jEUkXycW%?j0|^ zS6IV#G19!)SlxU*vUI`wK!)G4W|buwEnhRW@O5Sudc!E!B8k@wcF1AO{h3#d@`fku zEoj9o8h4tl4PqLu&OSY!xG{RE>nt56CLL*Z>Gd!cHO^L@(7pAWSO%C!w~1PBV{;Vm z_P$2itd4az7l+mqJg?!3E05`3v;1)@X6rd(RvG&zXQY&B$1b;$c+{+Xne`&&i|-DAGKR}AKi8k)?H!!g6uz1B%RG*pu?JRj>y?cMDBD}b z18Z)9t-Nnt?|sa26~3KUN{I74D>2hNfQ6vQ{isuIsX|Y?b<+L|&r#%^?7!*2;|@II zz*7!<)`908_<{p3Bfm{L7j`coJLO>itONhC1Aoqe|H6U)%7LGC;IBCF%MKj!ww1So z31BZqAO%wC_!MNUdB~=dFdMfM(-4e~4$@DEezu}h(#qu^o|`&42(ew!-6KXcwtMH! zz2SYaea1*M9D~$cZYE==z*?BGA)ZKB`8=facHD-!f}Mw04Eu>AvBdO|SSmh~#(8Ek zFo#jIISijmOjDCYHf7oAnT!RgoMl0F&dMZba*(s)Noo`6Y~G?yKEvn3b&`ntj2>m{ z@%oTnEhs%Sh(et1eZj?Zl{obUr~93R;%&rB@Vg135dSn@f{Xi#c%Q_n&PgafjUD9A zgGUIW2p;47L<=wT1n18ye2Vjz6@G;Ciwe(j{#VW^7h0pp|E6M}=k`6k6?x8ceS^ZE z??Q=%xfw)cChM@BrsxT`%&ug9`r<=i8+?avcsU_BC!V-fxu8CcMPC zC#5*_9U-`Qv(Pa4Oz=1lVxNS#e7}ggB2Jfx^A7e&L>!uf4?_BukVVWd+6f#Kec5i2`^${_sx5sF5L1s!_@5ys zNkskvrLog#{qzqfiP>s^)=woR{6+nAnwwk#{6Nm|bD-^{y-@w>KNq!j5WyAxFU_Ax WkwjfTJ +#include +#include +#include + +#include "util.h" + +void * +ecalloc(size_t nmemb, size_t size) +{ + void *p; + + if (!(p = calloc(nmemb, size))) + die("calloc:"); + return p; +} + +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); +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..f633b51 --- /dev/null +++ b/util.h @@ -0,0 +1,8 @@ +/* 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)) + +void die(const char *fmt, ...); +void *ecalloc(size_t nmemb, size_t size); diff --git a/util.o b/util.o new file mode 100644 index 0000000000000000000000000000000000000000..4607dbd27ccff1daf63334f1a4c614aad4f343e1 GIT binary patch literal 2264 zcmbVNJxo(k6h5UW6-!%z0fR$_r=bbHv?NASl@uuO)ED_RB7?QGuc5Jjd3`l#OvH#o z(==glaByI7F>x@_(S$|^S4SsdG{FG}6AdQyocHd9o9FAoA-CVT-}%nH=iNUZPpFe^ zE*Eff!3Z4oI1BLN*r6XY-57L%7v`1fzVhV94pK_hkcO4&f)rD#s-&pZxHO}px47Og zqW(}^y6X?AQpq2BF0G;5A9^WmnD|QCH1Un}*2MQx!^98L7sRwuYd<@Hj)DUYL>fx% z3U;vfO{uR-do0kW)EA_HQjbgF&v7Z>K^n0)%)-I%=)l?~P-+**GX|CFHWlnK14<3O zwMXt|w9>N`#JI^unkMXaZgp~Mi`K?uXRX9%jfz*Pn-!VF(x+51mn){C08``R(cr+; zjhmrhBrFffR15Ud51Q-Y9Jp2ku6}QaX9EZAL+*?j0r*2W>I*z=yXMzEW)!7a|n@&-;;|>G3@z2TJIFggOK{ynGzzrQj@6dI_|211ot8tDWCNr-Kv0JF$ zTr1>GVGA&g2(+$FyxfAn*n%Hv!N*(h*IV#2$W#5iZgW48n{A2@ zl#R5e>yVgIr^iOs#PsCk{KP_HVRTHLNWlF}NzWFH465{;R)9>YVx*vi2}K>W)vN)U zbv&Vh^O3b-JBjUY)3Vn@qS21iT66v#k!W`PM~gP~KJ>Z%tHApOep}#aU$}l(;6?o} zfu}v@`h9`#5%>-|_-Irooh`2SGM>JRZZqeD=#!D28n?^L;XL0jkjoG9MsgX^&@Eiy zp04GRvZ1XSAg7Z?66ED_8Dzak9g;`n2*{~oKCcx}QYadljIYD_vXM;PLmg#nD~XJr z%xk=iBV!OV>67g@D25zaH(L*@XKA@l*pH3YDuzw8-|UI-uzXrea{0}s`$l#S8}arR zWzIG)Y02#LAaXS3QS&2g%ppY4`24(H9^>