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; |