aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoe <rbo@gmx.us>2024-12-26 16:04:49 +0100
committerJoe <rbo@gmx.us>2024-12-26 16:04:49 +0100
commit9c8bc4e708647be16b6c69a28603c3542db9c100 (patch)
tree18ef2626c9db480d5122b99d34c2b40fa1afcd01
parentup (diff)
downloaddmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.gz
dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.bz2
dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.xz
dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.tar.zst
dmenu-9c8bc4e708647be16b6c69a28603c3542db9c100.zip
5.3
-rw-r--r--.gitignore1
-rw-r--r--LICENSE2
-rw-r--r--Makefile17
-rw-r--r--README68
-rw-r--r--config.def.h26
-rw-r--r--config.h2
-rw-r--r--config.mk9
-rw-r--r--dmenu.121
-rw-r--r--dmenu.c302
-rw-r--r--drw.c190
-rw-r--r--drw.h1
-rw-r--r--stest.c2
-rw-r--r--util.c36
-rw-r--r--util.h1
14 files changed, 320 insertions, 358 deletions
diff --git a/.gitignore b/.gitignore
index 3f7cb29..cba389a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -128,3 +128,4 @@ flycheck_*.el
dmenu
stest
.ccls-cache
+config.h
diff --git a/LICENSE b/LICENSE
index 3afd28e..2a64b28 100644
--- a/LICENSE
+++ b/LICENSE
@@ -8,7 +8,7 @@ MIT/X Consortium License
© 2009 Markus Schnalke <meillo@marmaro.de>
© 2009 Evan Gates <evan.gates@gmail.com>
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
-© 2014-2020 Hiltjo Posthuma <hiltjo@codemadness.org>
+© 2014-2022 Hiltjo Posthuma <hiltjo@codemadness.org>
© 2015-2019 Quentin Rameau <quinq@fifth.space>
Permission is hereby granted, free of charge, to any person obtaining a
diff --git a/Makefile b/Makefile
index b42c96e..af36567 100644
--- a/Makefile
+++ b/Makefile
@@ -6,17 +6,14 @@ 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)"
+all: dmenu stest
.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
@@ -26,11 +23,11 @@ stest: stest.o
$(CC) -o $@ stest.o $(LDFLAGS)
clean:
- rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz
+ rm -f dmenu stest $(OBJ) dmenu-$(VERSION).tar.gz config.h
dist: clean
mkdir -p dmenu-$(VERSION)
- cp LICENSE Makefile README arg.h config.h config.mk dmenu.1\
+ 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)
@@ -58,4 +55,4 @@ uninstall:
$(DESTDIR)$(MANPREFIX)/man1/dmenu.1\
$(DESTDIR)$(MANPREFIX)/man1/stest.1
-.PHONY: all options clean dist install uninstall
+.PHONY: all clean dist install uninstall
diff --git a/README b/README
index 2ac5178..a8fcdfe 100644
--- a/README
+++ b/README
@@ -1,56 +1,24 @@
-dmenu - dynamic menu for X
-===========================
+dmenu - dynamic menu
+====================
+dmenu is an efficient dynamic menu for X.
-Introduction
+
+Requirements
------------
-This is my personnal build of dmenu, a simple dynamic menu for
-X which sucks less. I've applied some patches.
+In order to build dmenu you need the Xlib header files.
-Depedencies
------------
-- A C compiler
-- xorg
-- make
Installation
------------
-To install this build, run these commands:
-
- $ git clone git://jozanleclerc.xyz/jozan/dmenu.git
- $ cd dmenu
- $ make
- # make install clean
-
-It is installed by default in the user /usr/local/bin directory but you can
-change it to something that matches more your $PATH by editing the PREFIX
-variable in config.mk.
-
-Applied patches
----------------
-You can find a list of patches and check what those I've applied are
-doing on dmenu's patches page.
-
-Here is my list:
- - border
- - case insensitive
- - center
- - fuzzyhighlight
- - fuzzymatch
- - line height
-
-Settings
---------
-Most settings are done by editing config.h and recompiling. All settings
-can also be configured via command line options, see
- $ man 1 dmenu
-for more informations.
-
- Fonts
- -----
- The default font is part of the Nerd Fonts collection but can
- be changed at config.h:11.
-
- Colors
- ------
- The colorscheme used in this build is gruvbox dark hard, you can
- change it at config.h:16.
+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/config.def.h b/config.def.h
new file mode 100644
index 0000000..f34ef7b
--- /dev/null
+++ b/config.def.h
@@ -0,0 +1,26 @@
+/* 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 fuzzy = 1; /* -F option; if 0, dmenu doesn't use fuzzy matching */
+/* -fn option overrides fonts[0]; default X11 font or font set */
+static const char *fonts[] = {
+ "BigBlueTermPlus Nerd Font:size=13"
+};
+static const char *prompt = NULL; /* -p option; prompt to the left of input field */
+static const char *colors[SchemeLast][2] = {
+ /* fg bg */
+ [SchemeNorm] = { "#ebdbb2", "#1d2021" },
+ [SchemeSel] = { "#ebdbb2", "#cc241d" },
+ [SchemeSelHighlight] = { "#fabd2f", "#cc241d" },
+ [SchemeNormHighlight] = { "#fabd2f", "#1d2021" },
+ [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
index cff6ad0..812ac10 100644
--- a/config.h
+++ b/config.h
@@ -8,7 +8,7 @@ static int centered = 1; /* -c option; centers dmenu on scree
static int min_width = 500; /* minimum width when centered */
/* -fn option overrides fonts[0]; default X11 font or font set */
static const char *fonts[] = {
- "Terminess Nerd Font:style=Bold:size=15"
+ "Terminess Nerd Font:style=Bold:size=16"
// "UbuntuMono Nerd Font:size=15"
};
static const char *prompt = NULL; /* -p option; prompt to the left of input field */
diff --git a/config.mk b/config.mk
index c4e82cd..b6ff7f8 100644
--- a/config.mk
+++ b/config.mk
@@ -1,12 +1,12 @@
# dmenu version
-VERSION = 5.0
+VERSION = 5.3
# paths
PREFIX = /usr/local
MANPREFIX = $(PREFIX)/share/man
-X11INC = /usr/local/include
-X11LIB = /usr/local/lib
+X11INC = /usr/X11R6/include
+X11LIB = /usr/X11R6/lib
# Xinerama, comment if you don't want it
XINERAMALIBS = -lXinerama
@@ -17,6 +17,7 @@ FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = $(X11INC)/freetype2
+#MANPREFIX = ${PREFIX}/man
# includes and libs
INCS = -I$(X11INC) -I$(FREETYPEINC)
@@ -24,7 +25,7 @@ LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS) -lm
# 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 -march=haswell -O3 -pipe $(INCS) $(CPPFLAGS)
+CFLAGS = -std=c99 -pedantic -Wall -Os -march=alderlake -pipe $(INCS) $(CPPFLAGS)
LDFLAGS = $(LIBS)
# compiler and linker
diff --git a/dmenu.1 b/dmenu.1
index 9165064..ff13eea 100644
--- a/dmenu.1
+++ b/dmenu.1
@@ -3,7 +3,7 @@
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
-.RB [ \-bfivP ]
+.RB [ \-bFfivP ]
.RB [ \-l
.IR lines ]
.RB [ \-m
@@ -48,8 +48,8 @@ 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.
+.B \-F
+disables fuzzy matching.
.TP
.B \-f
dmenu grabs the keyboard before reading stdin if not reading from a tty. This
@@ -64,9 +64,6 @@ dmenu will not directly display the keyboard input, but instead replace it with
.BI \-l " lines"
dmenu lists items vertically, with the given number of lines.
.TP
-.BI \-h " height"
-dmenu uses a menu line of at least 'height' pixels tall, but no less than 8.
-.TP
.BI \-m " monitor"
dmenu is displayed on the monitor number supplied. Monitor numbers are starting
from 0.
@@ -86,6 +83,12 @@ and X color names are supported.
.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
.BI \-nhb " color"
defines the normal highlight background color.
.TP
@@ -98,12 +101,6 @@ defines the selected highlight background color.
.BI \-shf " color"
defines the selected highlight 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
diff --git a/dmenu.c b/dmenu.c
index c1d2836..bade890 100644
--- a/dmenu.c
+++ b/dmenu.c
@@ -23,11 +23,11 @@
/* 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, SchemeNormHighlight, SchemeSelHighlight, SchemeLast }; /* color schemes */
+enum { SchemeNorm, SchemeSel, SchemeNormHighlight, SchemeSelHighlight,
+ SchemeOut, SchemeLast }; /* color schemes */
struct item {
char *text;
@@ -60,6 +60,13 @@ static Clr *scheme[SchemeLast];
static int (*fstrncmp)(const char *, const char *, size_t) = strncmp;
static char *(*fstrstr)(const char *, const char *) = strstr;
+static unsigned int
+textw_clamp(const char *str, unsigned int n)
+{
+ unsigned int w = drw_fontset_getwidth_clamp(drw, str, n) + lrpad;
+ return MIN(w, n);
+}
+
static void
appenditem(struct item *item, struct item **list, struct item **last)
{
@@ -84,22 +91,13 @@ calcoffsets(void)
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)
+ if ((i += (lines > 0) ? bh : textw_clamp(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)
+ if ((i += (lines > 0) ? bh : textw_clamp(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)
{
@@ -108,19 +106,29 @@ cleanup(void)
XUngrabKey(dpy, AnyKey, AnyModifier, root);
for (i = 0; i < SchemeLast; i++)
free(scheme[i]);
+ for (i = 0; items && items[i].text; ++i)
+ free(items[i].text);
+ free(items);
drw_free(drw);
XSync(dpy, False);
XCloseDisplay(dpy);
}
static char *
-cistrstr(const char *s, const char *sub)
+cistrstr(const char *h, const char *n)
{
- size_t len;
+ size_t i;
+
+ if (!n[0])
+ return (char *)h;
- for (len = strlen(sub); *s; s++)
- if (!strncasecmp(s, sub, len))
- return (char *)s;
+ for (; *h; ++h) {
+ for (i = 0; n[i] && tolower((unsigned char)n[i]) ==
+ tolower((unsigned char)h[i]); ++i)
+ ;
+ if (n[i] == '\0')
+ return (char *)h;
+ }
return NULL;
}
@@ -128,17 +136,16 @@ static void
drawhighlights(struct item *item, int x, int y, int maxw)
{
int i, indent;
- char *highlight;
- char c;
+ char c, *highlight;
if (!(strlen(item->text) && strlen(text)))
return;
drw_setscheme(drw, scheme[item == sel
- ? SchemeSelHighlight
- : SchemeNormHighlight]);
+ ? SchemeSelHighlight
+ : SchemeNormHighlight]);
for (i = 0, highlight = item->text; *highlight && text[i];) {
- if (!fstrncmp(&(*highlight), &text[i], 1)) {
+ if (!fstrncmp(highlight, &text[i], 1)) {
/* get indentation */
c = *highlight;
*highlight = '\0';
@@ -149,59 +156,24 @@ drawhighlights(struct item *item, int x, int y, int maxw)
c = highlight[1];
highlight[1] = '\0';
drw_text(
- drw,
- x + indent - (lrpad / 2),
- y,
- MIN(maxw - indent, TEXTW(highlight) - lrpad),
- bh, 0, highlight, 0
- );
+ drw,
+ x + indent - (lrpad / 2.),
+ y,
+ MIN(maxw - indent, TEXTW(highlight) - lrpad),
+ bh, 0, highlight, 0
+ );
highlight[1] = c;
- i++;
+ ++i;
}
- highlight++;
+ ++highlight;
}
}
-/* static void */
-/* drawhighlights(struct item *item, int x, int y, int maxw) */
-/* { */
-/* char restorechar, tokens[sizeof text], *highlight, *token; */
-/* int indentx, highlightlen; */
-/* */
-/* drw_setscheme(drw, scheme[item == sel ? SchemeSelHighlight : SchemeNormHighlight]); */
-/* strcpy(tokens, text); */
-/* for (token = strtok(tokens, " "); token; token = strtok(NULL, " ")) { */
-/* highlight = fstrstr(item->text, token); */
-/* while (highlight) { */
-/* // Move item str end, calc width for highlight indent, & restore */
-/* highlightlen = highlight - item->text; */
-/* restorechar = *highlight; */
-/* item->text[highlightlen] = '\0'; */
-/* indentx = TEXTW(item->text); */
-/* item->text[highlightlen] = restorechar; */
-/* */
-/* // Move highlight str end, draw highlight, & restore */
-/* restorechar = highlight[strlen(token)]; */
-/* highlight[strlen(token)] = '\0'; */
-/* if (indentx - (lrpad / 2) - 1 < maxw) */
-/* drw_text( */
-/* drw, */
-/* x + indentx - (lrpad / 2) - 1, */
-/* y, */
-/* MIN(maxw - indentx, TEXTW(highlight) - lrpad), */
-/* bh, 0, highlight, 0 */
-/* ); */
-/* highlight[strlen(token)] = restorechar; */
- /* */
- /* if (strlen(highlight) - strlen(token) < strlen(token)) break; */
- /* highlight = fstrstr(highlight + strlen(token), token); */
- /* } */
- /* } */
-/* } */
-
static int
drawitem(struct item *item, int x, int y, int w)
{
+ int r;
+
if (item == sel)
drw_setscheme(drw, scheme[SchemeSel]);
else if (item->out)
@@ -209,7 +181,7 @@ drawitem(struct item *item, int x, int y, int w)
else
drw_setscheme(drw, scheme[SchemeNorm]);
- int r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
+ r = drw_text(drw, x, y, w, bh, lrpad / 2, item->text, 0);
drawhighlights(item, x, y, w);
return r;
}
@@ -219,7 +191,7 @@ drawmenu(void)
{
unsigned int curpos;
struct item *item;
- int x = 0, y = 0, fh = drw->fonts->h, w;
+ int x = 0, y = 0, w;
char *censort;
drw_setscheme(drw, scheme[SchemeNorm]);
@@ -234,7 +206,7 @@ drawmenu(void)
drw_setscheme(drw, scheme[SchemeNorm]);
if (passwd) {
censort = ecalloc(1, sizeof(text));
- memset(censort, '*', strlen(text));
+ memset(censort, '.', strlen(text));
drw_text(drw, x, 0, w, bh, lrpad / 2, censort, 0);
free(censort);
} else drw_text(drw, x, 0, w, bh, lrpad / 2, text, 0);
@@ -242,7 +214,7 @@ drawmenu(void)
curpos = TEXTW(text) - TEXTW(&text[cursor]);
if ((curpos += lrpad / 2 - 1) < w) {
drw_setscheme(drw, scheme[SchemeNorm]);
- drw_rect(drw, x + curpos, 2 + (bh-fh)/2, 2, fh - 4, 1, 0);
+ drw_rect(drw, x + curpos, 2, 2, bh - 4, 1, 0);
}
if (lines > 0) {
@@ -259,7 +231,7 @@ drawmenu(void)
}
x += w;
for (item = curr; item != next; item = item->right)
- x = drawitem(item, x, 0, MIN(TEXTW(item->text), mw - x - TEXTW(">")));
+ x = drawitem(item, x, 0, textw_clamp(item->text, mw - x - TEXTW(">")));
if (next) {
w = TEXTW(">");
drw_setscheme(drw, scheme[SchemeNorm]);
@@ -286,6 +258,24 @@ grabfocus(void)
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");
+}
+
int
compare_distance(const void *a, const void *b)
{
@@ -313,18 +303,18 @@ fuzzymatch(void)
matches = matchend = NULL;
/* walk through all items */
- for (it = items; it && it->text; it++) {
+ for (it = items; it && it->text; ++it) {
if (text_len) {
itext_len = strlen(it->text);
pidx = 0; /* pointer */
sidx = eidx = -1; /* start of match, end of match */
/* walk through item text */
- for (i = 0; i < itext_len && (c = it->text[i]); i++) {
+ for (i = 0; i < itext_len && (c = it->text[i]); ++i) {
/* fuzzy match pattern */
if (!fstrncmp(&text[pidx], &c, 1)) {
if(sidx == -1)
sidx = i;
- pidx++;
+ ++pidx;
if (pidx == text_len) {
eidx = i;
break;
@@ -339,7 +329,7 @@ fuzzymatch(void)
it->distance = log(sidx + 2) + (double)(eidx - sidx - text_len);
/* fprintf(stderr, "distance %s %f\n", it->text, it->distance); */
appenditem(it, &matches, &matchend);
- number_of_matches++;
+ ++number_of_matches;
}
} else {
appenditem(it, &matches, &matchend);
@@ -348,19 +338,18 @@ fuzzymatch(void)
if (number_of_matches) {
/* initialize array with matches */
- if (!(fuzzymatches = realloc(fuzzymatches, number_of_matches * sizeof(struct item*))))
- die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item*));
- for (i = 0, it = matches; it && i < number_of_matches; i++, it = it->right) {
+ if (!(fuzzymatches = realloc(fuzzymatches,
+ number_of_matches * sizeof(struct item *))))
+ die("cannot realloc %u bytes:", number_of_matches * sizeof(struct item *));
+ for (i = 0, it = matches; it && i < number_of_matches; ++i, it = it->right)
fuzzymatches[i] = it;
- }
/* sort matches according to distance */
qsort(fuzzymatches, number_of_matches, sizeof(struct item*), compare_distance);
/* rebuild list of matches */
matches = matchend = NULL;
- for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it && \
- it->text; i++, it = fuzzymatches[i]) {
+ for (i = 0, it = fuzzymatches[i]; i < number_of_matches && it &&
+ it->text; ++i, it = fuzzymatches[i])
appenditem(it, &matches, &matchend);
- }
free(fuzzymatches);
}
curr = sel = matches;
@@ -368,24 +357,6 @@ fuzzymatch(void)
}
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)
{
if (fuzzy) {
@@ -404,7 +375,7 @@ match(void)
/* 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);
+ die("cannot realloc %zu bytes:", tokn * sizeof *tokv);
len = tokc ? strlen(tokv[0]) : 0;
matches = lprefix = lsubstr = matchend = prefixend = substrend = NULL;
@@ -486,19 +457,19 @@ movewordedge(int dir)
static void
keypress(XKeyEvent *ev)
{
- char buf[32];
+ char buf[64];
int len;
- KeySym ksym;
+ KeySym ksym = NoSymbol;
Status status;
len = XmbLookupString(xic, ev, buf, sizeof buf, &ksym, &status);
switch (status) {
default: /* XLookupNone, XBufferOverflow */
return;
- case XLookupChars:
+ case XLookupChars: /* composed string from input method */
goto insert;
case XLookupKeySym:
- case XLookupBoth:
+ case XLookupBoth: /* a KeySym and a string are returned: use keysym */
break;
}
@@ -511,10 +482,11 @@ keypress(XKeyEvent *ev)
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: ksym = XK_Down; break;
- case XK_k: ksym = XK_Up; break;
+ case XK_j: ksym = XK_Down; break;
+ case XK_k: ksym = XK_Up; break;
+ case XK_l: ksym = XK_Down; break;
+ case XK_h: ksym = XK_Up; break;
case XK_J: /* fallthrough */
case XK_m: /* fallthrough */
case XK_M: ksym = XK_Return; ev->state &= ~ControlMask; break;
@@ -536,9 +508,11 @@ keypress(XKeyEvent *ev)
utf8, utf8, win, CurrentTime);
return;
case XK_Left:
+ case XK_KP_Left:
movewordedge(-1);
goto draw;
case XK_Right:
+ case XK_KP_Right:
movewordedge(+1);
goto draw;
case XK_Return:
@@ -572,10 +546,11 @@ keypress(XKeyEvent *ev)
switch(ksym) {
default:
insert:
- if (!iscntrl(*buf))
+ if (!iscntrl((unsigned char)*buf))
insert(buf, len);
break;
case XK_Delete:
+ case XK_KP_Delete:
if (text[cursor] == '\0')
return;
cursor = nextrune(+1);
@@ -586,6 +561,7 @@ insert:
insert(NULL, nextrune(-1) - cursor);
break;
case XK_End:
+ case XK_KP_End:
if (text[cursor] != '\0') {
cursor = strlen(text);
break;
@@ -605,6 +581,7 @@ insert:
cleanup();
exit(1);
case XK_Home:
+ case XK_KP_Home:
if (sel == matches) {
cursor = 0;
break;
@@ -613,6 +590,7 @@ insert:
calcoffsets();
break;
case XK_Left:
+ case XK_KP_Left:
if (cursor > 0 && (!sel || !sel->left || lines > 0)) {
cursor = nextrune(-1);
break;
@@ -621,18 +599,21 @@ insert:
return;
/* fallthrough */
case XK_Up:
+ case XK_KP_Up:
if (sel && sel->left && (sel = sel->left)->right == curr) {
curr = prev;
calcoffsets();
}
break;
case XK_Next:
+ case XK_KP_Next:
if (!next)
return;
sel = curr = next;
calcoffsets();
break;
case XK_Prior:
+ case XK_KP_Prior:
if (!prev)
return;
sel = curr = prev;
@@ -649,6 +630,7 @@ insert:
sel->out = 1;
break;
case XK_Right:
+ case XK_KP_Right:
if (text[cursor] != '\0') {
cursor = nextrune(+1);
break;
@@ -657,6 +639,7 @@ insert:
return;
/* fallthrough */
case XK_Down:
+ case XK_KP_Down:
if (sel && sel->right && (sel = sel->right) == next) {
curr = next;
calcoffsets();
@@ -665,9 +648,9 @@ insert:
case XK_Tab:
if (!sel)
return;
- strncpy(text, sel->text, sizeof text - 1);
- text[sizeof text - 1] = '\0';
- cursor = strlen(text);
+ cursor = strnlen(sel->text, sizeof text - 1);
+ memcpy(text, sel->text, cursor);
+ text[cursor] = '\0';
match();
break;
}
@@ -697,34 +680,32 @@ paste(void)
static void
readstdin(void)
{
- char buf[sizeof text], *p;
- size_t i, imax = 0, size = 0;
- unsigned int tmpmax = 0;
- if(passwd){
+ char *line = NULL;
+ size_t i, itemsiz = 0, linesiz = 0;
+ ssize_t len;
+
+ if (passwd){
inputw = lines = 0;
return;
}
-
/* 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;
+ for (i = 0; (len = getline(&line, &linesiz, stdin)) != -1; i++) {
+ if (i + 1 >= itemsiz) {
+ itemsiz += 256;
+ if (!(items = realloc(items, itemsiz * sizeof(*items))))
+ die("cannot realloc %zu bytes:", itemsiz * sizeof(*items));
}
+ if (line[len - 1] == '\n')
+ line[len - 1] = '\0';
+ if (!(items[i].text = strdup(line)))
+ die("strdup:");
+
+ items[i].out = 0;
}
+ free(line);
if (items)
items[i].text = NULL;
- inputw = items ? TEXTW(items[imax].text) : 0;
lines = MIN(lines, i);
}
@@ -790,10 +771,8 @@ setup(void)
/* calculate menu geometry */
bh = drw->fonts->h + 2;
- bh = MAX(bh,lineheight); /* make a menu line AT LEAST 'lineheight' tall */
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))) {
@@ -817,19 +796,12 @@ setup(void)
/* 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]))
+ if (INTERSECT(x, y, 1, 1, info[i]) != 0)
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;
- }
-
+ x = info[i].x_org;
+ y = info[i].y_org + (topbar ? 0 : info[i].height - mh);
+ mw = info[i].width;
XFree(info);
} else
#endif
@@ -837,32 +809,23 @@ setup(void)
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;
- }
+ x = 0;
+ y = topbar ? 0 : wa.height - mh;
+ mw = wa.width;
}
promptw = (prompt && *prompt) ? TEXTW(prompt) - lrpad / 4 : 0;
- inputw = MIN(inputw, mw/3);
+ inputw = mw / 3; /* input width: ~33% of monitor width */
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, border_width,
+ win = XCreateWindow(dpy, root, x, y, mw, mh, 0,
CopyFromParent, CopyFromParent, CopyFromParent,
CWOverrideRedirect | CWBackPixel | CWEventMask, &swa);
- XSetWindowBorder(dpy, win, scheme[SchemeSel][ColBg].pixel);
XSetClassHint(dpy, win, &ch);
-
/* input methods */
if ((xim = XOpenIM(dpy, NULL, NULL, NULL)) == NULL)
die("XOpenIM failed: could not open input device");
@@ -872,6 +835,7 @@ setup(void)
XMapRaised(dpy, win);
if (embed) {
+ XReparentWindow(dpy, win, parentwin, x, y);
XSelectInput(dpy, parentwin, FocusChangeMask | SubstructureNotifyMask);
if (XQueryTree(dpy, parentwin, &dw, &w, &dws, &du) && dws) {
for (i = 0; i < du && dws[i] != win; ++i)
@@ -887,11 +851,9 @@ setup(void)
static void
usage(void)
{
- fputs("usage: dmenu [-bfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
- " [-h height]\n"
- " [-nb color] [-nf color] [-sb color] [-sf color]\n"
- " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]\n", stderr);
- exit(1);
+ die("usage: dmenu [-bFfivP] [-l lines] [-p prompt] [-fn font] [-m monitor]\n"
+ " [-nb color] [-nf color] [-sb color] [-sf color]\n"
+ " [-nhb color] [-nhf color] [-shb color] [-shf color] [-w windowid]");
}
int
@@ -907,12 +869,10 @@ main(int argc, char *argv[])
exit(0);
} else if (!strcmp(argv[i], "-b")) /* appears at the bottom of the screen */
topbar = 0;
+ else if (!strcmp(argv[i], "-F")) /* disables fuzzy matching */
+ fuzzy = 0;
else if (!strcmp(argv[i], "-f")) /* grabs keyboard before reading stdin */
fast = 1;
- else if (!strcmp(argv[i], "-F")) /* grabs keyboard before reading stdin */
- fuzzy = 0;
- 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;
@@ -929,10 +889,6 @@ main(int argc, char *argv[])
prompt = argv[++i];
else if (!strcmp(argv[i], "-fn")) /* font or font set */
fonts[0] = argv[++i];
- else if(!strcmp(argv[i], "-h")) { /* minimum height of one menu line */
- lineheight = atoi(argv[++i]);
- lineheight = MAX(lineheight,8); /* reasonable default in case of value too small/negative */
- }
else if (!strcmp(argv[i], "-nb")) /* normal background color */
colors[SchemeNorm][ColBg] = argv[++i];
else if (!strcmp(argv[i], "-nf")) /* normal foreground color */
diff --git a/drw.c b/drw.c
index 4cdbcbe..c41e6af 100644
--- a/drw.c
+++ b/drw.c
@@ -9,54 +9,40 @@
#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)
+static int
+utf8decode(const char *s_in, long *u, int *err)
{
- size_t i, j, len, type;
- long udecoded;
-
+ static const unsigned char lens[] = {
+ /* 0XXXX */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ /* 10XXX */ 0, 0, 0, 0, 0, 0, 0, 0, /* invalid */
+ /* 110XX */ 2, 2, 2, 2,
+ /* 1110X */ 3, 3,
+ /* 11110 */ 4,
+ /* 11111 */ 0, /* invalid */
+ };
+ static const unsigned char leading_mask[] = { 0x7F, 0x1F, 0x0F, 0x07 };
+ static const unsigned int overlong[] = { 0x0, 0x80, 0x0800, 0x10000 };
+
+ const unsigned char *s = (const unsigned char *)s_in;
+ int len = lens[*s >> 3];
*u = UTF_INVALID;
- if (!clen)
- return 0;
- udecoded = utf8decodebyte(c[0], &len);
- if (!BETWEEN(len, 1, UTF_SIZ))
+ *err = 1;
+ if (len == 0)
return 1;
- for (i = 1, j = 1; i < clen && j < len; ++i, ++j) {
- udecoded = (udecoded << 6) | utf8decodebyte(c[i], &type);
- if (type)
- return j;
+
+ long cp = s[0] & leading_mask[len - 1];
+ for (int i = 1; i < len; ++i) {
+ if (s[i] == '\0' || (s[i] & 0xC0) != 0x80)
+ return i;
+ cp = (cp << 6) | (s[i] & 0x3F);
}
- if (j < len)
- return 0;
- *u = udecoded;
- utf8validate(u, len);
+ /* out of range, surrogate, overlong encoding */
+ if (cp > 0x10FFFF || (cp >> 11) == 0x1B || cp < overlong[len - 1])
+ return len;
+ *err = 0;
+ *u = cp;
return len;
}
@@ -133,19 +119,6 @@ xfont_create(Drw *drw, const char *fontname, FcPattern *fontpattern)
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;
@@ -251,29 +224,32 @@ drw_rect(Drw *drw, int x, int y, unsigned int w, unsigned int h, int filled, int
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;
+ int ty, ellipsis_x = 0;
+ unsigned int tmpw, ew, ellipsis_w = 0, ellipsis_len, hash, h0, h1;
XftDraw *d = NULL;
Fnt *usedfont, *curfont, *nextfont;
- size_t i, len;
- int utf8strlen, utf8charlen, render = x || y || w || h;
+ int utf8strlen, utf8charlen, utf8err, render = x || y || w || h;
long utf8codepoint = 0;
const char *utf8str;
FcCharSet *fccharset;
FcPattern *fcpattern;
FcPattern *match;
XftResult result;
- int charexists = 0;
+ int charexists = 0, overflow = 0;
+ /* keep track of a couple codepoints for which we have no match. */
+ static unsigned int nomatches[128], ellipsis_width, invalid_width;
+ static const char invalid[] = "�";
- if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
+ if (!drw || (render && (!drw->scheme || !w)) || !text || !drw->fonts)
return 0;
if (!render) {
- w = ~w;
+ 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);
+ if (w < lpad)
+ return x + w;
d = XftDrawCreate(drw->dpy, drw->drawable,
DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen));
@@ -282,18 +258,40 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
usedfont = drw->fonts;
+ if (!ellipsis_width && render)
+ ellipsis_width = drw_fontset_getwidth(drw, "...");
+ if (!invalid_width && render)
+ invalid_width = drw_fontset_getwidth(drw, invalid);
while (1) {
- utf8strlen = 0;
+ ew = ellipsis_len = utf8err = utf8charlen = utf8strlen = 0;
utf8str = text;
nextfont = NULL;
while (*text) {
- utf8charlen = utf8decode(text, &utf8codepoint, UTF_SIZ);
+ utf8charlen = utf8decode(text, &utf8codepoint, &utf8err);
for (curfont = drw->fonts; curfont; curfont = curfont->next) {
charexists = charexists || XftCharExists(drw->dpy, curfont->xfont, utf8codepoint);
if (charexists) {
- if (curfont == usedfont) {
- utf8strlen += utf8charlen;
+ 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) {
text += utf8charlen;
+ utf8strlen += utf8err ? 0 : utf8charlen;
+ ew += utf8err ? 0 : tmpw;
} else {
nextfont = curfont;
}
@@ -301,36 +299,31 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
}
}
- if (!charexists || nextfont)
+ if (overflow || !charexists || nextfont || utf8err)
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 (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 (utf8err && (!render || invalid_width < w)) {
+ if (render)
+ drw_text(drw, x, y, w, h, 0, invalid, invert);
+ x += invalid_width;
+ w -= invalid_width;
+ }
+ if (render && overflow)
+ drw_text(drw, ellipsis_x, y, ellipsis_w, h, 0, "...", invert);
- if (!*text) {
+ if (!*text || overflow) {
break;
} else if (nextfont) {
charexists = 0;
@@ -340,6 +333,15 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
* 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);
@@ -351,7 +353,6 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
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);
@@ -368,6 +369,8 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
curfont->next = usedfont;
} else {
xfont_free(usedfont);
+ nomatches[nomatches[h0] ? h1 : h0] = utf8codepoint;
+no_match:
usedfont = drw->fonts;
}
}
@@ -397,6 +400,15 @@ drw_fontset_getwidth(Drw *drw, const char *text)
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)
{
diff --git a/drw.h b/drw.h
index 4c67419..fd7631b 100644
--- a/drw.h
+++ b/drw.h
@@ -35,6 +35,7 @@ void drw_free(Drw *drw);
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 */
diff --git a/stest.c b/stest.c
index 7a7b0bc..e27d3a5 100644
--- a/stest.c
+++ b/stest.c
@@ -84,7 +84,7 @@ main(int argc, char *argv[])
if (!argc) {
/* read list from stdin */
while ((n = getline(&line, &linesiz, stdin)) > 0) {
- if (n && line[n - 1] == '\n')
+ if (line[n - 1] == '\n')
line[n - 1] = '\0';
test(line, line);
}
diff --git a/util.c b/util.c
index fe044fc..8e26a51 100644
--- a/util.c
+++ b/util.c
@@ -1,4 +1,5 @@
/* See LICENSE file for copyright and license details. */
+#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
@@ -6,30 +7,31 @@
#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, ...) {
+die(const char *fmt, ...)
+{
va_list ap;
+ int saved_errno;
+
+ saved_errno = errno;
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);
- }
+ if (fmt[0] && fmt[strlen(fmt)-1] == ':')
+ fprintf(stderr, " %s", strerror(saved_errno));
+ fputc('\n', stderr);
exit(1);
}
+
+void *
+ecalloc(size_t nmemb, size_t size)
+{
+ void *p;
+
+ if (!(p = calloc(nmemb, size)))
+ die("calloc:");
+ return p;
+}
diff --git a/util.h b/util.h
index f633b51..c0a50d4 100644
--- a/util.h
+++ b/util.h
@@ -3,6 +3,7 @@
#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);