aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2009-04-02 16:31:29 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2009-04-02 16:31:29 +0000
commit2f1d394214c968181e9ab320f2c66f905f8352cf (patch)
treeaf349bf87374251f9c49c2edeeb8fc0168d70d92 /shell
parentb29eb6ed255ad87f49b70220d254810063c7ebf3 (diff)
downloadbusybox-w32-2f1d394214c968181e9ab320f2c66f905f8352cf.tar.gz
busybox-w32-2f1d394214c968181e9ab320f2c66f905f8352cf.tar.bz2
busybox-w32-2f1d394214c968181e9ab320f2c66f905f8352cf.zip
hush: make
a=55; echo $(($a + 1)) $((1 + $((2)) + `echo $a`)) work as expected function old new delta handle_dollar - 667 +667 parse_stream_dquoted - 316 +316 expand_variables 2124 2272 +148 is_assignment 134 215 +81 parse_stream 2038 1240 -798 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 2/1 up/down: 1212/-798) Total: 414 bytes
Diffstat (limited to 'shell')
-rw-r--r--shell/hush.c220
1 files changed, 164 insertions, 56 deletions
diff --git a/shell/hush.c b/shell/hush.c
index 3725191d8..64b6e87e8 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -1510,7 +1510,7 @@ static void debug_print_list(const char *prefix, o_string *o, int n)
1510 } 1510 }
1511 if (n) { 1511 if (n) {
1512 const char *p = o->data + (int)list[n - 1] + string_start; 1512 const char *p = o->data + (int)list[n - 1] + string_start;
1513 fprintf(stderr, " total_sz:%ld\n", (p + strlen(p) + 1) - o->data); 1513 fprintf(stderr, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
1514 } 1514 }
1515} 1515}
1516#else 1516#else
@@ -1644,6 +1644,14 @@ static char **o_finalize_list(o_string *o, int n)
1644} 1644}
1645 1645
1646 1646
1647/* Expansion can recurse */
1648#if ENABLE_HUSH_TICK
1649static int process_command_subs(o_string *dest,
1650 struct in_str *input, const char *subst_end);
1651#endif
1652static char *expand_string_to_string(const char *str);
1653static int parse_stream_dquoted(o_string *dest, struct in_str *input, int dquote_end);
1654
1647/* expand_strvec_to_strvec() takes a list of strings, expands 1655/* expand_strvec_to_strvec() takes a list of strings, expands
1648 * all variable references within and returns a pointer to 1656 * all variable references within and returns a pointer to
1649 * a list of expanded strings, possibly with larger number 1657 * a list of expanded strings, possibly with larger number
@@ -1678,11 +1686,6 @@ static int expand_on_ifs(o_string *output, int n, const char *str)
1678 return n; 1686 return n;
1679} 1687}
1680 1688
1681#if ENABLE_HUSH_TICK
1682static int process_command_subs(o_string *dest,
1683 struct in_str *input, const char *subst_end);
1684#endif
1685
1686/* Expand all variable references in given string, adding words to list[] 1689/* Expand all variable references in given string, adding words to list[]
1687 * at n, n+1,... positions. Return updated n (so that list[n] is next one 1690 * at n, n+1,... positions. Return updated n (so that list[n] is next one
1688 * to be filled). This routine is extremely tricky: has to deal with 1691 * to be filled). This routine is extremely tricky: has to deal with
@@ -1710,6 +1713,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1710#if ENABLE_HUSH_TICK 1713#if ENABLE_HUSH_TICK
1711 o_string subst_result = NULL_O_STRING; 1714 o_string subst_result = NULL_O_STRING;
1712#endif 1715#endif
1716#if ENABLE_SH_MATH_SUPPORT
1717 char arith_buf[sizeof(arith_t)*3 + 2];
1718#endif
1713 o_addstr(output, arg, p - arg); 1719 o_addstr(output, arg, p - arg);
1714 debug_print_list("expand_vars_to_list[1]", output, n); 1720 debug_print_list("expand_vars_to_list[1]", output, n);
1715 arg = ++p; 1721 arg = ++p;
@@ -1720,6 +1726,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1720 * expand to nothing (not even an empty string) */ 1726 * expand to nothing (not even an empty string) */
1721 if ((first_ch & 0x7f) != '@') 1727 if ((first_ch & 0x7f) != '@')
1722 ored_ch |= first_ch; 1728 ored_ch |= first_ch;
1729
1723 val = NULL; 1730 val = NULL;
1724 switch (first_ch & 0x7f) { 1731 switch (first_ch & 0x7f) {
1725 /* Highest bit in first_ch indicates that var is double-quoted */ 1732 /* Highest bit in first_ch indicates that var is double-quoted */
@@ -1803,19 +1810,52 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1803 } 1810 }
1804#endif 1811#endif
1805#if ENABLE_SH_MATH_SUPPORT 1812#if ENABLE_SH_MATH_SUPPORT
1806 case '+': { /* <SPECIAL_VAR_SYMBOL>(cmd<SPECIAL_VAR_SYMBOL> */ 1813 case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */
1807 arith_eval_hooks_t hooks; 1814 arith_eval_hooks_t hooks;
1808 arith_t res; 1815 arith_t res;
1809 char buf[30];
1810 int errcode; 1816 int errcode;
1817 char *exp_str;
1811 1818
1812 *p = '\0'; 1819 arg++; /* skip '+' */
1813 ++arg; 1820 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
1814 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); 1821 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
1822
1823 /* Optional: skip expansion if expr is simple ("a + 3", "i++" etc) */
1824 exp_str = arg;
1825 while (1) {
1826 unsigned char c = *exp_str++;
1827 if (c == '\0') {
1828 exp_str = NULL;
1829 goto skip_expand;
1830 }
1831 if (isdigit(c))
1832 continue;
1833 if (strchr(" \t+-*/%_", c) != NULL)
1834 continue;
1835 c |= 0x20; /* tolower */
1836 if (c >= 'a' && c <= 'z')
1837 continue;
1838 break;
1839 }
1840 /* We need to expand. Example: "echo $(($a + 1)) $((1 + $((2)) ))" */
1841 {
1842 struct in_str input;
1843 o_string dest = NULL_O_STRING;
1844
1845 setup_string_in_str(&input, arg);
1846 parse_stream_dquoted(&dest, &input, EOF);
1847 //bb_error_msg("'%s' -> '%s'", arg, dest.data);
1848 exp_str = expand_string_to_string(dest.data);
1849 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
1850 o_free(&dest);
1851 }
1852 skip_expand:
1815 hooks.lookupvar = lookup_param; 1853 hooks.lookupvar = lookup_param;
1816 hooks.setvar = arith_set_local_var; 1854 hooks.setvar = arith_set_local_var;
1817 hooks.endofname = endofname; 1855 hooks.endofname = endofname;
1818 res = arith(arg, &errcode, &hooks); 1856 res = arith(exp_str ? exp_str : arg, &errcode, &hooks);
1857 free(exp_str);
1858
1819 if (errcode < 0) { 1859 if (errcode < 0) {
1820 switch (errcode) { 1860 switch (errcode) {
1821 case -3: maybe_die("arith", "exponent less than 0"); break; 1861 case -3: maybe_die("arith", "exponent less than 0"); break;
@@ -1824,9 +1864,9 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1824 default: maybe_die("arith", "syntax error"); break; 1864 default: maybe_die("arith", "syntax error"); break;
1825 } 1865 }
1826 } 1866 }
1827 sprintf(buf, arith_t_fmt, res);
1828 o_addstrauto(output, buf);
1829 debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res); 1867 debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
1868 sprintf(arith_buf, arith_t_fmt, res);
1869 val = arith_buf;
1830 break; 1870 break;
1831 } 1871 }
1832#endif 1872#endif
@@ -1918,13 +1958,14 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1918 } else { /* quoted $VAR, val will be appended below */ 1958 } else { /* quoted $VAR, val will be appended below */
1919 debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote); 1959 debug_printf_expand("quoted '%s', output->o_quote:%d\n", val, output->o_quote);
1920 } 1960 }
1921 } 1961 } /* default: */
1922 } 1962 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
1963
1923 if (val) { 1964 if (val) {
1924 o_addQstr(output, val, strlen(val)); 1965 o_addQstr(output, val, strlen(val));
1925 } 1966 }
1926 /* Do the check to avoid writing to a const string */ 1967 /* Do the check to avoid writing to a const string */
1927 if (p && *p != SPECIAL_VAR_SYMBOL) 1968 if (*p != SPECIAL_VAR_SYMBOL)
1928 *p = SPECIAL_VAR_SYMBOL; 1969 *p = SPECIAL_VAR_SYMBOL;
1929 1970
1930#if ENABLE_HUSH_TICK 1971#if ENABLE_HUSH_TICK
@@ -3756,11 +3797,11 @@ static int process_command_subs(o_string *dest,
3756 } 3797 }
3757 3798
3758 debug_printf("done reading from pipe, pclose()ing\n"); 3799 debug_printf("done reading from pipe, pclose()ing\n");
3759 /* This is the step that waits for the child. Should be pretty 3800 /* Note: we got EOF, and we just close the read end of the pipe.
3760 * safe, since we just read an EOF from its stdout. We could try 3801 * We do not wait for the `cmd` child to terminate. bash and ash do.
3761 * to do better, by using waitpid, and keeping track of background jobs 3802 * Try this:
3762 * at the same time. That would be a lot of work, and contrary 3803 * echo `echo Hi; exec 1>&-; sleep 2`
3763 * to the KISS philosophy of this program. */ 3804 */
3764 retcode = fclose(p); 3805 retcode = fclose(p);
3765 free_pipe_list(inner.list_head, /* indent: */ 0); 3806 free_pipe_list(inner.list_head, /* indent: */ 0);
3766 debug_printf("closed FILE from child, retcode=%d\n", retcode); 3807 debug_printf("closed FILE from child, retcode=%d\n", retcode);
@@ -4056,7 +4097,7 @@ static int handle_dollar(o_string *dest, struct in_str *input)
4056 if (i_peek(input) == '(') { 4097 if (i_peek(input) == '(') {
4057 i_getch(input); 4098 i_getch(input);
4058 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4099 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4059 o_addchr(dest, quote_mask | '+'); 4100 o_addchr(dest, /*quote_mask |*/ '+');
4060 add_till_closing_paren(dest, input, true); 4101 add_till_closing_paren(dest, input, true);
4061 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4102 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4062 break; 4103 break;
@@ -4093,6 +4134,86 @@ static int handle_dollar(o_string *dest, struct in_str *input)
4093 return 0; 4134 return 0;
4094} 4135}
4095 4136
4137static int parse_stream_dquoted(o_string *dest, struct in_str *input, int dquote_end)
4138{
4139 int ch, m;
4140 int next;
4141
4142 again:
4143 ch = i_getch(input);
4144 if (ch == dquote_end) { /* may be only '"' or EOF */
4145 dest->nonnull = 1;
4146 if (dest->o_assignment == NOT_ASSIGNMENT)
4147 dest->o_quote ^= 1;
4148 debug_printf_parse("parse_stream_dquoted return 0\n");
4149 return 0;
4150 }
4151 if (ch == EOF) {
4152 syntax("unterminated \"");
4153 debug_printf_parse("parse_stream_dquoted return 1: unterminated \"\n");
4154 return 1;
4155 }
4156 next = '\0';
4157 m = G.charmap[ch];
4158 if (ch != '\n') {
4159 next = i_peek(input);
4160 }
4161 debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n",
4162 ch, ch, m, dest->o_quote);
4163 if (m != CHAR_SPECIAL) {
4164 o_addQchr(dest, ch);
4165 if ((dest->o_assignment == MAYBE_ASSIGNMENT
4166 || dest->o_assignment == WORD_IS_KEYWORD)
4167 && ch == '='
4168 && is_assignment(dest->data)
4169 ) {
4170 dest->o_assignment = DEFINITELY_ASSIGNMENT;
4171 }
4172 goto again;
4173 }
4174 if (ch == '\\') {
4175 if (next == EOF) {
4176 syntax("\\<eof>");
4177 debug_printf_parse("parse_stream_dquoted return 1: \\<eof>\n");
4178 return 1;
4179 }
4180 /* bash:
4181 * "The backslash retains its special meaning [in "..."]
4182 * only when followed by one of the following characters:
4183 * $, `, ", \, or <newline>. A double quote may be quoted
4184 * within double quotes by preceding it with a backslash.
4185 * If enabled, history expansion will be performed unless
4186 * an ! appearing in double quotes is escaped using
4187 * a backslash. The backslash preceding the ! is not removed."
4188 */
4189 if (strchr("$`\"\\", next) != NULL) {
4190 o_addqchr(dest, i_getch(input));
4191 } else {
4192 o_addqchr(dest, '\\');
4193 }
4194 goto again;
4195 }
4196 if (ch == '$') {
4197 if (handle_dollar(dest, input) != 0) {
4198 debug_printf_parse("parse_stream_dquoted return 1: handle_dollar returned non-0\n");
4199 return 1;
4200 }
4201 goto again;
4202 }
4203#if ENABLE_HUSH_TICK
4204 if (ch == '`') {
4205 //int pos = dest->length;
4206 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4207 o_addchr(dest, 0x80 | '`');
4208 add_till_backquote(dest, input);
4209 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4210 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
4211 /* fall through */
4212 }
4213#endif
4214 goto again;
4215}
4216
4096/* Scan input, call done_word() whenever full IFS delimited word was seen. 4217/* Scan input, call done_word() whenever full IFS delimited word was seen.
4097 * Call done_pipe if '\n' was seen (and end_trigger != NULL). 4218 * Call done_pipe if '\n' was seen (and end_trigger != NULL).
4098 * Return code is 0 if end_trigger char is met, 4219 * Return code is 0 if end_trigger char is met,
@@ -4104,7 +4225,7 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4104 int ch, m; 4225 int ch, m;
4105 int redir_fd; 4226 int redir_fd;
4106 redir_type redir_style; 4227 redir_type redir_style;
4107 int shadow_quote = dest->o_quote; 4228 int is_in_dquote;
4108 int next; 4229 int next;
4109 4230
4110 /* Only double-quote state is handled in the state variable dest->o_quote. 4231 /* Only double-quote state is handled in the state variable dest->o_quote.
@@ -4113,7 +4234,14 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4113 4234
4114 debug_printf_parse("parse_stream entered, end_trigger='%s' dest->o_assignment:%d\n", end_trigger, dest->o_assignment); 4235 debug_printf_parse("parse_stream entered, end_trigger='%s' dest->o_assignment:%d\n", end_trigger, dest->o_assignment);
4115 4236
4237 is_in_dquote = dest->o_quote;
4116 while (1) { 4238 while (1) {
4239 if (is_in_dquote) {
4240 if (parse_stream_dquoted(dest, input, '"'))
4241 return 1; /* propagate parse error */
4242 /* If we're here, we reached closing '"' */
4243 is_in_dquote = 0;
4244 }
4117 m = CHAR_IFS; 4245 m = CHAR_IFS;
4118 next = '\0'; 4246 next = '\0';
4119 ch = i_getch(input); 4247 ch = i_getch(input);
@@ -4125,14 +4253,7 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4125 } 4253 }
4126 debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n", 4254 debug_printf_parse(": ch=%c (%d) m=%d quote=%d\n",
4127 ch, ch, m, dest->o_quote); 4255 ch, ch, m, dest->o_quote);
4128 if (m == CHAR_ORDINARY 4256 if (m == CHAR_ORDINARY) {
4129 || (m != CHAR_SPECIAL && shadow_quote)
4130 ) {
4131 if (ch == EOF) {
4132 syntax("unterminated \"");
4133 debug_printf_parse("parse_stream return 1: unterminated \"\n");
4134 return 1;
4135 }
4136 o_addQchr(dest, ch); 4257 o_addQchr(dest, ch);
4137 if ((dest->o_assignment == MAYBE_ASSIGNMENT 4258 if ((dest->o_assignment == MAYBE_ASSIGNMENT
4138 || dest->o_assignment == WORD_IS_KEYWORD) 4259 || dest->o_assignment == WORD_IS_KEYWORD)
@@ -4143,6 +4264,8 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4143 } 4264 }
4144 continue; 4265 continue;
4145 } 4266 }
4267 /* m is SPECIAL ($,`), IFS, or ORDINARY_IF_QUOTED (*,#)
4268 */
4146 if (m == CHAR_IFS) { 4269 if (m == CHAR_IFS) {
4147 if (done_word(dest, ctx)) { 4270 if (done_word(dest, ctx)) {
4148 debug_printf_parse("parse_stream return 1: done_word!=0\n"); 4271 debug_printf_parse("parse_stream return 1: done_word!=0\n");
@@ -4152,7 +4275,7 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4152 break; 4275 break;
4153 /* If we aren't performing a substitution, treat 4276 /* If we aren't performing a substitution, treat
4154 * a newline as a command separator. 4277 * a newline as a command separator.
4155 * [why we don't handle it exactly like ';'? --vda] */ 4278 * [why don't we handle it exactly like ';'? --vda] */
4156 if (end_trigger && ch == '\n') { 4279 if (end_trigger && ch == '\n') {
4157#if ENABLE_HUSH_CASE 4280#if ENABLE_HUSH_CASE
4158 /* "case ... in <newline> word) ..." - 4281 /* "case ... in <newline> word) ..." -
@@ -4168,7 +4291,7 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4168 } 4291 }
4169 } 4292 }
4170 if (end_trigger) { 4293 if (end_trigger) {
4171 if (!shadow_quote && strchr(end_trigger, ch)) { 4294 if (strchr(end_trigger, ch)) {
4172 /* Special case: (...word) makes last word terminate, 4295 /* Special case: (...word) makes last word terminate,
4173 * as if ';' is seen */ 4296 * as if ';' is seen */
4174 if (ch == ')') { 4297 if (ch == ')') {
@@ -4188,15 +4311,17 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4188 if (m == CHAR_IFS) 4311 if (m == CHAR_IFS)
4189 continue; 4312 continue;
4190 4313
4314 /* m is SPECIAL (e.g. $,`) or ORDINARY_IF_QUOTED (*,#) */
4315
4191 if (dest->o_assignment == MAYBE_ASSIGNMENT) { 4316 if (dest->o_assignment == MAYBE_ASSIGNMENT) {
4192 /* ch is a special char and thus this word 4317 /* ch is a special char and thus this word
4193 * cannot be an assignment: */ 4318 * cannot be an assignment */
4194 dest->o_assignment = NOT_ASSIGNMENT; 4319 dest->o_assignment = NOT_ASSIGNMENT;
4195 } 4320 }
4196 4321
4197 switch (ch) { 4322 switch (ch) {
4198 case '#': 4323 case '#':
4199 if (dest->length == 0 && !shadow_quote) { 4324 if (dest->length == 0) {
4200 while (1) { 4325 while (1) {
4201 ch = i_peek(input); 4326 ch = i_peek(input);
4202 if (ch == EOF || ch == '\n') 4327 if (ch == EOF || ch == '\n')
@@ -4213,25 +4338,8 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4213 debug_printf_parse("parse_stream return 1: \\<eof>\n"); 4338 debug_printf_parse("parse_stream return 1: \\<eof>\n");
4214 return 1; 4339 return 1;
4215 } 4340 }
4216 /* bash: 4341 o_addchr(dest, '\\');
4217 * "The backslash retains its special meaning [in "..."] 4342 o_addchr(dest, i_getch(input));
4218 * only when followed by one of the following characters:
4219 * $, `, ", \, or <newline>. A double quote may be quoted
4220 * within double quotes by preceding it with a backslash.
4221 * If enabled, history expansion will be performed unless
4222 * an ! appearing in double quotes is escaped using
4223 * a backslash. The backslash preceding the ! is not removed."
4224 */
4225 if (shadow_quote) { //NOT SURE dest->o_quote) {
4226 if (strchr("$`\"\\", next) != NULL) {
4227 o_addqchr(dest, i_getch(input));
4228 } else {
4229 o_addqchr(dest, '\\');
4230 }
4231 } else {
4232 o_addchr(dest, '\\');
4233 o_addchr(dest, i_getch(input));
4234 }
4235 break; 4343 break;
4236 case '$': 4344 case '$':
4237 if (handle_dollar(dest, input) != 0) { 4345 if (handle_dollar(dest, input) != 0) {
@@ -4258,7 +4366,7 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4258 break; 4366 break;
4259 case '"': 4367 case '"':
4260 dest->nonnull = 1; 4368 dest->nonnull = 1;
4261 shadow_quote ^= 1; /* invert */ 4369 is_in_dquote ^= 1; /* invert */
4262 if (dest->o_assignment == NOT_ASSIGNMENT) 4370 if (dest->o_assignment == NOT_ASSIGNMENT)
4263 dest->o_quote ^= 1; 4371 dest->o_quote ^= 1;
4264 break; 4372 break;
@@ -4266,7 +4374,7 @@ static int parse_stream(o_string *dest, struct parse_context *ctx,
4266 case '`': { 4374 case '`': {
4267 //int pos = dest->length; 4375 //int pos = dest->length;
4268 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4376 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4269 o_addchr(dest, shadow_quote /*or dest->o_quote??*/ ? 0x80 | '`' : '`'); 4377 o_addchr(dest, '`');
4270 add_till_backquote(dest, input); 4378 add_till_backquote(dest, input);
4271 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4379 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4272 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos); 4380 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);