summaryrefslogtreecommitdiffstats
path: root/text-utils/more.c
diff options
context:
space:
mode:
Diffstat (limited to 'text-utils/more.c')
-rw-r--r--text-utils/more.c135
1 files changed, 87 insertions, 48 deletions
diff --git a/text-utils/more.c b/text-utils/more.c
index 22adaab..1badf67 100644
--- a/text-utils/more.c
+++ b/text-utils/more.c
@@ -198,6 +198,7 @@ struct more_control {
magic_t magic; /* libmagic database entries */
#endif
unsigned int
+ ignore_stdin:1, /* POLLHUP; peer closed pipe */
bad_stdout:1, /* true if overwriting does not turn off standout */
catch_suspend:1, /* we should catch the SIGTSTP signal */
clear_line_ends:1, /* do not scroll, paint each screen from the top */
@@ -298,7 +299,7 @@ static void argscan(struct more_control *ctl, int as_argc, char **as_argv)
}
}
if (move) {
- as_argc = remote_entry(as_argv, opt, as_argc);
+ as_argc = remove_entry(as_argv, opt, as_argc);
opt--;
}
}
@@ -353,12 +354,12 @@ static void env_argscan(struct more_control *ctl, const char *s)
char *str = xstrdup(s);
char *key = NULL, *tok;
- env_argv = xmalloc(sizeof(char *) * size);
+ env_argv = xreallocarray(NULL, size, sizeof(char *));
env_argv[0] = _("MORE environment variable"); /* program name */
for (tok = strtok_r(str, delim, &key); tok; tok = strtok_r(NULL, delim, &key)) {
if (size == env_argc) {
size *= 2;
- env_argv = xrealloc(env_argv, sizeof(char *) * size);
+ env_argv = xreallocarray(env_argv, size, sizeof(char *));
}
env_argv[env_argc++] = tok;
}
@@ -625,9 +626,6 @@ static int get_line(struct more_control *ctl, int *length)
*p++ = 'L';
column += 2;
ctl->is_paused = 1;
- } else if (c == EOF) {
- *length = p - ctl->line_buf;
- return column;
} else {
#ifdef HAVE_WIDECHAR
if (ctl->fold_long_lines && MB_CUR_MAX > 1) {
@@ -1253,8 +1251,7 @@ static void __attribute__((__format__ (__printf__, 3, 4)))
}
va_end(argp);
- args = alloca(sizeof(char *) * (argcount + 1));
- args[argcount] = NULL;
+ args = xcalloc(argcount + 1, sizeof(char *));
va_start(argp, cmd);
arg = va_arg(argp, char *);
@@ -1352,50 +1349,92 @@ static void read_line(struct more_control *ctl)
*p = '\0';
}
+/* returns: 0 timeout or nothing; <0 error, >0 success */
static int more_poll(struct more_control *ctl, int timeout)
{
- struct pollfd pfd[2];
+ enum {
+ POLLFD_SIGNAL = 0,
+ POLLFD_STDIN,
+ POLLFD_STDERR,
+ };
+ struct pollfd pfd[] = {
+ [POLLFD_SIGNAL] = { .fd = ctl->sigfd, .events = POLLIN | POLLERR | POLLHUP },
+ [POLLFD_STDIN] = { .fd = STDIN_FILENO, .events = POLLIN | POLLERR | POLLHUP },
+ [POLLFD_STDERR] = { .fd = STDERR_FILENO, .events = POLLIN | POLLERR | POLLHUP }
+ };
+ int has_data = 0;
- pfd[0].fd = ctl->sigfd;
- pfd[0].events = POLLIN | POLLERR | POLLHUP;
- pfd[1].fd = STDIN_FILENO;
- pfd[1].events = POLLIN;
+ while (!has_data) {
+ int rc;
- if (poll(pfd, 2, timeout) < 0) {
- if (errno == EAGAIN)
- return 1;
- more_error(ctl, _("poll failed"));
- return 1;
- }
- if (pfd[0].revents != 0) {
- struct signalfd_siginfo info;
- ssize_t sz;
-
- sz = read(pfd[0].fd, &info, sizeof(info));
- assert(sz == sizeof(info));
- switch (info.ssi_signo) {
- case SIGINT:
- more_exit(ctl);
- break;
- case SIGQUIT:
- sigquit_handler(ctl);
- break;
- case SIGTSTP:
- sigtstp_handler(ctl);
- break;
- case SIGCONT:
- sigcont_handler(ctl);
- break;
- case SIGWINCH:
- sigwinch_handler(ctl);
- break;
- default:
- abort();
+ if (ctl->ignore_stdin)
+ pfd[POLLFD_STDIN].fd = -1; /* probably closed, ignore */
+
+ rc = poll(pfd, ARRAY_SIZE(pfd), timeout);
+
+ /* error */
+ if (rc < 0) {
+ if (errno == EAGAIN)
+ continue;
+
+ more_error(ctl, _("poll failed"));
+ return rc;
+ }
+
+ /* timeout */
+ if (rc == 0)
+ return 0;
+
+ /* event on signal FD */
+ if (pfd[POLLFD_SIGNAL].revents) {
+ struct signalfd_siginfo info;
+ ssize_t sz;
+
+ sz = read(pfd[POLLFD_SIGNAL].fd, &info, sizeof(info));
+ assert(sz == sizeof(info));
+ switch (info.ssi_signo) {
+ case SIGINT:
+ more_exit(ctl);
+ break;
+ case SIGQUIT:
+ sigquit_handler(ctl);
+ break;
+ case SIGTSTP:
+ sigtstp_handler(ctl);
+ break;
+ case SIGCONT:
+ sigcont_handler(ctl);
+ break;
+ case SIGWINCH:
+ sigwinch_handler(ctl);
+ break;
+ default:
+ abort();
+ }
}
+
+ /* event on stdin */
+ if (pfd[POLLFD_STDIN].revents) {
+ /* Check for POLLERR and POLLHUP in stdin revents */
+ if ((pfd[POLLFD_STDIN].revents & POLLERR) &&
+ (pfd[POLLFD_STDIN].revents & POLLHUP))
+ more_exit(ctl);
+
+ /* poll() return POLLHUP event after pipe close() and POLLNVAL
+ * means that fd is already closed. */
+ if ((pfd[POLLFD_STDIN].revents & POLLHUP) ||
+ (pfd[POLLFD_STDIN].revents & POLLNVAL))
+ ctl->ignore_stdin = 1;
+ else
+ has_data++;
+ }
+
+ /* event on stderr (we reads user commands from stderr!) */
+ if (pfd[POLLFD_STDERR].revents)
+ has_data++;
}
- if (pfd[1].revents == 0)
- return 1;
- return 0;
+
+ return has_data;
}
/* Search for nth occurrence of regular expression contained in buf in
@@ -1463,7 +1502,7 @@ static void search(struct more_control *ctl, char buf[], int n)
}
break;
}
- more_poll(ctl, 1);
+ more_poll(ctl, 0);
}
/* Move ctrl+c signal handling back to more_key_command(). */
signal(SIGINT, SIG_DFL);
@@ -1627,7 +1666,7 @@ static int more_key_command(struct more_control *ctl, char *filename)
ctl->report_errors = 0;
ctl->search_called = 0;
for (;;) {
- if (more_poll(ctl, -1) != 0)
+ if (more_poll(ctl, -1) <= 0)
continue;
cmd = read_command(ctl);
if (cmd.key == more_kc_unknown_command)