Skip to content

Commit 423c87d

Browse files
committed
Implement poll using kqueue on macOS
Work-around for the broken poll implementation on macOS that does not support character devices like /dev/tty.
1 parent 2b66bdd commit 423c87d

File tree

7 files changed

+117
-4
lines changed

7 files changed

+117
-4
lines changed

Makefile.am

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,11 @@ AM_CFLAGS=-Wall -Wextra
44
AM_CPPFLAGS=-D_GNU_SOURCE
55

66
bin_PROGRAMS=pick
7-
pick_SOURCES=pick.c compat-reallocarray.c compat-strtonum.c compat.h
7+
pick_SOURCES=compat-poll.c \
8+
compat-reallocarray.c \
9+
compat-strtonum.c \
10+
compat.h \
11+
pick.c
812
pick_CPPFLAGS=$(AM_CPPFLAGS) $(NCURSES_CFLAGS)
913
pick_LDADD=$(NCURSES_LIBS)
1014

compat-poll.c

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
#ifdef HAVE_CONFIG_H
2+
#include "config.h"
3+
#endif
4+
5+
int unused;
6+
7+
#ifdef HAVE_POLL
8+
9+
#include <poll.h>
10+
11+
int
12+
xpoll(struct pollfd *fds, nfds_t nfds, int timeout)
13+
{
14+
return poll(fds, nfds, timeout);
15+
}
16+
17+
#else
18+
19+
#include <sys/types.h>
20+
#include <sys/event.h>
21+
#include <sys/time.h>
22+
23+
#include <assert.h>
24+
#include <err.h>
25+
#include <poll.h>
26+
#include <string.h>
27+
28+
int
29+
xpoll(struct pollfd *fds, nfds_t nfds, int timeout)
30+
{
31+
#define NKEVENT 2
32+
static int kq = -1;
33+
struct kevent kev[NKEVENT];
34+
struct pollfd *pfd;
35+
struct timespec ts, *tsp;
36+
unsigned int i;
37+
int filter, nevents;
38+
39+
assert(nfds <= NKEVENT);
40+
41+
if (kq == -1) {
42+
kq = kqueue();
43+
if (kq == -1)
44+
return -1;
45+
for (i = 0; i < nfds; i++) {
46+
if (fds[i].events & POLLIN)
47+
filter = EVFILT_READ;
48+
else
49+
errx(1, "%x: unknown poll event",
50+
fds[i].events);
51+
EV_SET(&kev[i], fds[i].fd, filter, EV_ADD, 0, 0,
52+
&fds[i]);
53+
}
54+
if (kevent(kq, kev, nfds, NULL, 0, NULL) == -1)
55+
return -1;
56+
}
57+
58+
if (timeout >= 0) {
59+
ts.tv_sec = timeout / 1000;
60+
ts.tv_nsec = (timeout % 1000) * 1000000;
61+
tsp = &ts;
62+
} else {
63+
tsp = NULL;
64+
}
65+
nevents = kevent(kq, NULL, 0, kev, nfds, tsp);
66+
if (nevents == -1)
67+
return -1;
68+
for (i = 0; i < nfds; i++)
69+
fds[i].revents = 0;
70+
for (i = 0; i < (unsigned int)nevents; i++) {
71+
if (kev[i].flags & EV_ERROR)
72+
errx(1, "kevent: %s", strerror(kev[i].data));
73+
74+
pfd = kev[i].udata;
75+
if (kev[i].filter == EVFILT_READ)
76+
pfd->revents |= POLLIN;
77+
else
78+
errx(1, "%d: unknown kqueue filter", kev[i].filter);
79+
}
80+
return nevents;
81+
}
82+
83+
#endif /* !HAVE_POLL */

compat.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,5 @@ long long strtonum(const char *, long long, long long, const char **);
3232
#endif /* !HAVE_STRTONUM */
3333

3434
#endif /* COMPAT_H */
35+
36+
int xpoll(struct pollfd *, nfds_t, int);

config.h.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
/* Define to 1 if you have the `pledge' function. */
77
#undef HAVE_PLEDGE
88

9+
/* Define if poll(2) works with character devices */
10+
#undef HAVE_POLL
11+
912
/* Define to 1 if you have the `reallocarray' function. */
1013
#undef HAVE_REALLOCARRAY
1114

configure.ac

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ AC_PREREQ([2.61])
22
AC_INIT([pick], [2.0.2], [[email protected]])
33
AM_INIT_AUTOMAKE([subdir-objects])
44
AC_CONFIG_HEADERS([config.h])
5+
AC_CANONICAL_HOST
56
AC_PROG_CC
67
AM_PROG_CC_C_O
78
AC_CHECK_FUNCS([pledge reallocarray strtonum])
@@ -16,7 +17,6 @@ AC_SEARCH_LIBS([setupterm], [curses], [], [
1617
)
1718
])
1819
AC_DEFUN([AC_MALLOC_OPTIONS], [
19-
AC_CANONICAL_HOST
2020
AC_MSG_CHECKING([for $host_os malloc hardening options])
2121
case "$host_os" in
2222
openbsd*) malloc_options="RS";;
@@ -30,5 +30,20 @@ AC_DEFUN([AC_MALLOC_OPTIONS], [
3030
AC_SUBST([MALLOC_OPTIONS], [$malloc_options])
3131
])
3232
AC_MALLOC_OPTIONS
33+
AC_DEFUN([AC_POLL], [
34+
AC_MSG_CHECKING([whether poll(2) support character devices])
35+
case "$host_os" in
36+
darwin*) res=0;;
37+
*) res=1;;
38+
esac
39+
if test "$res" -eq 1; then
40+
AC_MSG_RESULT([yes])
41+
else
42+
AC_MSG_RESULT([no])
43+
fi
44+
AC_DEFINE([HAVE_POLL], [$res], [
45+
Define if poll(2) works with character devices])
46+
])
47+
AC_POLL
3348
AC_CONFIG_FILES([Makefile])
3449
AC_OUTPUT

pick.c

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,7 @@ selected_choice(void)
331331
for (;;) {
332332
dokey = doread = 0;
333333
toggle_sigwinch(1);
334-
if (poll(fds, nfds, timo) == -1 && errno != EINTR)
334+
if (xpoll(fds, nfds, timo) == -1 && errno != EINTR)
335335
err(1, "poll");
336336
toggle_sigwinch(0);
337337
if (gotsigwinch) {
@@ -357,7 +357,12 @@ selected_choice(void)
357357
if (query_length > 0)
358358
dofilter = 1;
359359
} else {
360-
nfds = 1; /* EOF */
360+
/*
361+
* if xpoll uses kqueue, close the fd in order
362+
* to remove it from the event queue.
363+
*/
364+
close(STDIN_FILENO);
365+
nfds = 1;
361366
}
362367
}
363368
if (!dokey && query_length == 0 && length >= choices_lines)

tests/pick-test.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <errno.h>
77
#include <fcntl.h>
88
#include <limits.h>
9+
#include <poll.h>
910
#include <signal.h>
1011
#include <stdio.h>
1112
#include <stdlib.h>

0 commit comments

Comments
 (0)