aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2009-03-28 18:55:03 +0000
committerMike Frysinger <vapier@gentoo.org>2009-03-28 18:55:03 +0000
commit6379bb4fde15dae3be641625e9af377eeb5a64d8 (patch)
treed93d855ce89dcf5aff36554b4730f6ab4254b4e3
parent42ab86520e08d8b598f46b7a4754b2730e55f173 (diff)
downloadbusybox-w32-6379bb4fde15dae3be641625e9af377eeb5a64d8.tar.gz
busybox-w32-6379bb4fde15dae3be641625e9af377eeb5a64d8.tar.bz2
busybox-w32-6379bb4fde15dae3be641625e9af377eeb5a64d8.zip
implement most POSIX parameter expansions (~+500bytes)
-rw-r--r--shell/hush.c158
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 */
592static void syntax(const char *msg) 593static 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}
604static 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 */
924static int set_local_var(char *str, int flg_export) 933static 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 */
3626static int handle_dollar(o_string *dest, struct in_str *input) 3701static 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;