Compare commits

...

134 Commits
4.5 ... master

Author SHA1 Message Date
Hiltjo Posthuma 8df553e004 Makefile: remove the options target
The Makefile used to suppress output (by using @), so this target made sense at
the time.

But the Makefile should be simple and make debugging with less abstractions or
fancy printing.  The Makefile was made verbose and doesn't hide the build
output, so remove this target.

Prompted by a question on the mailing list about the options target.
2023-09-22 15:16:44 +02:00
NRK 7ab0cb5ef0 drw: minor improvement to the nomatches cache
1. use `unsigned int` to store the codepoints, this avoids waste on
   common case where `long` is 64bits. and POSIX guarantees `int` to be
   at least 32bits so there's no risk of truncation.
2. since switching to `unsigned int` cuts down the memory requirement by
   half, double the cache size from 64 to 128.
3. instead of a linear search, use a simple hash-table for O(1) lookups.
2023-07-07 15:03:57 +02:00
Lucas de Sena 0fe460dbd4 fix BadMatch error when embedding on some windows
When embedded into another window, dmenu will fail with the BadMatch
error if that window have not the same colormap/depth/visual as the
root window.

That happens because dmenu inherits the colormap/depth/visual from
its parent, but draws on a pixmap created based on the root window
using a GC created for the root window (see drw.c).  A BadMatch will
occur when copying the content of the pixmap into dmenu's window.

A solution is to create dmenu's window inside root and then reparent
it if embeded.

See this mail[1] on ports@openbsd.org mailing list for context.

[1]: https://marc.info/?l=openbsd-ports&m=168072150814664&w=2
2023-04-06 20:28:56 +02:00
Hiltjo Posthuma dfbbf7f6e1 readstdin: reduce memory-usage by duplicating the line from getline()
Improves upon commit 32db2b1251

The getline() implementation often uses a more greedy way of allocating memory.
Using this buffer directly and forcing an allocation (by setting it to NULL)
would waste a bit of extra space, depending on the implementation of course.

Tested on musl libc and glibc.
The current glibc version allocates a minimum of 120 bytes per line.
For smaller lines musl libc seems less wasteful but still wastes a few bytes
per line.

On a dmenu_path listing on my system the memory usage was about 350kb (old) vs
30kb (new) on Void Linux glibc.

Side-note that getline() also reads NUL bytes in lines, while strdup() would
read until the NUL byte. Since dmenu reads text lines either is probably
fine(tm). Also rename junk to linesiz.
2023-03-08 21:28:51 +01:00
Hiltjo Posthuma ba1a347dca readstdin: allocate amount of items
Keep track of the amount of items (not a total buffer size), allocate an array of
new items. For now change BUFSIZ bytes to 256 * sizeof(struct item)).
2022-10-31 11:52:30 +01:00
Hiltjo Posthuma bcbc1ef5c4 readstdin: add a comment
Maybe too obvious / redundant, but OK.
2022-10-31 11:46:10 +01:00
NRK 689d9bfcf6 fix leak when getline fails
according to the getline(3) documentation, the calling code needs to
free the buffer even if getline fails.

dmenu currently doesn't do that which results in a small leak in case of
failure (e.g when piped /dev/null)

	$ ./dmenu < /dev/null
	==8201==ERROR: LeakSanitizer: detected memory leaks
	Direct leak of 120 byte(s) in 1 object(s) allocated from:
	    #0 0x7f6bf5785ef7 in malloc
	    #1 0x7f6bf538ec84 in __getdelim
	    #2 0x405d0c in readstdin dmenu.c:557

moving `line = NULL` inside the loop body wasn't strictly necessary, but
IMO it makes it more apparent that `line` is getting cleared to NULL
after each successful iteration.
2022-10-31 11:40:35 +01:00
Hiltjo Posthuma e42c036634 dmenu: small XmbLookupString code improvements
* Increase the length of composed strings to the same limit as st (32 to 64 bytes).
* Initialize ksym to NoSymbol to be safe: currently this is not an issue though.
* Add comments to clarify the return values of XmbLookupString a bit.
2022-10-26 09:43:17 +02:00
Hiltjo Posthuma 1d2b462acf bump version to 5.2 2022-10-04 19:36:02 +02:00
Tom Schwindl 7ec32fe494 dmenu: use die() to print the usage message 2022-10-01 13:20:40 +02:00
Hiltjo Posthuma fce06f437d remove workaround for a crash with color emojis on some systems, now fixed in libXft 2.3.5
https://gitlab.freedesktop.org/xorg/lib/libxft/-/blob/libXft-2.3.5/NEWS
2022-09-17 15:32:26 +02:00
Hiltjo Posthuma 1e8c5b68f4 fix a regression in the previous commit for tab complete
Reported by Santtu Lakkala <inz@inz.fi>, thanks!
2022-09-02 19:09:50 +02:00
NRK 528d39b011 tab-complete: figure out the size before copying
we already need to know the string length since `cursor` needs to be
adjusted.

so just calculate the length beforehand and use `memcpy` to copy exactly
as much as needed (as opposed to `strncpy` which always writes `n`
bytes).
2022-09-02 13:00:48 +02:00
NRK 32db2b1251 readstdin: use getline(3)
currently readstdin():
   - fgets() into a local buffer,
   - strchr() the buffer to eleminate the newline
   - stdups() the buffer into items

a simpler way is to just use getline(3), which will do the allocation
for us; eliminating the need for stdup()-ing.

additionally getline returns back the amount of bytes read, which
eliminates the need for strchr()-ing to find the newline.
2022-09-02 12:53:34 +02:00
Hiltjo Posthuma e35976f4a5 sync code-style patch from libsl 2022-08-08 10:42:54 +02:00
Hiltjo Posthuma 28fb3e2812 Makefile: add manual path for OpenBSD 2022-05-01 18:38:25 +02:00
Hiltjo Posthuma fe5d5c6709 fix incorrect comment, math is hard 2022-04-30 13:19:33 +02:00
Hiltjo Posthuma e1e1de7b3b inputw: improve correctness and startup performance, by NRK
Always use ~30% of the monitor width for the input in horizontal mode.

Patch adapted from NRK patches.
This also does not calculate inputw when using vertical mode anymore (because
the code is removed).
2022-04-29 20:18:02 +02:00
NRK 33685b06e9 drw_text: account for fallback fonts in ellipsis_width
additionally, ellipsis_width (which shouldn't change) is made static to
avoid re-calculating it on each drw_text() call.
2022-04-16 16:21:01 +02:00
NRK e4827b0c40 drw_text: don't segfault when called with 0 width
this patch just rejects *any* 0 width draws, which is surely an error by
the caller.

this also guards against cases where the width is too small for the
ellipsis to fit, so ellipsis_w will remain 0.
reported by Bakkeby <bakkeby@gmail.com>
2022-04-16 16:21:01 +02:00
Hiltjo Posthuma e73651f12a fix UB with the function iscntrl()
From commit 6818e07291 by NRK, thanks
2022-03-26 17:58:47 +01:00
Hiltjo Posthuma 31fa07b984 Revert "avoid redraw when there's no change"
This reverts commit 6818e07291.

This broke keys such as ^W to delete-backward-word
2022-03-26 17:57:50 +01:00
NRK 6818e07291 avoid redraw when there's no change
while i was timing the performance issue, i noticed that there was lots
of random redrawing going on.

turns out there were coming from here; if someone presses CTRL/ALT etc
without pressing anything else, nothing will be inserted, so nothing
will change. but the code will `break`, go down and do a needless redraw.

this patch changes it to simply return if the keypress iscntrl()

also avoid potential UB by casting *buf into an unsigned char.
2022-03-25 22:53:50 +01:00
NRK b43ec0577f free all allocated items, use %zu for size_t
`items` itself is not checked for NULL as calling free on NULL is defined to be
a no-op.
2022-03-25 22:53:50 +01:00
NRK 22511c41d5 drw_text: improve performance when there's no match
this was the last piece of the puzzle, the case where we can't find any
font to draw the codepoint.

in such cases, we use XftFontMatch() which is INSANELY slow. but that's
not the real problem. the real problem was we were continuously trying
to match the same thing over and over again.

this patch introduces a small cache, which keeps track a couple
codepoints for which we know we won't find any matches.

with this, i can dump lots of emojies into dmenu where some of them
don't have any matching font, and still not have dmenu lag insanely or
FREEZE completely when scrolling up and down.

this also improves startup time, which will of course depend on the
system and all installed fonts; but on my system and test case i see the
following startup time drop:

before -> after
60ms   -> 34ms
2022-03-25 22:49:07 +01:00
NRK 77526f756e inputw: improve correctness and startup performance
a massive amount of time inside readstdin() is spent trying to get the
max input width and then put it into inputw, only for it to get clamped
down to mw/3 inside setup().

it makes more sense to calculate inputw inside setup() once we have mw
available. similar to the last patch, i see noticeable startup
performance improvement:

before -> after
160ms  -> 60ms

additionally this will take fallback fonts into account compared to the
previous version, so it's not only more performant but also more correct.
2022-03-25 22:49:07 +01:00
NRK 7269c5355d significantly improve performance on large strings
this replaces inefficient pattern of `MIN(TEXTW(..), n)` with
drw_fontset_getwidth_clamp() instead, which is far more efficient when
we only want up to a certain width.

dumping a decently sized (unicode) emoji file into dmenu, I see the
startup time drop significantly with this patch.

before -> after
360ms  -> 160ms

this should also noticeably improve input latency (responsiveness) given
that calcoffsets() and drawmenu() are pretty hot functions.
2022-03-25 22:49:07 +01:00
NRK 6be057f060 introduce drw_fontset_getwidth_clamp()
getting the width of a string is an O(n) operation, and in many cases
users only care about getting the width upto a certain number.

instead of calling drw_fontset_getwidth() and *then* clamping the
result, this patch introduces drw_fontset_getwidth_clamp() function,
similar to strnlen(), which will stop once we reach n.

the `invert` parameter was overloaded internally to preserve the API,
however library users should be calling drw_fontset_getwidth_clamp() and
not depend upon internal behavior of drw_text().
2022-03-25 22:49:07 +01:00
NRK 41fdabbf7c drw_text: improve both performance and correctness
this patch makes some non-trivial changes, which significantly improves
the performance of drawing large strings as well as fixes any issues
regarding the printing of the ellipsis when string gets truncated.

* performance:

before there were two O(n) loops, one which finds how long we can go
without changing font, and the second loop would (incorrectly) truncate
the string if it's too big.

this patch merges the overflow calculation into the first loop and exits
out when overflow is detected. when dumping lots of emojies into dmenu,
i see some noticeable startup time improvement:

before -> after
460ms  -> 360ms

input latency when scrolling up/down is also noticeably better and can
be tested with the following:

	for _ in $(seq 20); do
		cat /dev/urandom | base64 | tr -d '\n' | head -c 1000000
	echo
	done | ./dmenu -l 10

* correctness:

the previous version would incorrectly assumed single byte chars and
would overwrite them with '.' , this caused a whole bunch of obvious
problems, including the ellipsis not getting rendered if then font
changed.

in addition to exiting out when we detect overflow, this patch also
keeps track of the last x-position where the ellipsis would fit. if we
detect overflow, we simply make a recursing call to drw_text() at the
ellipsis_x position and overwrite what was there.

so now the ellipsis will always be printed properly, regardless of
weather the font changes or if the string is single byte char or not.

the idea of rendering the ellipsis on top incase of overflow was
from Bakkeby <bakkeby@gmail.com>, thanks! however the original patch had
some issues incorrectly truncating the prompt (-p flag) and cutting off
emojies. those have been fixed in here.
2022-03-25 22:49:07 +01:00
Hiltjo Posthuma 3a505cebe8 remove false-positive warning for int comparison as bool
Reported by Prathu Baronia <prathu.baronia@praton.me>, patch slightly changed.

Thanks!
2022-03-01 22:45:39 +01:00
Hiltjo Posthuma 308fe78b83 bump version to 5.1 2022-02-11 12:26:35 +01:00
Hiltjo Posthuma c4b656e0da code-style: rm newline (oops) 2022-02-08 21:45:28 +01:00
Hiltjo Posthuma 3e39c526d2 revert using strcasestr and use a more optimized portable version
... compared to the old cistrstr().

Thanks for the feedback!
2022-02-08 19:38:23 +01:00
Hiltjo Posthuma a9a3836861 follow-up fix: add -D_GNU_SOURCE for strcasestr for some systems 2022-02-07 10:36:13 +01:00
Hiltjo Posthuma eb96af27f4 improve performance of case-insensitive matching 2022-02-07 00:21:12 +01:00
Hiltjo Posthuma d78ff08d99 Revert "Improve speed of drw_text when provided with large strings"
This reverts commit c585e8e498.

It causes issues with truncation of characters when the text does not fit and
so on.  The patch should be reworked and properly tested.
2021-08-20 23:05:53 +02:00
Hiltjo Posthuma cd2133a5f6 add support for more keypad keys
The keypad Enter key was already supported. On some keyboard layouts like my
laptop the page-up and page-down key is more comfortable to use.
This adds a few lines but no complexity.
2021-08-09 18:39:25 +02:00
Miles Alan c585e8e498 Improve speed of drw_text when provided with large strings
Calculates len & ew in drw_font_getexts loop by incrementing instead of
decrementing; as such avoids proportional increase in time spent in loop
based on provided strings size.
2021-08-09 18:20:51 +02:00
Guilherme Janczak 523aa08f51 remove always true condition in if statement 2021-07-25 10:55:45 +02:00
Hiltjo Posthuma 1a13d0465d bump version to 5.0
... and bump LICENSE year.
2020-09-02 18:31:23 +02:00
Hiltjo Posthuma 9b38fda6fe Fix memory leaks in drw
Synced from dwm.
Patch by Alex Flierl <shad0w73@freenet.de>, thanks.
2020-06-11 18:45:33 +02:00
Hiltjo Posthuma db6093f6ec revert IME support
dmenu will not handle IME support (st will, atleast for now).

revert parts of commit 377bd37e21
this commit also broke input focus.
2019-03-03 13:08:54 +01:00
Hiltjo Posthuma a9b1de384a improve xopenim error message
die() already prints a newline.
2019-02-12 22:58:35 +01:00
Hiltjo Posthuma 43b0c2c3dd make dmenu_path script executable
(as dmenu_run is)
2019-02-12 22:13:58 +01:00
Hiltjo Posthuma f5036b90ef fix crash when XOpenIM returns NULL
for example when IME variables are set, but the program is not started (yet).
2019-02-12 19:10:43 +01:00
Quentin Rameau 153aaf88bf Close when the embedding window is destroyed 2019-02-04 19:49:34 +01:00
Anselm R Garbe 65be875f5a Prepared 4.9 release. 2019-02-02 04:54:15 -08:00
Hiltjo Posthuma 7d19b2055d dmenu.1: document improved fastgrab behaviour from previous patch 2019-01-27 15:28:02 +01:00
dok 11a65377da Use slow path if stdin is a tty
If stdin is a tty and dmenu is ran with the fast option then it's
impossible to close stdin because the keyboard is already grabbed.
2019-01-27 15:26:04 +01:00
Quentin Rameau bbc464dc80 dmenu_path: always use the cachedir 2018-07-21 12:49:00 +02:00
Hiltjo Posthuma a314412f4b Makefile: just show the compiler output
Don't be fancy and just show the actual output so debugging is simpler.
2018-06-02 17:09:01 +02:00
Hiltjo Posthuma a9eae39e93 Do not strip at link stage
Building with debug symbols is worthless unless LDFLAGS are manually adjusted
as well.
2018-06-02 17:01:24 +02:00
Hiltjo Posthuma 851b73d178 code-style for pledge: check the return code -1, not < 0
this is the proper idiom
2018-05-25 13:07:17 +02:00
Hiltjo Posthuma 05c138f5b8 code-style for pledge(2)
feedback from Klemens, thanks
2018-05-25 13:03:25 +02:00
Hiltjo Posthuma cd132c8d5b Pledge on OpenBSD 2018-05-25 12:04:22 +02:00
David Demelier e75494b730 Use bold for keyboard shortcuts in dmenu.1
Like dwm, use the same syntax for all keyboard shortcuts for
consistency.
2018-05-12 19:12:25 +02:00
Quentin Rameau 0f76dd2fb8 Fix cursor drawn position with wide glyphs 2018-04-22 14:19:20 +02:00
Quentin Rameau 0b57480218 Makefile: bikesheddingly replace ${} with $() 2018-04-22 14:09:05 +02:00
Quentin Rameau 377bd37e21 Handle IME input
Thanks to nzl <uruabi@gmail.com> for the patch!
2018-04-22 14:09:05 +02:00
Hiltjo Posthuma b6d2cc9aea Fix handling of input strings 2018-04-22 14:09:05 +02:00
Quentin Rameau 2f398981fe Update LICENSE
Only "meaningful" commits and contributors who made changes over the
years have been added.
2018-03-15 18:29:32 +01:00
Hiltjo Posthuma 23051d78dd bump version to 4.8 2018-03-14 19:48:05 +01:00
Quentin Rameau e2a280541e add key bindings for moving to the word start or end
Mod1+b/^Left and Mod1+f/^Right
2018-03-13 20:10:46 +01:00
Hiltjo Posthuma 889512811d Fix regression in 84a1bc5
Reported by Jochen Sprickerhof, thanks!

Applied patch with minor change (only initialize `i` for XINERAMA).
2018-01-04 23:45:49 +01:00
Vincent Carluer 84a1bc5d0d Instantiate j var outside #ifdef XINEMARA directive because it is used in loop outside directive 2018-01-04 18:14:41 +01:00
Hiltjo Posthuma f0a5b75d6a drw: drw_scm_create: use Clr type
in this context XftColor is a too low-level type.
2017-11-03 21:10:38 +01:00
Hiltjo Posthuma 1cabeda550 fix a possible free of a uninitialize variable in paste() 2017-11-03 21:07:02 +01:00
Hiltjo Posthuma 41379f7c39 init colors using SchemeLast
this makes it slightly easier to add colors to schemes.
2017-11-03 21:05:29 +01:00
Omar Sandoval 64ab2801fb Set class name on menu window
WM_CLASS is a standard ICCCM property which is used to identify windows.
Window managers and compositors use it to allow per-application
configurable behavior.
2017-11-03 20:41:03 +01:00
Hiltjo Posthuma f428f3e01a release 4.7 2017-05-02 18:32:04 +02:00
Andrew Gregory 5cd66e2c6c Revert "fix input text matching"
This reverts commit 09d0a36e03.

Using strncmp with the length of the user input turns it into a prefix
match rather than an exact match as it's supposed to be.
2016-12-11 12:33:16 +01:00
Thomas Gardner e90b88e12a Xinerama: correct variable declarations in preprocessor conditional 2016-11-25 14:21:40 +01:00
Hiltjo Posthuma a280bdad1f die() on calloc failure
thanks Markus Teich and David!
2016-11-05 11:36:42 +01:00
Quentin Rameau a9a5c6cc2d add embedding support with -w option 2016-10-14 15:26:35 +02:00
Quentin Rameau a97f550aa7 dmenu.1: group single options 2016-10-14 15:26:34 +02:00
Quentin Rameau ff8daf8847 dmenu.1: fix -l option 2016-10-14 15:26:34 +02:00
Hiltjo Posthuma 026827fd65 die() consistency: always add newline 2016-08-12 14:39:30 +02:00
Hiltjo Posthuma d14670b995 fix crash if negative monitor (< -1) was passed
for example: dmenu -m '-9001'
2016-07-26 23:13:06 +02:00
Hiltjo Posthuma aa92cd68bd Revert "Print highlighted input text only on single match"
This reverts commit a4053bc4e4.

My bad, was working on the wrong branch and accidently pushed it...
*facepalm*
2016-07-26 23:02:34 +02:00
Quentin Rameau a4053bc4e4 Print highlighted input text only on single match
When the input text fully matches a single item, do not draw the item
and highlight the input text to show that it matches an item in
opposition to regular input text not matching anything.
2016-07-26 18:29:42 +02:00
Quentin Rameau 657122f781 Partially revert 44c7de3: fix items text width offset calculation
Without this, we discard the item if it's longer than assigned width
instead of truncating it.
2016-07-25 19:57:31 +02:00
Hiltjo Posthuma 3c91eed0fb config.def.h: style improvement, use color Scheme enum 2016-06-28 17:56:25 +02:00
S. Gilles 76eb578304 fix: Do not crash on e.g. dmenu < /dev/null 2016-06-28 17:51:28 +02:00
Markus Teich 44c7de3dcf import new drw from libsl and minor fixes.
- extract drawitem function (code deduplication)
- fix bug where inputw was not correctly calculated from the widest item, but
  just from the one with the longest strlen() which is not the same. It's better
  now, but does not account for fallback fonts, since it would be too slow to
  calculate all the correct item widths on startup.
- minor code style fixes (indentation, useless line breaks)
2016-06-03 19:13:15 +02:00
Lucas Gabriel Vuotto b3d9451c2d arg.h: fixed argv checks order
This prevents accessing to a potentially out-of-bounds memory section.

Signed-off-by: Lucas Gabriel Vuotto <l.vuotto92@gmail.com>
2016-02-23 00:31:03 +01:00
Klemens Nanni 3de85ca21c Typofix 2016-01-14 23:02:27 +01:00
Quentin Rameau bf3deb6357 Shut up glibc about _BSD_SOURCE being deprecated 2015-12-19 20:32:17 +01:00
Quentin Rameau 120e840107 Add config option for word delimiters
Let the user configure word boundaries other than ' ', only works with
the portable character set.
2015-12-19 20:32:14 +01:00
Hiltjo Posthuma 32f2564dbb arg.h: remove unused ARGNUM* macros 2015-11-08 23:37:47 +01:00
Hiltjo Posthuma cc596365ac unboolify dmenu 2015-11-08 23:03:34 +01:00
Hiltjo Posthuma c9e4e152e6 Makefile: package config.def.h on make dist 2015-11-08 16:46:34 +01:00
Hiltjo Posthuma cc99007809 remove .hgtags file 2015-11-08 16:44:41 +01:00
Hiltjo Posthuma 34f549adb2 dmenu.1: clarify monitor numbers are starting from 0 (first mon) 2015-11-07 12:53:00 +01:00
Hiltjo Posthuma da0b9eb6c9 fix: multimon: always spawn client on first monitor if specified with -m 0
This was always broken.

Reproduce:
	focus client on second monitor, spawn dmenu with -m 0.

Result:
	Old wrong behaviour: dmenu spawns on second monitor (focused client).
	Now: dmenu spawns on specified monitor (first).
2015-11-07 12:43:00 +01:00
Hiltjo Posthuma 44b242c763 drw: cleanup drw_text, prevent gcc warning false-positive of unused var
... we don't allow passing text is NULL anymore either, for that behaviour
just use drw_rect() (it is used in dwm).
2015-10-20 22:56:57 +02:00
Hiltjo Posthuma e2e7fcb219 drw: simplify drw_font_xcreate and prevent a potential unneeded allocation 2015-10-20 22:55:39 +02:00
Hiltjo Posthuma 1f2226df13 drw: a valid (non-NULL) Drw and Fnt context must be passed
don't do these checks on this level. However for resource drw_*_free
we will allow it.
2015-10-20 22:53:55 +02:00
Hiltjo Posthuma 5a20b409c6 add sbase-style ecalloc(), calloc: or die
... remove intermediary variables
2015-10-20 22:51:57 +02:00
Hiltjo Posthuma 164986763a free schemes as array
this makes it slightly easier to add custom schemes aswell
2015-10-18 18:37:20 +02:00
Quentin Rameau 09d0a36e03 fix input text matching
just compare the size of the input string
2015-10-04 15:04:59 +02:00
Davide Del Zompo acbf35a5e3 fix incorrect ordering of match results
look for exact matches comparing the user input against the item text
2015-10-04 14:03:07 +02:00
Hiltjo Posthuma 240a7810e4 fix paste from clipboard (ctrl+shift+y)
from Joshua Lloret on the ML:

"attached is a small patch to enable pasting from clipboard, as well as primary.

It seems like there was already code in there to allow this, but since there was never any case to match the upper case 'Y', that inline if would always evaluate to false."
2015-10-04 12:32:07 +02:00
Hiltjo Posthuma d5ce8df9d9 minor style fix 2015-09-28 00:38:17 +02:00
FRIGN ba75bb30fa Remove function prototypes and reorder functions accordingly 2015-09-28 00:33:29 +02:00
FRIGN 10fd4f275f Untypedef struct item
Adds clarity. Typedefs for structs are definitely a discussion matter,
but there's no reason to hide a simple data-structure behind a
meaningless typedef.
2015-09-28 00:33:26 +02:00
Quentin Rameau b048eacc9d Fix the conversion from microseconds to nanoseconds 2015-09-28 00:27:09 +02:00
Quentin Rameau c42c378027 Replace deprecated usleep() with nanosleep() 2015-09-28 00:15:13 +02:00
Hiltjo Posthuma c15a95a061 config.mk: use -std=c99 2015-09-28 00:02:28 +02:00
Hiltjo Posthuma cf0fb79cd8 dmenu: style improvements
- move main to bottom, usage above main.
- dont use variable names with function prototypes.
- space before if, for, while, etc: 'if(' -> 'if ('.

this makes the code-style more consistent
2015-09-27 23:57:39 +02:00
Hiltjo Posthuma 03cb1ec55a drw style improvements
this makes the code-style more consistent aswell.
2015-09-27 23:56:02 +02:00
Hiltjo Posthuma bc20c13db0 separate program-specific c99 bool and X11
True, False are X11-specific, make sure to use c99 stdbool for
program-specific things.

... also remove left-over vim mode string in config.
2015-09-27 23:02:33 +02:00
Hiltjo Posthuma 96e60c66bc config.mk: fix _XOPEN_SOURCE=700 for getline() 2015-09-27 22:55:21 +02:00
Hiltjo Posthuma 2d2175ff6f config.mk: improve feature test check
this fixes a crash on NetBSD because it requires -D_XOPEN_SOURCE (strdup, usleep).

thanks k0ga and stateless for reporting and fixing this issue!
2015-09-27 22:38:14 +02:00
Eric Pruitt bbf7b95dc9 Fixed typo introduced by shared code 2015-08-06 12:47:51 +02:00
Hiltjo Posthuma d6742ef8a6 stest: get rid of getopt, use suckless arg.h
... also some style improvements.
2015-07-19 21:38:42 +02:00
Hiltjo Posthuma 5feb0c689d config.mk: add FREETYPELIBS and FREETYPEINC
These variables make it simpler to change the paths to this for ports.
`pkg-config` is avoided because it sucks, in particular for cross-compilation.

A commented path for *BSD is added, the Xft includes are located at:
/usr/X11R6/include there.

Also already bump the version number to 4.6, a release will come
approximately in August.
2015-07-19 20:32:08 +02:00
Hiltjo Posthuma 1c242df124 fix prompt color style how it used to be
This is the style how it was before the big Xft change. The colors were
inverted, this was not the case before the change.

Reported by "zvz" on #suckless IRC, thanks!
2015-07-19 20:29:26 +02:00
Hiltjo Posthuma 4b1fecd44e Use libdraw: add Xft and fallback-fonts support to graphics lib
- libdraw, util: add drw.{c,h}, util.{c,h} and update code.
- libdraw: fix drw_rect(): use w and h parameter.
- libdraw: print errstr if last character in string was ":" (sbase).
- libdraw: drw_clr_free() allow valid free(NULL).
- config.def.h: set default font to monospace.
- cleanup() on exit.
- LICENSE: update license string for dmenu -v to 2015.
- LICENSE: add myself to LICENSE
2015-06-27 21:47:10 +02:00
Anselm R Garbe 13a529ce63 applied Hiltjo's patch as suggested on the ml to fix ControlMask for C-j and C-m 2014-09-17 13:40:11 +02:00
Hiltjo Posthuma aa69426670 fix crash with ctrl-enter as input
reproduce: ./dmenu; send EOF; press ctrl+enter.
2014-07-30 20:07:47 +01:00
Anselm R Garbe 4c50e43df4 updated copyright notices in LICENSE and dmenu.c file 2014-05-29 18:03:53 +02:00
Anselm R Garbe 5ed5e90bfb applied Martin Kühl's inverse matching flag to stest 2013-08-13 19:15:04 +02:00
Anselm R Garbe 13f787306f applied Martti Kühne's dmenu monitor patch
f6581ca966/monarg.patch becomes upstream now
2013-08-02 22:30:20 +02:00
Anselm R Garbe 597d4b4337 accepted vi'is exit approach ^[ (suggested by Arkaduisz) 2013-06-28 22:06:02 +02:00
Anselm R Garbe 55c9be06ca forgot to add config.def.h, thanks William 2013-04-17 21:16:51 +02:00
Anselm R Garbe 14f72a2763 adopted Alex Sedov's config.h revival patch to tip 2013-04-17 21:04:05 +02:00
Anselm R Garbe ec64f273fb applied Alex Sedov's Tab buffer termination patch, thanks 2013-04-17 20:59:12 +02:00
Anselm R Garbe 0d12a47415 applied multisel patch to mainline 2013-04-17 20:56:54 +02:00
Quentin Glidic dec9a28863 dmenu_run: Split cache logic to dmenu_path again
This allows to run dmenu_path to update the cache using
a packager manager hook system
2012-07-30 17:02:12 +02:00
Moritz Wilhelmy 1299e41447 ignore prompt if it is empty in addition to NULL 2012-05-15 11:47:54 +02:00
Connor Lane Smith 38fccafada _POSIX_C_SOURCE=200809L 2012-04-06 16:38:01 +01:00
Connor Lane Smith 1f11faf7b5 listen for C-S-[jm] 2012-02-10 00:37:42 +00:00
Karl F 50145384c8 add ^G escape keybinding 2012-01-19 22:52:17 +00:00
Connor Lane Smith 7707111c10 remove _POSIX_C_SOURCE cflag 2012-01-18 23:56:13 +00:00
18 changed files with 1355 additions and 743 deletions

49
.hgtags
View File

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

11
LICENSE
View File

@ -1,12 +1,15 @@
MIT/X Consortium License
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 2006-2012 Anselm R Garbe <anselm@garbe.us>
© 2006-2019 Anselm R Garbe <anselm@garbe.ca>
© 2006-2008 Sander van Dijk <a.h.vandijk@gmail.com>
© 2006-2007 Michał Janeczek <janeczek@gmail.com>
© 2007 Kris Maglione <jg@suckless.org>
© 2009 Gottox <gottox@s01.de>
© 2009 Markus Schnalke <meillo@marmaro.de>
© 2009 Evan Gates <evan.gates@gmail.com>
© 2006-2008 Sander van Dijk <a dot h dot vandijk at gmail dot com>
© 2006-2007 Michał Janeczek <janeczek at gmail dot com>
© 2010-2012 Connor Lane Smith <cls@lubutu.com>
© 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
copy of this software and associated documentation files (the "Software"),

View File

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

49
arg.h Normal file
View File

@ -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

23
config.def.h Normal file
View File

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

View File

@ -1,9 +1,9 @@
# dmenu version
VERSION = 4.5-tip
VERSION = 5.2
# paths
PREFIX = /usr/local
MANPREFIX = ${PREFIX}/share/man
MANPREFIX = $(PREFIX)/share/man
X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
@ -12,15 +12,21 @@ X11LIB = /usr/X11R6/lib
XINERAMALIBS = -lXinerama
XINERAMAFLAGS = -DXINERAMA
# freetype
FREETYPELIBS = -lfontconfig -lXft
FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = $(X11INC)/freetype2
#MANPREFIX = ${PREFIX}/man
# includes and libs
INCS = -I${X11INC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS}
INCS = -I$(X11INC) -I$(FREETYPEINC)
LIBS = -L$(X11LIB) -lX11 $(XINERAMALIBS) $(FREETYPELIBS)
# flags
CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
#CFLAGS = -g -std=c99 -pedantic -Wall -O0 ${INCS} ${CPPFLAGS}
CFLAGS = -std=c99 -pedantic -Wall -Os ${INCS} ${CPPFLAGS}
LDFLAGS = -s ${LIBS}
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

89
dmenu.1
View File

@ -3,11 +3,11 @@
dmenu \- dynamic menu
.SH SYNOPSIS
.B dmenu
.RB [ \-b ]
.RB [ \-f ]
.RB [ \-i ]
.RB [ \-bfiv ]
.RB [ \-l
.IR lines ]
.RB [ \-m
.IR monitor ]
.RB [ \-p
.IR prompt ]
.RB [ \-fn
@ -20,7 +20,8 @@ dmenu \- dynamic menu
.IR color ]
.RB [ \-sf
.IR color ]
.RB [ \-v ]
.RB [ \-w
.IR windowid ]
.P
.BR dmenu_run " ..."
.SH DESCRIPTION
@ -40,8 +41,8 @@ which lists programs in the user's $PATH and runs the result in their $SHELL.
dmenu appears at the bottom of the screen.
.TP
.B \-f
dmenu grabs the keyboard before reading stdin. This is faster, but will lock up
X until stdin reaches end\-of\-file.
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.
@ -49,6 +50,10 @@ dmenu matches menu items case insensitively.
.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
@ -72,6 +77,9 @@ 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.
@ -83,82 +91,103 @@ Copy the selected item to the input field.
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
C\-a
.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
C\-b
.B C\-b
Left
.TP
C\-c
.B C\-c
Escape
.TP
C\-d
.B C\-d
Delete
.TP
C\-e
.B C\-e
End
.TP
C\-f
.B C\-f
Right
.TP
C\-h
.B C\-g
Escape
.TP
.B C\-h
Backspace
.TP
C\-i
.B C\-i
Tab
.TP
C\-j
.B C\-j
Return
.TP
C\-J
.B C\-J
Shift-Return
.TP
C\-k
.B C\-k
Delete line right
.TP
C\-m
.B C\-m
Return
.TP
C\-n
.B C\-M
Shift-Return
.TP
.B C\-n
Down
.TP
C\-p
.B C\-p
Up
.TP
C\-u
.B C\-u
Delete line left
.TP
C\-w
.B C\-w
Delete word left
.TP
C\-y
.B C\-y
Paste from primary X selection
.TP
C\-Y
.B C\-Y
Paste from X clipboard
.TP
M\-g
.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
M\-G
.B M\-G
End
.TP
M\-h
.B M\-h
Up
.TP
M\-j
.B M\-j
Page down
.TP
M\-k
.B M\-k
Page up
.TP
M\-l
.B M\-l
Down
.SH SEE ALSO
.IR dwm (1),

849
dmenu.c

File diff suppressed because it is too large Load Diff

13
dmenu_path Executable file
View File

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

View File

@ -1,15 +1,2 @@
#!/bin/sh
cachedir=${XDG_CACHE_HOME:-"$HOME/.cache"}
if [ -d "$cachedir" ]; then
cache=$cachedir/dmenu_run
else
cache=$HOME/.dmenu_cache # if no xdg dir, fall back to dotfile in ~
fi
(
IFS=:
if stest -dqr -n "$cache" $PATH; then
stest -flx $PATH | sort -u | tee "$cache" | dmenu "$@"
else
dmenu "$@" < "$cache"
fi
) | ${SHELL:-"/bin/sh"} &
dmenu_path | dmenu "$@" | ${SHELL:-"/bin/sh"} &

177
draw.c
View File

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

35
draw.h
View File

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

451
drw.c Normal file
View File

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

58
drw.h Normal file
View File

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

View File

@ -67,6 +67,9 @@ Test that files are not empty.
.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

127
stest.c
View File

@ -1,66 +1,31 @@
/* See LICENSE file for copyright and license details. */
#include <sys/stat.h>
#include <dirent.h>
#include <stdbool.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include "arg.h"
char *argv0;
#define FLAG(x) (flag[(x)-'a'])
static void test(const char *, const char *);
static void usage(void);
static bool match = false;
static bool flag[26];
static int match = 0;
static int flag[26];
static struct stat old, new;
int
main(int argc, char *argv[]) {
struct dirent *d;
char buf[BUFSIZ], *p;
DIR *dir;
int opt;
while((opt = getopt(argc, argv, "abcdefghln:o:pqrsuwx")) != -1)
switch(opt) {
case 'n': /* newer than file */
case 'o': /* older than file */
if(!(FLAG(opt) = !stat(optarg, (opt == 'n' ? &new : &old))))
perror(optarg);
break;
default: /* miscellaneous operators */
FLAG(opt) = true;
break;
case '?': /* error: unknown flag */
fprintf(stderr, "usage: %s [-abcdefghlpqrsuwx] [-n file] [-o file] [file...]\n", argv[0]);
exit(2);
}
if(optind == argc)
while(fgets(buf, sizeof buf, stdin)) {
if((p = strchr(buf, '\n')))
*p = '\0';
test(buf, buf);
}
for(; optind < argc; optind++)
if(FLAG('l') && (dir = opendir(argv[optind]))) {
/* test directory contents */
while((d = readdir(dir)))
if(snprintf(buf, sizeof buf, "%s/%s", argv[optind], d->d_name) < sizeof buf)
test(buf, d->d_name);
closedir(dir);
}
else
test(argv[optind], argv[optind]);
return match ? 0 : 1;
}
void
test(const char *path, const char *name) {
static void
test(const char *path, const char *name)
{
struct stat st, ln;
if(!stat(path, &st) && (FLAG('a') || name[0] != '.') /* hidden files */
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 */
@ -75,10 +40,70 @@ test(const char *path, const char *name) {
&& (!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)) { /* executable */
if(FLAG('q'))
&& (!FLAG('x') || access(path, X_OK) == 0)) != FLAG('v')) { /* executable */
if (FLAG('q'))
exit(0);
match = true;
match = 1;
puts(name);
}
}
static void
usage(void)
{
fprintf(stderr, "usage: %s [-abcdefghlpqrsuvwx] "
"[-n file] [-o file] [file...]\n", argv0);
exit(2); /* like test(1) return > 1 on error */
}
int
main(int argc, char *argv[])
{
struct dirent *d;
char path[PATH_MAX], *line = NULL, *file;
size_t linesiz = 0;
ssize_t n;
DIR *dir;
int r;
ARGBEGIN {
case 'n': /* newer than file */
case 'o': /* older than file */
file = EARGF(usage());
if (!(FLAG(ARGC()) = !stat(file, (ARGC() == 'n' ? &new : &old))))
perror(file);
break;
default:
/* miscellaneous operators */
if (strchr("abcdefghlpqrsuvwx", ARGC()))
FLAG(ARGC()) = 1;
else
usage(); /* unknown flag */
} ARGEND;
if (!argc) {
/* read list from stdin */
while ((n = getline(&line, &linesiz, stdin)) > 0) {
if (line[n - 1] == '\n')
line[n - 1] = '\0';
test(line, line);
}
free(line);
} else {
for (; argc; argc--, argv++) {
if (FLAG('l') && (dir = opendir(*argv))) {
/* test directory contents */
while ((d = readdir(dir))) {
r = snprintf(path, sizeof path, "%s/%s",
*argv, d->d_name);
if (r >= 0 && (size_t)r < sizeof path)
test(path, d->d_name);
}
closedir(dir);
} else {
test(*argv, *argv);
}
}
}
return match ? 0 : 1;
}

36
util.c Normal file
View File

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

9
util.h Normal file
View File

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