diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-17 05:11:43 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-17 05:11:43 +0000 |
commit | b61e13d24741536fc713dab1c10a4925197b2072 (patch) | |
tree | 60608273ed2cf376466bd719c6cf4a745a2f0da0 | |
parent | ccce59d5622dd32101e396580f7a148dfb3cb2d8 (diff) | |
download | busybox-w32-b61e13d24741536fc713dab1c10a4925197b2072.tar.gz busybox-w32-b61e13d24741536fc713dab1c10a4925197b2072.tar.bz2 busybox-w32-b61e13d24741536fc713dab1c10a4925197b2072.zip |
hush: fix memory leak. it was actually rather invloved problem.
Now finally glob/variable expansion is done IN THE RIGHT ORDER!
It opens up a possibility to cleanly fix remaining known bugs.
function old new delta
o_save_ptr 115 286 +171
o_save_ptr_helper - 115 +115
done_word 591 690 +99
o_get_last_ptr - 31 +31
expand_on_ifs 125 97 -28
add_string_to_strings 28 - -28
run_list 1895 1862 -33
debug_print_strings 42 - -42
add_strings_to_strings 126 - -126
expand_variables 1550 1394 -156
o_debug_list 168 - -168
expand_strvec_to_strvec 388 10 -378
------------------------------------------------------------------------------
(add/remove: 2/4 grow/shrink: 2/4 up/down: 416/-959) Total: -543 bytes
-rw-r--r-- | shell/hush.c | 313 |
1 files changed, 175 insertions, 138 deletions
diff --git a/shell/hush.c b/shell/hush.c index 0e1976443..0b92c29d4 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -104,6 +104,8 @@ | |||
104 | #define debug_printf_exec(...) do {} while (0) | 104 | #define debug_printf_exec(...) do {} while (0) |
105 | #define debug_printf_jobs(...) do {} while (0) | 105 | #define debug_printf_jobs(...) do {} while (0) |
106 | #define debug_printf_expand(...) do {} while (0) | 106 | #define debug_printf_expand(...) do {} while (0) |
107 | #define debug_printf_glob(...) do {} while (0) | ||
108 | #define debug_printf_list(...) do {} while (0) | ||
107 | #define debug_printf_clean(...) do {} while (0) | 109 | #define debug_printf_clean(...) do {} while (0) |
108 | 110 | ||
109 | #ifndef debug_printf | 111 | #ifndef debug_printf |
@@ -120,12 +122,27 @@ | |||
120 | 122 | ||
121 | #ifndef debug_printf_jobs | 123 | #ifndef debug_printf_jobs |
122 | #define debug_printf_jobs(...) fprintf(stderr, __VA_ARGS__) | 124 | #define debug_printf_jobs(...) fprintf(stderr, __VA_ARGS__) |
123 | #define DEBUG_SHELL_JOBS 1 | 125 | #define DEBUG_JOBS 1 |
126 | #else | ||
127 | #define DEBUG_JOBS 0 | ||
124 | #endif | 128 | #endif |
125 | 129 | ||
126 | #ifndef debug_printf_expand | 130 | #ifndef debug_printf_expand |
127 | #define debug_printf_expand(...) fprintf(stderr, __VA_ARGS__) | 131 | #define debug_printf_expand(...) fprintf(stderr, __VA_ARGS__) |
128 | #define DEBUG_EXPAND 1 | 132 | #define DEBUG_EXPAND 1 |
133 | #else | ||
134 | #define DEBUG_EXPAND 0 | ||
135 | #endif | ||
136 | |||
137 | #ifndef debug_printf_glob | ||
138 | #define debug_printf_glob(...) fprintf(stderr, __VA_ARGS__) | ||
139 | #define DEBUG_GLOB 1 | ||
140 | #else | ||
141 | #define DEBUG_GLOB 0 | ||
142 | #endif | ||
143 | |||
144 | #ifndef debug_printf_list | ||
145 | #define debug_printf_list(...) fprintf(stderr, __VA_ARGS__) | ||
129 | #endif | 146 | #endif |
130 | 147 | ||
131 | /* Keep unconditionally on for now */ | 148 | /* Keep unconditionally on for now */ |
@@ -143,6 +160,17 @@ static const char *indenter(int i) | |||
143 | #define DEBUG_CLEAN 1 | 160 | #define DEBUG_CLEAN 1 |
144 | #endif | 161 | #endif |
145 | 162 | ||
163 | #if DEBUG_EXPAND | ||
164 | static void debug_print_strings(const char *prefix, char **vv) | ||
165 | { | ||
166 | fprintf(stderr, "%s:\n", prefix); | ||
167 | while (*vv) | ||
168 | fprintf(stderr, " '%s'\n", *vv++); | ||
169 | } | ||
170 | #else | ||
171 | #define debug_print_strings(prefix, vv) ((void)0) | ||
172 | #endif | ||
173 | |||
146 | 174 | ||
147 | /* | 175 | /* |
148 | * Leak hunting. Use hush_leaktool.sh for post-processing. | 176 | * Leak hunting. Use hush_leaktool.sh for post-processing. |
@@ -309,6 +337,7 @@ typedef struct { | |||
309 | int length; | 337 | int length; |
310 | int maxlen; | 338 | int maxlen; |
311 | smallint o_quote; | 339 | smallint o_quote; |
340 | smallint o_glob; | ||
312 | smallint nonnull; | 341 | smallint nonnull; |
313 | smallint has_empty_slot; | 342 | smallint has_empty_slot; |
314 | } o_string; | 343 | } o_string; |
@@ -531,6 +560,18 @@ static int set_local_var(char *str, int flg_export); | |||
531 | static void unset_local_var(const char *name); | 560 | static void unset_local_var(const char *name); |
532 | 561 | ||
533 | 562 | ||
563 | static int glob_needed(const char *s) | ||
564 | { | ||
565 | while (*s) { | ||
566 | if (*s == '\\') | ||
567 | s++; | ||
568 | if (*s == '*' || *s == '[' || *s == '?') | ||
569 | return 1; | ||
570 | s++; | ||
571 | } | ||
572 | return 0; | ||
573 | } | ||
574 | |||
534 | static char **add_strings_to_strings(int need_xstrdup, char **strings, char **add) | 575 | static char **add_strings_to_strings(int need_xstrdup, char **strings, char **add) |
535 | { | 576 | { |
536 | int i; | 577 | int i; |
@@ -592,17 +633,6 @@ static char **alloc_ptrs(char **argv) | |||
592 | } | 633 | } |
593 | #endif | 634 | #endif |
594 | 635 | ||
595 | #ifdef DEBUG_EXPAND | ||
596 | static void debug_print_strings(const char *prefix, char **vv) | ||
597 | { | ||
598 | fprintf(stderr, "%s:\n", prefix); | ||
599 | while (*vv) | ||
600 | fprintf(stderr, " '%s'\n", *vv++); | ||
601 | } | ||
602 | #else | ||
603 | #define debug_print_strings(prefix, vv) ((void)0) | ||
604 | #endif | ||
605 | |||
606 | 636 | ||
607 | /* Function prototypes for builtins */ | 637 | /* Function prototypes for builtins */ |
608 | static int builtin_cd(char **argv); | 638 | static int builtin_cd(char **argv); |
@@ -1260,7 +1290,33 @@ static void o_addQstr(o_string *o, const char *str, int len) | |||
1260 | * o_finalize_list() operation post-processes this structure - calculates | 1290 | * o_finalize_list() operation post-processes this structure - calculates |
1261 | * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well. | 1291 | * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well. |
1262 | */ | 1292 | */ |
1263 | static int o_save_ptr(o_string *o, int n) | 1293 | #if DEBUG_EXPAND || DEBUG_GLOB |
1294 | static void debug_print_list(const char *prefix, o_string *o, int n) | ||
1295 | { | ||
1296 | char **list = (char**)o->data; | ||
1297 | int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); | ||
1298 | int i = 0; | ||
1299 | fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n", | ||
1300 | prefix, list, n, string_start, o->length, o->maxlen); | ||
1301 | while (i < n) { | ||
1302 | fprintf(stderr, " list[%d]=%d '%s' %p\n", i, (int)list[i], | ||
1303 | o->data + (int)list[i] + string_start, | ||
1304 | o->data + (int)list[i] + string_start); | ||
1305 | i++; | ||
1306 | } | ||
1307 | if (n) { | ||
1308 | const char *p = o->data + (int)list[n - 1] + string_start; | ||
1309 | fprintf(stderr, " total_sz:%d\n", (p + strlen(p) + 1) - o->data); | ||
1310 | } | ||
1311 | } | ||
1312 | #else | ||
1313 | #define debug_print_list(prefix, o, n) ((void)0) | ||
1314 | #endif | ||
1315 | |||
1316 | /* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value | ||
1317 | * in list[n] so that it points past last stored byte so far. | ||
1318 | * It returns n+1. */ | ||
1319 | static int o_save_ptr_helper(o_string *o, int n) | ||
1264 | { | 1320 | { |
1265 | char **list = (char**)o->data; | 1321 | char **list = (char**)o->data; |
1266 | int string_start; | 1322 | int string_start; |
@@ -1270,26 +1326,27 @@ static int o_save_ptr(o_string *o, int n) | |||
1270 | string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); | 1326 | string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); |
1271 | string_len = o->length - string_start; | 1327 | string_len = o->length - string_start; |
1272 | if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */ | 1328 | if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */ |
1273 | //bb_error_msg("list[%d]=%d string_start=%d (growing)", n, string_len, string_start); | 1329 | debug_printf_list("list[%d]=%d string_start=%d (growing)", n, string_len, string_start); |
1274 | /* list[n] points to string_start, make space for 16 more pointers */ | 1330 | /* list[n] points to string_start, make space for 16 more pointers */ |
1275 | o->maxlen += 0x10 * sizeof(list[0]); | 1331 | o->maxlen += 0x10 * sizeof(list[0]); |
1276 | o->data = xrealloc(o->data, o->maxlen + 1); | 1332 | o->data = xrealloc(o->data, o->maxlen + 1); |
1277 | list = (char**)o->data; | 1333 | list = (char**)o->data; |
1278 | memmove(list + n + 0x10, list + n, string_len); | 1334 | memmove(list + n + 0x10, list + n, string_len); |
1279 | o->length += 0x10 * sizeof(list[0]); | 1335 | o->length += 0x10 * sizeof(list[0]); |
1280 | } | 1336 | } else |
1281 | //else bb_error_msg("list[%d]=%d string_start=%d", n, string_len, string_start); | 1337 | debug_printf_list("list[%d]=%d string_start=%d", n, string_len, string_start); |
1282 | } else { | 1338 | } else { |
1283 | /* We have empty slot at list[n], reuse without growth */ | 1339 | /* We have empty slot at list[n], reuse without growth */ |
1284 | string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */ | 1340 | string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */ |
1285 | string_len = o->length - string_start; | 1341 | string_len = o->length - string_start; |
1286 | //bb_error_msg("list[%d]=%d string_start=%d (empty slot)", n, string_len, string_start); | 1342 | debug_printf_list("list[%d]=%d string_start=%d (empty slot)", n, string_len, string_start); |
1287 | o->has_empty_slot = 0; | 1343 | o->has_empty_slot = 0; |
1288 | } | 1344 | } |
1289 | list[n] = (char*)string_len; | 1345 | list[n] = (char*)string_len; |
1290 | return n + 1; | 1346 | return n + 1; |
1291 | } | 1347 | } |
1292 | 1348 | ||
1349 | /* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */ | ||
1293 | static int o_get_last_ptr(o_string *o, int n) | 1350 | static int o_get_last_ptr(o_string *o, int n) |
1294 | { | 1351 | { |
1295 | char **list = (char**)o->data; | 1352 | char **list = (char**)o->data; |
@@ -1298,14 +1355,89 @@ static int o_get_last_ptr(o_string *o, int n) | |||
1298 | return ((int)list[n-1]) + string_start; | 1355 | return ((int)list[n-1]) + string_start; |
1299 | } | 1356 | } |
1300 | 1357 | ||
1358 | /* Convert every \x to x in-place, return ptr past NUL. */ | ||
1359 | static char *unbackslash(char *src) | ||
1360 | { | ||
1361 | char *dst = src; | ||
1362 | while (1) { | ||
1363 | if (*src == '\\') | ||
1364 | src++; | ||
1365 | if ((*dst++ = *src++) == '\0') | ||
1366 | break; | ||
1367 | } | ||
1368 | return dst; | ||
1369 | } | ||
1370 | |||
1371 | static int o_glob(o_string *o, int n) | ||
1372 | { | ||
1373 | glob_t globdata; | ||
1374 | int gr; | ||
1375 | char *pattern; | ||
1376 | |||
1377 | debug_printf_glob("start o_glob: n:%d o->data:%p", n, o->data); | ||
1378 | if (!o->data) | ||
1379 | return o_save_ptr_helper(o, n); | ||
1380 | pattern = o->data + o_get_last_ptr(o, n); | ||
1381 | debug_printf_glob("glob pattern '%s'", pattern); | ||
1382 | if (!glob_needed(pattern)) { | ||
1383 | literal: | ||
1384 | o->length = unbackslash(pattern) - o->data; | ||
1385 | debug_printf_glob("glob pattern '%s' is literal", pattern); | ||
1386 | return o_save_ptr_helper(o, n); | ||
1387 | } | ||
1388 | |||
1389 | memset(&globdata, 0, sizeof(globdata)); | ||
1390 | gr = glob(pattern, 0, NULL, &globdata); | ||
1391 | debug_printf_glob("glob('%s'):%d\n", pattern, gr); | ||
1392 | if (gr == GLOB_NOSPACE) | ||
1393 | bb_error_msg_and_die("out of memory during glob"); | ||
1394 | if (gr == GLOB_NOMATCH) { | ||
1395 | globfree(&globdata); | ||
1396 | goto literal; | ||
1397 | } | ||
1398 | if (gr != 0) { /* GLOB_ABORTED ? */ | ||
1399 | //TODO: testcase for bad glob pattern behavior | ||
1400 | bb_error_msg("glob(3) error %d on '%s'", gr, pattern); | ||
1401 | } | ||
1402 | if (globdata.gl_pathv && globdata.gl_pathv[0]) { | ||
1403 | char **argv = globdata.gl_pathv; | ||
1404 | o->length = pattern - o->data; /* "forget" pattern */ | ||
1405 | while (1) { | ||
1406 | o_addstr(o, *argv, strlen(*argv) + 1); | ||
1407 | n = o_save_ptr_helper(o, n); | ||
1408 | argv++; | ||
1409 | if (!*argv) | ||
1410 | break; | ||
1411 | } | ||
1412 | } | ||
1413 | globfree(&globdata); | ||
1414 | if (DEBUG_GLOB) | ||
1415 | debug_print_list("o_glob returning", o, n); | ||
1416 | return n; | ||
1417 | } | ||
1418 | |||
1419 | /* o_save_ptr_helper + but glob the string so far remembered | ||
1420 | * if o->o_glob == 1 */ | ||
1421 | static int o_save_ptr(o_string *o, int n) | ||
1422 | { | ||
1423 | if (o->o_glob) | ||
1424 | return o_glob(o, n); /* o_save_ptr_helper is inside */ | ||
1425 | return o_save_ptr_helper(o, n); | ||
1426 | } | ||
1427 | |||
1428 | /* "Please convert list[n] to real char* ptrs, and NULL terminate it." */ | ||
1301 | static char **o_finalize_list(o_string *o, int n) | 1429 | static char **o_finalize_list(o_string *o, int n) |
1302 | { | 1430 | { |
1303 | char **list = (char**)o->data; | 1431 | char **list; |
1304 | int string_start; | 1432 | int string_start; |
1305 | 1433 | ||
1306 | o_save_ptr(o, n); /* force growth for list[n] if necessary */ | 1434 | n = o_save_ptr(o, n); /* force growth for list[n] if necessary */ |
1307 | string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); | 1435 | if (DEBUG_EXPAND) |
1308 | list[n] = NULL; | 1436 | debug_print_list("finalized", o, n); |
1437 | debug_printf_expand("finalized n:%d", n); | ||
1438 | list = (char**)o->data; | ||
1439 | string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); | ||
1440 | list[--n] = NULL; | ||
1309 | while (n) { | 1441 | while (n) { |
1310 | n--; | 1442 | n--; |
1311 | list[n] = o->data + (int)list[n] + string_start; | 1443 | list[n] = o->data + (int)list[n] + string_start; |
@@ -1313,28 +1445,6 @@ static char **o_finalize_list(o_string *o, int n) | |||
1313 | return list; | 1445 | return list; |
1314 | } | 1446 | } |
1315 | 1447 | ||
1316 | #ifdef DEBUG_EXPAND | ||
1317 | static void o_debug_list(const char *prefix, o_string *o, int n) | ||
1318 | { | ||
1319 | char **list = (char**)o->data; | ||
1320 | int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); | ||
1321 | int i = 0; | ||
1322 | fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n", | ||
1323 | prefix, list, n, string_start, o->length, o->maxlen); | ||
1324 | while (i < n) { | ||
1325 | fprintf(stderr, " list[%d]=%d '%s'\n", i, (int)list[i], | ||
1326 | o->data + (int)list[i] + string_start); | ||
1327 | i++; | ||
1328 | } | ||
1329 | if (n) { | ||
1330 | const char *p = o->data + (int)list[n] + string_start; | ||
1331 | fprintf(stderr, " total_sz:%d\n", (p + strlen(p) + 1) - o->data); | ||
1332 | } | ||
1333 | } | ||
1334 | #else | ||
1335 | #define o_debug_list(prefix, o, n) ((void)0) | ||
1336 | #endif | ||
1337 | |||
1338 | 1448 | ||
1339 | /* | 1449 | /* |
1340 | * in_str support | 1450 | * in_str support |
@@ -1782,7 +1892,7 @@ static int checkjobs(struct pipe* fg_pipe) | |||
1782 | while ((childpid = waitpid(-1, &status, attributes)) > 0) { | 1892 | while ((childpid = waitpid(-1, &status, attributes)) > 0) { |
1783 | const int dead = WIFEXITED(status) || WIFSIGNALED(status); | 1893 | const int dead = WIFEXITED(status) || WIFSIGNALED(status); |
1784 | 1894 | ||
1785 | #ifdef DEBUG_SHELL_JOBS | 1895 | #if DEBUG_JOBS |
1786 | if (WIFSTOPPED(status)) | 1896 | if (WIFSTOPPED(status)) |
1787 | debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", | 1897 | debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n", |
1788 | childpid, WSTOPSIG(status), WEXITSTATUS(status)); | 1898 | childpid, WSTOPSIG(status), WEXITSTATUS(status)); |
@@ -2502,11 +2612,11 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
2502 | if (!*str) /* EOL - do not finalize word */ | 2612 | if (!*str) /* EOL - do not finalize word */ |
2503 | break; | 2613 | break; |
2504 | o_addchr(output, '\0'); | 2614 | o_addchr(output, '\0'); |
2505 | o_debug_list("expand_on_ifs", output, n); | 2615 | debug_print_list("expand_on_ifs", output, n); |
2506 | n = o_save_ptr(output, n); | 2616 | n = o_save_ptr(output, n); |
2507 | str += strspn(str, ifs); /* skip ifs chars */ | 2617 | str += strspn(str, ifs); /* skip ifs chars */ |
2508 | } | 2618 | } |
2509 | o_debug_list("expand_on_ifs[1]", output, n); | 2619 | debug_print_list("expand_on_ifs[1]", output, n); |
2510 | return n; | 2620 | return n; |
2511 | } | 2621 | } |
2512 | 2622 | ||
@@ -2530,9 +2640,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2530 | ored_ch = 0; | 2640 | ored_ch = 0; |
2531 | 2641 | ||
2532 | debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); | 2642 | debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); |
2533 | o_debug_list("expand_vars_to_list", output, n); | 2643 | debug_print_list("expand_vars_to_list", output, n); |
2534 | n = o_save_ptr(output, n); | 2644 | n = o_save_ptr(output, n); |
2535 | o_debug_list("expand_vars_to_list[0]", output, n); | 2645 | debug_print_list("expand_vars_to_list[0]", output, n); |
2536 | 2646 | ||
2537 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { | 2647 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { |
2538 | #if ENABLE_HUSH_TICK | 2648 | #if ENABLE_HUSH_TICK |
@@ -2540,7 +2650,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2540 | #endif | 2650 | #endif |
2541 | 2651 | ||
2542 | o_addQstr(output, arg, p - arg); | 2652 | o_addQstr(output, arg, p - arg); |
2543 | o_debug_list("expand_vars_to_list[1]", output, n); | 2653 | debug_print_list("expand_vars_to_list[1]", output, n); |
2544 | arg = ++p; | 2654 | arg = ++p; |
2545 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 2655 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
2546 | 2656 | ||
@@ -2575,9 +2685,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2575 | /* this argv[] is not empty and not last: | 2685 | /* this argv[] is not empty and not last: |
2576 | * put terminating NUL, start new word */ | 2686 | * put terminating NUL, start new word */ |
2577 | o_addchr(output, '\0'); | 2687 | o_addchr(output, '\0'); |
2578 | o_debug_list("expand_vars_to_list[2]", output, n); | 2688 | debug_print_list("expand_vars_to_list[2]", output, n); |
2579 | n = o_save_ptr(output, n); | 2689 | n = o_save_ptr(output, n); |
2580 | o_debug_list("expand_vars_to_list[3]", output, n); | 2690 | debug_print_list("expand_vars_to_list[3]", output, n); |
2581 | } | 2691 | } |
2582 | } | 2692 | } |
2583 | } else | 2693 | } else |
@@ -2589,7 +2699,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2589 | if (++i >= global_argc) | 2699 | if (++i >= global_argc) |
2590 | break; | 2700 | break; |
2591 | o_addchr(output, '\0'); | 2701 | o_addchr(output, '\0'); |
2592 | o_debug_list("expand_vars_to_list[4]", output, n); | 2702 | debug_print_list("expand_vars_to_list[4]", output, n); |
2593 | n = o_save_ptr(output, n); | 2703 | n = o_save_ptr(output, n); |
2594 | } | 2704 | } |
2595 | } else { /* quoted $*: add as one word */ | 2705 | } else { /* quoted $*: add as one word */ |
@@ -2647,9 +2757,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2647 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ | 2757 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ |
2648 | 2758 | ||
2649 | if (arg[0]) { | 2759 | if (arg[0]) { |
2650 | o_debug_list("expand_vars_to_list[a]", output, n); | 2760 | debug_print_list("expand_vars_to_list[a]", output, n); |
2651 | o_addQstr(output, arg, strlen(arg) + 1); | 2761 | o_addQstr(output, arg, strlen(arg) + 1); |
2652 | o_debug_list("expand_vars_to_list[b]", output, n); | 2762 | debug_print_list("expand_vars_to_list[b]", output, n); |
2653 | } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ | 2763 | } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ |
2654 | && !(ored_ch & 0x80) /* and all vars were not quoted. */ | 2764 | && !(ored_ch & 0x80) /* and all vars were not quoted. */ |
2655 | ) { | 2765 | ) { |
@@ -2662,106 +2772,33 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
2662 | return n; | 2772 | return n; |
2663 | } | 2773 | } |
2664 | 2774 | ||
2665 | static char **expand_variables(char **argv, char or_mask) | 2775 | static char **expand_variables(char **argv, int or_mask) |
2666 | { | 2776 | { |
2667 | int n; | 2777 | int n; |
2668 | char **list; | 2778 | char **list; |
2669 | char **v; | 2779 | char **v; |
2670 | o_string output = NULL_O_STRING; | 2780 | o_string output = NULL_O_STRING; |
2671 | 2781 | ||
2782 | if (or_mask & 0x100) | ||
2783 | output.o_glob = 1; | ||
2784 | |||
2672 | n = 0; | 2785 | n = 0; |
2673 | v = argv; | 2786 | v = argv; |
2674 | while (*v) | 2787 | while (*v) { |
2675 | n = expand_vars_to_list(&output, n, *v++, or_mask); | 2788 | n = expand_vars_to_list(&output, n, *v, (char)or_mask); |
2676 | o_debug_list("expand_variables", &output, n); | 2789 | v++; |
2790 | } | ||
2791 | debug_print_list("expand_variables", &output, n); | ||
2677 | 2792 | ||
2678 | /* output.data (malloced in one block) gets returned in "list" */ | 2793 | /* output.data (malloced in one block) gets returned in "list" */ |
2679 | list = o_finalize_list(&output, n); | 2794 | list = o_finalize_list(&output, n); |
2795 | debug_print_strings("expand_variables[1]", list); | ||
2680 | return list; | 2796 | return list; |
2681 | } | 2797 | } |
2682 | 2798 | ||
2683 | /* Remove non-backslashed backslashes and add to "strings" vector. | ||
2684 | * XXX broken if the last character is '\\', check that before calling. | ||
2685 | */ | ||
2686 | static char **add_unq_string_to_strings(char **strings, const char *src) | ||
2687 | { | ||
2688 | int cnt; | ||
2689 | const char *s; | ||
2690 | char *v, *dest; | ||
2691 | |||
2692 | for (cnt = 1, s = src; s && *s; s++) { | ||
2693 | if (*s == '\\') s++; | ||
2694 | cnt++; | ||
2695 | } | ||
2696 | v = dest = xmalloc(cnt); | ||
2697 | for (s = src; s && *s; s++, dest++) { | ||
2698 | if (*s == '\\') s++; | ||
2699 | *dest = *s; | ||
2700 | } | ||
2701 | *dest = '\0'; | ||
2702 | |||
2703 | return add_string_to_strings(strings, v); | ||
2704 | } | ||
2705 | |||
2706 | /* XXX broken if the last character is '\\', check that before calling */ | ||
2707 | static int glob_needed(const char *s) | ||
2708 | { | ||
2709 | for (; *s; s++) { | ||
2710 | if (*s == '\\') | ||
2711 | s++; | ||
2712 | if (strchr("*[?", *s)) | ||
2713 | return 1; | ||
2714 | } | ||
2715 | return 0; | ||
2716 | } | ||
2717 | |||
2718 | static void xglob(char ***pglob, const char *pattern) | ||
2719 | { | ||
2720 | if (glob_needed(pattern)) { | ||
2721 | glob_t globdata; | ||
2722 | int gr; | ||
2723 | |||
2724 | memset(&globdata, 0, sizeof(globdata)); | ||
2725 | gr = glob(pattern, 0, NULL, &globdata); | ||
2726 | debug_printf("glob returned %d\n", gr); | ||
2727 | if (gr == GLOB_NOSPACE) | ||
2728 | bb_error_msg_and_die("out of memory during glob"); | ||
2729 | if (gr == GLOB_NOMATCH) { | ||
2730 | globfree(&globdata); | ||
2731 | goto literal; | ||
2732 | } | ||
2733 | if (gr != 0) { /* GLOB_ABORTED ? */ | ||
2734 | bb_error_msg("glob(3) error %d on '%s'", gr, pattern); | ||
2735 | //TODO: testcase for bad glob pattern behavior | ||
2736 | } | ||
2737 | if (globdata.gl_pathv && globdata.gl_pathv[0]) | ||
2738 | *pglob = add_strings_to_strings(1, *pglob, globdata.gl_pathv); | ||
2739 | globfree(&globdata); | ||
2740 | return; | ||
2741 | } | ||
2742 | |||
2743 | literal: | ||
2744 | /* quote removal, or more accurately, backslash removal */ | ||
2745 | *pglob = add_unq_string_to_strings(*pglob, pattern); | ||
2746 | debug_print_strings("after xglob", *pglob); | ||
2747 | } | ||
2748 | |||
2749 | //LEAK is here: callers expect result to be free()able, but we | ||
2750 | //actually require free_strings(). free() leaks strings. | ||
2751 | static char **expand_strvec_to_strvec(char **argv) | 2799 | static char **expand_strvec_to_strvec(char **argv) |
2752 | { | 2800 | { |
2753 | char **exp; | 2801 | return expand_variables(argv, 0x100); |
2754 | char **res = xzalloc(sizeof(res[0])); | ||
2755 | |||
2756 | debug_print_strings("expand_strvec_to_strvec: pre expand", argv); | ||
2757 | exp = argv = expand_variables(argv, 0); | ||
2758 | debug_print_strings("expand_strvec_to_strvec: post expand", argv); | ||
2759 | while (*argv) { | ||
2760 | xglob(&res, *argv++); | ||
2761 | } | ||
2762 | free(exp); | ||
2763 | debug_print_strings("expand_strvec_to_strvec: res", res); | ||
2764 | return res; | ||
2765 | } | 2802 | } |
2766 | 2803 | ||
2767 | /* used for expansion of right hand of assignments */ | 2804 | /* used for expansion of right hand of assignments */ |