diff options
author | Mike Frysinger <vapier@gentoo.org> | 2009-03-28 18:55:03 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2009-03-28 18:55:03 +0000 |
commit | 6379bb4fde15dae3be641625e9af377eeb5a64d8 (patch) | |
tree | d93d855ce89dcf5aff36554b4730f6ab4254b4e3 | |
parent | 42ab86520e08d8b598f46b7a4754b2730e55f173 (diff) | |
download | busybox-w32-6379bb4fde15dae3be641625e9af377eeb5a64d8.tar.gz busybox-w32-6379bb4fde15dae3be641625e9af377eeb5a64d8.tar.bz2 busybox-w32-6379bb4fde15dae3be641625e9af377eeb5a64d8.zip |
implement most POSIX parameter expansions (~+500bytes)
-rw-r--r-- | shell/hush.c | 158 |
1 files changed, 140 insertions, 18 deletions
diff --git a/shell/hush.c b/shell/hush.c index 9aeb0f6a8..84dd9e13c 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -43,12 +43,13 @@ | |||
43 | * Here Documents ( << word ) | 43 | * Here Documents ( << word ) |
44 | * Functions | 44 | * Functions |
45 | * Tilde Expansion | 45 | * Tilde Expansion |
46 | * fancy forms of Parameter Expansion: ${var:-val} | 46 | * Parameter Expansion for substring processing ${var#word} ${var%word} |
47 | * | 47 | * |
48 | * Bash stuff maybe optional enable: | 48 | * Bash stuff maybe optional enable: |
49 | * &> and >& redirection of stdout+stderr | 49 | * &> and >& redirection of stdout+stderr |
50 | * Brace expansion | 50 | * Brace expansion |
51 | * reserved words: [[ ]] function select | 51 | * reserved words: [[ ]] function select |
52 | * substrings ${var:1:5} | ||
52 | * | 53 | * |
53 | * Major bugs: | 54 | * Major bugs: |
54 | * job handling woefully incomplete and buggy (improved --vda) | 55 | * job handling woefully incomplete and buggy (improved --vda) |
@@ -589,18 +590,20 @@ static const struct built_in_command bltins[] = { | |||
589 | 590 | ||
590 | #if 1 | 591 | #if 1 |
591 | /* Normal */ | 592 | /* Normal */ |
592 | static void syntax(const char *msg) | 593 | static void maybe_die(const char *notice, const char *msg) |
593 | { | 594 | { |
594 | #if ENABLE_HUSH_INTERACTIVE | ||
595 | /* Was using fancy stuff: | 595 | /* Was using fancy stuff: |
596 | * (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...) | 596 | * (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die)(...params...) |
597 | * but it SEGVs. ?! Oh well... explicit temp ptr works around that */ | 597 | * but it SEGVs. ?! Oh well... explicit temp ptr works around that */ |
598 | void FAST_FUNC (*fp)(const char *s, ...); | 598 | void FAST_FUNC (*fp)(const char *s, ...) = bb_error_msg_and_die; |
599 | #if ENABLE_HUSH_INTERACTIVE | ||
599 | fp = (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die); | 600 | fp = (G.interactive_fd ? bb_error_msg : bb_error_msg_and_die); |
600 | fp(msg ? "%s: %s" : "syntax error", "syntax error", msg); | ||
601 | #else | ||
602 | bb_error_msg_and_die(msg ? "%s: %s" : "syntax error", "syntax error", msg); | ||
603 | #endif | 601 | #endif |
602 | fp(msg ? "%s: %s" : notice, notice, msg); | ||
603 | } | ||
604 | static void syntax(const char *msg) | ||
605 | { | ||
606 | maybe_die("syntax error", msg); | ||
604 | } | 607 | } |
605 | #else | 608 | #else |
606 | /* Debug */ | 609 | /* Debug */ |
@@ -920,7 +923,13 @@ static const char *lookup_param(const char *src) | |||
920 | } | 923 | } |
921 | 924 | ||
922 | /* str holds "NAME=VAL" and is expected to be malloced. | 925 | /* str holds "NAME=VAL" and is expected to be malloced. |
923 | * We take ownership of it. */ | 926 | * We take ownership of it. |
927 | * flg_export is used by: | ||
928 | * 0: do not export | ||
929 | * 1: export | ||
930 | * -1: if NAME is set, leave export status alone | ||
931 | * if NAME is not set, do not export | ||
932 | */ | ||
924 | static int set_local_var(char *str, int flg_export) | 933 | static int set_local_var(char *str, int flg_export) |
925 | { | 934 | { |
926 | struct variable *cur; | 935 | struct variable *cur; |
@@ -980,7 +989,7 @@ static int set_local_var(char *str, int flg_export) | |||
980 | set_str_and_exp: | 989 | set_str_and_exp: |
981 | cur->varstr = str; | 990 | cur->varstr = str; |
982 | exp: | 991 | exp: |
983 | if (flg_export) | 992 | if (flg_export == 1) |
984 | cur->flg_export = 1; | 993 | cur->flg_export = 1; |
985 | if (cur->flg_export) { | 994 | if (cur->flg_export) { |
986 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); | 995 | debug_printf_env("%s: putenv '%s'\n", __func__, cur->varstr); |
@@ -1548,6 +1557,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1548 | val = utoa(G.last_return_code); | 1557 | val = utoa(G.last_return_code); |
1549 | break; | 1558 | break; |
1550 | case '#': /* argc */ | 1559 | case '#': /* argc */ |
1560 | if (arg[1] != SPECIAL_VAR_SYMBOL) | ||
1561 | /* actually, it's a ${#var} */ | ||
1562 | goto case_default; | ||
1551 | val = utoa(G.global_argc ? G.global_argc-1 : 0); | 1563 | val = utoa(G.global_argc ? G.global_argc-1 : 0); |
1552 | break; | 1564 | break; |
1553 | case '*': | 1565 | case '*': |
@@ -1615,20 +1627,79 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1615 | } | 1627 | } |
1616 | #endif | 1628 | #endif |
1617 | default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ | 1629 | default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ |
1630 | case_default: { | ||
1631 | bool exp_len = false, exp_null = false; | ||
1632 | char *var = arg, exp_save, exp_op, *exp_word; | ||
1633 | size_t exp_off = 0; | ||
1618 | *p = '\0'; | 1634 | *p = '\0'; |
1619 | arg[0] = first_ch & 0x7f; | 1635 | arg[0] = first_ch & 0x7f; |
1620 | if (isdigit(arg[0])) { | 1636 | |
1621 | i = xatoi_u(arg); | 1637 | /* prepare for expansions */ |
1638 | if (var[0] == '#') { | ||
1639 | /* handle length expansion ${#var} */ | ||
1640 | exp_len = true; | ||
1641 | ++var; | ||
1642 | } else { | ||
1643 | /* maybe handle parameter expansion */ | ||
1644 | exp_off = strcspn(var, ":-=+?"); | ||
1645 | if (!var[exp_off]) | ||
1646 | exp_off = 0; | ||
1647 | if (exp_off) { | ||
1648 | exp_save = var[exp_off]; | ||
1649 | exp_null = exp_save == ':'; | ||
1650 | exp_word = var + exp_off; | ||
1651 | if (exp_null) ++exp_word; | ||
1652 | exp_op = *exp_word++; | ||
1653 | var[exp_off] = '\0'; | ||
1654 | } | ||
1655 | } | ||
1656 | |||
1657 | /* lookup the variable in question */ | ||
1658 | if (isdigit(var[0])) { | ||
1659 | i = xatoi_u(var); | ||
1622 | if (i < G.global_argc) | 1660 | if (i < G.global_argc) |
1623 | val = G.global_argv[i]; | 1661 | val = G.global_argv[i]; |
1624 | /* else val remains NULL: $N with too big N */ | 1662 | /* else val remains NULL: $N with too big N */ |
1625 | } else | 1663 | } else |
1626 | val = lookup_param(arg); | 1664 | val = lookup_param(var); |
1665 | |||
1666 | /* handle any expansions */ | ||
1667 | if (exp_len) { | ||
1668 | debug_printf_expand("expand: length of '%s' = ", val); | ||
1669 | val = utoa(val ? strlen(val) : 0); | ||
1670 | debug_printf_expand("%s\n", val); | ||
1671 | } else if (exp_off) { | ||
1672 | /* we need to do an expansion */ | ||
1673 | int exp_test = (!val || (exp_null && !val[0])); | ||
1674 | if (exp_op == '+') | ||
1675 | exp_test = !exp_test; | ||
1676 | debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op, | ||
1677 | exp_null ? "true" : "false", exp_test); | ||
1678 | if (exp_test) { | ||
1679 | if (exp_op == '?') | ||
1680 | maybe_die(var, *exp_word ? exp_word : "parameter null or not set"); | ||
1681 | else | ||
1682 | val = exp_word; | ||
1683 | |||
1684 | if (exp_op == '=') { | ||
1685 | if (isdigit(var[0]) || var[0] == '#') { | ||
1686 | maybe_die(var, "special vars cannot assign in this way"); | ||
1687 | val = NULL; | ||
1688 | } else { | ||
1689 | char *new_var = xmalloc(strlen(var) + strlen(val) + 2); | ||
1690 | sprintf(new_var, "%s=%s", var, val); | ||
1691 | set_local_var(new_var, -1); | ||
1692 | } | ||
1693 | } | ||
1694 | } | ||
1695 | var[exp_off] = exp_save; | ||
1696 | } | ||
1697 | |||
1627 | arg[0] = first_ch; | 1698 | arg[0] = first_ch; |
1699 | |||
1628 | #if ENABLE_HUSH_TICK | 1700 | #if ENABLE_HUSH_TICK |
1629 | store_val: | 1701 | store_val: |
1630 | #endif | 1702 | #endif |
1631 | *p = SPECIAL_VAR_SYMBOL; | ||
1632 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ | 1703 | if (!(first_ch & 0x80)) { /* unquoted $VAR */ |
1633 | debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote); | 1704 | debug_printf_expand("unquoted '%s', output->o_quote:%d\n", val, output->o_quote); |
1634 | if (val) { | 1705 | if (val) { |
@@ -1643,9 +1714,13 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask) | |||
1643 | debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); | 1714 | debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); |
1644 | } | 1715 | } |
1645 | } | 1716 | } |
1717 | } | ||
1646 | if (val) { | 1718 | if (val) { |
1647 | o_addQstr(output, val, strlen(val)); | 1719 | o_addQstr(output, val, strlen(val)); |
1648 | } | 1720 | } |
1721 | /* Do the check to avoid writing to a const string */ | ||
1722 | if (p && *p != SPECIAL_VAR_SYMBOL) | ||
1723 | *p = SPECIAL_VAR_SYMBOL; | ||
1649 | 1724 | ||
1650 | #if ENABLE_HUSH_TICK | 1725 | #if ENABLE_HUSH_TICK |
1651 | o_free(&subst_result); | 1726 | o_free(&subst_result); |
@@ -3625,6 +3700,7 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input) | |||
3625 | /* Return code: 0 for OK, 1 for syntax error */ | 3700 | /* Return code: 0 for OK, 1 for syntax error */ |
3626 | static int handle_dollar(o_string *dest, struct in_str *input) | 3701 | static int handle_dollar(o_string *dest, struct in_str *input) |
3627 | { | 3702 | { |
3703 | int expansion; | ||
3628 | int ch = i_peek(input); /* first character after the $ */ | 3704 | int ch = i_peek(input); /* first character after the $ */ |
3629 | unsigned char quote_mask = dest->o_quote ? 0x80 : 0; | 3705 | unsigned char quote_mask = dest->o_quote ? 0x80 : 0; |
3630 | 3706 | ||
@@ -3658,25 +3734,71 @@ static int handle_dollar(o_string *dest, struct in_str *input) | |||
3658 | case '*': /* args */ | 3734 | case '*': /* args */ |
3659 | case '@': /* args */ | 3735 | case '@': /* args */ |
3660 | goto make_one_char_var; | 3736 | goto make_one_char_var; |
3661 | case '{': | 3737 | case '{': { |
3738 | bool first_char; | ||
3739 | |||
3662 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 3740 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3663 | i_getch(input); | 3741 | i_getch(input); |
3664 | /* XXX maybe someone will try to escape the '}' */ | 3742 | /* XXX maybe someone will try to escape the '}' */ |
3743 | expansion = 0; | ||
3744 | first_char = true; | ||
3665 | while (1) { | 3745 | while (1) { |
3666 | ch = i_getch(input); | 3746 | ch = i_getch(input); |
3667 | if (ch == '}') | 3747 | if (ch == '}') |
3668 | break; | 3748 | break; |
3669 | if (!isalnum(ch) && ch != '_') { | 3749 | |
3670 | syntax("unterminated ${name}"); | 3750 | if (ch == '#' && first_char) |
3671 | debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); | 3751 | /* ${#var}: length of var contents */; |
3672 | return 1; | 3752 | |
3753 | else if (expansion < 2 && !isalnum(ch) && ch != '_') { | ||
3754 | /* handle parameter expansions | ||
3755 | * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02 | ||
3756 | */ | ||
3757 | if (first_char) | ||
3758 | goto case_default; | ||
3759 | switch (ch) { | ||
3760 | case ':': /* null modifier */ | ||
3761 | if (expansion == 0) { | ||
3762 | debug_printf_parse(": null modifier\n"); | ||
3763 | ++expansion; | ||
3764 | break; | ||
3765 | } | ||
3766 | goto case_default; | ||
3767 | |||
3768 | #if 0 /* not implemented yet :( */ | ||
3769 | case '#': /* remove prefix */ | ||
3770 | case '%': /* remove suffix */ | ||
3771 | if (expansion == 0) { | ||
3772 | debug_printf_parse(": remove suffix/prefix\n"); | ||
3773 | expansion = 2; | ||
3774 | break; | ||
3775 | } | ||
3776 | goto case_default; | ||
3777 | #endif | ||
3778 | |||
3779 | case '-': /* default value */ | ||
3780 | case '=': /* assign default */ | ||
3781 | case '+': /* alternative */ | ||
3782 | case '?': /* error indicate */ | ||
3783 | debug_printf_parse(": parameter expansion\n"); | ||
3784 | expansion = 2; | ||
3785 | break; | ||
3786 | |||
3787 | default: | ||
3788 | case_default: | ||
3789 | syntax("unterminated ${name}"); | ||
3790 | debug_printf_parse("handle_dollar return 1: unterminated ${name}\n"); | ||
3791 | return 1; | ||
3792 | } | ||
3673 | } | 3793 | } |
3674 | debug_printf_parse(": '%c'\n", ch); | 3794 | debug_printf_parse(": '%c'\n", ch); |
3675 | o_addchr(dest, ch | quote_mask); | 3795 | o_addchr(dest, ch | quote_mask); |
3676 | quote_mask = 0; | 3796 | quote_mask = 0; |
3797 | first_char = false; | ||
3677 | } | 3798 | } |
3678 | o_addchr(dest, SPECIAL_VAR_SYMBOL); | 3799 | o_addchr(dest, SPECIAL_VAR_SYMBOL); |
3679 | break; | 3800 | break; |
3801 | } | ||
3680 | #if ENABLE_HUSH_TICK | 3802 | #if ENABLE_HUSH_TICK |
3681 | case '(': { | 3803 | case '(': { |
3682 | //int pos = dest->length; | 3804 | //int pos = dest->length; |