aboutsummaryrefslogtreecommitdiff
path: root/miscutils
diff options
context:
space:
mode:
authorDenys Vlasenko <vda.linux@googlemail.com>2026-02-27 11:01:04 +0100
committerDenys Vlasenko <vda.linux@googlemail.com>2026-02-27 11:01:04 +0100
commitca3f4c6639b0cd638f5f8d08a76a5c231f543800 (patch)
tree69dce4f36ce730eaa4a313ded7417a89940d5bc0 /miscutils
parentf51819afa57b085e55776ca4b9dc29215384e1ec (diff)
downloadbusybox-w32-ca3f4c6639b0cd638f5f8d08a76a5c231f543800.tar.gz
busybox-w32-ca3f4c6639b0cd638f5f8d08a76a5c231f543800.tar.bz2
busybox-w32-ca3f4c6639b0cd638f5f8d08a76a5c231f543800.zip
less: improve O_NONBLOCK manipulations on stdin
"less FILE" case needs it to be set just once. In other cases, manipulate it less often. function old new delta read_lines 702 783 +81 reinitialize 198 199 +1 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 2/0 up/down: 82/0) Total: 82 bytes Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'miscutils')
-rw-r--r--miscutils/less.c76
1 files changed, 44 insertions, 32 deletions
diff --git a/miscutils/less.c b/miscutils/less.c
index 65a8c4bed..12bb31537 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -201,8 +201,8 @@ struct globals {
201#if ENABLE_FEATURE_LESS_WINCH 201#if ENABLE_FEATURE_LESS_WINCH
202 unsigned winch_counter; 202 unsigned winch_counter;
203#endif 203#endif
204 int stdin_GETFL_flags; 204 smallint ndelay_set;
205 ssize_t eof_error; /* eof if 0, error if < 0, > 0 if last read got some chars */ 205 ssize_t eof_error_ok; /* eof if 0, error if < 0, > 0 if last read did not indicate either */
206 size_t readpos; 206 size_t readpos;
207 size_t read_size; 207 size_t read_size;
208 const char **buffer; 208 const char **buffer;
@@ -252,7 +252,6 @@ struct globals {
252#define winch_counter (G.winch_counter ) 252#define winch_counter (G.winch_counter )
253/* This one is 100% not cached by compiler on read access */ 253/* This one is 100% not cached by compiler on read access */
254#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter) 254#define WINCH_COUNTER (*(volatile unsigned *)&winch_counter)
255#define eof_error (G.eof_error )
256#define readpos (G.readpos ) 255#define readpos (G.readpos )
257#define read_size (G.read_size ) 256#define read_size (G.read_size )
258#define buffer (G.buffer ) 257#define buffer (G.buffer )
@@ -282,7 +281,7 @@ struct globals {
282 less_gets_pos = -1; \ 281 less_gets_pos = -1; \
283 empty_line_marker = "~"; \ 282 empty_line_marker = "~"; \
284 current_file = 1; \ 283 current_file = 1; \
285 eof_error = 1; \ 284 G.eof_error_ok = 1; \
286 terminated = 1; \ 285 terminated = 1; \
287 IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \ 286 IF_FEATURE_LESS_REGEXP(wanted_match = -1;) \
288} while (0) 287} while (0)
@@ -463,13 +462,14 @@ static int at_end(void)
463 * readbuf[readpos] - next character to add to current line. 462 * readbuf[readpos] - next character to add to current line.
464 * last_line_pos - screen line position of next char to be read 463 * last_line_pos - screen line position of next char to be read
465 * (takes into account tabs and backspaces) 464 * (takes into account tabs and backspaces)
466 * eof_error - < 0 error, == 0 EOF, > 0 not EOF/error 465 * G.eof_error_ok - < 0 error, == 0 EOF, > 0 not EOF/error
467 * 466 *
468 * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs, 467 * "git log -p | less -m" on the kernel git tree is a good test for EAGAINs,
469 * "/search on very long input" and "reaching max line count" corner cases. 468 * "/search on very long input" and "reaching max line count" corner cases.
470 */ 469 */
471static void read_lines(void) 470static void read_lines(void)
472{ 471{
472 int ndelay_set, fdflags;
473 char *current_line, *p; 473 char *current_line, *p;
474 int w = width; 474 int w = width;
475 char last_terminated = terminated; 475 char last_terminated = terminated;
@@ -498,6 +498,12 @@ static void read_lines(void)
498 last_line_pos = 0; 498 last_line_pos = 0;
499 } 499 }
500 500
501 // Consider these cases:
502 // "less FILE": can set O_NONBLOCK on open.
503 // "true | less": can't.
504 // " { less; cat; } <FILE": can't. And must not confuse cat.
505 ndelay_set = -1; // "don't know whetherstdin is nonblocking"
506
501 while (1) { /* read lines until we reach cur_fline or wanted_match */ 507 while (1) { /* read lines until we reach cur_fline or wanted_match */
502 *p = '\0'; 508 *p = '\0';
503 terminated = 0; 509 terminated = 0;
@@ -505,22 +511,29 @@ static void read_lines(void)
505 char c; 511 char c;
506 /* if no unprocessed chars left, eat more */ 512 /* if no unprocessed chars left, eat more */
507 if (readpos >= read_size) { 513 if (readpos >= read_size) {
508 // Read stdin, temporarily making it nonblocking (if it's not already). 514 // Read stdin, temporarily make it nonblocking (if it's not already)
509 // NB: we do NOT check eof_error status before reading. 515 if (ndelay_set < 0) {
516 ndelay_set = G.ndelay_set; // can be only 0 or 1
517 if (ndelay_set == 0) {
518 fdflags = ndelay_on(STDIN_FILENO);
519 if (fdflags & O_NONBLOCK)
520 ndelay_set = 2; // it _was_ nonblocking, do NOT restore later
521 } //else: G.ndelay_set=1: set nonblocking at open(FILE) time
522 } //else: we were here already, stdin is already nonblocking
523
524 // NB: we do NOT check last eof_error_ok before reading.
510 // This has the effect that e.g. PageDown on a regular file 525 // This has the effect that e.g. PageDown on a regular file
511 // where we already reached EOF *will try reading anyway*, 526 // where we already reached EOF *will try reading anyway*,
512 // if the file is a growing log file, less *will* show the new data. 527 // if the file is a growing log file, less *will* show the new data.
513 int flags = G.stdin_GETFL_flags; 528 G.eof_error_ok = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
514 if (!(flags & O_NONBLOCK))
515 fcntl(STDIN_FILENO, F_SETFL, flags|O_NONBLOCK);
516 eof_error = safe_read(STDIN_FILENO, readbuf, COMMON_BUFSIZE);
517 if (!(flags & O_NONBLOCK))
518 fcntl(STDIN_FILENO, F_SETFL, flags);
519
520 readpos = 0; 529 readpos = 0;
521 read_size = (eof_error < 0 ? 0 : eof_error); 530 read_size = G.eof_error_ok;
522 if (eof_error <= 0) 531 if (G.eof_error_ok <= 0) {
532 read_size = 0; // -1 would be seen as UNIT_MAX, prevent
533 if (G.eof_error_ok < 0 && errno == EAGAIN)
534 G.eof_error_ok = 1; // "neither EOF nor error"
523 goto reached_eof; 535 goto reached_eof;
536 }
524 } 537 }
525 c = readbuf[readpos]; 538 c = readbuf[readpos];
526 /* backspace? [needed for manpages] */ 539 /* backspace? [needed for manpages] */
@@ -598,7 +611,7 @@ static void read_lines(void)
598 max_lineno++; 611 max_lineno++;
599 612
600 if (max_fline >= MAXLINES) { 613 if (max_fline >= MAXLINES) {
601 eof_error = 0; /* Pretend we saw EOF */ 614 G.eof_error_ok = 0; /* Pretend we saw EOF */
602 break; 615 break;
603 } 616 }
604 if (!at_end()) { 617 if (!at_end()) {
@@ -613,24 +626,21 @@ static void read_lines(void)
613 break; 626 break;
614#endif 627#endif
615 } 628 }
616 if (eof_error <= 0) { 629 if (G.eof_error_ok <= 0) /* EOF or error? */
617 break; 630 break;
618 }
619 max_fline++; 631 max_fline++;
620 current_line = ((char*)xmalloc(w + 5)) + 4; 632 current_line = ((char*)xmalloc(w + 5)) + 4;
621 p = current_line; 633 p = current_line;
622 last_line_pos = 0; 634 last_line_pos = 0;
623 } /* end of "read lines until we reach cur_fline" loop */ 635 } /* end of "read lines until we reach cur_fline" loop */
624 636
625 if (eof_error < 0) { 637 if (ndelay_set == 0) // stdin was not nonblocking, restore that
626 if (errno == EAGAIN) { 638 fcntl(STDIN_FILENO, F_SETFL, fdflags);
627 eof_error = 1; /* "neither EOF nor error" */ 639
628 } else { 640 if (G.eof_error_ok < 0) // error?
629 print_statusline(bb_msg_read_error); 641 print_statusline(bb_msg_read_error);
630 }
631 }
632#if ENABLE_FEATURE_LESS_FLAGS 642#if ENABLE_FEATURE_LESS_FLAGS
633 else if (eof_error == 0) 643 else if (G.eof_error_ok == 0) // EOF?
634 num_lines = max_lineno; 644 num_lines = max_lineno;
635#endif 645#endif
636 646
@@ -904,7 +914,7 @@ static void buffer_print(void)
904 print_ascii(buffer[i]); 914 print_ascii(buffer[i]);
905 } 915 }
906 if ((option_mask32 & (FLAG_E|FLAG_F)) 916 if ((option_mask32 & (FLAG_E|FLAG_F))
907 && eof_error <= 0 917 && G.eof_error_ok <= 0
908 ) { 918 ) {
909 i = (option_mask32 & FLAG_F) ? 0 : cur_fline; 919 i = (option_mask32 & FLAG_F) ? 0 : cur_fline;
910 if (max_fline - i <= max_displayed_line) 920 if (max_fline - i <= max_displayed_line)
@@ -959,7 +969,7 @@ static void goto_lineno(int target)
959 while (LINENO(flines[cur_fline]) != target && cur_fline < max_fline) 969 while (LINENO(flines[cur_fline]) != target && cur_fline < max_fline)
960 ++cur_fline; 970 ++cur_fline;
961 /* target not reached but more input is available */ 971 /* target not reached but more input is available */
962 if (LINENO(flines[cur_fline]) != target && eof_error > 0) { 972 if (LINENO(flines[cur_fline]) != target && G.eof_error_ok > 0) {
963 read_lines(); 973 read_lines();
964 goto retry; 974 goto retry;
965 } 975 }
@@ -1042,8 +1052,11 @@ static void buffer_lineno(int lineno)
1042 1052
1043static void open_file_and_read_lines(void) 1053static void open_file_and_read_lines(void)
1044{ 1054{
1055 G.ndelay_set = 0;
1045 if (filename) { 1056 if (filename) {
1046 xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO); 1057 xmove_fd(xopen(filename, O_RDONLY), STDIN_FILENO);
1058 ndelay_on(STDIN_FILENO);
1059 G.ndelay_set = 1;
1047#if ENABLE_FEATURE_LESS_FLAGS 1060#if ENABLE_FEATURE_LESS_FLAGS
1048 num_lines = REOPEN_AND_COUNT; 1061 num_lines = REOPEN_AND_COUNT;
1049#endif 1062#endif
@@ -1055,7 +1068,6 @@ static void open_file_and_read_lines(void)
1055 num_lines = REOPEN_STDIN; 1068 num_lines = REOPEN_STDIN;
1056#endif 1069#endif
1057 } 1070 }
1058 G.stdin_GETFL_flags = fcntl(STDIN_FILENO, F_GETFL);
1059 readpos = 0; 1071 readpos = 0;
1060 read_size = 0; 1072 read_size = 0;
1061 last_line_pos = 0; 1073 last_line_pos = 0;
@@ -1111,7 +1123,7 @@ static int64_t getch_nowait(void)
1111 dont_poll_stdin = 1; 1123 dont_poll_stdin = 1;
1112 /* Are we interested in stdin? */ 1124 /* Are we interested in stdin? */
1113 if (at_end()) { 1125 if (at_end()) {
1114 if (eof_error > 0) /* did NOT reach eof yet */ 1126 if (G.eof_error_ok > 0) /* did NOT reach EOF/error yet */
1115 dont_poll_stdin = 0; /* yes, we are interested in stdin */ 1127 dont_poll_stdin = 0; /* yes, we are interested in stdin */
1116 } 1128 }
1117 /* Position cursor if line input is done */ 1129 /* Position cursor if line input is done */
@@ -1325,7 +1337,7 @@ static void goto_match(int match)
1325 if (match < 0) 1337 if (match < 0)
1326 match = 0; 1338 match = 0;
1327 /* Try to find next match if eof isn't reached yet */ 1339 /* Try to find next match if eof isn't reached yet */
1328 if (match >= num_matches && eof_error > 0) { 1340 if (match >= num_matches && G.eof_error_ok > 0) {
1329 wanted_match = match; /* "I want to read until I see N'th match" */ 1341 wanted_match = match; /* "I want to read until I see N'th match" */
1330 read_lines(); 1342 read_lines();
1331 } 1343 }