diff options
author | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-10 18:04:32 +0000 |
---|---|---|
committer | Denis Vlasenko <vda.linux@googlemail.com> | 2008-06-10 18:04:32 +0000 |
commit | 7b4f3f13d305d0b610f232699fac9f54cc74ec3c (patch) | |
tree | 5c5103a7d798d4d7319217c94af11aa295f1ad72 /shell | |
parent | 76d50418b37e59c4b474137ffa2eba4d3f7dbbb8 (diff) | |
download | busybox-w32-7b4f3f13d305d0b610f232699fac9f54cc74ec3c.tar.gz busybox-w32-7b4f3f13d305d0b610f232699fac9f54cc74ec3c.tar.bz2 busybox-w32-7b4f3f13d305d0b610f232699fac9f54cc74ec3c.zip |
hush: fix two nasty bugs:
hush-bugs/tick2.tests: ok
hush-bugs/tick.tests: ok
function old new delta
parse_stream 1332 1557 +225
b_addptr - 97 +97
add_till_backquote - 82 +82
b_addstr - 58 +58
b_grow_by - 50 +50
setup_string_in_str - 29 +29
expand_variables 1196 1199 +3
expand_on_ifs 100 97 -3
b_addqchr 57 50 -7
parse_and_run_string 48 31 -17
lookup_param 27 - -27
b_addchr 75 45 -30
count_ifs 44 - -44
process_command_subs 222 - -222
------------------------------------------------------------------------------
(add/remove: 5/3 grow/shrink: 2/4 up/down: 544/-350) Total: 194 bytes
text data bss dec hex filename
759354 604 6684 766642 bb2b2 busybox_old
759534 604 6684 766822 bb366 busybox_unstripped
Diffstat (limited to 'shell')
-rw-r--r-- | shell/hush.c | 487 |
1 files changed, 297 insertions, 190 deletions
diff --git a/shell/hush.c b/shell/hush.c index d7b9c39ec..58100d4d1 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -341,8 +341,9 @@ typedef struct { | |||
341 | int maxlen; | 341 | int maxlen; |
342 | smallint o_quote; | 342 | smallint o_quote; |
343 | smallint nonnull; | 343 | smallint nonnull; |
344 | smallint has_empty_slot; | ||
344 | } o_string; | 345 | } o_string; |
345 | #define NULL_O_STRING {NULL,0,0,0,0} | 346 | #define NULL_O_STRING { NULL } |
346 | /* used for initialization: o_string foo = NULL_O_STRING; */ | 347 | /* used for initialization: o_string foo = NULL_O_STRING; */ |
347 | 348 | ||
348 | /* I can almost use ordinary FILE *. Is open_memstream() universally | 349 | /* I can almost use ordinary FILE *. Is open_memstream() universally |
@@ -464,8 +465,6 @@ enum { run_list_level = 0 }; | |||
464 | } while (0) | 465 | } while (0) |
465 | 466 | ||
466 | 467 | ||
467 | #define B_CHUNK 100 | ||
468 | #define B_NOSPAC 1 | ||
469 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" | 468 | #define JOB_STATUS_FORMAT "[%d] %-22s %.40s\n" |
470 | 469 | ||
471 | #if 1 | 470 | #if 1 |
@@ -494,11 +493,6 @@ static void syntax_lineno(int line) | |||
494 | #endif | 493 | #endif |
495 | 494 | ||
496 | /* Index of subroutines: */ | 495 | /* Index of subroutines: */ |
497 | /* o_string manipulation: */ | ||
498 | static int b_check_space(o_string *o, int len); | ||
499 | static int b_addchr(o_string *o, int ch); | ||
500 | static void b_reset(o_string *o); | ||
501 | static int b_addqchr(o_string *o, int ch, int quote); | ||
502 | /* in_str manipulations: */ | 496 | /* in_str manipulations: */ |
503 | static int static_get(struct in_str *i); | 497 | static int static_get(struct in_str *i); |
504 | static int static_peek(struct in_str *i); | 498 | static int static_peek(struct in_str *i); |
@@ -1176,57 +1170,143 @@ static int builtin_unset(char **argv) | |||
1176 | // return EXIT_FAILURE; | 1170 | // return EXIT_FAILURE; |
1177 | //} | 1171 | //} |
1178 | 1172 | ||
1179 | static int b_check_space(o_string *o, int len) | 1173 | /* |
1174 | * o_string support | ||
1175 | */ | ||
1176 | #define B_CHUNK (32 * sizeof(char*)) | ||
1177 | |||
1178 | static void b_reset(o_string *o) | ||
1179 | { | ||
1180 | o->length = 0; | ||
1181 | o->nonnull = 0; | ||
1182 | if (o->data) | ||
1183 | o->data[0] = '\0'; | ||
1184 | } | ||
1185 | |||
1186 | static void b_free(o_string *o) | ||
1187 | { | ||
1188 | free(o->data); | ||
1189 | memset(o, 0, sizeof(*o)); | ||
1190 | } | ||
1191 | |||
1192 | static void b_grow_by(o_string *o, int len) | ||
1180 | { | 1193 | { |
1181 | /* It would be easy to drop a more restrictive policy | ||
1182 | * in here, such as setting a maximum string length */ | ||
1183 | if (o->length + len > o->maxlen) { | 1194 | if (o->length + len > o->maxlen) { |
1184 | /* assert(data == NULL || o->maxlen != 0); */ | ||
1185 | o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK); | 1195 | o->maxlen += (2*len > B_CHUNK ? 2*len : B_CHUNK); |
1186 | o->data = xrealloc(o->data, 1 + o->maxlen); | 1196 | o->data = xrealloc(o->data, 1 + o->maxlen); |
1187 | } | 1197 | } |
1188 | return o->data == NULL; | ||
1189 | } | 1198 | } |
1190 | 1199 | ||
1191 | static int b_addchr(o_string *o, int ch) | 1200 | static void b_addchr(o_string *o, int ch) |
1192 | { | 1201 | { |
1193 | debug_printf("b_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); | 1202 | debug_printf("b_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o); |
1194 | if (b_check_space(o, 1)) | 1203 | b_grow_by(o, 1); |
1195 | return B_NOSPAC; | ||
1196 | o->data[o->length] = ch; | 1204 | o->data[o->length] = ch; |
1197 | o->length++; | 1205 | o->length++; |
1198 | o->data[o->length] = '\0'; | 1206 | o->data[o->length] = '\0'; |
1199 | return 0; | ||
1200 | } | ||
1201 | |||
1202 | static void b_reset(o_string *o) | ||
1203 | { | ||
1204 | o->length = 0; | ||
1205 | o->nonnull = 0; | ||
1206 | if (o->data) | ||
1207 | o->data[0] = '\0'; | ||
1208 | } | 1207 | } |
1209 | 1208 | ||
1210 | static void b_free(o_string *o) | 1209 | static void b_addstr(o_string *o, const char *str, int len) |
1211 | { | 1210 | { |
1212 | free(o->data); | 1211 | b_grow_by(o, len); |
1213 | memset(o, 0, sizeof(*o)); | 1212 | memcpy(&o->data[o->length], str, len); |
1213 | o->length += len; | ||
1214 | o->data[o->length] = '\0'; | ||
1214 | } | 1215 | } |
1215 | 1216 | ||
1216 | /* My analysis of quoting semantics tells me that state information | 1217 | /* My analysis of quoting semantics tells me that state information |
1217 | * is associated with a destination, not a source. | 1218 | * is associated with a destination, not a source. |
1218 | */ | 1219 | */ |
1219 | static int b_addqchr(o_string *o, int ch, int quote) | 1220 | static void b_addqchr(o_string *o, int ch, int quote) |
1220 | { | 1221 | { |
1221 | if (quote && strchr("*?[\\", ch)) { | 1222 | if (quote && strchr("*?[\\", ch)) { |
1222 | int rc; | 1223 | b_addchr(o, '\\'); |
1223 | rc = b_addchr(o, '\\'); | 1224 | } |
1224 | if (rc) | 1225 | b_addchr(o, ch); |
1225 | return rc; | 1226 | } |
1227 | |||
1228 | /* A special kind of o_string for $VAR and `cmd` expansion. | ||
1229 | * It contains char* list[] at the beginning, which is grown in 16 element | ||
1230 | * increments. Actual string data starts at the next multiple of 16. | ||
1231 | * list[i] contains an INDEX (int!) into this string data. | ||
1232 | * It means that if list[] needs to grow, data needs to be moved higher up | ||
1233 | * but list[i]'s need not be modified. | ||
1234 | * NB: remembering how many list[i]'s you have there is crucial. | ||
1235 | * b_finalize_list() operation post-processes this structure - calculates | ||
1236 | * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well. | ||
1237 | */ | ||
1238 | static int b_addptr(o_string *o, int n) | ||
1239 | { | ||
1240 | char **list = (char**)o->data; | ||
1241 | int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); | ||
1242 | int string_len = o->length - string_start; | ||
1243 | |||
1244 | if (!o->has_empty_slot) { | ||
1245 | if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */ | ||
1246 | /* list[n] points to string_start, make space for 16 more pointers */ | ||
1247 | o->maxlen += 0x10 * sizeof(list[0]); | ||
1248 | o->data = xrealloc(o->data, o->maxlen + 1); | ||
1249 | list = (char**)o->data; | ||
1250 | memmove(list + n + 0x10, list + n, string_len); | ||
1251 | o->length += 0x10 * sizeof(list[0]); | ||
1252 | } | ||
1253 | } else { | ||
1254 | /* We have empty slot at list[n], reuse without growth */ | ||
1255 | o->has_empty_slot = 0; | ||
1256 | } | ||
1257 | list[n] = (char*)string_len; | ||
1258 | return n + 1; | ||
1259 | } | ||
1260 | |||
1261 | static int b_get_last_ptr(o_string *o, int n) | ||
1262 | { | ||
1263 | char **list = (char**)o->data; | ||
1264 | int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); | ||
1265 | |||
1266 | return ((int)list[n-1]) + string_start; | ||
1267 | } | ||
1268 | |||
1269 | static char **b_finalize_list(o_string *o, int n) | ||
1270 | { | ||
1271 | char **list = (char**)o->data; | ||
1272 | int string_start; | ||
1273 | |||
1274 | b_addptr(o, n); /* force growth for list[n] if necessary */ | ||
1275 | string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); | ||
1276 | list[n] = NULL; | ||
1277 | while (n) { | ||
1278 | n--; | ||
1279 | list[n] = o->data + (int)list[n] + string_start; | ||
1280 | } | ||
1281 | return list; | ||
1282 | } | ||
1283 | |||
1284 | #ifdef DEBUG_EXPAND | ||
1285 | static void b_debug_list(const char *prefix, o_string *o, int n) | ||
1286 | { | ||
1287 | char **list = (char**)o->data; | ||
1288 | int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]); | ||
1289 | int i = 0; | ||
1290 | fprintf(stderr, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d\n", | ||
1291 | prefix, list, n, string_start, o->length, o->maxlen); | ||
1292 | while (i < n) { | ||
1293 | fprintf(stderr, " list[%d]=%d '%s'\n", i, (int)list[i], | ||
1294 | o->data + (int)list[i] + string_start); | ||
1295 | i++; | ||
1296 | } | ||
1297 | if (n) { | ||
1298 | const char *p = o->data + (int)list[n] + string_start; | ||
1299 | fprintf(stderr, " total_sz:%d\n", (p + strlen(p) + 1) - o->data); | ||
1226 | } | 1300 | } |
1227 | return b_addchr(o, ch); | ||
1228 | } | 1301 | } |
1302 | #else | ||
1303 | #define b_debug_list(prefix, o, n) ((void)0) | ||
1304 | #endif | ||
1305 | |||
1229 | 1306 | ||
1307 | /* | ||
1308 | * in_str support | ||
1309 | */ | ||
1230 | static int static_get(struct in_str *i) | 1310 | static int static_get(struct in_str *i) |
1231 | { | 1311 | { |
1232 | int ch = *i->p++; | 1312 | int ch = *i->p++; |
@@ -2461,106 +2541,25 @@ static int xglob(o_string *dest, char ***pglob) | |||
2461 | * followed by strings themself. | 2541 | * followed by strings themself. |
2462 | * Caller can deallocate entire list by single free(list). */ | 2542 | * Caller can deallocate entire list by single free(list). */ |
2463 | 2543 | ||
2464 | /* Helpers first: | ||
2465 | * count_XXX estimates size of the block we need. It's okay | ||
2466 | * to over-estimate sizes a bit, if it makes code simpler */ | ||
2467 | static int count_ifs(const char *str) | ||
2468 | { | ||
2469 | int cnt = 0; | ||
2470 | debug_printf_expand("count_ifs('%s') ifs='%s'", str, ifs); | ||
2471 | while (1) { | ||
2472 | str += strcspn(str, ifs); | ||
2473 | if (!*str) break; | ||
2474 | str++; /* str += strspn(str, ifs); */ | ||
2475 | cnt++; /* cnt += strspn(str, ifs); - but this code is larger */ | ||
2476 | } | ||
2477 | debug_printf_expand(" return %d\n", cnt); | ||
2478 | return cnt; | ||
2479 | } | ||
2480 | |||
2481 | static void count_var_expansion_space(int *countp, int *lenp, char *arg) | ||
2482 | { | ||
2483 | char first_ch; | ||
2484 | int i; | ||
2485 | int len = *lenp; | ||
2486 | int count = *countp; | ||
2487 | const char *val; | ||
2488 | char *p; | ||
2489 | |||
2490 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL))) { | ||
2491 | len += p - arg; | ||
2492 | arg = ++p; | ||
2493 | p = strchr(p, SPECIAL_VAR_SYMBOL); | ||
2494 | first_ch = arg[0]; | ||
2495 | |||
2496 | switch (first_ch & 0x7f) { | ||
2497 | /* high bit in 1st_ch indicates that var is double-quoted */ | ||
2498 | case '$': /* pid */ | ||
2499 | case '!': /* bg pid */ | ||
2500 | case '?': /* exitcode */ | ||
2501 | case '#': /* argc */ | ||
2502 | len += sizeof(int)*3 + 1; /* enough for int */ | ||
2503 | break; | ||
2504 | case '*': | ||
2505 | case '@': | ||
2506 | for (i = 1; global_argv[i]; i++) { | ||
2507 | len += strlen(global_argv[i]) + 1; | ||
2508 | count++; | ||
2509 | if (!(first_ch & 0x80)) | ||
2510 | count += count_ifs(global_argv[i]); | ||
2511 | } | ||
2512 | break; | ||
2513 | default: | ||
2514 | *p = '\0'; | ||
2515 | arg[0] = first_ch & 0x7f; | ||
2516 | if (isdigit(arg[0])) { | ||
2517 | i = xatoi_u(arg); | ||
2518 | val = NULL; | ||
2519 | if (i < global_argc) | ||
2520 | val = global_argv[i]; | ||
2521 | } else | ||
2522 | val = lookup_param(arg); | ||
2523 | arg[0] = first_ch; | ||
2524 | *p = SPECIAL_VAR_SYMBOL; | ||
2525 | |||
2526 | if (val) { | ||
2527 | len += strlen(val) + 1; | ||
2528 | if (!(first_ch & 0x80)) | ||
2529 | count += count_ifs(val); | ||
2530 | } | ||
2531 | } | ||
2532 | arg = ++p; | ||
2533 | } | ||
2534 | |||
2535 | len += strlen(arg) + 1; | ||
2536 | count++; | ||
2537 | *lenp = len; | ||
2538 | *countp = count; | ||
2539 | } | ||
2540 | |||
2541 | /* Store given string, finalizing the word and starting new one whenever | 2544 | /* Store given string, finalizing the word and starting new one whenever |
2542 | * we encounter ifs char(s). This is used for expanding variable values. | 2545 | * we encounter ifs char(s). This is used for expanding variable values. |
2543 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ | 2546 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ |
2544 | static int expand_on_ifs(char **list, int n, char **posp, const char *str) | 2547 | static int expand_on_ifs(o_string *output, int n, const char *str) |
2545 | { | 2548 | { |
2546 | char *pos = *posp; | ||
2547 | while (1) { | 2549 | while (1) { |
2548 | int word_len = strcspn(str, ifs); | 2550 | int word_len = strcspn(str, ifs); |
2549 | if (word_len) { | 2551 | if (word_len) { |
2550 | memcpy(pos, str, word_len); /* store non-ifs chars */ | 2552 | b_addstr(output, str, word_len); /* store non-ifs chars */ |
2551 | pos += word_len; | ||
2552 | str += word_len; | 2553 | str += word_len; |
2553 | } | 2554 | } |
2554 | if (!*str) /* EOL - do not finalize word */ | 2555 | if (!*str) /* EOL - do not finalize word */ |
2555 | break; | 2556 | break; |
2556 | *pos++ = '\0'; | 2557 | b_addchr(output, '\0'); |
2557 | if (n) debug_printf_expand("expand_on_ifs finalized list[%d]=%p '%s' " | 2558 | b_debug_list("expand_on_ifs", output, n); |
2558 | "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1], | 2559 | n = b_addptr(output, n); |
2559 | strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos); | ||
2560 | list[n++] = pos; | ||
2561 | str += strspn(str, ifs); /* skip ifs chars */ | 2560 | str += strspn(str, ifs); /* skip ifs chars */ |
2562 | } | 2561 | } |
2563 | *posp = pos; | 2562 | b_debug_list("expand_on_ifs[1]", output, n); |
2564 | return n; | 2563 | return n; |
2565 | } | 2564 | } |
2566 | 2565 | ||
@@ -2571,7 +2570,7 @@ static int expand_on_ifs(char **list, int n, char **posp, const char *str) | |||
2571 | * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ | 2570 | * 'echo -$*-'. If you play here, you must run testsuite afterwards! */ |
2572 | /* NB: another bug is that we cannot detect empty strings yet: | 2571 | /* NB: another bug is that we cannot detect empty strings yet: |
2573 | * "" or $empty"" expands to zero words, has to expand to empty word */ | 2572 | * "" or $empty"" expands to zero words, has to expand to empty word */ |
2574 | static int expand_vars_to_list(char **list, int n, char **posp, char *arg, char or_mask) | 2573 | static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) |
2575 | { | 2574 | { |
2576 | /* or_mask is either 0 (normal case) or 0x80 | 2575 | /* or_mask is either 0 (normal case) or 0x80 |
2577 | * (expansion of right-hand side of assignment == 1-element expand) */ | 2576 | * (expansion of right-hand side of assignment == 1-element expand) */ |
@@ -2580,18 +2579,19 @@ static int expand_vars_to_list(char **list, int n, char **posp, char *arg, char | |||
2580 | int i; | 2579 | int i; |
2581 | const char *val; | 2580 | const char *val; |
2582 | char *p; | 2581 | char *p; |
2583 | char *pos = *posp; | ||
2584 | 2582 | ||
2585 | ored_ch = 0; | 2583 | ored_ch = 0; |
2586 | 2584 | ||
2587 | if (n) debug_printf_expand("expand_vars_to_list finalized list[%d]=%p '%s' " | 2585 | debug_printf_expand("expand_vars_to_list: arg '%s'\n", arg); |
2588 | "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1], | 2586 | b_debug_list("expand_vars_to_list", output, n); |
2589 | strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos); | 2587 | n = b_addptr(output, n); |
2590 | list[n++] = pos; | 2588 | b_debug_list("expand_vars_to_list[0]", output, n); |
2589 | |||
2590 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) { | ||
2591 | o_string subst_result = NULL_O_STRING; | ||
2591 | 2592 | ||
2592 | while ((p = strchr(arg, SPECIAL_VAR_SYMBOL))) { | 2593 | b_addstr(output, arg, p - arg); |
2593 | memcpy(pos, arg, p - arg); | 2594 | b_debug_list("expand_vars_to_list[1]", output, n); |
2594 | pos += (p - arg); | ||
2595 | arg = ++p; | 2595 | arg = ++p; |
2596 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 2596 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
2597 | 2597 | ||
@@ -2620,16 +2620,15 @@ static int expand_vars_to_list(char **list, int n, char **posp, char *arg, char | |||
2620 | break; | 2620 | break; |
2621 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ | 2621 | if (!(first_ch & 0x80)) { /* unquoted $* or $@ */ |
2622 | while (global_argv[i]) { | 2622 | while (global_argv[i]) { |
2623 | n = expand_on_ifs(list, n, &pos, global_argv[i]); | 2623 | n = expand_on_ifs(output, n, global_argv[i]); |
2624 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, global_argc-1); | 2624 | debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, global_argc-1); |
2625 | if (global_argv[i++][0] && global_argv[i]) { | 2625 | if (global_argv[i++][0] && global_argv[i]) { |
2626 | /* this argv[] is not empty and not last: | 2626 | /* this argv[] is not empty and not last: |
2627 | * put terminating NUL, start new word */ | 2627 | * put terminating NUL, start new word */ |
2628 | *pos++ = '\0'; | 2628 | b_addchr(output, '\0'); |
2629 | if (n) debug_printf_expand("expand_vars_to_list 2 finalized list[%d]=%p '%s' " | 2629 | b_debug_list("expand_vars_to_list[2]", output, n); |
2630 | "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1], | 2630 | n = b_addptr(output, n); |
2631 | strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos); | 2631 | b_debug_list("expand_vars_to_list[3]", output, n); |
2632 | list[n++] = pos; | ||
2633 | } | 2632 | } |
2634 | } | 2633 | } |
2635 | } else | 2634 | } else |
@@ -2637,27 +2636,34 @@ static int expand_vars_to_list(char **list, int n, char **posp, char *arg, char | |||
2637 | * and in this case should treat it like '$*' - see 'else...' below */ | 2636 | * and in this case should treat it like '$*' - see 'else...' below */ |
2638 | if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ | 2637 | if (first_ch == ('@'|0x80) && !or_mask) { /* quoted $@ */ |
2639 | while (1) { | 2638 | while (1) { |
2640 | strcpy(pos, global_argv[i]); | 2639 | b_addstr(output, global_argv[i], strlen(global_argv[i])); |
2641 | pos += strlen(global_argv[i]); | ||
2642 | if (++i >= global_argc) | 2640 | if (++i >= global_argc) |
2643 | break; | 2641 | break; |
2644 | *pos++ = '\0'; | 2642 | b_addchr(output, '\0'); |
2645 | if (n) debug_printf_expand("expand_vars_to_list 3 finalized list[%d]=%p '%s' " | 2643 | b_debug_list("expand_vars_to_list[4]", output, n); |
2646 | "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1], | 2644 | n = b_addptr(output, n); |
2647 | strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos); | ||
2648 | list[n++] = pos; | ||
2649 | } | 2645 | } |
2650 | } else { /* quoted $*: add as one word */ | 2646 | } else { /* quoted $*: add as one word */ |
2651 | while (1) { | 2647 | while (1) { |
2652 | strcpy(pos, global_argv[i]); | 2648 | b_addstr(output, global_argv[i], strlen(global_argv[i])); |
2653 | pos += strlen(global_argv[i]); | ||
2654 | if (!global_argv[++i]) | 2649 | if (!global_argv[++i]) |
2655 | break; | 2650 | break; |
2656 | if (ifs[0]) | 2651 | if (ifs[0]) |
2657 | *pos++ = ifs[0]; | 2652 | b_addchr(output, ifs[0]); |
2658 | } | 2653 | } |
2659 | } | 2654 | } |
2660 | break; | 2655 | break; |
2656 | case '`': { | ||
2657 | struct in_str input; | ||
2658 | *p = '\0'; | ||
2659 | arg++; | ||
2660 | //bb_error_msg("SUBST '%s' first_ch %x", arg, first_ch); | ||
2661 | setup_string_in_str(&input, arg); | ||
2662 | process_command_subs(&subst_result, &input, NULL); | ||
2663 | //bb_error_msg("RES '%s'", subst_result.data); | ||
2664 | val = subst_result.data; | ||
2665 | goto store_val; | ||
2666 | } | ||
2661 | default: | 2667 | default: |
2662 | *p = '\0'; | 2668 | *p = '\0'; |
2663 | arg[0] = first_ch & 0x7f; | 2669 | arg[0] = first_ch & 0x7f; |
@@ -2669,66 +2675,52 @@ static int expand_vars_to_list(char **list, int n, char **posp, char *arg, char | |||
2669 | } else | 2675 | } else |
2670 | val = lookup_param(arg); | 2676 | val = lookup_param(arg); |
2671 | arg[0] = first_ch; | 2677 | arg[0] = first_ch; |
2678 | store_val: | ||
2672 | *p = SPECIAL_VAR_SYMBOL; | 2679 | *p = SPECIAL_VAR_SYMBOL; |
2673 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | 2680 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ |
2674 | if (val) { | 2681 | if (val) { |
2675 | n = expand_on_ifs(list, n, &pos, val); | 2682 | n = expand_on_ifs(output, n, val); |
2676 | val = NULL; | 2683 | val = NULL; |
2677 | } | 2684 | } |
2678 | } /* else: quoted $VAR, val will be appended at pos */ | 2685 | } /* else: quoted $VAR, val will be appended below */ |
2679 | } | ||
2680 | if (val) { | ||
2681 | strcpy(pos, val); | ||
2682 | pos += strlen(val); | ||
2683 | } | 2686 | } |
2687 | if (val) | ||
2688 | b_addstr(output, val, strlen(val)); | ||
2689 | |||
2690 | b_free(&subst_result); | ||
2684 | arg = ++p; | 2691 | arg = ++p; |
2685 | } | 2692 | } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */ |
2686 | debug_printf_expand("expand_vars_to_list adding tail '%s' at %p\n", arg, pos); | 2693 | |
2687 | strcpy(pos, arg); | 2694 | b_debug_list("expand_vars_to_list[a]", output, n); |
2688 | pos += strlen(arg) + 1; | 2695 | b_addstr(output, arg, strlen(arg) + 1); |
2689 | if (pos == list[n-1] + 1) { /* expansion is empty */ | 2696 | b_debug_list("expand_vars_to_list[b]", output, n); |
2697 | //TESTME | ||
2698 | if (output->length - 1 == b_get_last_ptr(output, n)) { /* expansion is empty */ | ||
2690 | if (!(ored_ch & 0x80)) { /* all vars were not quoted... */ | 2699 | if (!(ored_ch & 0x80)) { /* all vars were not quoted... */ |
2691 | debug_printf_expand("expand_vars_to_list list[%d] empty, going back\n", n); | ||
2692 | pos--; | ||
2693 | n--; | 2700 | n--; |
2701 | /* allow to reuse list[n] later without re-growth */ | ||
2702 | output->has_empty_slot = 1; | ||
2694 | } | 2703 | } |
2695 | } | 2704 | } |
2696 | 2705 | ||
2697 | *posp = pos; | ||
2698 | return n; | 2706 | return n; |
2699 | } | 2707 | } |
2700 | 2708 | ||
2701 | static char **expand_variables(char **argv, char or_mask) | 2709 | static char **expand_variables(char **argv, char or_mask) |
2702 | { | 2710 | { |
2703 | int n; | 2711 | int n; |
2704 | int count = 1; | 2712 | char **list; |
2705 | int len = 0; | 2713 | char **v; |
2706 | char *pos, **v, **list; | 2714 | o_string output = NULL_O_STRING; |
2707 | 2715 | ||
2708 | v = argv; | ||
2709 | if (!*v) debug_printf_expand("count_var_expansion_space: " | ||
2710 | "argv[0]=NULL count=%d len=%d alloc_space=%d\n", | ||
2711 | count, len, sizeof(char*) * count + len); | ||
2712 | while (*v) { | ||
2713 | count_var_expansion_space(&count, &len, *v); | ||
2714 | debug_printf_expand("count_var_expansion_space: " | ||
2715 | "'%s' count=%d len=%d alloc_space=%d\n", | ||
2716 | *v, count, len, sizeof(char*) * count + len); | ||
2717 | v++; | ||
2718 | } | ||
2719 | len += sizeof(char*) * count; /* total to alloc */ | ||
2720 | list = xmalloc(len); | ||
2721 | pos = (char*)(list + count); | ||
2722 | debug_printf_expand("list=%p, list[0] should be %p\n", list, pos); | ||
2723 | n = 0; | 2716 | n = 0; |
2724 | v = argv; | 2717 | v = argv; |
2725 | while (*v) | 2718 | while (*v) |
2726 | n = expand_vars_to_list(list, n, &pos, *v++, or_mask); | 2719 | n = expand_vars_to_list(&output, n, *v++, or_mask); |
2720 | b_debug_list("expand_variables", &output, n); | ||
2727 | 2721 | ||
2728 | if (n) debug_printf_expand("finalized list[%d]=%p '%s' " | 2722 | /* output.data (malloced) gets returned in "list" */ |
2729 | "strlen=%d next=%p pos=%p\n", n-1, list[n-1], list[n-1], | 2723 | list = b_finalize_list(&output, n); |
2730 | strlen(list[n-1]), list[n-1] + strlen(list[n-1]) + 1, pos); | ||
2731 | list[n] = NULL; | ||
2732 | 2724 | ||
2733 | #ifdef DEBUG_EXPAND | 2725 | #ifdef DEBUG_EXPAND |
2734 | { | 2726 | { |
@@ -2737,12 +2729,8 @@ static char **expand_variables(char **argv, char or_mask) | |||
2737 | debug_printf_expand("list[%d]=%p '%s'\n", m, list[m], list[m]); | 2729 | debug_printf_expand("list[%d]=%p '%s'\n", m, list[m], list[m]); |
2738 | m++; | 2730 | m++; |
2739 | } | 2731 | } |
2740 | debug_printf_expand("used_space=%d\n", pos - (char*)list); | ||
2741 | } | 2732 | } |
2742 | #endif | 2733 | #endif |
2743 | if (ENABLE_HUSH_DEBUG) | ||
2744 | if (pos - (char*)list > len) | ||
2745 | bb_error_msg_and_die("BUG in varexp"); | ||
2746 | return list; | 2734 | return list; |
2747 | } | 2735 | } |
2748 | 2736 | ||
@@ -3391,6 +3379,116 @@ static const char *lookup_param(const char *src) | |||
3391 | return NULL; | 3379 | return NULL; |
3392 | } | 3380 | } |
3393 | 3381 | ||
3382 | #if ENABLE_HUSH_TICK | ||
3383 | /* Subroutines for copying $(...) and `...` things */ | ||
3384 | static void add_till_backquote(o_string *dest, struct in_str *input); | ||
3385 | /* '...' */ | ||
3386 | static void add_till_single_quote(o_string *dest, struct in_str *input) | ||
3387 | { | ||
3388 | while (1) { | ||
3389 | int ch = b_getch(input); | ||
3390 | if (ch == EOF) | ||
3391 | break; | ||
3392 | if (ch == '\'') | ||
3393 | break; | ||
3394 | b_addchr(dest, ch); | ||
3395 | } | ||
3396 | } | ||
3397 | /* "...\"...`..`...." - do we need to handle "...$(..)..." too? */ | ||
3398 | static void add_till_double_quote(o_string *dest, struct in_str *input) | ||
3399 | { | ||
3400 | while (1) { | ||
3401 | int ch = b_getch(input); | ||
3402 | if (ch == '"') | ||
3403 | break; | ||
3404 | if (ch == '\\') { /* \x. Copy both chars. */ | ||
3405 | b_addchr(dest, ch); | ||
3406 | ch = b_getch(input); | ||
3407 | } | ||
3408 | if (ch == EOF) | ||
3409 | break; | ||
3410 | b_addchr(dest, ch); | ||
3411 | if (ch == '`') { | ||
3412 | add_till_backquote(dest, input); | ||
3413 | b_addchr(dest, ch); | ||
3414 | continue; | ||
3415 | } | ||
3416 | // if (ch == '$') ... | ||
3417 | } | ||
3418 | } | ||
3419 | /* Process `cmd` - copy contents until "`" is seen. Complicated by | ||
3420 | * \` quoting. | ||
3421 | * "Within the backquoted style of command substitution, backslash | ||
3422 | * shall retain its literal meaning, except when followed by: '$', '`', or '\'. | ||
3423 | * The search for the matching backquote shall be satisfied by the first | ||
3424 | * backquote found without a preceding backslash; during this search, | ||
3425 | * if a non-escaped backquote is encountered within a shell comment, | ||
3426 | * a here-document, an embedded command substitution of the $(command) | ||
3427 | * form, or a quoted string, undefined results occur. A single-quoted | ||
3428 | * or double-quoted string that begins, but does not end, within the | ||
3429 | * "`...`" sequence produces undefined results." | ||
3430 | * Example Output | ||
3431 | * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST | ||
3432 | */ | ||
3433 | static void add_till_backquote(o_string *dest, struct in_str *input) | ||
3434 | { | ||
3435 | while (1) { | ||
3436 | int ch = b_getch(input); | ||
3437 | //bb_error_msg("ADD '%c'", ch); | ||
3438 | if (ch == '`') | ||
3439 | break; | ||
3440 | if (ch == '\\') { /* \x. Copy both chars unless it is \` */ | ||
3441 | int ch2 = b_getch(input); | ||
3442 | if (ch2 != '`' && ch2 != '$' && ch2 != '\\') | ||
3443 | b_addchr(dest, ch); | ||
3444 | ch = ch2; | ||
3445 | } | ||
3446 | if (ch == EOF) | ||
3447 | break; | ||
3448 | b_addchr(dest, ch); | ||
3449 | } | ||
3450 | } | ||
3451 | /* Process $(cmd) - copy contents until ")" is seen. Complicated by | ||
3452 | * quoting and nested ()s. | ||
3453 | * "With the $(command) style of command substitution, all characters | ||
3454 | * following the open parenthesis to the matching closing parenthesis | ||
3455 | * constitute the command. Any valid shell script can be used for command, | ||
3456 | * except a script consisting solely of redirections which produces | ||
3457 | * unspecified results." | ||
3458 | * Example Output | ||
3459 | * echo $(echo '(TEST)' BEST) (TEST) BEST | ||
3460 | * echo $(echo 'TEST)' BEST) TEST) BEST | ||
3461 | * echo $(echo \(\(TEST\) BEST) ((TEST) BEST | ||
3462 | */ | ||
3463 | static void add_till_closing_curly_brace(o_string *dest, struct in_str *input) | ||
3464 | { | ||
3465 | int count = 0; | ||
3466 | while (1) { | ||
3467 | int ch = b_getch(input); | ||
3468 | if (ch == EOF) | ||
3469 | break; | ||
3470 | if (ch == '(') | ||
3471 | count++; | ||
3472 | if (ch == ')') | ||
3473 | if (--count < 0) | ||
3474 | break; | ||
3475 | b_addchr(dest, ch); | ||
3476 | if (ch == '\'') { | ||
3477 | add_till_single_quote(dest, input); | ||
3478 | b_addchr(dest, ch); | ||
3479 | continue; | ||
3480 | } | ||
3481 | if (ch == '"') { | ||
3482 | add_till_double_quote(dest, input); | ||
3483 | b_addchr(dest, ch); | ||
3484 | continue; | ||
3485 | } | ||
3486 | } | ||
3487 | } | ||
3488 | #endif /* ENABLE_HUSH_TICK */ | ||
3489 | |||
3490 | //FIXME: remove ctx and sp | ||
3491 | |||
3394 | /* return code: 0 for OK, 1 for syntax error */ | 3492 | /* return code: 0 for OK, 1 for syntax error */ |
3395 | static int handle_dollar(o_string *dest, /*struct p_context *ctx,*/ struct in_str *input) | 3493 | static int handle_dollar(o_string *dest, /*struct p_context *ctx,*/ struct in_str *input) |
3396 | { | 3494 | { |
@@ -3450,7 +3548,10 @@ static int handle_dollar(o_string *dest, /*struct p_context *ctx,*/ struct in_st | |||
3450 | #if ENABLE_HUSH_TICK | 3548 | #if ENABLE_HUSH_TICK |
3451 | case '(': | 3549 | case '(': |
3452 | b_getch(input); | 3550 | b_getch(input); |
3453 | process_command_subs(dest, /*ctx,*/ input, ")"); | 3551 | b_addchr(dest, SPECIAL_VAR_SYMBOL); |
3552 | b_addchr(dest, quote_mask | '`'); | ||
3553 | add_till_closing_curly_brace(dest, input); | ||
3554 | b_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
3454 | break; | 3555 | break; |
3455 | #endif | 3556 | #endif |
3456 | case '-': | 3557 | case '-': |
@@ -3572,9 +3673,15 @@ static int parse_stream(o_string *dest, struct p_context *ctx, | |||
3572 | dest->o_quote ^= 1; /* invert */ | 3673 | dest->o_quote ^= 1; /* invert */ |
3573 | break; | 3674 | break; |
3574 | #if ENABLE_HUSH_TICK | 3675 | #if ENABLE_HUSH_TICK |
3575 | case '`': | 3676 | case '`': { |
3576 | process_command_subs(dest, /*ctx,*/ input, "`"); | 3677 | //int pos = dest->length; |
3678 | b_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
3679 | b_addchr(dest, dest->o_quote ? 0x80 | '`' : '`'); | ||
3680 | add_till_backquote(dest, input); | ||
3681 | b_addchr(dest, SPECIAL_VAR_SYMBOL); | ||
3682 | //bb_error_msg("RES '%s'", dest->data + pos); | ||
3577 | break; | 3683 | break; |
3684 | } | ||
3578 | #endif | 3685 | #endif |
3579 | case '>': | 3686 | case '>': |
3580 | redir_fd = redirect_opt_num(dest); | 3687 | redir_fd = redirect_opt_num(dest); |