diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2026-02-27 11:01:04 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2026-02-27 11:01:04 +0100 |
| commit | ca3f4c6639b0cd638f5f8d08a76a5c231f543800 (patch) | |
| tree | 69dce4f36ce730eaa4a313ded7417a89940d5bc0 /miscutils | |
| parent | f51819afa57b085e55776ca4b9dc29215384e1ec (diff) | |
| download | busybox-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.c | 76 |
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 | */ |
| 471 | static void read_lines(void) | 470 | static 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 | ||
| 1043 | static void open_file_and_read_lines(void) | 1053 | static 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 | } |
