diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2013-11-28 03:14:16 +0100 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2013-11-28 03:14:16 +0100 |
| commit | 259b3c047aea430c4aaecbdb9580a07e67691e8d (patch) | |
| tree | 937c646d6384e0f79c11ef3d6527601ce164911b | |
| parent | 8bd810bd276d20451faafdae88df4af9c2dd96d1 (diff) | |
| download | busybox-w32-259b3c047aea430c4aaecbdb9580a07e67691e8d.tar.gz busybox-w32-259b3c047aea430c4aaecbdb9580a07e67691e8d.tar.bz2 busybox-w32-259b3c047aea430c4aaecbdb9580a07e67691e8d.zip | |
sed: open input files sequentially to avoid EMFILE
Currently, sed pre-opens all files, which may cause EMFILE errors
on systems with low ulimit -n. Change sed to open one file at a time.
function old new delta
get_next_line 177 235 +58
sed_main 682 652 -30
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 1/1 up/down: 58/-30) Total: 28 bytes
Based on the patch by Daniel Borca <dborca@yahoo.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | editors/sed.c | 60 | ||||
| -rw-r--r-- | libbb/fclose_nonstdin.c | 3 |
2 files changed, 36 insertions, 27 deletions
diff --git a/editors/sed.c b/editors/sed.c index 777f38308..87fa00291 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
| @@ -23,7 +23,7 @@ | |||
| 23 | * resulting sed_cmd_t structures are appended to a linked list | 23 | * resulting sed_cmd_t structures are appended to a linked list |
| 24 | * (G.sed_cmd_head/G.sed_cmd_tail). | 24 | * (G.sed_cmd_head/G.sed_cmd_tail). |
| 25 | * | 25 | * |
| 26 | * add_input_file() adds a FILE* to the list of input files. We need to | 26 | * add_input_file() adds a char* to the list of input files. We need to |
| 27 | * know all input sources ahead of time to find the last line for the $ match. | 27 | * know all input sources ahead of time to find the last line for the $ match. |
| 28 | * | 28 | * |
| 29 | * process_files() does actual sedding, reading data lines from each input FILE* | 29 | * process_files() does actual sedding, reading data lines from each input FILE* |
| @@ -135,12 +135,15 @@ static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v"; | |||
| 135 | struct globals { | 135 | struct globals { |
| 136 | /* options */ | 136 | /* options */ |
| 137 | int be_quiet, regex_type; | 137 | int be_quiet, regex_type; |
| 138 | |||
| 138 | FILE *nonstdout; | 139 | FILE *nonstdout; |
| 139 | char *outname, *hold_space; | 140 | char *outname, *hold_space; |
| 141 | smallint exitcode; | ||
| 140 | 142 | ||
| 141 | /* List of input files */ | 143 | /* list of input files */ |
| 142 | int input_file_count, current_input_file; | 144 | int input_file_count, current_input_file; |
| 143 | FILE **input_file_list; | 145 | const char **input_file_list; |
| 146 | FILE *current_fp; | ||
| 144 | 147 | ||
| 145 | regmatch_t regmatch[10]; | 148 | regmatch_t regmatch[10]; |
| 146 | regex_t *previous_regex_ptr; | 149 | regex_t *previous_regex_ptr; |
| @@ -148,7 +151,7 @@ struct globals { | |||
| 148 | /* linked list of sed commands */ | 151 | /* linked list of sed commands */ |
| 149 | sed_cmd_t *sed_cmd_head, **sed_cmd_tail; | 152 | sed_cmd_t *sed_cmd_head, **sed_cmd_tail; |
| 150 | 153 | ||
| 151 | /* Linked list of append lines */ | 154 | /* linked list of append lines */ |
| 152 | llist_t *append_head; | 155 | llist_t *append_head; |
| 153 | 156 | ||
| 154 | char *add_cmd_line; | 157 | char *add_cmd_line; |
| @@ -200,8 +203,8 @@ static void sed_free_and_close_stuff(void) | |||
| 200 | 203 | ||
| 201 | free(G.hold_space); | 204 | free(G.hold_space); |
| 202 | 205 | ||
| 203 | while (G.current_input_file < G.input_file_count) | 206 | if (G.current_fp) |
| 204 | fclose(G.input_file_list[G.current_input_file++]); | 207 | fclose(G.current_fp); |
| 205 | } | 208 | } |
| 206 | #else | 209 | #else |
| 207 | void sed_free_and_close_stuff(void); | 210 | void sed_free_and_close_stuff(void); |
| @@ -939,8 +942,20 @@ static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets | |||
| 939 | /* will be returned if last line in the file | 942 | /* will be returned if last line in the file |
| 940 | * doesn't end with either '\n' or '\0' */ | 943 | * doesn't end with either '\n' or '\0' */ |
| 941 | gc = NO_EOL_CHAR; | 944 | gc = NO_EOL_CHAR; |
| 942 | while (G.current_input_file < G.input_file_count) { | 945 | for (; G.input_file_list[G.current_input_file]; G.current_input_file++) { |
| 943 | FILE *fp = G.input_file_list[G.current_input_file]; | 946 | FILE *fp = G.current_fp; |
| 947 | if (!fp) { | ||
| 948 | const char *path = G.input_file_list[G.current_input_file]; | ||
| 949 | fp = stdin; | ||
| 950 | if (path != bb_msg_standard_input) { | ||
| 951 | fp = fopen_or_warn(path, "r"); | ||
| 952 | if (!fp) { | ||
| 953 | G.exitcode = EXIT_FAILURE; | ||
| 954 | continue; | ||
| 955 | } | ||
| 956 | } | ||
| 957 | G.current_fp = fp; | ||
| 958 | } | ||
| 944 | /* Read line up to a newline or NUL byte, inclusive, | 959 | /* Read line up to a newline or NUL byte, inclusive, |
| 945 | * return malloc'ed char[]. length of the chunk read | 960 | * return malloc'ed char[]. length of the chunk read |
| 946 | * is stored in len. NULL if EOF/error */ | 961 | * is stored in len. NULL if EOF/error */ |
| @@ -971,8 +986,8 @@ static char *get_next_line(char *gets_char, char *last_puts_char, char last_gets | |||
| 971 | * (note: *no* newline after "b bang"!) */ | 986 | * (note: *no* newline after "b bang"!) */ |
| 972 | } | 987 | } |
| 973 | /* Close this file and advance to next one */ | 988 | /* Close this file and advance to next one */ |
| 974 | fclose(fp); | 989 | fclose_if_not_stdin(fp); |
| 975 | G.current_input_file++; | 990 | G.current_fp = NULL; |
| 976 | } | 991 | } |
| 977 | *gets_char = gc; | 992 | *gets_char = gc; |
| 978 | return temp; | 993 | return temp; |
| @@ -1399,7 +1414,7 @@ static void add_cmd_block(char *cmdstr) | |||
| 1399 | free(sv); | 1414 | free(sv); |
| 1400 | } | 1415 | } |
| 1401 | 1416 | ||
| 1402 | static void add_input_file(FILE *file) | 1417 | static void add_input_file(const char *file) |
| 1403 | { | 1418 | { |
| 1404 | G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); | 1419 | G.input_file_list = xrealloc_vector(G.input_file_list, 2, G.input_file_count); |
| 1405 | G.input_file_list[G.input_file_count++] = file; | 1420 | G.input_file_list[G.input_file_count++] = file; |
| @@ -1423,8 +1438,6 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
| 1423 | "file\0" Required_argument "f"; | 1438 | "file\0" Required_argument "f"; |
| 1424 | #endif | 1439 | #endif |
| 1425 | 1440 | ||
| 1426 | int status = EXIT_SUCCESS; | ||
| 1427 | |||
| 1428 | INIT_G(); | 1441 | INIT_G(); |
| 1429 | 1442 | ||
| 1430 | /* destroy command strings on exit */ | 1443 | /* destroy command strings on exit */ |
| @@ -1491,27 +1504,21 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
| 1491 | if (argv[0] == NULL) { | 1504 | if (argv[0] == NULL) { |
| 1492 | if (opt & OPT_in_place) | 1505 | if (opt & OPT_in_place) |
| 1493 | bb_error_msg_and_die(bb_msg_requires_arg, "-i"); | 1506 | bb_error_msg_and_die(bb_msg_requires_arg, "-i"); |
| 1494 | add_input_file(stdin); | 1507 | add_input_file(bb_msg_standard_input); |
| 1495 | } else { | 1508 | } else { |
| 1496 | int i; | 1509 | int i; |
| 1497 | 1510 | ||
| 1498 | for (i = 0; argv[i]; i++) { | 1511 | for (i = 0; argv[i]; i++) { |
| 1499 | struct stat statbuf; | 1512 | struct stat statbuf; |
| 1500 | int nonstdoutfd; | 1513 | int nonstdoutfd; |
| 1501 | FILE *file; | ||
| 1502 | sed_cmd_t *sed_cmd; | 1514 | sed_cmd_t *sed_cmd; |
| 1503 | 1515 | ||
| 1504 | if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { | 1516 | if (LONE_DASH(argv[i]) && !(opt & OPT_in_place)) { |
| 1505 | add_input_file(stdin); | 1517 | add_input_file(bb_msg_standard_input); |
| 1506 | process_files(); | 1518 | process_files(); |
| 1507 | continue; | 1519 | continue; |
| 1508 | } | 1520 | } |
| 1509 | file = fopen_or_warn(argv[i], "r"); | 1521 | add_input_file(argv[i]); |
| 1510 | if (!file) { | ||
| 1511 | status = EXIT_FAILURE; | ||
| 1512 | continue; | ||
| 1513 | } | ||
| 1514 | add_input_file(file); | ||
| 1515 | if (!(opt & OPT_in_place)) { | 1522 | if (!(opt & OPT_in_place)) { |
| 1516 | continue; | 1523 | continue; |
| 1517 | } | 1524 | } |
| @@ -1523,7 +1530,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
| 1523 | G.nonstdout = xfdopen_for_write(nonstdoutfd); | 1530 | G.nonstdout = xfdopen_for_write(nonstdoutfd); |
| 1524 | 1531 | ||
| 1525 | /* Set permissions/owner of output file */ | 1532 | /* Set permissions/owner of output file */ |
| 1526 | fstat(fileno(file), &statbuf); | 1533 | stat(argv[i], &statbuf); |
| 1527 | /* chmod'ing AFTER chown would preserve suid/sgid bits, | 1534 | /* chmod'ing AFTER chown would preserve suid/sgid bits, |
| 1528 | * but GNU sed 4.2.1 does not preserve them either */ | 1535 | * but GNU sed 4.2.1 does not preserve them either */ |
| 1529 | fchmod(nonstdoutfd, statbuf.st_mode); | 1536 | fchmod(nonstdoutfd, statbuf.st_mode); |
| @@ -1549,12 +1556,13 @@ int sed_main(int argc UNUSED_PARAM, char **argv) | |||
| 1549 | } | 1556 | } |
| 1550 | } | 1557 | } |
| 1551 | /* Here, to handle "sed 'cmds' nonexistent_file" case we did: | 1558 | /* Here, to handle "sed 'cmds' nonexistent_file" case we did: |
| 1552 | * if (G.current_input_file >= G.input_file_count) | 1559 | * if (G.current_input_file[G.current_input_file] == NULL) |
| 1553 | * return status; | 1560 | * return G.exitcode; |
| 1554 | * but it's not needed since process_files() works correctly | 1561 | * but it's not needed since process_files() works correctly |
| 1555 | * in this case too. */ | 1562 | * in this case too. */ |
| 1556 | } | 1563 | } |
| 1564 | |||
| 1557 | process_files(); | 1565 | process_files(); |
| 1558 | 1566 | ||
| 1559 | return status; | 1567 | return G.exitcode; |
| 1560 | } | 1568 | } |
diff --git a/libbb/fclose_nonstdin.c b/libbb/fclose_nonstdin.c index 5ce9d5b48..1b1441347 100644 --- a/libbb/fclose_nonstdin.c +++ b/libbb/fclose_nonstdin.c | |||
| @@ -18,7 +18,8 @@ int FAST_FUNC fclose_if_not_stdin(FILE *f) | |||
| 18 | { | 18 | { |
| 19 | /* Some more paranoid applets want ferror() check too */ | 19 | /* Some more paranoid applets want ferror() check too */ |
| 20 | int r = ferror(f); /* NB: does NOT set errno! */ | 20 | int r = ferror(f); /* NB: does NOT set errno! */ |
| 21 | if (r) errno = EIO; /* so we'll help it */ | 21 | if (r) |
| 22 | errno = EIO; /* so we'll help it */ | ||
| 22 | if (f != stdin) | 23 | if (f != stdin) |
| 23 | return (r | fclose(f)); /* fclose does set errno on error */ | 24 | return (r | fclose(f)); /* fclose does set errno on error */ |
| 24 | return r; | 25 | return r; |
