diff options
Diffstat (limited to 'text-utils/more.c')
-rw-r--r-- | text-utils/more.c | 135 |
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) |