Skip to content

Commit

Permalink
Tentative macOS poll work-around
Browse files Browse the repository at this point in the history
  • Loading branch information
mptre committed Apr 16, 2018
1 parent 49cbb86 commit faef555
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 42 deletions.
6 changes: 5 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ AM_CFLAGS=-Wall -Wextra
AM_CPPFLAGS=-D_GNU_SOURCE

bin_PROGRAMS=pick
pick_SOURCES=pick.c compat-reallocarray.c compat-strtonum.c compat.h
pick_SOURCES=compat-fdwait.c \
compat-reallocarray.c \
compat-strtonum.c \
compat.h \
pick.c
pick_CPPFLAGS=$(AM_CPPFLAGS) $(NCURSES_CFLAGS)
pick_LDADD=$(NCURSES_LIBS)

Expand Down
154 changes: 154 additions & 0 deletions compat-fdwait.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <sys/types.h>

#include "compat.h"

#ifdef HAVE_BROKEN_POLL

#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <unistd.h>

extern int tty_getc_peek;

static int ttygetchar(int);

int
fdwait(int ttyfd, int infd, int timeout)
{
static int state;
struct pollfd pfd;
int flags, nready;
int res = 0;

/* XXX filter_choices() poll disabled */
if (infd == -1 && timeout == 0)
return 0;

if (state == 0) {
flags = fcntl(ttyfd, F_GETFL, 0);
if (flags == -1)
return -1;
flags |= O_NONBLOCK;
if (fcntl(ttyfd, F_SETFL, flags) == -1)
return -1;
state++;
}

if (state == 1) {
if (timeout < 0)
timeout = 100;
pfd.fd = infd;
pfd.events = POLLIN;
while (res == 0) {
if (infd == -1) {
state++;
break;
}

nready = poll(&pfd, 1, timeout);
if (nready == -1)
return -1;
if (nready > 0) {
if (pfd.revents & (POLLERR | POLLNVAL)) {
errno = EBADF;
return -1;
} else if (pfd.revents & (POLLIN | POLLHUP)) {
res |= FD_STDIN_READY;
}
}

nready = ttygetchar(ttyfd);
if (nready == -1) {
if (errno == EAGAIN)
continue;
return -1;
} else if (nready > 0) {
res |= FD_TTY_READY;
}
}
}

if (state == 2) {
flags = fcntl(ttyfd, F_GETFL, 0);
if (flags == -1)
return -1;
flags &= ~O_NONBLOCK;
if (fcntl(ttyfd, F_SETFL, flags) == -1)
return -1;
state++;
}

if (state == 3) {
nready = ttygetchar(ttyfd);
if (nready == -1)
return -1;
else if (nready > 0)
res |= FD_TTY_READY;
}

return res;
}

static int
ttygetchar(int fd)
{
ssize_t n;
unsigned char c;

n = read(fd, &c, sizeof(c));
if (n > 0) {
assert(tty_getc_peek == -1);
tty_getc_peek = c;
}
return n;
}

#else

#include <errno.h>
#include <poll.h>

int
fdwait(int ttyfd, int infd, int timeout)
{
struct pollfd fds[2];
int i, nready;
int nfds = 0;
int res = 0;

if (ttyfd != -1) {
fds[nfds].fd = ttyfd;
fds[nfds].events = POLLIN;
nfds++;
}
if (infd != -1) {
fds[nfds].fd = infd;
fds[nfds].events = POLLIN;
nfds++;
}
nready = poll(fds, nfds, timeout);
if (nready <= 0)
return nready;
for (i = 0; i < nfds; i++) {
if (fds[i].revents & (POLLERR | POLLNVAL)) {
errno = EBADF;
return -1;
} else if ((fds[i].revents & (POLLIN | POLLHUP)) == 0) {
continue;
}

if (fds[i].fd == ttyfd)
res |= FD_TTY_READY;
else if (fds[i].fd == infd)
res |= FD_STDIN_READY;
}
return res;
}

#endif /* HAVE_BROKEN_POLL */
5 changes: 5 additions & 0 deletions compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,8 @@ long long strtonum(const char *, long long, long long, const char **);
#endif /* !HAVE_STRTONUM */

#endif /* COMPAT_H */

#define FD_STDIN_READY 0x1
#define FD_TTY_READY 0x2

int fdwait(int, int, int);
3 changes: 3 additions & 0 deletions config.h.in
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
/* config.h.in. Generated from configure.ac by autoheader. */

/* Define if poll does not support character devices */
#undef HAVE_BROKEN_POLL

/* Define if ncursesw is available */
#undef HAVE_NCURSESW_H

Expand Down
17 changes: 16 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ AC_PREREQ([2.61])
AC_INIT([pick], [2.0.2], [pick-maintainers@calleerlandsson.com])
AM_INIT_AUTOMAKE([subdir-objects])
AC_CONFIG_HEADERS([config.h])
AC_CANONICAL_HOST
AC_PROG_CC
AM_PROG_CC_C_O
AC_CHECK_FUNCS([pledge reallocarray strtonum])
Expand All @@ -16,7 +17,6 @@ AC_SEARCH_LIBS([setupterm], [curses], [], [
)
])
AC_DEFUN([AC_MALLOC_OPTIONS], [
AC_CANONICAL_HOST
AC_MSG_CHECKING([for $host_os malloc hardening options])
case "$host_os" in
openbsd*) malloc_options="RS";;
Expand All @@ -30,5 +30,20 @@ AC_DEFUN([AC_MALLOC_OPTIONS], [
AC_SUBST([MALLOC_OPTIONS], [$malloc_options])
])
AC_MALLOC_OPTIONS
AC_DEFUN([AC_BROKEN_POLL], [
AC_MSG_CHECKING([for broken poll implementation])
case "$host_os" in
darwin*) broken=1;;
*) broken=0;;
esac
if test "$broken" -eq 1; then
AC_MSG_RESULT([yes])
AC_DEFINE([HAVE_BROKEN_POLL], [1], [
Define if poll does not support character devices])
else
AC_MSG_RESULT([no])
fi
])
AC_BROKEN_POLL
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
69 changes: 29 additions & 40 deletions pick.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
#include <errno.h>
#include <limits.h>
#include <locale.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
Expand Down Expand Up @@ -70,13 +69,15 @@ struct choice {
double score;
};

int tty_getc_peek = -1;

static int choice_cmp(const void *, const void *);
static const char *choice_description(const struct choice *);
static const char *choice_string(const struct choice *);
static void delete_between(char *, size_t, size_t, size_t);
static char *eager_strpbrk(const char *, const char *);
static int filter_choices(size_t);
static size_t get_choices(int, size_t);
static size_t get_choices(int, ssize_t);
static enum key get_key(const char **);
static void handle_sigwinch(int);
static int isu8cont(unsigned char);
Expand Down Expand Up @@ -224,7 +225,7 @@ usage(int status)
}

size_t
get_choices(int fd, size_t insert)
get_choices(int fd, ssize_t insert)
{
static const char *ifs;
static char *buf;
Expand Down Expand Up @@ -291,7 +292,7 @@ get_choices(int fd, size_t insert)
}
offset = start - buf;
dchoices = choices.length - nchoices;
if (dchoices == 0 || nchoices == 0)
if (dchoices == 0 || nchoices == 0 || insert == -1)
return n;

/* Move new choices after the given insert index. */
Expand Down Expand Up @@ -323,66 +324,52 @@ eager_strpbrk(const char *string, const char *separators)
const struct choice *
selected_choice(void)
{
struct pollfd fds[2];
const char *buf;
size_t cursor_position, i, j, length, nfds, xscroll;
size_t cursor_position, i, j, length, xscroll;
size_t choices_count = 0;
size_t selection = 0;
size_t yscroll = 0;
int dokey, doread, timo;
int fd, ready, timo;
int choices_reset = 1;
int dochoices = 0;
int dofilter = 1;

cursor_position = query_length;

fds[0].fd = fileno(tty_in);
fds[0].events = POLLIN;
fds[1].fd = STDIN_FILENO;
fds[1].events = POLLIN;
nfds = 2;
/* No timeout on first call to poll in order to render the UI fast. */
/* No timeout on first call to fdwait in order to render the UI fast. */
timo = 0;
fd = STDIN_FILENO;
for (;;) {
dokey = doread = 0;
toggle_sigwinch(1);
if (poll(fds, nfds, timo) == -1 && errno != EINTR)
err(1, "poll");
ready = fdwait(fileno(tty_in), fd, timo);
if (ready == -1) {
if (errno == EINTR)
ready = 0;
else
err(1, "fdwait");
}
toggle_sigwinch(0);
if (gotsigwinch) {
gotsigwinch = 0;
goto resize;
}
timo = -1;
for (i = 0; i < nfds; i++) {
if (fds[i].revents & (POLLERR | POLLNVAL))
errx(1, "%d: bad file descriptor", fds[i].fd);
if ((fds[i].revents & (POLLIN | POLLHUP)) == 0)
continue;

if (fds[i].fd == fds[0].fd)
dokey = 1;
else if (fds[i].fd == fds[1].fd)
doread = 1;
}

length = choices.length;
if (doread) {
if (ready & FD_STDIN_READY) {
if (get_choices(STDIN_FILENO, query_length > 0 ?
choices_count : 0)) {
choices_count : -1)) {
if (query_length > 0) {
dofilter = 1;
choices_reset = 0;
choices_count += choices.length -
length;
}
} else {
nfds = 1; /* EOF */
fd = -1; /* EOF */
}
}
if (!dokey && query_length == 0 && length >= choices_lines)
continue; /* prevent redundant rendering */
if (!dokey)
if ((ready & FD_TTY_READY) == 0)
goto render;

switch (get_key(&buf)) {
Expand Down Expand Up @@ -590,9 +577,8 @@ int
filter_choices(size_t nchoices)
{
struct choice *c;
struct pollfd pfd;
size_t i, match_length;
int nready;
int ready;

for (i = 0; i < nchoices; i++) {
c = &choices.v[i];
Expand All @@ -608,11 +594,8 @@ filter_choices(size_t nchoices)
}

if (i > 0 && i % 50 == 0) {
pfd.fd = fileno(tty_in);
pfd.events = POLLIN;
if ((nready = poll(&pfd, 1, 0)) == -1)
err(1, "poll");
if (nready == 1 && pfd.revents & (POLLIN | POLLHUP))
ready = fdwait(fileno(tty_in), -1, 0);
if (ready != -1 && (ready & FD_TTY_READY) != 0)
return 0;
}
}
Expand Down Expand Up @@ -1116,6 +1099,12 @@ tty_getc(void)
ssize_t n;
unsigned char c;

if (tty_getc_peek != -1) {
c = tty_getc_peek;
tty_getc_peek = -1;
return c;
}

n = read(fileno(tty_in), &c, sizeof(c));
if (n == -1)
err(1, "read");
Expand Down

0 comments on commit faef555

Please sign in to comment.