fixed bugs, no more config.h, updated manpage, new libdraw

This commit is contained in:
Connor Lane Smith 2010-08-02 14:22:54 +01:00
parent a3606ecb0e
commit a7aee433cc
7 changed files with 162 additions and 220 deletions

View file

@ -3,9 +3,6 @@
include config.mk
SRC = dmenu.c
OBJ = ${SRC:.c=.o}
all: options dmenu
options:
@ -14,34 +11,28 @@ options:
@echo "LDFLAGS = ${LDFLAGS}"
@echo "CC = ${CC}"
.c.o:
dmenu.o: dmenu.c config.mk
@echo CC $<
@${CC} -c ${CFLAGS} $<
${OBJ}: config.h config.mk
config.h:
@echo creating $@ from config.def.h
@cp config.def.h $@
dmenu: ${OBJ}
dmenu: dmenu.o
@echo CC -o $@
@${CC} -o $@ $+ ${LDFLAGS}
clean:
@echo cleaning
@rm -f dmenu ${OBJ} dmenu-${VERSION}.tar.gz
@rm -f dmenu dmenu.o dmenu-${VERSION}.tar.gz
dist: clean
@echo creating dist tarball
@mkdir -p dmenu-${VERSION}
@cp -R LICENSE Makefile README config.mk dmenu.1 config.def.h dmenu_path dmenu_run ${SRC} dmenu-${VERSION}
@cp -R LICENSE Makefile README config.mk dmenu.1 dmenu.c dmenu_path dmenu_run dmenu-${VERSION}
@tar -cf dmenu-${VERSION}.tar dmenu-${VERSION}
@gzip dmenu-${VERSION}.tar
@rm -rf dmenu-${VERSION}
install: all
@echo installing executable file to ${DESTDIR}${PREFIX}/bin
@echo installing executables to ${DESTDIR}${PREFIX}/bin
@mkdir -p ${DESTDIR}${PREFIX}/bin
@cp -f dmenu dmenu_path dmenu_run ${DESTDIR}${PREFIX}/bin
@chmod 755 ${DESTDIR}${PREFIX}/bin/dmenu
@ -53,8 +44,9 @@ install: all
@chmod 644 ${DESTDIR}${MANPREFIX}/man1/dmenu.1
uninstall:
@echo removing executable file from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu ${DESTDIR}${PREFIX}/bin/dmenu_path
@echo removing executables from ${DESTDIR}${PREFIX}/bin
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_path
@rm -f ${DESTDIR}${PREFIX}/bin/dmenu_run
@echo removing manual page from ${DESTDIR}${MANPREFIX}/man1
@rm -f ${DESTDIR}${MANPREFIX}/man1/dmenu.1

11
README
View file

@ -1,21 +1,22 @@
dmenu - dynamic menu
====================
dmenu is a generic and efficient menu for X.
dmenu is an efficient dynamic menu for X.
Requirements
------------
In order to build dmenu you need the Xlib header files.
You also need libdraw, available from http://hg.suckless.org/libdraw
Installation
------------
Edit config.mk to match your local setup (dmenu is installed into the
/usr/local namespace by default).
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):
Afterwards enter the following command to build and install dmenu
(if necessary as root):
make clean install

View file

@ -1,8 +0,0 @@
/* See LICENSE file for copyright and license details. */
/* appearance */
static const char *font = "-*-terminus-medium-r-normal-*-14-*-*-*-*-*-*-*";
static const char *normbgcolor = "#cccccc";
static const char *normfgcolor = "#000000";
static const char *selbgcolor = "#0066ff";
static const char *selfgcolor = "#ffffff";

View file

@ -1,5 +1,5 @@
# dmenu version
VERSION = 4.1.1
VERSION = 4.2
# Customize below to fit your system
@ -7,25 +7,22 @@ VERSION = 4.1.1
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
# Xlib
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
# Xinerama, comment if you don't want it
XINERAMALIBS = -L${X11LIB} -lXinerama
XINERAMALIBS = -lXinerama
XINERAMAFLAGS = -DXINERAMA
# includes and libs
INCS = -I. -I/usr/include -I${X11INC}
LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -ldraw ${XINERAMALIBS}
INCS = -I${X11INC}
LIBS = -L${X11LIB} -ldraw -lX11 ${XINERAMALIBS}
# flags
CPPFLAGS = -D_BSD_SOURCE -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS}
# Solaris
#CFLAGS = -fast ${INCS} -DVERSION=\"${VERSION}\"
#LDFLAGS = ${LIBS}
# compiler and linker
CC = cc

98
dmenu.1
View file

@ -5,81 +5,76 @@ dmenu \- dynamic menu
.B dmenu
.RB [ \-b ]
.RB [ \-i ]
.RB [ \-l " <lines>]"
.RB [ \-p " <prompt>]"
.RB [ \-fn " <font>]"
.RB [ \-nb " <color>]"
.RB [ \-nf " <color>]"
.RB [ \-sb " <color>]"
.RB [ \-sf " <color>]"
.RB [ \-l
.IR lines ]
.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 [ \-v ]
.B dmenu_run
.RB [ \-b ]
.RB [ \-i ]
.RB [ \-l " <lines>]"
.RB [ \-p " <prompt>]"
.RB [ \-fn " <font>]"
.RB [ \-nb " <color>]"
.RB [ \-nf " <color>]"
.RB [ \-sb " <color>]"
.RB [ \-sf " <color>]"
.RB [ \-v ]
.P
.BR dmenu_run " ..."
.P
.B dmenu_path
.SH DESCRIPTION
.SS Overview
.B dmenu
is a generic menu for X, originally designed for
is a dynamic menu for X, originally designed for
.BR dwm (1).
It manages huge amounts (10000 and more) of user defined menu items efficiently.
It manages huge numbers of user-defined menu items efficiently.
.P
dmenu reads a list of newline-separated items from standard input and creates a
menu. When the user selects an item or enters any text and presses Return,
their choice is printed to standard output and dmenu terminates.
.P
.B dmenu_run
is a dmenu script which lists programs in the user's PATH and executes
the selected item.
is a dmenu script used by dwm which lists programs in the user's PATH and
executes the selected item.
.P
.B dmenu_path
is a script used by
.I dmenu_run
to find and cache a list of programs.
.SS Options
is a script used by dmenu_run to find and cache a list of programs.
.SH OPTIONS
.TP
.B \-b
dmenu appears at the bottom of the screen.
.TP
.B \-i
dmenu matches menu entries case insensitively.
dmenu matches menu items case insensitively.
.TP
.B \-l <lines>
.BI \-l " lines"
dmenu lists items vertically, with the given number of lines.
.TP
.B \-p <prompt>
sets the prompt to be displayed to the left of the input area.
.BI \-p " prompt"
defines the prompt to be displayed to the left of the input area.
.TP
.B \-fn <font>
sets the font.
.BI \-fn " font"
defines the font set used.
.TP
.B \-nb <color>
sets the background color (#RGB, #RRGGBB, and color names are supported).
.BI \-nb " color"
defines the normal background color.
.IR #RGB ,
.IR #RRGGBB ,
and color names are supported.
.TP
.B \-nf <color>
sets the foreground color (#RGB, #RRGGBB, and color names are supported).
.BI \-nf " color"
defines the normal foreground color.
.TP
.B \-sb <color>
sets the background color of selected items (#RGB, #RRGGBB, and color names are
supported).
.BI \-sb " color"
defines the selected background color.
.TP
.B \-sf <color>
sets the foreground color of selected items (#RGB, #RRGGBB, and color names are
supported).
.BI \-sf " color"
defines the selected foreground color.
.TP
.B \-v
prints version information to standard output, then exits.
.SH USAGE
dmenu reads a list of newline-separated items from standard input and creates a
menu. When the user selects an item or enters any text and presses Return,
their choice is printed to standard output and dmenu terminates.
.P
dmenu is completely controlled by the keyboard. Besides standard Unix line
editing and item selection (Up/Down/Left/Right, PageUp/PageDown, Home/End), the
following keys are recognized:
@ -96,10 +91,9 @@ Confirm input. Prints the input text to standard output and exits, returning
success.
.TP
.B Escape (Control\-c)
Quit without selecting an item, returning failure.
Exit without selecting an item, returning failure.
.TP
.B Control\-y
Paste the current X selection into the input field.
.SH SEE ALSO
.BR dwm (1),
.BR wmii (1).
.BR dwm (1)

212
dmenu.c
View file

@ -1,11 +1,9 @@
/* See LICENSE file for copyright and license details. */
#include <ctype.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/keysym.h>
#include <X11/Xatom.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@ -13,7 +11,6 @@
#include <X11/extensions/Xinerama.h>
#endif
#include <draw.h>
#include "config.h"
#define INRECT(x,y,rx,ry,rw,rh) ((x) >= (rx) && (x) < (rx)+(rw) && (y) >= (ry) && (y) < (ry)+(rh))
#define MIN(a,b) ((a) < (b) ? (a) : (b))
@ -38,25 +35,27 @@ static void grabkeyboard(void);
static void insert(const char *s, ssize_t n);
static void keypress(XKeyEvent *e);
static void match(void);
static void paste(Atom atom);
static void paste(void);
static void readstdin(void);
static void run(void);
static void setup(void);
static void usage(void);
static char *prompt;
static char text[4096];
static int screen;
static size_t cursor = 0;
static const char *prompt = NULL;
static const char *normbgcolor = "#cccccc";
static const char *normfgcolor = "#000000";
static const char *selbgcolor = "#0066ff";
static const char *selfgcolor = "#ffffff";
static unsigned int inputw = 0;
static unsigned int lines = 0;
static unsigned int mw, mh;
static unsigned int promptw = 0;
static unsigned long normcol[ColLast];
static unsigned long selcol[ColLast];
static Atom utf8;
static Bool topbar = True;
static DC dc;
static DC *dc;
static Item *allitems, *matches;
static Item *curr, *prev, *next, *sel;
static Window root, win;
@ -67,7 +66,7 @@ static void (*calcoffsets)(void) = calcoffsetsh;
void
appenditem(Item *item, Item **list, Item **last) {
if(!(*last))
if(!*last)
*list = item;
else
(*last)->right = item;
@ -80,12 +79,12 @@ void
calcoffsetsh(void) {
unsigned int w, x;
w = promptw + inputw + textw(&dc, "<") + textw(&dc, ">");
w = (prompt ? textw(dc, prompt) : 0) + inputw + textw(dc, "<") + textw(dc, ">");
for(x = w, next = curr; next; next = next->right)
if((x += MIN(textw(&dc, next->text), mw / 3)) > mw)
if((x += MIN(textw(dc, next->text), mw / 3)) > mw)
break;
for(x = w, prev = curr; prev && prev->left; prev = prev->left)
if((x += MIN(textw(&dc, prev->left->text), mw / 3)) > mw)
if((x += MIN(textw(dc, prev->left->text), mw / 3)) > mw)
break;
}
@ -96,101 +95,75 @@ calcoffsetsv(void) {
next = prev = curr;
for(i = 0; i < lines && next; i++)
next = next->right;
mh = (dc.font.height + 2) * (i + 1);
for(i = 0; i < lines && prev && prev->left; i++)
prev = prev->left;
}
char *
cistrstr(const char *s, const char *sub) {
int c, csub;
unsigned int len;
size_t len;
if(!sub)
for(len = strlen(sub); *s; s++)
if(!strncasecmp(s, sub, len))
return (char *)s;
if((c = tolower(*sub++)) != '\0') {
len = strlen(sub);
do {
do {
if((csub = *s++) == '\0')
return NULL;
}
while(tolower(csub) != c);
}
while(strncasecmp(s, sub, len) != 0);
s--;
}
return (char *)s;
}
void
drawmenu(void) {
dc.x = 0;
dc.y = 0;
dc.w = mw;
dc.h = mh;
drawbox(&dc, normcol);
dc.h = dc.font.height + 2;
dc.y = topbar ? 0 : mh - dc.h;
dc->x = 0;
dc->y = 0;
drawrect(dc, 0, 0, mw, mh, BG(dc, normcol));
dc->h = dc->font.height + 2;
dc->y = topbar ? 0 : mh - dc->h;
/* print prompt? */
if(prompt) {
dc.w = promptw;
drawbox(&dc, selcol);
drawtext(&dc, prompt, selcol);
dc.x += dc.w;
dc->w = textw(dc, prompt);
drawtext(dc, prompt, selcol);
dc->x = dc->w;
}
dc.w = mw - dc.x;
dc->w = mw - dc->x;
/* print input area */
if(matches && lines == 0 && textw(&dc, text) <= inputw)
dc.w = inputw;
drawtext(&dc, text, normcol);
drawline(&dc, textnw(&dc, text, cursor) + dc.h/2 - 2, 2, 1, dc.h-4, normcol);
if(matches && lines == 0 && textw(dc, text) <= inputw)
dc->w = inputw;
drawtext(dc, text, normcol);
drawrect(dc, textnw(dc, text, cursor) + dc->h/2 - 2, 2, 1, dc->h - 4, FG(dc, normcol));
if(lines > 0)
drawmenuv();
else if(curr && (dc.w == inputw || curr->next))
else if(curr && (dc->w == inputw || curr->next))
drawmenuh();
commitdraw(&dc, win);
commitdraw(dc, win);
}
void
drawmenuh(void) {
Item *item;
dc.x += inputw;
dc.w = textw(&dc, "<");
dc->x += inputw;
dc->w = textw(dc, "<");
if(curr->left)
drawtext(&dc, "<", normcol);
dc.x += dc.w;
drawtext(dc, "<", normcol);
for(item = curr; item != next; item = item->right) {
dc.w = MIN(textw(&dc, item->text), mw / 3);
if(item == sel)
drawbox(&dc, selcol);
drawtext(&dc, item->text, (item == sel) ? selcol : normcol);
dc.x += dc.w;
dc->x += dc->w;
dc->w = MIN(textw(dc, item->text), mw / 3);
drawtext(dc, item->text, (item == sel) ? selcol : normcol);
}
dc.w = textw(&dc, ">");
dc.x = mw - dc.w;
dc->w = textw(dc, ">");
dc->x = mw - dc->w;
if(next)
drawtext(&dc, ">", normcol);
drawtext(dc, ">", normcol);
}
void
drawmenuv(void) {
Item *item;
XWindowAttributes wa;
dc.y = topbar ? dc.h : 0;
dc.w = mw - dc.x;
dc->y = topbar ? dc->h : 0;
dc->w = mw - dc->x;
for(item = curr; item != next; item = item->right) {
if(item == sel)
drawbox(&dc, selcol);
drawtext(&dc, item->text, (item == sel) ? selcol : normcol);
dc.y += dc.h;
drawtext(dc, item->text, (item == sel) ? selcol : normcol);
dc->y += dc->h;
}
if(!XGetWindowAttributes(dc.dpy, win, &wa))
eprintf("cannot get window attributes\n");
if(wa.height != mh)
XMoveResizeWindow(dc.dpy, win, wa.x, wa.y + (topbar ? 0 : wa.height - mh), mw, mh);
}
void
@ -198,7 +171,7 @@ grabkeyboard(void) {
int i;
for(i = 0; i < 1000; i++) {
if(!XGrabKeyboard(dc.dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime))
if(!XGrabKeyboard(dc->dpy, root, True, GrabModeAsync, GrabModeAsync, CurrentTime))
return;
usleep(1000);
}
@ -254,6 +227,7 @@ keypress(XKeyEvent *e) {
break;
case XK_k: /* delete right */
text[cursor] = '\0';
match();
break;
case XK_n:
ksym = XK_Down;
@ -270,10 +244,10 @@ keypress(XKeyEvent *e) {
n = 0;
while(cursor - n++ > 0 && text[cursor - n] == ' ');
while(cursor - n++ > 0 && text[cursor - n] != ' ');
insert(NULL, -(--n));
insert(NULL, 1-n);
break;
case XK_y: /* paste selection */
XConvertSelection(dc.dpy, XA_PRIMARY, utf8, None, win, CurrentTime);
XConvertSelection(dc->dpy, XA_PRIMARY, utf8, None, win, CurrentTime);
/* causes SelectionNotify event */
return;
}
@ -348,7 +322,7 @@ keypress(XKeyEvent *e) {
break;
case XK_Return:
case XK_KP_Enter:
fputs(((e->state & ShiftMask) || sel) ? sel->text : text, stdout);
fputs((sel && !(e->state & ShiftMask)) ? sel->text : text, stdout);
fflush(stdout);
exit(EXIT_SUCCESS);
case XK_Right:
@ -418,14 +392,13 @@ match(void) {
}
void
paste(Atom atom)
{
paste(void) {
char *p, *q;
int di;
unsigned long dl;
Atom da;
XGetWindowProperty(dc.dpy, win, atom, 0, sizeof text - cursor, True,
XGetWindowProperty(dc->dpy, win, utf8, 0, sizeof text - cursor, True,
utf8, &da, &di, &dl, &dl, (unsigned char **)&p);
insert(p, (q = strchr(p, '\n')) ? q-p : strlen(p));
XFree(p);
@ -434,20 +407,18 @@ paste(Atom atom)
void
readstdin(void) {
char buf[sizeof text];
size_t len;
char buf[sizeof text], *p;
Item *item, *new;
allitems = NULL;
for(item = NULL; fgets(buf, sizeof buf, stdin); item = new) {
len = strlen(buf);
if(buf[len-1] == '\n')
buf[--len] = '\0';
if((p = strchr(buf, '\n')))
*p = '\0';
if(!(new = malloc(sizeof *new)))
eprintf("cannot malloc %u bytes\n", sizeof *new);
if(!(new->text = strdup(buf)))
eprintf("cannot strdup %u bytes\n", len);
inputw = MAX(inputw, textw(&dc, new->text));
eprintf("cannot strdup %u bytes\n", strlen(buf));
inputw = MAX(inputw, textw(dc, new->text));
new->next = new->left = new->right = NULL;
if(item)
item->next = new;
@ -460,7 +431,7 @@ void
run(void) {
XEvent ev;
while(!XNextEvent(dc.dpy, &ev))
while(!XNextEvent(dc->dpy, &ev))
switch(ev.type) {
case Expose:
if(ev.xexpose.count == 0)
@ -470,39 +441,43 @@ run(void) {
keypress(&ev.xkey);
break;
case SelectionNotify:
if(ev.xselection.property != None)
paste(ev.xselection.property);
if(ev.xselection.property == utf8)
paste();
break;
case VisibilityNotify:
if(ev.xvisibility.state != VisibilityUnobscured)
XRaiseWindow(dc.dpy, win);
XRaiseWindow(dc->dpy, win);
break;
}
}
void
setup(void) {
int x, y;
int x, y, screen;
XSetWindowAttributes wa;
#ifdef XINERAMA
int i, n;
int n;
XineramaScreenInfo *info;
#endif
XSetWindowAttributes wa;
normcol[ColBG] = getcolor(&dc, normbgcolor);
normcol[ColFG] = getcolor(&dc, normfgcolor);
selcol[ColBG] = getcolor(&dc, selbgcolor);
selcol[ColFG] = getcolor(&dc, selfgcolor);
screen = DefaultScreen(dc->dpy);
root = RootWindow(dc->dpy, screen);
utf8 = XInternAtom(dc->dpy, "UTF8_STRING", False);
normcol[ColBG] = getcolor(dc, normbgcolor);
normcol[ColFG] = getcolor(dc, normfgcolor);
selcol[ColBG] = getcolor(dc, selbgcolor);
selcol[ColFG] = getcolor(dc, selfgcolor);
/* input window geometry */
mh = (dc.font.height + 2) * (lines + 1);
mh = (dc->font.height + 2) * (lines + 1);
#ifdef XINERAMA
if((info = XineramaQueryScreens(dc.dpy, &n))) {
int di;
if((info = XineramaQueryScreens(dc->dpy, &n))) {
int i, di;
unsigned int du;
Window dw;
XQueryPointer(dc.dpy, root, &dw, &dw, &x, &y, &di, &di, &du);
XQueryPointer(dc->dpy, root, &dw, &dw, &x, &y, &di, &di, &du);
for(i = 0; i < n; i++)
if(INRECT(x, y, info[i].x_org, info[i].y_org, info[i].width, info[i].height))
break;
@ -515,31 +490,30 @@ setup(void) {
#endif
{
x = 0;
y = topbar ? 0 : DisplayHeight(dc.dpy, screen) - mh;
mw = DisplayWidth(dc.dpy, screen);
y = topbar ? 0 : DisplayHeight(dc->dpy, screen) - mh;
mw = DisplayWidth(dc->dpy, screen);
}
/* input window */
wa.override_redirect = True;
wa.background_pixmap = ParentRelative;
wa.event_mask = ExposureMask | KeyPressMask | VisibilityChangeMask;
win = XCreateWindow(dc.dpy, root, x, y, mw, mh, 0,
DefaultDepth(dc.dpy, screen), CopyFromParent,
DefaultVisual(dc.dpy, screen),
win = XCreateWindow(dc->dpy, root, x, y, mw, mh, 0,
DefaultDepth(dc->dpy, screen), CopyFromParent,
DefaultVisual(dc->dpy, screen),
CWOverrideRedirect | CWBackPixmap | CWEventMask, &wa);
match();
grabkeyboard();
setupdraw(&dc, win);
setcanvas(dc, win, mw, mh);
inputw = MIN(inputw, mw/3);
utf8 = XInternAtom(dc.dpy, "UTF8_STRING", False);
XMapRaised(dc.dpy, win);
XMapRaised(dc->dpy, win);
match();
}
void
usage(void) {
fputs("usage: dmenu [-b] [-i] [-l <lines>] [-p <prompt>] [-fn <font>] [-nb <color>]\n"
" [-nf <color>] [-sb <color>] [-sf <color>] [-v]\n", stderr);
fputs("usage: dmenu [-b] [-i] [-l lines] [-p prompt] [-fn font] [-nb color]\n"
" [-nf color] [-sb color] [-sf color] [-v]\n", stderr);
exit(EXIT_FAILURE);
}
@ -548,8 +522,10 @@ main(int argc, char *argv[]) {
int i;
progname = "dmenu";
dc = initdraw();
for(i = 1; i < argc; i++)
/* 1-arg flags */
/* single flags */
if(!strcmp(argv[i], "-v")) {
fputs("dmenu-"VERSION", © 2006-2010 dmenu engineers, see LICENSE for details\n", stdout);
exit(EXIT_SUCCESS);
@ -562,17 +538,15 @@ main(int argc, char *argv[]) {
}
else if(i == argc-1)
usage();
/* 2-arg flags */
/* double flags */
else if(!strcmp(argv[i], "-l")) {
if((lines = atoi(argv[++i])) > 0)
calcoffsets = calcoffsetsv;
}
else if(!strcmp(argv[i], "-p")) {
else if(!strcmp(argv[i], "-p"))
prompt = argv[++i];
promptw = MIN(textw(&dc, prompt), mw/5);
}
else if(!strcmp(argv[i], "-fn"))
font = argv[++i];
initfont(dc, argv[i++]);
else if(!strcmp(argv[i], "-nb"))
normbgcolor = argv[++i];
else if(!strcmp(argv[i], "-nf"))
@ -584,14 +558,6 @@ main(int argc, char *argv[]) {
else
usage();
if(!setlocale(LC_CTYPE, "") || !XSupportsLocale())
fputs("dmenu: warning: no locale support\n", stderr);
if(!(dc.dpy = XOpenDisplay(NULL)))
eprintf("cannot open display\n");
screen = DefaultScreen(dc.dpy);
root = RootWindow(dc.dpy, screen);
initfont(&dc, font);
readstdin();
setup();
run();

View file

@ -19,7 +19,7 @@ then
do
test -x "$file" && echo "$file"
done
done | sort | uniq > "$CACHE".$$ &&
done | sort -u > "$CACHE".$$ &&
mv "$CACHE".$$ "$CACHE"
fi