diff options
author | Denys Vlasenko <vda.linux@googlemail.com> | 2010-06-14 00:57:05 +0200 |
---|---|---|
committer | Denys Vlasenko <vda.linux@googlemail.com> | 2010-06-14 00:57:05 +0200 |
commit | 7a4021debaa1f89c0ee2ad41f446a8234f8261c7 (patch) | |
tree | 3075619c214be7cda3130e9df32465845ce93665 /findutils/xargs.c | |
parent | aaa24e09f98f2694f25abb08cee1a74d7f114b7f (diff) | |
download | busybox-w32-7a4021debaa1f89c0ee2ad41f446a8234f8261c7.tar.gz busybox-w32-7a4021debaa1f89c0ee2ad41f446a8234f8261c7.tar.bz2 busybox-w32-7a4021debaa1f89c0ee2ad41f446a8234f8261c7.zip |
xargs: simplify logic
Removed double-buffering in a linked list.
function old new delta
store_param - 56 +56
xargs_main 871 840 -31
process_stdin 458 396 -62
process0_stdin 267 143 -124
------------------------------------------------------------------------------
(add/remove: 1/0 grow/shrink: 0/3 up/down: 56/-217) Total: -161 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
Diffstat (limited to 'findutils/xargs.c')
-rw-r--r-- | findutils/xargs.c | 383 |
1 files changed, 184 insertions, 199 deletions
diff --git a/findutils/xargs.c b/findutils/xargs.c index 9988e3dd5..857773df9 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c | |||
@@ -59,10 +59,6 @@ | |||
59 | //config: are not special. | 59 | //config: are not special. |
60 | 60 | ||
61 | #include "libbb.h" | 61 | #include "libbb.h" |
62 | |||
63 | /* This is a NOEXEC applet. Be very careful! */ | ||
64 | |||
65 | |||
66 | /* COMPAT: SYSV version defaults size (and has a max value of) to 470. | 62 | /* COMPAT: SYSV version defaults size (and has a max value of) to 470. |
67 | We try to make it as large as possible. */ | 63 | We try to make it as large as possible. */ |
68 | #if !defined(ARG_MAX) && defined(_SC_ARG_MAX) | 64 | #if !defined(ARG_MAX) && defined(_SC_ARG_MAX) |
@@ -72,6 +68,12 @@ | |||
72 | # define ARG_MAX 470 | 68 | # define ARG_MAX 470 |
73 | #endif | 69 | #endif |
74 | 70 | ||
71 | /* This is a NOEXEC applet. Be very careful! */ | ||
72 | |||
73 | |||
74 | //#define dbg_msg(...) bb_error_msg(__VA_ARGS__) | ||
75 | #define dbg_msg(...) ((void)0) | ||
76 | |||
75 | 77 | ||
76 | #ifdef TEST | 78 | #ifdef TEST |
77 | # ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION | 79 | # ifndef ENABLE_FEATURE_XARGS_SUPPORT_CONFIRMATION |
@@ -88,26 +90,36 @@ | |||
88 | # endif | 90 | # endif |
89 | #endif | 91 | #endif |
90 | 92 | ||
93 | |||
94 | struct globals { | ||
95 | char **args; | ||
96 | const char *eof_str; | ||
97 | int idx; | ||
98 | } FIX_ALIASING; | ||
99 | #define G (*(struct globals*)&bb_common_bufsiz1) | ||
100 | #define INIT_G() do { } while (0) | ||
101 | |||
102 | |||
91 | /* | 103 | /* |
92 | * This function has special algorithm. | 104 | * This function has special algorithm. |
93 | * Don't use fork and include to main! | 105 | * Don't use fork and include to main! |
94 | */ | 106 | */ |
95 | static int xargs_exec(char **args) | 107 | static int xargs_exec(void) |
96 | { | 108 | { |
97 | int status; | 109 | int status; |
98 | 110 | ||
99 | status = spawn_and_wait(args); | 111 | status = spawn_and_wait(G.args); |
100 | if (status < 0) { | 112 | if (status < 0) { |
101 | bb_simple_perror_msg(args[0]); | 113 | bb_simple_perror_msg(G.args[0]); |
102 | return errno == ENOENT ? 127 : 126; | 114 | return errno == ENOENT ? 127 : 126; |
103 | } | 115 | } |
104 | if (status == 255) { | 116 | if (status == 255) { |
105 | bb_error_msg("%s: exited with status 255; aborting", args[0]); | 117 | bb_error_msg("%s: exited with status 255; aborting", G.args[0]); |
106 | return 124; | 118 | return 124; |
107 | } | 119 | } |
108 | if (status >= 0x180) { | 120 | if (status >= 0x180) { |
109 | bb_error_msg("%s: terminated by signal %d", | 121 | bb_error_msg("%s: terminated by signal %d", |
110 | args[0], status - 0x180); | 122 | G.args[0], status - 0x180); |
111 | return 125; | 123 | return 125; |
112 | } | 124 | } |
113 | if (status) | 125 | if (status) |
@@ -115,51 +127,59 @@ static int xargs_exec(char **args) | |||
115 | return 0; | 127 | return 0; |
116 | } | 128 | } |
117 | 129 | ||
118 | |||
119 | typedef struct xlist_t { | ||
120 | struct xlist_t *link; | ||
121 | size_t length; /* length of xstr[] including NUL */ | ||
122 | char xstr[1]; | ||
123 | } xlist_t; | ||
124 | |||
125 | /* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. | 130 | /* In POSIX/C locale isspace is only these chars: "\t\n\v\f\r" and space. |
126 | * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. | 131 | * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. |
127 | */ | 132 | */ |
128 | #define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); }) | 133 | #define ISSPACE(a) ({ unsigned char xargs__isspace = (a) - 9; xargs__isspace == (' ' - 9) || xargs__isspace <= (13 - 9); }) |
129 | 134 | ||
135 | static void store_param(char *s) | ||
136 | { | ||
137 | /* Grow by 256 elements at once */ | ||
138 | if (!(G.idx & 0xff)) { /* G.idx == N*256 */ | ||
139 | /* Enlarge, make G.args[(N+1)*256 - 1] last valid idx */ | ||
140 | G.args = xrealloc(G.args, sizeof(G.args[0]) * (G.idx + 0x100)); | ||
141 | } | ||
142 | G.args[G.idx++] = s; | ||
143 | } | ||
144 | |||
145 | /* process[0]_stdin: | ||
146 | * Read characters into buf[n_max_chars+1], and when parameter delimiter | ||
147 | * is seen, store the address of a new parameter to args[]. | ||
148 | * If reading discovers that last chars do not form the complete | ||
149 | * parameter, the pointer to the first such "tail character" is returned. | ||
150 | * (buf has extra byte at the end to accomodate terminating NUL | ||
151 | * of "tail characters" string). | ||
152 | * Otherwise, the returned pointer points to NUL byte. | ||
153 | * The args[] vector is NULL-terminated. | ||
154 | * On entry, buf[] may contain some "seed chars" which are to become | ||
155 | * the beginning of the first parameter. | ||
156 | */ | ||
157 | |||
130 | #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES | 158 | #if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES |
131 | static xlist_t* process_stdin(xlist_t *list_arg, | 159 | static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) |
132 | const char *eof_str, size_t n_max_chars, char *buf) | ||
133 | { | 160 | { |
134 | #define NORM 0 | 161 | #define NORM 0 |
135 | #define QUOTE 1 | 162 | #define QUOTE 1 |
136 | #define BACKSLASH 2 | 163 | #define BACKSLASH 2 |
137 | #define SPACE 4 | 164 | #define SPACE 4 |
138 | char *s = NULL; /* start of the word */ | 165 | char *s; /* start of the word */ |
139 | char *p = NULL; /* pointer to end of the word */ | 166 | char *p; /* pointer to end of the word */ |
140 | char q = '\0'; /* quote char */ | 167 | char q = '\0'; /* quote char */ |
141 | char state = NORM; | 168 | char state = NORM; |
142 | char eof_str_detected = 0; | 169 | |
143 | size_t line_l = 0; /* size of loaded args */ | 170 | s = buf; |
144 | xlist_t *cur; | 171 | p = s + strlen(buf); |
145 | xlist_t *prev; | 172 | |
146 | 173 | /* "goto ret" is used instead of "break" to make control flow | |
147 | prev = cur = list_arg; | 174 | * more obvious: */ |
148 | while (cur) { | ||
149 | prev = cur; | ||
150 | line_l += cur->length; | ||
151 | cur = cur->link; | ||
152 | } | ||
153 | 175 | ||
154 | while (1) { | 176 | while (1) { |
155 | int c = getchar(); | 177 | int c = getchar(); |
156 | if (c == EOF) { | 178 | if (c == EOF) { |
157 | if (s) | 179 | if (p != s) |
158 | goto unexpected_eof; | 180 | goto close_word; |
159 | break; | 181 | goto ret; |
160 | } | 182 | } |
161 | if (eof_str_detected) /* skip till EOF */ | ||
162 | continue; | ||
163 | if (state == BACKSLASH) { | 183 | if (state == BACKSLASH) { |
164 | state = NORM; | 184 | state = NORM; |
165 | goto set; | 185 | goto set; |
@@ -171,15 +191,13 @@ static xlist_t* process_stdin(xlist_t *list_arg, | |||
171 | state = NORM; | 191 | state = NORM; |
172 | } else { /* if (state == NORM) */ | 192 | } else { /* if (state == NORM) */ |
173 | if (ISSPACE(c)) { | 193 | if (ISSPACE(c)) { |
174 | if (s) { | 194 | if (p != s) { |
175 | unexpected_eof: | 195 | close_word: |
176 | state = SPACE; | 196 | state = SPACE; |
177 | c = '\0'; | 197 | c = '\0'; |
178 | goto set; | 198 | goto set; |
179 | } | 199 | } |
180 | } else { | 200 | } else { |
181 | if (s == NULL) | ||
182 | s = p = buf; | ||
183 | if (c == '\\') { | 201 | if (c == '\\') { |
184 | state = BACKSLASH; | 202 | state = BACKSLASH; |
185 | } else if (c == '\'' || c == '"') { | 203 | } else if (c == '\'' || c == '"') { |
@@ -187,8 +205,6 @@ static xlist_t* process_stdin(xlist_t *list_arg, | |||
187 | state = QUOTE; | 205 | state = QUOTE; |
188 | } else { | 206 | } else { |
189 | set: | 207 | set: |
190 | if ((size_t)(p - buf) >= n_max_chars) | ||
191 | bb_error_msg_and_die("argument line too long"); | ||
192 | *p++ = c; | 208 | *p++ = c; |
193 | } | 209 | } |
194 | } | 210 | } |
@@ -199,149 +215,128 @@ static xlist_t* process_stdin(xlist_t *list_arg, | |||
199 | q == '\'' ? "single" : "double"); | 215 | q == '\'' ? "single" : "double"); |
200 | } | 216 | } |
201 | /* A full word is loaded */ | 217 | /* A full word is loaded */ |
202 | if (eof_str) { | 218 | if (G.eof_str) { |
203 | eof_str_detected = (strcmp(s, eof_str) == 0); | 219 | if (strcmp(s, G.eof_str) == 0) { |
204 | } | 220 | while (getchar() != EOF) |
205 | if (!eof_str_detected) { | 221 | continue; |
206 | size_t length = (p - buf); | 222 | p = s; |
207 | /* Dont xzalloc - it can be quite big */ | 223 | goto ret; |
208 | cur = xmalloc(offsetof(xlist_t, xstr) + length); | ||
209 | cur->link = NULL; | ||
210 | cur->length = length; | ||
211 | memcpy(cur->xstr, s, length); | ||
212 | if (prev == NULL) { | ||
213 | list_arg = cur; | ||
214 | } else { | ||
215 | prev->link = cur; | ||
216 | } | 224 | } |
217 | prev = cur; | ||
218 | line_l += length; | ||
219 | if (line_l >= n_max_chars) /* limit memory usage */ | ||
220 | break; | ||
221 | } | 225 | } |
222 | s = NULL; | 226 | n_max_chars -= (p - s); |
227 | /* if (n_max_chars < 0) impossible */ | ||
228 | store_param(s); | ||
229 | dbg_msg("args[]:'%s'", s); | ||
230 | s = p; | ||
231 | n_max_arg--; | ||
232 | if (n_max_arg == 0 || n_max_chars == 0) { | ||
233 | goto ret; | ||
234 | } | ||
223 | state = NORM; | 235 | state = NORM; |
236 | } else /* state != SPACE */ | ||
237 | if (p - s >= n_max_chars) { | ||
238 | dbg_msg("s:'%s' p-s:%d n_max_chars:%d", s, (int)(p-s), n_max_chars); | ||
239 | goto ret; | ||
224 | } | 240 | } |
225 | } | 241 | } |
226 | return list_arg; | 242 | ret: |
243 | *p = '\0'; | ||
244 | store_param(NULL); | ||
245 | dbg_msg("return:'%s'", s); | ||
246 | return s; | ||
227 | } | 247 | } |
228 | #else | 248 | #else |
229 | /* The variant does not support single quotes, double quotes or backslash */ | 249 | /* The variant does not support single quotes, double quotes or backslash */ |
230 | static xlist_t* process_stdin(xlist_t *list_arg, | 250 | static char* FAST_FUNC process_stdin(int n_max_chars, int n_max_arg, char *buf) |
231 | const char *eof_str, size_t n_max_chars, char *buf) | ||
232 | { | 251 | { |
233 | char eof_str_detected = 0; | 252 | char *s; /* start of the word */ |
234 | char *s = NULL; /* start of the word */ | 253 | char *p; /* pointer to end of the word */ |
235 | char *p = NULL; /* pointer to end of the word */ | 254 | |
236 | size_t line_l = 0; /* size of loaded args */ | 255 | s = buf; |
237 | xlist_t *cur; | 256 | p = s + strlen(buf); |
238 | xlist_t *prev; | ||
239 | |||
240 | prev = cur = list_arg; | ||
241 | while (cur) { | ||
242 | prev = cur; | ||
243 | line_l += cur->length; | ||
244 | cur = cur->link; | ||
245 | } | ||
246 | 257 | ||
247 | while (1) { | 258 | while (1) { |
248 | int c = getchar(); | 259 | int c = getchar(); |
249 | if (c == EOF) { | 260 | if (c == EOF) { |
250 | if (s == NULL) | 261 | if (p == s) |
251 | break; | 262 | goto ret; |
252 | } | ||
253 | if (eof_str_detected) { /* skip till EOF */ | ||
254 | continue; | ||
255 | } | 263 | } |
256 | if (c == EOF || ISSPACE(c)) { | 264 | if (c == EOF || ISSPACE(c)) { |
257 | if (s == NULL) | 265 | if (p == s) |
258 | continue; | 266 | continue; |
259 | c = EOF; | 267 | c = EOF; |
260 | } | 268 | } |
261 | if (s == NULL) | ||
262 | s = p = buf; | ||
263 | if ((size_t)(p - buf) >= n_max_chars) | ||
264 | bb_error_msg_and_die("argument line too long"); | ||
265 | *p++ = (c == EOF ? '\0' : c); | 269 | *p++ = (c == EOF ? '\0' : c); |
266 | if (c == EOF) { /* word's delimiter or EOF detected */ | 270 | if (c == EOF) { /* word's delimiter or EOF detected */ |
267 | /* A full word is loaded */ | 271 | /* A full word is loaded */ |
268 | if (eof_str) { | 272 | if (G.eof_str) { |
269 | eof_str_detected = (strcmp(s, eof_str) == 0); | 273 | if (strcmp(s, G.eof_str) == 0) { |
270 | } | 274 | while (getchar() != EOF) |
271 | if (!eof_str_detected) { | 275 | continue; |
272 | size_t length = (p - buf); | 276 | p = s; |
273 | /* Dont xzalloc - it can be quite big */ | 277 | goto ret; |
274 | cur = xmalloc(offsetof(xlist_t, xstr) + length); | ||
275 | cur->link = NULL; | ||
276 | cur->length = length; | ||
277 | memcpy(cur->xstr, s, length); | ||
278 | if (prev == NULL) { | ||
279 | list_arg = cur; | ||
280 | } else { | ||
281 | prev->link = cur; | ||
282 | } | 278 | } |
283 | prev = cur; | ||
284 | line_l += length; | ||
285 | if (line_l >= n_max_chars) /* limit memory usage */ | ||
286 | break; | ||
287 | } | 279 | } |
288 | s = NULL; | 280 | n_max_chars -= (p - s); |
281 | /* if (n_max_chars < 0) impossible */ | ||
282 | store_param(s); | ||
283 | dbg_msg("args[]:'%s'", s); | ||
284 | s = p; | ||
285 | n_max_arg--; | ||
286 | if (n_max_arg == 0 || n_max_chars == 0) { | ||
287 | goto ret; | ||
288 | } | ||
289 | } else /* c != EOF */ | ||
290 | if (p - s >= n_max_chars) { | ||
291 | goto ret; | ||
289 | } | 292 | } |
290 | } | 293 | } |
291 | return list_arg; | 294 | ret: |
295 | *p = '\0'; | ||
296 | store_param(NULL); | ||
297 | dbg_msg("return:'%s'", s); | ||
298 | return s; | ||
292 | } | 299 | } |
293 | #endif /* FEATURE_XARGS_SUPPORT_QUOTES */ | 300 | #endif /* FEATURE_XARGS_SUPPORT_QUOTES */ |
294 | 301 | ||
295 | #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM | 302 | #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM |
296 | static xlist_t* process0_stdin(xlist_t *list_arg, | 303 | static char* FAST_FUNC process0_stdin(int n_max_chars, int n_max_arg, char *buf) |
297 | const char *eof_str UNUSED_PARAM, size_t n_max_chars, char *buf) | ||
298 | { | 304 | { |
299 | char *s = NULL; /* start of the word */ | 305 | char *s; /* start of the word */ |
300 | char *p = NULL; /* pointer to end of the word */ | 306 | char *p; /* pointer to end of the word */ |
301 | size_t line_l = 0; /* size of loaded args */ | 307 | |
302 | xlist_t *cur; | 308 | s = buf; |
303 | xlist_t *prev; | 309 | p = s + strlen(buf); |
304 | |||
305 | prev = cur = list_arg; | ||
306 | while (cur) { | ||
307 | prev = cur; | ||
308 | line_l += cur->length; | ||
309 | cur = cur->link; | ||
310 | } | ||
311 | 310 | ||
312 | while (1) { | 311 | while (1) { |
313 | int c = getchar(); | 312 | int c = getchar(); |
314 | if (c == EOF) { | 313 | if (c == EOF) { |
315 | if (s == NULL) | 314 | if (p == s) |
316 | break; | 315 | goto ret; |
317 | c = '\0'; | 316 | c = '\0'; |
318 | } | 317 | } |
319 | if (s == NULL) | ||
320 | s = p = buf; | ||
321 | if ((size_t)(p - buf) >= n_max_chars) | ||
322 | bb_error_msg_and_die("argument line too long"); | ||
323 | *p++ = c; | 318 | *p++ = c; |
324 | if (c == '\0') { /* word's delimiter or EOF detected */ | 319 | if (c == '\0') { /* word's delimiter or EOF detected */ |
325 | /* A full word is loaded */ | 320 | /* A full word is loaded */ |
326 | size_t length = (p - buf); | 321 | n_max_chars -= (p - s); |
327 | /* Dont xzalloc - it can be quite big */ | 322 | /* if (n_max_chars < 0) impossible */ |
328 | cur = xmalloc(offsetof(xlist_t, xstr) + length); | 323 | store_param(s); |
329 | cur->link = NULL; | 324 | dbg_msg("args[]:'%s'", s); |
330 | cur->length = length; | 325 | n_max_arg--; |
331 | memcpy(cur->xstr, s, length); | 326 | s = p; |
332 | if (prev == NULL) { | 327 | if (n_max_arg == 0 || n_max_chars == 0) { |
333 | list_arg = cur; | 328 | goto ret; |
334 | } else { | ||
335 | prev->link = cur; | ||
336 | } | 329 | } |
337 | prev = cur; | 330 | } else /* c != '\0' */ |
338 | line_l += length; | 331 | if (p - s >= n_max_chars) { |
339 | if (line_l >= n_max_chars) /* limit memory usage */ | 332 | goto ret; |
340 | break; | ||
341 | s = NULL; | ||
342 | } | 333 | } |
343 | } | 334 | } |
344 | return list_arg; | 335 | ret: |
336 | *p = '\0'; | ||
337 | store_param(NULL); | ||
338 | dbg_msg("return:'%s'", s); | ||
339 | return s; | ||
345 | } | 340 | } |
346 | #endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ | 341 | #endif /* FEATURE_XARGS_SUPPORT_ZERO_TERM */ |
347 | 342 | ||
@@ -397,28 +392,30 @@ enum { | |||
397 | int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 392 | int xargs_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
398 | int xargs_main(int argc, char **argv) | 393 | int xargs_main(int argc, char **argv) |
399 | { | 394 | { |
400 | xlist_t *list = NULL; | 395 | int i; |
401 | int child_error = 0; | 396 | int child_error = 0; |
402 | char *max_args; | 397 | char *max_args; |
403 | char *max_chars; | 398 | char *max_chars; |
404 | char *buf; | 399 | char *buf; |
405 | int n_max_arg; | ||
406 | const char *eof_str = NULL; | ||
407 | unsigned opt; | 400 | unsigned opt; |
408 | size_t n_max_chars; | 401 | int n_max_chars; |
402 | int n_max_arg; | ||
409 | #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM | 403 | #if ENABLE_FEATURE_XARGS_SUPPORT_ZERO_TERM |
410 | xlist_t* (*read_args)(xlist_t*, const char*, size_t, char*) = process_stdin; | 404 | char* FAST_FUNC (*read_args)(int, int, char*) = process_stdin; |
411 | #else | 405 | #else |
412 | #define read_args process_stdin | 406 | #define read_args process_stdin |
413 | #endif | 407 | #endif |
414 | 408 | ||
415 | opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &eof_str, &eof_str); | 409 | INIT_G(); |
410 | |||
411 | G.eof_str = NULL; | ||
412 | opt = getopt32(argv, OPTION_STR, &max_args, &max_chars, &G.eof_str, &G.eof_str); | ||
416 | 413 | ||
417 | /* -E ""? You may wonder why not just omit -E? | 414 | /* -E ""? You may wonder why not just omit -E? |
418 | * This is used for portability: | 415 | * This is used for portability: |
419 | * old xargs was using "_" as default for -E / -e */ | 416 | * old xargs was using "_" as default for -E / -e */ |
420 | if ((opt & OPT_EOF_STRING1) && eof_str[0] == '\0') | 417 | if ((opt & OPT_EOF_STRING1) && G.eof_str[0] == '\0') |
421 | eof_str = NULL; | 418 | G.eof_str = NULL; |
422 | 419 | ||
423 | if (opt & OPT_ZEROTERM) | 420 | if (opt & OPT_ZEROTERM) |
424 | IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); | 421 | IF_FEATURE_XARGS_SUPPORT_ZERO_TERM(read_args = process0_stdin); |
@@ -451,93 +448,81 @@ int xargs_main(int argc, char **argv) | |||
451 | n_max_chars = 20 * 1024; | 448 | n_max_chars = 20 * 1024; |
452 | 449 | ||
453 | if (opt & OPT_UPTO_SIZE) { | 450 | if (opt & OPT_UPTO_SIZE) { |
454 | int i; | ||
455 | size_t n_chars = 0; | 451 | size_t n_chars = 0; |
456 | n_max_chars = xatoul_range(max_chars, 1, INT_MAX); | 452 | n_max_chars = xatou_range(max_chars, 1, INT_MAX); |
457 | for (i = 0; argv[i]; i++) { | 453 | for (i = 0; argv[i]; i++) { |
458 | n_chars += strlen(argv[i]) + 1; | 454 | n_chars += strlen(argv[i]) + 1; |
459 | } | 455 | } |
460 | n_max_chars -= n_chars; | 456 | n_max_chars -= n_chars; |
461 | if ((ssize_t)n_max_chars <= 0) { | 457 | if (n_max_chars <= 0) { |
462 | bb_error_msg_and_die("can't fit single argument within argument list size limit"); | 458 | bb_error_msg_and_die("can't fit single argument within argument list size limit"); |
463 | } | 459 | } |
464 | } | 460 | } |
465 | 461 | ||
466 | buf = xmalloc(n_max_chars); | 462 | buf = xzalloc(n_max_chars + 1); |
467 | 463 | ||
468 | if (opt & OPT_UPTO_NUMBER) { | 464 | if (opt & OPT_UPTO_NUMBER) { |
469 | n_max_arg = xatoul_range(max_args, 1, INT_MAX); | 465 | n_max_arg = xatou_range(max_args, 1, INT_MAX); |
470 | if (n_max_arg < n_max_chars) | 466 | if (n_max_arg < n_max_chars) |
471 | goto skip; | 467 | goto skip; |
472 | } | 468 | } |
473 | n_max_arg = n_max_chars; | 469 | n_max_arg = n_max_chars; |
474 | skip: | 470 | skip: |
475 | 471 | ||
476 | while ((list = read_args(list, eof_str, n_max_chars, buf)) != NULL | 472 | /* Allocate pointers for execvp */ |
477 | || !(opt & OPT_NO_EMPTY) | 473 | /* We can statically allocate (argc + n_max_arg + 1) elements |
478 | ) { | 474 | * and do not bother with resizing args[], but on 64-bit machines |
479 | char **args; | 475 | * this results in args[] vector which is ~8 times bigger |
480 | xlist_t *cur; | 476 | * than n_max_chars! That is, with n_max_chars == 20k, |
481 | int i, n; | 477 | * args[] will take 160k (!), which will most likely be |
482 | size_t n_chars; | 478 | * almost entirely unused. |
479 | */ | ||
480 | /* See store_param() for matching 256-step growth logic */ | ||
481 | G.args = xmalloc(sizeof(G.args[0]) * ((argc + 0xff) & ~0xff)); | ||
483 | 482 | ||
484 | opt |= OPT_NO_EMPTY; | 483 | /* Store the command to be executed, part 1 */ |
484 | for (i = 0; argv[i]; i++) | ||
485 | G.args[i] = argv[i]; | ||
485 | 486 | ||
486 | /* take args from list, not exceeding arg and char limits */ | 487 | while (1) { |
487 | n_chars = 0; | 488 | char *rem; |
488 | n = 0; | 489 | |
489 | for (cur = list; cur; cur = cur->link) { | 490 | G.idx = argc; |
490 | n_chars += cur->length; | 491 | rem = read_args(n_max_chars, n_max_arg, buf); |
491 | if (n_chars > n_max_chars || n >= n_max_arg) { | ||
492 | if (opt & OPT_TERMINATE) | ||
493 | bb_error_msg_and_die("argument list too long"); | ||
494 | break; | ||
495 | } | ||
496 | n++; | ||
497 | } | ||
498 | 492 | ||
499 | /* allocate pointers for execvp */ | 493 | if (!G.args[argc]) { |
500 | args = xzalloc(sizeof(args[0]) * (argc + n + 1)); | 494 | if (*rem != '\0') |
501 | 495 | bb_error_msg_and_die("argument line too long"); | |
502 | /* store the command to be executed | 496 | if (opt & OPT_NO_EMPTY) |
503 | * (taken from the command line) */ | 497 | break; |
504 | for (i = 0; argv[i]; i++) | ||
505 | args[i] = argv[i]; | ||
506 | /* (taken from stdin) */ | ||
507 | for (cur = list; n; cur = cur->link) { | ||
508 | args[i++] = cur->xstr; | ||
509 | n--; | ||
510 | } | 498 | } |
499 | opt |= OPT_NO_EMPTY; | ||
511 | 500 | ||
512 | if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { | 501 | if (opt & (OPT_INTERACTIVE | OPT_VERBOSE)) { |
513 | for (i = 0; args[i]; i++) { | 502 | for (i = 0; G.args[i]; i++) { |
514 | if (i) | 503 | if (i) |
515 | bb_putchar_stderr(' '); | 504 | bb_putchar_stderr(' '); |
516 | fputs(args[i], stderr); | 505 | fputs(G.args[i], stderr); |
517 | } | 506 | } |
518 | if (!(opt & OPT_INTERACTIVE)) | 507 | if (!(opt & OPT_INTERACTIVE)) |
519 | bb_putchar_stderr('\n'); | 508 | bb_putchar_stderr('\n'); |
520 | } | 509 | } |
521 | 510 | ||
522 | if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { | 511 | if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { |
523 | child_error = xargs_exec(args); | 512 | child_error = xargs_exec(); |
524 | } | ||
525 | |||
526 | /* remove list elements which we consumed */ | ||
527 | for (i = argc; args[i]; i++) { | ||
528 | cur = list; | ||
529 | list = list->link; | ||
530 | free(cur); | ||
531 | } | 513 | } |
532 | free(args); | ||
533 | 514 | ||
534 | if (child_error > 0 && child_error != 123) { | 515 | if (child_error > 0 && child_error != 123) { |
535 | break; | 516 | break; |
536 | } | 517 | } |
518 | |||
519 | overlapping_strcpy(buf, rem); | ||
537 | } /* while */ | 520 | } /* while */ |
538 | 521 | ||
539 | if (ENABLE_FEATURE_CLEAN_UP) | 522 | if (ENABLE_FEATURE_CLEAN_UP) { |
523 | free(G.args); | ||
540 | free(buf); | 524 | free(buf); |
525 | } | ||
541 | 526 | ||
542 | return child_error; | 527 | return child_error; |
543 | } | 528 | } |