diff options
author | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2011-01-04 19:40:30 +0700 |
---|---|---|
committer | Nguyễn Thái Ngọc Duy <pclouds@gmail.com> | 2011-01-04 19:40:30 +0700 |
commit | 3b5c308768d76298bb964814ecc34de47bcac0b4 (patch) | |
tree | 795340e9d8f5e5bf9e8d895641099af343eec2a0 /shell | |
parent | 2b9a0e715ec459198f486653023d963b79291da7 (diff) | |
parent | 5fe2f863b9cee5ab0e7ac873538bce48846dbad8 (diff) | |
download | busybox-w32-3b5c308768d76298bb964814ecc34de47bcac0b4.tar.gz busybox-w32-3b5c308768d76298bb964814ecc34de47bcac0b4.tar.bz2 busybox-w32-3b5c308768d76298bb964814ecc34de47bcac0b4.zip |
Merge commit '06f719fd79fe15ce6fd5431bc58fcb22851de24d^'
Diffstat (limited to 'shell')
-rw-r--r-- | shell/ash.c | 45 | ||||
-rw-r--r-- | shell/ash_test/ash-arith/arith.right | 56 | ||||
-rw-r--r-- | shell/ash_test/ash-signals/signal8.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-signals/signal8.tests | 18 | ||||
-rw-r--r-- | shell/ash_test/ash-signals/signal9.right | 3 | ||||
-rwxr-xr-x | shell/ash_test/ash-signals/signal9.tests | 21 | ||||
-rw-r--r-- | shell/hush.c | 183 | ||||
-rw-r--r-- | shell/hush_test/hush-arith/arith.right | 54 | ||||
-rw-r--r-- | shell/hush_test/hush-glob/bash_brace1.right | 4 | ||||
-rwxr-xr-x | shell/hush_test/hush-glob/bash_brace1.tests | 10 | ||||
-rw-r--r-- | shell/math.c | 684 | ||||
-rw-r--r-- | shell/math.h | 96 |
12 files changed, 637 insertions, 540 deletions
diff --git a/shell/ash.c b/shell/ash.c index f1f044cbf..45ec13097 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -5766,25 +5766,17 @@ redirectsafe(union node *redir, int flags) | |||
5766 | static arith_t | 5766 | static arith_t |
5767 | ash_arith(const char *s) | 5767 | ash_arith(const char *s) |
5768 | { | 5768 | { |
5769 | arith_eval_hooks_t math_hooks; | 5769 | arith_state_t math_state; |
5770 | arith_t result; | 5770 | arith_t result; |
5771 | int errcode = 0; | ||
5772 | 5771 | ||
5773 | math_hooks.lookupvar = lookupvar; | 5772 | math_state.lookupvar = lookupvar; |
5774 | math_hooks.setvar = setvar2; | 5773 | math_state.setvar = setvar2; |
5775 | //math_hooks.endofname = endofname; | 5774 | //math_state.endofname = endofname; |
5776 | 5775 | ||
5777 | INT_OFF; | 5776 | INT_OFF; |
5778 | result = arith(s, &errcode, &math_hooks); | 5777 | result = arith(&math_state, s); |
5779 | if (errcode < 0) { | 5778 | if (math_state.errmsg) |
5780 | if (errcode == -3) | 5779 | ash_msg_and_raise_error(math_state.errmsg); |
5781 | ash_msg_and_raise_error("exponent less than 0"); | ||
5782 | if (errcode == -2) | ||
5783 | ash_msg_and_raise_error("divide by zero"); | ||
5784 | if (errcode == -5) | ||
5785 | ash_msg_and_raise_error("expression recursion loop detected"); | ||
5786 | raise_error_syntax(s); | ||
5787 | } | ||
5788 | INT_ON; | 5780 | INT_ON; |
5789 | 5781 | ||
5790 | return result; | 5782 | return result; |
@@ -5848,7 +5840,7 @@ cvtnum(arith_t num) | |||
5848 | int len; | 5840 | int len; |
5849 | 5841 | ||
5850 | expdest = makestrspace(32, expdest); | 5842 | expdest = makestrspace(32, expdest); |
5851 | len = fmtstr(expdest, 32, arith_t_fmt, num); | 5843 | len = fmtstr(expdest, 32, ARITH_FMT, num); |
5852 | STADJUST(len, expdest); | 5844 | STADJUST(len, expdest); |
5853 | return len; | 5845 | return len; |
5854 | } | 5846 | } |
@@ -8624,7 +8616,7 @@ static int evalstring(char *s, int mask); | |||
8624 | 8616 | ||
8625 | /* Called to execute a trap. | 8617 | /* Called to execute a trap. |
8626 | * Single callsite - at the end of evaltree(). | 8618 | * Single callsite - at the end of evaltree(). |
8627 | * If we return non-zero, exaltree raises EXEXIT exception. | 8619 | * If we return non-zero, evaltree raises EXEXIT exception. |
8628 | * | 8620 | * |
8629 | * Perhaps we should avoid entering new trap handlers | 8621 | * Perhaps we should avoid entering new trap handlers |
8630 | * while we are executing a trap handler. [is it a TODO?] | 8622 | * while we are executing a trap handler. [is it a TODO?] |
@@ -8814,11 +8806,15 @@ evaltree(union node *n, int flags) | |||
8814 | 8806 | ||
8815 | out: | 8807 | out: |
8816 | exception_handler = savehandler; | 8808 | exception_handler = savehandler; |
8809 | |||
8817 | out1: | 8810 | out1: |
8811 | /* Order of checks below is important: | ||
8812 | * signal handlers trigger before exit caused by "set -e". | ||
8813 | */ | ||
8814 | if (pending_sig && dotrap()) | ||
8815 | goto exexit; | ||
8818 | if (checkexit & exitstatus) | 8816 | if (checkexit & exitstatus) |
8819 | evalskip |= SKIPEVAL; | 8817 | evalskip |= SKIPEVAL; |
8820 | else if (pending_sig && dotrap()) | ||
8821 | goto exexit; | ||
8822 | 8818 | ||
8823 | if (flags & EV_EXIT) { | 8819 | if (flags & EV_EXIT) { |
8824 | exexit: | 8820 | exexit: |
@@ -9212,7 +9208,7 @@ poplocalvars(void) | |||
9212 | while ((lvp = localvars) != NULL) { | 9208 | while ((lvp = localvars) != NULL) { |
9213 | localvars = lvp->next; | 9209 | localvars = lvp->next; |
9214 | vp = lvp->vp; | 9210 | vp = lvp->vp; |
9215 | TRACE(("poplocalvar %s\n", vp ? vp->text : "-")); | 9211 | TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-")); |
9216 | if (vp == NULL) { /* $- saved */ | 9212 | if (vp == NULL) { /* $- saved */ |
9217 | memcpy(optlist, lvp->text, sizeof(optlist)); | 9213 | memcpy(optlist, lvp->text, sizeof(optlist)); |
9218 | free((char*)lvp->text); | 9214 | free((char*)lvp->text); |
@@ -13383,7 +13379,7 @@ init(void) | |||
13383 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. | 13379 | /* bash re-enables SIGHUP which is SIG_IGNed on entry. |
13384 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" | 13380 | * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" |
13385 | */ | 13381 | */ |
13386 | signal(SIGHUP, SIG_DFL); | 13382 | signal(SIGHUP, SIG_DFL); |
13387 | 13383 | ||
13388 | /* from var.c: */ | 13384 | /* from var.c: */ |
13389 | { | 13385 | { |
@@ -13598,10 +13594,12 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13598 | if (e == EXERROR) | 13594 | if (e == EXERROR) |
13599 | exitstatus = 2; | 13595 | exitstatus = 2; |
13600 | s = state; | 13596 | s = state; |
13601 | if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) | 13597 | if (e == EXEXIT || s == 0 || iflag == 0 || shlvl) { |
13602 | exitshell(); | 13598 | exitshell(); |
13603 | if (e == EXINT) | 13599 | } |
13600 | if (e == EXINT) { | ||
13604 | outcslow('\n', stderr); | 13601 | outcslow('\n', stderr); |
13602 | } | ||
13605 | 13603 | ||
13606 | popstackmark(&smark); | 13604 | popstackmark(&smark); |
13607 | FORCE_INT_ON; /* enable interrupts */ | 13605 | FORCE_INT_ON; /* enable interrupts */ |
@@ -13705,6 +13703,7 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13705 | _mcleanup(); | 13703 | _mcleanup(); |
13706 | } | 13704 | } |
13707 | #endif | 13705 | #endif |
13706 | TRACE(("End of main reached\n")); | ||
13708 | exitshell(); | 13707 | exitshell(); |
13709 | /* NOTREACHED */ | 13708 | /* NOTREACHED */ |
13710 | } | 13709 | } |
diff --git a/shell/ash_test/ash-arith/arith.right b/shell/ash_test/ash-arith/arith.right index 3ea7ce680..9b9ca8e2f 100644 --- a/shell/ash_test/ash-arith/arith.right +++ b/shell/ash_test/ash-arith/arith.right | |||
@@ -55,28 +55,28 @@ Format: 'expected actual' | |||
55 | 30 30 | 55 | 30 30 |
56 | 20 20 | 56 | 20 20 |
57 | 30 30 | 57 | 30 30 |
58 | ./arith.tests: line 117: syntax error: 1 ? 20 : x+=2 | 58 | ./arith.tests: line 117: arithmetic syntax error |
59 | 6 6 | 59 | 6 6 |
60 | 6,5,3 6,5,3 | 60 | 6,5,3 6,5,3 |
61 | 263 263 | 61 | 263 263 |
62 | 255 255 | 62 | 255 255 |
63 | 40 40 | 63 | 40 40 |
64 | ./arith.tests: line 163: syntax error: 7 = 43 | 64 | ./arith.tests: line 163: arithmetic syntax error |
65 | ./arith.tests: line 165: divide by zero | 65 | ./arith.tests: line 165: divide by zero |
66 | ./arith.tests: let: line 166: syntax error: jv += $iv | 66 | ./arith.tests: let: line 166: arithmetic syntax error |
67 | ./arith.tests: line 167: syntax error: jv += $iv | 67 | ./arith.tests: line 167: arithmetic syntax error |
68 | ./arith.tests: let: line 168: syntax error: rv = 7 + (43 * 6 | 68 | ./arith.tests: let: line 168: arithmetic syntax error |
69 | abc | 69 | abc |
70 | def | 70 | def |
71 | ghi | 71 | ghi |
72 | ./arith.tests: line 191: syntax error: ( 4 + A ) + 4 | 72 | ./arith.tests: line 191: arithmetic syntax error |
73 | 16 16 | 73 | 16 16 |
74 | ./arith.tests: line 196: syntax error: 4 ? : 3 + 5 | 74 | ./arith.tests: line 196: arithmetic syntax error |
75 | ./arith.tests: line 197: syntax error: 1 ? 20 | 75 | ./arith.tests: line 197: malformed ?: operator |
76 | ./arith.tests: line 198: syntax error: 4 ? 20 : | 76 | ./arith.tests: line 198: arithmetic syntax error |
77 | 9 9 | 77 | 9 9 |
78 | ./arith.tests: line 205: syntax error: 0 && B=42 | 78 | ./arith.tests: line 205: arithmetic syntax error |
79 | ./arith.tests: line 208: syntax error: 1 || B=88 | 79 | ./arith.tests: line 208: arithmetic syntax error |
80 | 9 9 | 80 | 9 9 |
81 | 9 9 | 81 | 9 9 |
82 | 9 9 | 82 | 9 9 |
@@ -97,18 +97,18 @@ ghi | |||
97 | 3 3 | 97 | 3 3 |
98 | 4 4 | 98 | 4 4 |
99 | 4 4 | 99 | 4 4 |
100 | ./arith.tests: line 257: syntax error: 7-- | 100 | ./arith.tests: line 257: arithmetic syntax error |
101 | ./arith.tests: line 259: syntax error: --x=7 | 101 | ./arith.tests: line 259: arithmetic syntax error |
102 | ./arith.tests: line 260: syntax error: ++x=7 | 102 | ./arith.tests: line 260: arithmetic syntax error |
103 | ./arith.tests: line 262: syntax error: x++=7 | 103 | ./arith.tests: line 262: arithmetic syntax error |
104 | ./arith.tests: line 263: syntax error: x--=7 | 104 | ./arith.tests: line 263: arithmetic syntax error |
105 | 4 4 | 105 | 4 4 |
106 | 7 7 | 106 | 7 7 |
107 | -7 -7 | 107 | -7 -7 |
108 | ./arith1.sub: line 2: syntax error: 4-- | 108 | ./arith1.sub: line 2: arithmetic syntax error |
109 | ./arith1.sub: line 3: syntax error: 4++ | 109 | ./arith1.sub: line 3: arithmetic syntax error |
110 | ./arith1.sub: line 4: syntax error: 4 -- | 110 | ./arith1.sub: line 4: arithmetic syntax error |
111 | ./arith1.sub: line 5: syntax error: 4 ++ | 111 | ./arith1.sub: line 5: arithmetic syntax error |
112 | 6 6 | 112 | 6 6 |
113 | 3 3 | 113 | 3 3 |
114 | 7 7 | 114 | 7 7 |
@@ -119,19 +119,19 @@ ghi | |||
119 | 2 2 | 119 | 2 2 |
120 | -2 -2 | 120 | -2 -2 |
121 | 1 1 | 121 | 1 1 |
122 | ./arith1.sub: line 37: syntax error: +++7 | 122 | ./arith1.sub: line 37: arithmetic syntax error |
123 | ./arith2.sub: line 2: syntax error: --7 | 123 | ./arith2.sub: line 2: arithmetic syntax error |
124 | ./arith2.sub: line 3: syntax error: ++7 | 124 | ./arith2.sub: line 3: arithmetic syntax error |
125 | ./arith2.sub: line 4: syntax error: -- 7 | 125 | ./arith2.sub: line 4: arithmetic syntax error |
126 | ./arith2.sub: line 5: syntax error: ++ 7 | 126 | ./arith2.sub: line 5: arithmetic syntax error |
127 | 5 5 | 127 | 5 5 |
128 | 1 1 | 128 | 1 1 |
129 | 4 4 | 129 | 4 4 |
130 | 0 0 | 130 | 0 0 |
131 | ./arith2.sub: line 42: syntax error: -- - 7 | 131 | ./arith2.sub: line 42: arithmetic syntax error |
132 | ./arith2.sub: line 47: syntax error: ++ + 7 | 132 | ./arith2.sub: line 47: arithmetic syntax error |
133 | 8 12 | 133 | 8 12 |
134 | ./arith.tests: line 290: syntax error: a b | 134 | ./arith.tests: line 290: arithmetic syntax error |
135 | 42 | 135 | 42 |
136 | 42 | 136 | 42 |
137 | 42 | 137 | 42 |
diff --git a/shell/ash_test/ash-signals/signal8.right b/shell/ash_test/ash-signals/signal8.right new file mode 100644 index 000000000..39572f30e --- /dev/null +++ b/shell/ash_test/ash-signals/signal8.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Removing traps | ||
2 | End of exit_func | ||
3 | Done: 0 | ||
diff --git a/shell/ash_test/ash-signals/signal8.tests b/shell/ash_test/ash-signals/signal8.tests new file mode 100755 index 000000000..731af7477 --- /dev/null +++ b/shell/ash_test/ash-signals/signal8.tests | |||
@@ -0,0 +1,18 @@ | |||
1 | "$THIS_SH" -c ' | ||
2 | exit_func() { | ||
3 | echo "Removing traps" | ||
4 | trap - EXIT TERM INT | ||
5 | echo "End of exit_func" | ||
6 | } | ||
7 | set -e | ||
8 | trap exit_func EXIT TERM INT | ||
9 | sleep 2 | ||
10 | exit 77 | ||
11 | ' & | ||
12 | |||
13 | sleep 1 | ||
14 | # BUG: ash kills -PGRP, but in non-interactive shell we do not create pgrps! | ||
15 | # In this case, bash kills by PID, not PGRP. | ||
16 | kill -TERM %1 | ||
17 | wait | ||
18 | echo Done: $? | ||
diff --git a/shell/ash_test/ash-signals/signal9.right b/shell/ash_test/ash-signals/signal9.right new file mode 100644 index 000000000..39572f30e --- /dev/null +++ b/shell/ash_test/ash-signals/signal9.right | |||
@@ -0,0 +1,3 @@ | |||
1 | Removing traps | ||
2 | End of exit_func | ||
3 | Done: 0 | ||
diff --git a/shell/ash_test/ash-signals/signal9.tests b/shell/ash_test/ash-signals/signal9.tests new file mode 100755 index 000000000..18e71012b --- /dev/null +++ b/shell/ash_test/ash-signals/signal9.tests | |||
@@ -0,0 +1,21 @@ | |||
1 | # Note: the inner script is a test which checks for a different bug | ||
2 | # (ordering between INT handler and exit on "set -e"), | ||
3 | # but so far I did not figure out how to simulate it non-interactively. | ||
4 | |||
5 | "$THIS_SH" -c ' | ||
6 | exit_func() { | ||
7 | echo "Removing traps" | ||
8 | trap - EXIT TERM INT | ||
9 | echo "End of exit_func" | ||
10 | } | ||
11 | set -e | ||
12 | trap exit_func EXIT TERM INT | ||
13 | sleep 2 | ||
14 | exit 77 | ||
15 | ' & | ||
16 | |||
17 | child=$! | ||
18 | sleep 1 | ||
19 | kill -TERM $child | ||
20 | wait | ||
21 | echo Done: $? | ||
diff --git a/shell/hush.c b/shell/hush.c index 752efd0c8..75083dc2e 100644 --- a/shell/hush.c +++ b/shell/hush.c | |||
@@ -8,6 +8,8 @@ | |||
8 | * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> | 8 | * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org> |
9 | * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com> | 9 | * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com> |
10 | * | 10 | * |
11 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
12 | * | ||
11 | * Credits: | 13 | * Credits: |
12 | * The parser routines proper are all original material, first | 14 | * The parser routines proper are all original material, first |
13 | * written Dec 2000 and Jan 2001 by Larry Doolittle. The | 15 | * written Dec 2000 and Jan 2001 by Larry Doolittle. The |
@@ -50,7 +52,6 @@ | |||
50 | * | 52 | * |
51 | * Bash compat TODO: | 53 | * Bash compat TODO: |
52 | * redirection of stdout+stderr: &> and >& | 54 | * redirection of stdout+stderr: &> and >& |
53 | * brace expansion: one/{two,three,four} | ||
54 | * reserved words: function select | 55 | * reserved words: function select |
55 | * advanced test: [[ ]] | 56 | * advanced test: [[ ]] |
56 | * process substitution: <(list) and >(list) | 57 | * process substitution: <(list) and >(list) |
@@ -63,7 +64,9 @@ | |||
63 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. | 64 | * The EXPR is evaluated according to ARITHMETIC EVALUATION. |
64 | * This is exactly equivalent to let "EXPR". | 65 | * This is exactly equivalent to let "EXPR". |
65 | * $[EXPR]: synonym for $((EXPR)) | 66 | * $[EXPR]: synonym for $((EXPR)) |
66 | * export builtin should be special, its arguments are assignments | 67 | * |
68 | * Won't do: | ||
69 | * In bash, export builtin is special, its arguments are assignments | ||
67 | * and therefore expansion of them should be "one-word" expansion: | 70 | * and therefore expansion of them should be "one-word" expansion: |
68 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" | 71 | * $ export i=`echo 'a b'` # export has one arg: "i=a b" |
69 | * compare with: | 72 | * compare with: |
@@ -77,8 +80,6 @@ | |||
77 | * aaa bbb | 80 | * aaa bbb |
78 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" | 81 | * $ "export" i=`echo 'aaa bbb'`; echo "$i" |
79 | * aaa | 82 | * aaa |
80 | * | ||
81 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
82 | */ | 83 | */ |
83 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ | 84 | #include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */ |
84 | #include <malloc.h> /* for malloc_trim */ | 85 | #include <malloc.h> /* for malloc_trim */ |
@@ -119,8 +120,8 @@ | |||
119 | //config: | 120 | //config: |
120 | //config: It will compile and work on no-mmu systems. | 121 | //config: It will compile and work on no-mmu systems. |
121 | //config: | 122 | //config: |
122 | //config: It does not handle select, aliases, brace expansion, | 123 | //config: It does not handle select, aliases, tilde expansion, |
123 | //config: tilde expansion, &>file and >&file redirection of stdout+stderr. | 124 | //config: &>file and >&file redirection of stdout+stderr. |
124 | //config: | 125 | //config: |
125 | //config:config HUSH_BASH_COMPAT | 126 | //config:config HUSH_BASH_COMPAT |
126 | //config: bool "bash-compatible extensions" | 127 | //config: bool "bash-compatible extensions" |
@@ -129,6 +130,13 @@ | |||
129 | //config: help | 130 | //config: help |
130 | //config: Enable bash-compatible extensions. | 131 | //config: Enable bash-compatible extensions. |
131 | //config: | 132 | //config: |
133 | //config:config HUSH_BRACE_EXPANSION | ||
134 | //config: bool "Brace expansion" | ||
135 | //config: default y | ||
136 | //config: depends on HUSH_BASH_COMPAT | ||
137 | //config: help | ||
138 | //config: Enable {abc,def} extension. | ||
139 | //config: | ||
132 | //config:config HUSH_HELP | 140 | //config:config HUSH_HELP |
133 | //config: bool "help builtin" | 141 | //config: bool "help builtin" |
134 | //config: default y | 142 | //config: default y |
@@ -391,18 +399,10 @@ enum { | |||
391 | RES_SNTX | 399 | RES_SNTX |
392 | }; | 400 | }; |
393 | 401 | ||
394 | enum { | ||
395 | EXP_FLAG_GLOB = 0x200, | ||
396 | EXP_FLAG_ESC_GLOB_CHARS = 0x100, | ||
397 | EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ | ||
398 | }; | ||
399 | |||
400 | typedef struct o_string { | 402 | typedef struct o_string { |
401 | char *data; | 403 | char *data; |
402 | int length; /* position where data is appended */ | 404 | int length; /* position where data is appended */ |
403 | int maxlen; | 405 | int maxlen; |
404 | /* Protect newly added chars against globbing | ||
405 | * (by prepending \ to *, ?, [, \) */ | ||
406 | int o_expflags; | 406 | int o_expflags; |
407 | /* At least some part of the string was inside '' or "", | 407 | /* At least some part of the string was inside '' or "", |
408 | * possibly empty one: word"", wo''rd etc. */ | 408 | * possibly empty one: word"", wo''rd etc. */ |
@@ -411,10 +411,18 @@ typedef struct o_string { | |||
411 | smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ | 411 | smallint o_assignment; /* 0:maybe, 1:yes, 2:no */ |
412 | } o_string; | 412 | } o_string; |
413 | enum { | 413 | enum { |
414 | MAYBE_ASSIGNMENT = 0, | 414 | EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */ |
415 | EXP_FLAG_GLOB = 0x2, | ||
416 | /* Protect newly added chars against globbing | ||
417 | * by prepending \ to *, ?, [, \ */ | ||
418 | EXP_FLAG_ESC_GLOB_CHARS = 0x1, | ||
419 | }; | ||
420 | enum { | ||
421 | MAYBE_ASSIGNMENT = 0, | ||
415 | DEFINITELY_ASSIGNMENT = 1, | 422 | DEFINITELY_ASSIGNMENT = 1, |
416 | NOT_ASSIGNMENT = 2, | 423 | NOT_ASSIGNMENT = 2, |
417 | WORD_IS_KEYWORD = 3, /* not assigment, but next word may be: "if v=xyz cmd;" */ | 424 | /* Not an assigment, but next word may be: "if v=xyz cmd;" */ |
425 | WORD_IS_KEYWORD = 3, | ||
418 | }; | 426 | }; |
419 | /* Used for initialization: o_string foo = NULL_O_STRING; */ | 427 | /* Used for initialization: o_string foo = NULL_O_STRING; */ |
420 | #define NULL_O_STRING { NULL } | 428 | #define NULL_O_STRING { NULL } |
@@ -707,8 +715,7 @@ struct globals { | |||
707 | #endif | 715 | #endif |
708 | const char *ifs; | 716 | const char *ifs; |
709 | const char *cwd; | 717 | const char *cwd; |
710 | struct variable *top_var; /* = &G.shell_ver (set in main()) */ | 718 | struct variable *top_var; |
711 | struct variable shell_ver; | ||
712 | char **expanded_assignments; | 719 | char **expanded_assignments; |
713 | #if ENABLE_HUSH_FUNCTIONS | 720 | #if ENABLE_HUSH_FUNCTIONS |
714 | struct function *top_func; | 721 | struct function *top_func; |
@@ -2001,26 +2008,8 @@ static void o_addstr_with_NUL(o_string *o, const char *str) | |||
2001 | o_addblock(o, str, strlen(str) + 1); | 2008 | o_addblock(o, str, strlen(str) + 1); |
2002 | } | 2009 | } |
2003 | 2010 | ||
2004 | static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) | ||
2005 | { | ||
2006 | while (len) { | ||
2007 | len--; | ||
2008 | o_addchr(o, *str); | ||
2009 | if (*str++ == '\\') { | ||
2010 | /* \z -> \\\z; \<eol> -> \\<eol> */ | ||
2011 | o_addchr(o, '\\'); | ||
2012 | if (len) { | ||
2013 | len--; | ||
2014 | o_addchr(o, '\\'); | ||
2015 | o_addchr(o, *str++); | ||
2016 | } | ||
2017 | } | ||
2018 | } | ||
2019 | } | ||
2020 | |||
2021 | #undef HUSH_BRACE_EXP | ||
2022 | /* | 2011 | /* |
2023 | * HUSH_BRACE_EXP code needs corresponding quoting on variable expansion side. | 2012 | * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side. |
2024 | * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v. | 2013 | * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v. |
2025 | * Apparently, on unquoted $v bash still does globbing | 2014 | * Apparently, on unquoted $v bash still does globbing |
2026 | * ("v='*.txt'; echo $v" prints all .txt files), | 2015 | * ("v='*.txt'; echo $v" prints all .txt files), |
@@ -2030,7 +2019,7 @@ static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len | |||
2030 | * We have only second one. | 2019 | * We have only second one. |
2031 | */ | 2020 | */ |
2032 | 2021 | ||
2033 | #ifdef HUSH_BRACE_EXP | 2022 | #if ENABLE_HUSH_BRACE_EXPANSION |
2034 | # define MAYBE_BRACES "{}" | 2023 | # define MAYBE_BRACES "{}" |
2035 | #else | 2024 | #else |
2036 | # define MAYBE_BRACES "" | 2025 | # define MAYBE_BRACES "" |
@@ -2198,7 +2187,7 @@ static int o_get_last_ptr(o_string *o, int n) | |||
2198 | return ((int)(uintptr_t)list[n-1]) + string_start; | 2187 | return ((int)(uintptr_t)list[n-1]) + string_start; |
2199 | } | 2188 | } |
2200 | 2189 | ||
2201 | #ifdef HUSH_BRACE_EXP | 2190 | #if ENABLE_HUSH_BRACE_EXPANSION |
2202 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: | 2191 | /* There in a GNU extension, GLOB_BRACE, but it is not usable: |
2203 | * first, it processes even {a} (no commas), second, | 2192 | * first, it processes even {a} (no commas), second, |
2204 | * I didn't manage to make it return strings when they don't match | 2193 | * I didn't manage to make it return strings when they don't match |
@@ -2394,7 +2383,7 @@ static int perform_glob(o_string *o, int n) | |||
2394 | return n; | 2383 | return n; |
2395 | } | 2384 | } |
2396 | 2385 | ||
2397 | #else /* !HUSH_BRACE_EXP */ | 2386 | #else /* !HUSH_BRACE_EXPANSION */ |
2398 | 2387 | ||
2399 | /* Helper */ | 2388 | /* Helper */ |
2400 | static int glob_needed(const char *s) | 2389 | static int glob_needed(const char *s) |
@@ -2471,7 +2460,7 @@ static int perform_glob(o_string *o, int n) | |||
2471 | return n; | 2460 | return n; |
2472 | } | 2461 | } |
2473 | 2462 | ||
2474 | #endif /* !HUSH_BRACE_EXP */ | 2463 | #endif /* !HUSH_BRACE_EXPANSION */ |
2475 | 2464 | ||
2476 | /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. | 2465 | /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered. |
2477 | * Otherwise, just finish current list[] and start new */ | 2466 | * Otherwise, just finish current list[] and start new */ |
@@ -4388,6 +4377,37 @@ static int process_command_subs(o_string *dest, const char *s); | |||
4388 | * followed by strings themselves. | 4377 | * followed by strings themselves. |
4389 | * Caller can deallocate entire list by single free(list). */ | 4378 | * Caller can deallocate entire list by single free(list). */ |
4390 | 4379 | ||
4380 | /* A horde of its helpers come first: */ | ||
4381 | |||
4382 | static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len) | ||
4383 | { | ||
4384 | while (--len >= 0) { | ||
4385 | char c = *str++; | ||
4386 | |||
4387 | #if ENABLE_HUSH_BRACE_EXPANSION | ||
4388 | if (c == '{' || c == '}') { | ||
4389 | /* { -> \{, } -> \} */ | ||
4390 | o_addchr(o, '\\'); | ||
4391 | /* And now we want to add { or } and continue: | ||
4392 | * o_addchr(o, c); | ||
4393 | * continue; | ||
4394 | * luckily, just falling throught achieves this. | ||
4395 | */ | ||
4396 | } | ||
4397 | #endif | ||
4398 | o_addchr(o, c); | ||
4399 | if (c == '\\') { | ||
4400 | /* \z -> \\\z; \<eol> -> \\<eol> */ | ||
4401 | o_addchr(o, '\\'); | ||
4402 | if (len) { | ||
4403 | len--; | ||
4404 | o_addchr(o, '\\'); | ||
4405 | o_addchr(o, *str++); | ||
4406 | } | ||
4407 | } | ||
4408 | } | ||
4409 | } | ||
4410 | |||
4391 | /* Store given string, finalizing the word and starting new one whenever | 4411 | /* Store given string, finalizing the word and starting new one whenever |
4392 | * we encounter IFS char(s). This is used for expanding variable values. | 4412 | * we encounter IFS char(s). This is used for expanding variable values. |
4393 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ | 4413 | * End-of-string does NOT finalize word: think about 'echo -$VAR-' */ |
@@ -4396,9 +4416,9 @@ static int expand_on_ifs(o_string *output, int n, const char *str) | |||
4396 | while (1) { | 4416 | while (1) { |
4397 | int word_len = strcspn(str, G.ifs); | 4417 | int word_len = strcspn(str, G.ifs); |
4398 | if (word_len) { | 4418 | if (word_len) { |
4399 | if (!(output->o_expflags & EXP_FLAG_GLOB)) | 4419 | if (!(output->o_expflags & EXP_FLAG_GLOB)) { |
4400 | o_addblock(output, str, word_len); | 4420 | o_addblock(output, str, word_len); |
4401 | else { | 4421 | } else { |
4402 | /* Protect backslashes against globbing up :) | 4422 | /* Protect backslashes against globbing up :) |
4403 | * Example: "v='\*'; echo b$v" prints "b\*" | 4423 | * Example: "v='\*'; echo b$v" prints "b\*" |
4404 | * (and does not try to glob on "*") | 4424 | * (and does not try to glob on "*") |
@@ -4461,18 +4481,22 @@ static char *encode_then_expand_string(const char *str, int process_bkslash, int | |||
4461 | } | 4481 | } |
4462 | 4482 | ||
4463 | #if ENABLE_SH_MATH_SUPPORT | 4483 | #if ENABLE_SH_MATH_SUPPORT |
4464 | static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p) | 4484 | static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) |
4465 | { | 4485 | { |
4466 | arith_eval_hooks_t hooks; | 4486 | arith_state_t math_state; |
4467 | arith_t res; | 4487 | arith_t res; |
4468 | char *exp_str; | 4488 | char *exp_str; |
4469 | 4489 | ||
4470 | hooks.lookupvar = get_local_var_value; | 4490 | math_state.lookupvar = get_local_var_value; |
4471 | hooks.setvar = set_local_var_from_halves; | 4491 | math_state.setvar = set_local_var_from_halves; |
4472 | //hooks.endofname = endofname; | 4492 | //math_state.endofname = endofname; |
4473 | exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); | 4493 | exp_str = encode_then_expand_string(arg, /*process_bkslash:*/ 1, /*unbackslash:*/ 1); |
4474 | res = arith(exp_str ? exp_str : arg, errcode_p, &hooks); | 4494 | res = arith(&math_state, exp_str ? exp_str : arg); |
4475 | free(exp_str); | 4495 | free(exp_str); |
4496 | if (errmsg_p) | ||
4497 | *errmsg_p = math_state.errmsg; | ||
4498 | if (math_state.errmsg) | ||
4499 | die_if_script(math_state.errmsg); | ||
4476 | return res; | 4500 | return res; |
4477 | } | 4501 | } |
4478 | #endif | 4502 | #endif |
@@ -4713,24 +4737,28 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
4713 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> | 4737 | * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL> |
4714 | */ | 4738 | */ |
4715 | arith_t beg, len; | 4739 | arith_t beg, len; |
4716 | int errcode = 0; | 4740 | const char *errmsg; |
4717 | 4741 | ||
4718 | beg = expand_and_evaluate_arith(exp_word, &errcode); | 4742 | beg = expand_and_evaluate_arith(exp_word, &errmsg); |
4743 | if (errmsg) | ||
4744 | goto arith_err; | ||
4719 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); | 4745 | debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); |
4720 | *p++ = SPECIAL_VAR_SYMBOL; | 4746 | *p++ = SPECIAL_VAR_SYMBOL; |
4721 | exp_word = p; | 4747 | exp_word = p; |
4722 | p = strchr(p, SPECIAL_VAR_SYMBOL); | 4748 | p = strchr(p, SPECIAL_VAR_SYMBOL); |
4723 | *p = '\0'; | 4749 | *p = '\0'; |
4724 | len = expand_and_evaluate_arith(exp_word, &errcode); | 4750 | len = expand_and_evaluate_arith(exp_word, &errmsg); |
4751 | if (errmsg) | ||
4752 | goto arith_err; | ||
4725 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); | 4753 | debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); |
4726 | 4754 | if (len >= 0) { /* bash compat: len < 0 is illegal */ | |
4727 | if (errcode >= 0 && len >= 0) { /* bash compat: len < 0 is illegal */ | ||
4728 | if (beg < 0) /* bash compat */ | 4755 | if (beg < 0) /* bash compat */ |
4729 | beg = 0; | 4756 | beg = 0; |
4730 | debug_printf_varexp("from val:'%s'\n", val); | 4757 | debug_printf_varexp("from val:'%s'\n", val); |
4731 | if (len == 0 || !val || beg >= strlen(val)) | 4758 | if (len == 0 || !val || beg >= strlen(val)) { |
4732 | val = ""; | 4759 | arith_err: |
4733 | else { | 4760 | val = NULL; |
4761 | } else { | ||
4734 | /* Paranoia. What if user entered 9999999999999 | 4762 | /* Paranoia. What if user entered 9999999999999 |
4735 | * which fits in arith_t but not int? */ | 4763 | * which fits in arith_t but not int? */ |
4736 | if (len >= INT_MAX) | 4764 | if (len >= INT_MAX) |
@@ -4742,7 +4770,7 @@ static NOINLINE const char *expand_one_var(char **to_be_freed_pp, char *arg, cha | |||
4742 | #endif | 4770 | #endif |
4743 | { | 4771 | { |
4744 | die_if_script("malformed ${%s:...}", var); | 4772 | die_if_script("malformed ${%s:...}", var); |
4745 | val = ""; | 4773 | val = NULL; |
4746 | } | 4774 | } |
4747 | } else { /* one of "-=+?" */ | 4775 | } else { /* one of "-=+?" */ |
4748 | /* Standard-mandated substitution ops: | 4776 | /* Standard-mandated substitution ops: |
@@ -4925,30 +4953,13 @@ static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg) | |||
4925 | #if ENABLE_SH_MATH_SUPPORT | 4953 | #if ENABLE_SH_MATH_SUPPORT |
4926 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ | 4954 | case '+': { /* <SPECIAL_VAR_SYMBOL>+cmd<SPECIAL_VAR_SYMBOL> */ |
4927 | arith_t res; | 4955 | arith_t res; |
4928 | int errcode; | ||
4929 | 4956 | ||
4930 | arg++; /* skip '+' */ | 4957 | arg++; /* skip '+' */ |
4931 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ | 4958 | *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */ |
4932 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); | 4959 | debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch); |
4933 | res = expand_and_evaluate_arith(arg, &errcode); | 4960 | res = expand_and_evaluate_arith(arg, NULL); |
4934 | 4961 | debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res); | |
4935 | if (errcode < 0) { | 4962 | sprintf(arith_buf, ARITH_FMT, res); |
4936 | const char *msg = "error in arithmetic"; | ||
4937 | switch (errcode) { | ||
4938 | case -3: | ||
4939 | msg = "exponent less than 0"; | ||
4940 | break; | ||
4941 | case -2: | ||
4942 | msg = "divide by 0"; | ||
4943 | break; | ||
4944 | case -5: | ||
4945 | msg = "expression recursion loop detected"; | ||
4946 | break; | ||
4947 | } | ||
4948 | die_if_script(msg); | ||
4949 | } | ||
4950 | debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res); | ||
4951 | sprintf(arith_buf, arith_t_fmt, res); | ||
4952 | val = arith_buf; | 4963 | val = arith_buf; |
4953 | break; | 4964 | break; |
4954 | } | 4965 | } |
@@ -7346,6 +7357,7 @@ int hush_main(int argc, char **argv) | |||
7346 | unsigned builtin_argc; | 7357 | unsigned builtin_argc; |
7347 | char **e; | 7358 | char **e; |
7348 | struct variable *cur_var; | 7359 | struct variable *cur_var; |
7360 | struct variable shell_ver; | ||
7349 | 7361 | ||
7350 | INIT_G(); | 7362 | INIT_G(); |
7351 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ | 7363 | if (EXIT_SUCCESS) /* if EXIT_SUCCESS == 0, it is already done */ |
@@ -7354,12 +7366,13 @@ int hush_main(int argc, char **argv) | |||
7354 | G.argv0_for_re_execing = argv[0]; | 7366 | G.argv0_for_re_execing = argv[0]; |
7355 | #endif | 7367 | #endif |
7356 | /* Deal with HUSH_VERSION */ | 7368 | /* Deal with HUSH_VERSION */ |
7357 | G.shell_ver.flg_export = 1; | 7369 | memset(&shell_ver, 0, sizeof(shell_ver)); |
7358 | G.shell_ver.flg_read_only = 1; | 7370 | shell_ver.flg_export = 1; |
7371 | shell_ver.flg_read_only = 1; | ||
7359 | /* Code which handles ${var<op>...} needs writable values for all variables, | 7372 | /* Code which handles ${var<op>...} needs writable values for all variables, |
7360 | * therefore we xstrdup: */ | 7373 | * therefore we xstrdup: */ |
7361 | G.shell_ver.varstr = xstrdup(hush_version_str), | 7374 | shell_ver.varstr = xstrdup(hush_version_str), |
7362 | G.top_var = &G.shell_ver; | 7375 | G.top_var = &shell_ver; |
7363 | /* Create shell local variables from the values | 7376 | /* Create shell local variables from the values |
7364 | * currently living in the environment */ | 7377 | * currently living in the environment */ |
7365 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); | 7378 | debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION"); |
@@ -7378,8 +7391,8 @@ int hush_main(int argc, char **argv) | |||
7378 | e++; | 7391 | e++; |
7379 | } | 7392 | } |
7380 | /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */ | 7393 | /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */ |
7381 | debug_printf_env("putenv '%s'\n", G.shell_ver.varstr); | 7394 | debug_printf_env("putenv '%s'\n", shell_ver.varstr); |
7382 | putenv(G.shell_ver.varstr); | 7395 | putenv(shell_ver.varstr); |
7383 | 7396 | ||
7384 | /* Export PWD */ | 7397 | /* Export PWD */ |
7385 | set_pwd_var(/*exp:*/ 1); | 7398 | set_pwd_var(/*exp:*/ 1); |
diff --git a/shell/hush_test/hush-arith/arith.right b/shell/hush_test/hush-arith/arith.right index 718c26ad0..8a201fb3b 100644 --- a/shell/hush_test/hush-arith/arith.right +++ b/shell/hush_test/hush-arith/arith.right | |||
@@ -61,7 +61,7 @@ check that the unevaluated part of the ternary operator does not do evaluation o | |||
61 | 20 20 | 61 | 20 20 |
62 | 30 30 | 62 | 30 30 |
63 | check precedence of assignment vs. conditional operator | 63 | check precedence of assignment vs. conditional operator |
64 | hush: error in arithmetic | 64 | hush: arithmetic syntax error |
65 | check precedence of assignment vs. conditional operator | 65 | check precedence of assignment vs. conditional operator |
66 | associativity of assignment-operator operator | 66 | associativity of assignment-operator operator |
67 | 6 6 | 67 | 6 6 |
@@ -70,22 +70,22 @@ octal, hex | |||
70 | 263 263 | 70 | 263 263 |
71 | 255 255 | 71 | 255 255 |
72 | 40 40 | 72 | 40 40 |
73 | hush: error in arithmetic | 73 | hush: arithmetic syntax error |
74 | hush: divide by 0 | 74 | hush: divide by zero |
75 | hush: can't execute 'let': No such file or directory | 75 | hush: can't execute 'let': No such file or directory |
76 | hush: error in arithmetic | 76 | hush: arithmetic syntax error |
77 | hush: can't execute 'let': No such file or directory | 77 | hush: can't execute 'let': No such file or directory |
78 | abc | 78 | abc |
79 | def | 79 | def |
80 | ghi | 80 | ghi |
81 | hush: error in arithmetic | 81 | hush: arithmetic syntax error |
82 | 16 16 | 82 | 16 16 |
83 | hush: error in arithmetic | 83 | hush: arithmetic syntax error |
84 | hush: error in arithmetic | 84 | hush: malformed ?: operator |
85 | hush: error in arithmetic | 85 | hush: arithmetic syntax error |
86 | 9 9 | 86 | 9 9 |
87 | hush: error in arithmetic | 87 | hush: arithmetic syntax error |
88 | hush: error in arithmetic | 88 | hush: arithmetic syntax error |
89 | 9 9 | 89 | 9 9 |
90 | 9 9 | 90 | 9 9 |
91 | 9 9 | 91 | 9 9 |
@@ -106,18 +106,18 @@ hush: error in arithmetic | |||
106 | 3 3 | 106 | 3 3 |
107 | 4 4 | 107 | 4 4 |
108 | 4 4 | 108 | 4 4 |
109 | hush: error in arithmetic | 109 | hush: arithmetic syntax error |
110 | hush: error in arithmetic | 110 | hush: arithmetic syntax error |
111 | hush: error in arithmetic | 111 | hush: arithmetic syntax error |
112 | hush: error in arithmetic | 112 | hush: arithmetic syntax error |
113 | hush: error in arithmetic | 113 | hush: arithmetic syntax error |
114 | 4 4 | 114 | 4 4 |
115 | 7 7 | 115 | 7 7 |
116 | -7 -7 | 116 | -7 -7 |
117 | hush: error in arithmetic | 117 | hush: arithmetic syntax error |
118 | hush: error in arithmetic | 118 | hush: arithmetic syntax error |
119 | hush: error in arithmetic | 119 | hush: arithmetic syntax error |
120 | hush: error in arithmetic | 120 | hush: arithmetic syntax error |
121 | 6 6 | 121 | 6 6 |
122 | 3 3 | 122 | 3 3 |
123 | 7 7 | 123 | 7 7 |
@@ -128,19 +128,19 @@ hush: error in arithmetic | |||
128 | 2 2 | 128 | 2 2 |
129 | -2 -2 | 129 | -2 -2 |
130 | 1 1 | 130 | 1 1 |
131 | hush: error in arithmetic | 131 | hush: arithmetic syntax error |
132 | hush: error in arithmetic | 132 | hush: arithmetic syntax error |
133 | hush: error in arithmetic | 133 | hush: arithmetic syntax error |
134 | hush: error in arithmetic | 134 | hush: arithmetic syntax error |
135 | hush: error in arithmetic | 135 | hush: arithmetic syntax error |
136 | 5 5 | 136 | 5 5 |
137 | 1 1 | 137 | 1 1 |
138 | 4 4 | 138 | 4 4 |
139 | 0 0 | 139 | 0 0 |
140 | hush: error in arithmetic | 140 | hush: arithmetic syntax error |
141 | hush: error in arithmetic | 141 | hush: arithmetic syntax error |
142 | 8 12 | 142 | 8 12 |
143 | hush: error in arithmetic | 143 | hush: arithmetic syntax error |
144 | 42 | 144 | 42 |
145 | 42 | 145 | 42 |
146 | 42 | 146 | 42 |
diff --git a/shell/hush_test/hush-glob/bash_brace1.right b/shell/hush_test/hush-glob/bash_brace1.right new file mode 100644 index 000000000..63365c9e4 --- /dev/null +++ b/shell/hush_test/hush-glob/bash_brace1.right | |||
@@ -0,0 +1,4 @@ | |||
1 | bash_brace1.tests | ||
2 | *{b,b}race1.t* | ||
3 | bash_brace1.tests bash_brace1.tests | ||
4 | Done: 0 | ||
diff --git a/shell/hush_test/hush-glob/bash_brace1.tests b/shell/hush_test/hush-glob/bash_brace1.tests new file mode 100755 index 000000000..eb2f0e974 --- /dev/null +++ b/shell/hush_test/hush-glob/bash_brace1.tests | |||
@@ -0,0 +1,10 @@ | |||
1 | # unquoted $v should be globbed: | ||
2 | v='*brace1.t*'; echo $v | ||
3 | |||
4 | # ...but not brace expanded: | ||
5 | v='*{b,b}race1.t*'; echo $v | ||
6 | |||
7 | # whereas direct brces are expanded: | ||
8 | echo *{b,b}race1.t* | ||
9 | |||
10 | echo Done: $? | ||
diff --git a/shell/math.c b/shell/math.c index a4c55a4d0..760645d0f 100644 --- a/shell/math.c +++ b/shell/math.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * arithmetic code ripped out of ash shell for code sharing | 2 | * Arithmetic code ripped out of ash shell for code sharing. |
3 | * | 3 | * |
4 | * This code is derived from software contributed to Berkeley by | 4 | * This code is derived from software contributed to Berkeley by |
5 | * Kenneth Almquist. | 5 | * Kenneth Almquist. |
@@ -26,43 +26,41 @@ | |||
26 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 26 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
27 | */ | 27 | */ |
28 | /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> | 28 | /* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com> |
29 | 29 | * | |
30 | Permission is hereby granted, free of charge, to any person obtaining | 30 | * Permission is hereby granted, free of charge, to any person obtaining |
31 | a copy of this software and associated documentation files (the | 31 | * a copy of this software and associated documentation files (the |
32 | "Software"), to deal in the Software without restriction, including | 32 | * "Software"), to deal in the Software without restriction, including |
33 | without limitation the rights to use, copy, modify, merge, publish, | 33 | * without limitation the rights to use, copy, modify, merge, publish, |
34 | distribute, sublicense, and/or sell copies of the Software, and to | 34 | * distribute, sublicense, and/or sell copies of the Software, and to |
35 | permit persons to whom the Software is furnished to do so, subject to | 35 | * permit persons to whom the Software is furnished to do so, subject to |
36 | the following conditions: | 36 | * the following conditions: |
37 | 37 | * | |
38 | The above copyright notice and this permission notice shall be | 38 | * The above copyright notice and this permission notice shall be |
39 | included in all copies or substantial portions of the Software. | 39 | * included in all copies or substantial portions of the Software. |
40 | 40 | * | |
41 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 41 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
42 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 42 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
43 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 43 | * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
44 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | 44 | * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
45 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | 45 | * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
46 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | 46 | * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
47 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 47 | * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
48 | */ | 48 | */ |
49 | 49 | ||
50 | /* This is my infix parser/evaluator. It is optimized for size, intended | 50 | /* This is my infix parser/evaluator. It is optimized for size, intended |
51 | * as a replacement for yacc-based parsers. However, it may well be faster | 51 | * as a replacement for yacc-based parsers. However, it may well be faster |
52 | * than a comparable parser written in yacc. The supported operators are | 52 | * than a comparable parser written in yacc. The supported operators are |
53 | * listed in #defines below. Parens, order of operations, and error handling | 53 | * listed in #defines below. Parens, order of operations, and error handling |
54 | * are supported. This code is thread safe. The exact expression format should | 54 | * are supported. This code is thread safe. The exact expression format should |
55 | * be that which POSIX specifies for shells. */ | 55 | * be that which POSIX specifies for shells. |
56 | 56 | * | |
57 | /* The code uses a simple two-stack algorithm. See | 57 | * The code uses a simple two-stack algorithm. See |
58 | * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html | 58 | * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html |
59 | * for a detailed explanation of the infix-to-postfix algorithm on which | 59 | * for a detailed explanation of the infix-to-postfix algorithm on which |
60 | * this is based (this code differs in that it applies operators immediately | 60 | * this is based (this code differs in that it applies operators immediately |
61 | * to the stack instead of adding them to a queue to end up with an | 61 | * to the stack instead of adding them to a queue to end up with an |
62 | * expression). */ | 62 | * expression). |
63 | 63 | */ | |
64 | /* To use the routine, call it with an expression string and error return | ||
65 | * pointer */ | ||
66 | 64 | ||
67 | /* | 65 | /* |
68 | * Aug 24, 2001 Manuel Novoa III | 66 | * Aug 24, 2001 Manuel Novoa III |
@@ -104,28 +102,23 @@ | |||
104 | * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> | 102 | * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru> |
105 | * | 103 | * |
106 | * - allow access to variable, | 104 | * - allow access to variable, |
107 | * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6) | 105 | * use recursive value indirection: c="2*2"; a="c"; echo $((a+=2)) produce 6 |
108 | * - realize assign syntax (VAR=expr, +=, *= etc) | 106 | * - implement assign syntax (VAR=expr, +=, *= etc) |
109 | * - realize exponentiation (** operator) | 107 | * - implement exponentiation (** operator) |
110 | * - realize comma separated - expr, expr | 108 | * - implement comma separated - expr, expr |
111 | * - realise ++expr --expr expr++ expr-- | 109 | * - implement ++expr --expr expr++ expr-- |
112 | * - realise expr ? expr : expr (but, second expr calculate always) | 110 | * - implement expr ? expr : expr (but second expr is always calculated) |
113 | * - allow hexadecimal and octal numbers | 111 | * - allow hexadecimal and octal numbers |
114 | * - was restored loses XOR operator | 112 | * - restore lost XOR operator |
115 | * - remove one goto label, added three ;-) | 113 | * - protect $((num num)) as true zero expr (Manuel's error) |
116 | * - protect $((num num)) as true zero expr (Manuel`s error) | ||
117 | * - always use special isspace(), see comment from bash ;-) | 114 | * - always use special isspace(), see comment from bash ;-) |
118 | */ | 115 | */ |
119 | #include "libbb.h" | 116 | #include "libbb.h" |
120 | #include "math.h" | 117 | #include "math.h" |
121 | 118 | ||
122 | #define a_e_h_t arith_eval_hooks_t | 119 | #define lookupvar (math_state->lookupvar) |
123 | #define lookupvar (math_hooks->lookupvar) | 120 | #define setvar (math_state->setvar ) |
124 | #define setvar (math_hooks->setvar ) | 121 | //#define endofname (math_state->endofname) |
125 | //#define endofname (math_hooks->endofname) | ||
126 | |||
127 | #define arith_isspace(arithval) \ | ||
128 | (arithval == ' ' || arithval == '\n' || arithval == '\t') | ||
129 | 122 | ||
130 | typedef unsigned char operator; | 123 | typedef unsigned char operator; |
131 | 124 | ||
@@ -133,181 +126,199 @@ typedef unsigned char operator; | |||
133 | * precedence, and 3 high bits are an ID unique across operators of that | 126 | * precedence, and 3 high bits are an ID unique across operators of that |
134 | * precedence. The ID portion is so that multiple operators can have the | 127 | * precedence. The ID portion is so that multiple operators can have the |
135 | * same precedence, ensuring that the leftmost one is evaluated first. | 128 | * same precedence, ensuring that the leftmost one is evaluated first. |
136 | * Consider * and /. */ | 129 | * Consider * and / |
137 | 130 | */ | |
138 | #define tok_decl(prec,id) (((id)<<5)|(prec)) | 131 | #define tok_decl(prec,id) (((id)<<5) | (prec)) |
139 | #define PREC(op) ((op) & 0x1F) | 132 | #define PREC(op) ((op) & 0x1F) |
140 | |||
141 | #define TOK_LPAREN tok_decl(0,0) | ||
142 | 133 | ||
143 | #define TOK_COMMA tok_decl(1,0) | 134 | #define TOK_LPAREN tok_decl(0,0) |
144 | 135 | ||
145 | #define TOK_ASSIGN tok_decl(2,0) | 136 | #define TOK_COMMA tok_decl(1,0) |
146 | #define TOK_AND_ASSIGN tok_decl(2,1) | ||
147 | #define TOK_OR_ASSIGN tok_decl(2,2) | ||
148 | #define TOK_XOR_ASSIGN tok_decl(2,3) | ||
149 | #define TOK_PLUS_ASSIGN tok_decl(2,4) | ||
150 | #define TOK_MINUS_ASSIGN tok_decl(2,5) | ||
151 | #define TOK_LSHIFT_ASSIGN tok_decl(2,6) | ||
152 | #define TOK_RSHIFT_ASSIGN tok_decl(2,7) | ||
153 | 137 | ||
154 | #define TOK_MUL_ASSIGN tok_decl(3,0) | 138 | /* All assignments are right associative and have the same precedence, |
155 | #define TOK_DIV_ASSIGN tok_decl(3,1) | 139 | * but there are 11 of them, which doesn't fit into 3 bits for unique id. |
156 | #define TOK_REM_ASSIGN tok_decl(3,2) | 140 | * Abusing another precedence level: |
141 | */ | ||
142 | #define TOK_ASSIGN tok_decl(2,0) | ||
143 | #define TOK_AND_ASSIGN tok_decl(2,1) | ||
144 | #define TOK_OR_ASSIGN tok_decl(2,2) | ||
145 | #define TOK_XOR_ASSIGN tok_decl(2,3) | ||
146 | #define TOK_PLUS_ASSIGN tok_decl(2,4) | ||
147 | #define TOK_MINUS_ASSIGN tok_decl(2,5) | ||
148 | #define TOK_LSHIFT_ASSIGN tok_decl(2,6) | ||
149 | #define TOK_RSHIFT_ASSIGN tok_decl(2,7) | ||
157 | 150 | ||
158 | /* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */ | 151 | #define TOK_MUL_ASSIGN tok_decl(3,0) |
159 | #define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0) | 152 | #define TOK_DIV_ASSIGN tok_decl(3,1) |
153 | #define TOK_REM_ASSIGN tok_decl(3,2) | ||
160 | 154 | ||
161 | /* conditional is right associativity too */ | 155 | #define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) |
162 | #define TOK_CONDITIONAL tok_decl(4,0) | ||
163 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) | ||
164 | 156 | ||
165 | #define TOK_OR tok_decl(5,0) | 157 | /* Ternary conditional operator is right associative too */ |
158 | #define TOK_CONDITIONAL tok_decl(4,0) | ||
159 | #define TOK_CONDITIONAL_SEP tok_decl(4,1) | ||
166 | 160 | ||
167 | #define TOK_AND tok_decl(6,0) | 161 | #define TOK_OR tok_decl(5,0) |
168 | 162 | ||
169 | #define TOK_BOR tok_decl(7,0) | 163 | #define TOK_AND tok_decl(6,0) |
170 | 164 | ||
171 | #define TOK_BXOR tok_decl(8,0) | 165 | #define TOK_BOR tok_decl(7,0) |
172 | 166 | ||
173 | #define TOK_BAND tok_decl(9,0) | 167 | #define TOK_BXOR tok_decl(8,0) |
174 | 168 | ||
175 | #define TOK_EQ tok_decl(10,0) | 169 | #define TOK_BAND tok_decl(9,0) |
176 | #define TOK_NE tok_decl(10,1) | ||
177 | 170 | ||
178 | #define TOK_LT tok_decl(11,0) | 171 | #define TOK_EQ tok_decl(10,0) |
179 | #define TOK_GT tok_decl(11,1) | 172 | #define TOK_NE tok_decl(10,1) |
180 | #define TOK_GE tok_decl(11,2) | ||
181 | #define TOK_LE tok_decl(11,3) | ||
182 | 173 | ||
183 | #define TOK_LSHIFT tok_decl(12,0) | 174 | #define TOK_LT tok_decl(11,0) |
184 | #define TOK_RSHIFT tok_decl(12,1) | 175 | #define TOK_GT tok_decl(11,1) |
176 | #define TOK_GE tok_decl(11,2) | ||
177 | #define TOK_LE tok_decl(11,3) | ||
185 | 178 | ||
186 | #define TOK_ADD tok_decl(13,0) | 179 | #define TOK_LSHIFT tok_decl(12,0) |
187 | #define TOK_SUB tok_decl(13,1) | 180 | #define TOK_RSHIFT tok_decl(12,1) |
188 | 181 | ||
189 | #define TOK_MUL tok_decl(14,0) | 182 | #define TOK_ADD tok_decl(13,0) |
190 | #define TOK_DIV tok_decl(14,1) | 183 | #define TOK_SUB tok_decl(13,1) |
191 | #define TOK_REM tok_decl(14,2) | ||
192 | 184 | ||
193 | /* exponent is right associativity */ | 185 | #define TOK_MUL tok_decl(14,0) |
194 | #define TOK_EXPONENT tok_decl(15,1) | 186 | #define TOK_DIV tok_decl(14,1) |
187 | #define TOK_REM tok_decl(14,2) | ||
195 | 188 | ||
196 | /* For now unary operators. */ | 189 | /* Exponent is right associative */ |
197 | #define UNARYPREC 16 | 190 | #define TOK_EXPONENT tok_decl(15,1) |
198 | #define TOK_BNOT tok_decl(UNARYPREC,0) | ||
199 | #define TOK_NOT tok_decl(UNARYPREC,1) | ||
200 | 191 | ||
201 | #define TOK_UMINUS tok_decl(UNARYPREC+1,0) | 192 | /* Unary operators */ |
202 | #define TOK_UPLUS tok_decl(UNARYPREC+1,1) | 193 | #define UNARYPREC 16 |
194 | #define TOK_BNOT tok_decl(UNARYPREC,0) | ||
195 | #define TOK_NOT tok_decl(UNARYPREC,1) | ||
203 | 196 | ||
204 | #define PREC_PRE (UNARYPREC+2) | 197 | #define TOK_UMINUS tok_decl(UNARYPREC+1,0) |
198 | #define TOK_UPLUS tok_decl(UNARYPREC+1,1) | ||
205 | 199 | ||
206 | #define TOK_PRE_INC tok_decl(PREC_PRE, 0) | 200 | #define PREC_PRE (UNARYPREC+2) |
207 | #define TOK_PRE_DEC tok_decl(PREC_PRE, 1) | ||
208 | 201 | ||
209 | #define PREC_POST (UNARYPREC+3) | 202 | #define TOK_PRE_INC tok_decl(PREC_PRE, 0) |
203 | #define TOK_PRE_DEC tok_decl(PREC_PRE, 1) | ||
210 | 204 | ||
211 | #define TOK_POST_INC tok_decl(PREC_POST, 0) | 205 | #define PREC_POST (UNARYPREC+3) |
212 | #define TOK_POST_DEC tok_decl(PREC_POST, 1) | ||
213 | 206 | ||
214 | #define SPEC_PREC (UNARYPREC+4) | 207 | #define TOK_POST_INC tok_decl(PREC_POST, 0) |
208 | #define TOK_POST_DEC tok_decl(PREC_POST, 1) | ||
215 | 209 | ||
216 | #define TOK_NUM tok_decl(SPEC_PREC, 0) | 210 | #define SPEC_PREC (UNARYPREC+4) |
217 | #define TOK_RPAREN tok_decl(SPEC_PREC, 1) | ||
218 | 211 | ||
219 | #define NUMPTR (*numstackptr) | 212 | #define TOK_NUM tok_decl(SPEC_PREC, 0) |
213 | #define TOK_RPAREN tok_decl(SPEC_PREC, 1) | ||
220 | 214 | ||
221 | static int | 215 | static int |
222 | tok_have_assign(operator op) | 216 | is_assign_op(operator op) |
223 | { | 217 | { |
224 | operator prec = PREC(op); | 218 | operator prec = PREC(op); |
225 | 219 | fix_assignment_prec(prec); | |
226 | convert_prec_is_assing(prec); | 220 | return prec == PREC(TOK_ASSIGN) |
227 | return (prec == PREC(TOK_ASSIGN) || | 221 | || prec == PREC_PRE |
228 | prec == PREC_PRE || prec == PREC_POST); | 222 | || prec == PREC_POST; |
229 | } | 223 | } |
230 | 224 | ||
231 | static int | 225 | static int |
232 | is_right_associativity(operator prec) | 226 | is_right_associative(operator prec) |
233 | { | 227 | { |
234 | return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) | 228 | return prec == PREC(TOK_ASSIGN) |
235 | || prec == PREC(TOK_CONDITIONAL)); | 229 | || prec == PREC(TOK_EXPONENT) |
230 | || prec == PREC(TOK_CONDITIONAL); | ||
236 | } | 231 | } |
237 | 232 | ||
233 | |||
238 | typedef struct { | 234 | typedef struct { |
239 | arith_t val; | 235 | arith_t val; |
240 | arith_t contidional_second_val; | 236 | /* We acquire second_val only when "expr1 : expr2" part |
241 | char contidional_second_val_initialized; | 237 | * of ternary ?: op is evaluated. |
242 | char *var; /* if NULL then is regular number, | 238 | * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). |
243 | else is variable name */ | 239 | * ':' produces a new value which has two parts, val and second_val; |
244 | } v_n_t; | 240 | * then '?' selects one of them based on its left side. |
245 | 241 | */ | |
246 | typedef struct chk_var_recursive_looped_t { | 242 | arith_t second_val; |
243 | char second_val_present; | ||
244 | /* If NULL then it's just a number, else it's a named variable */ | ||
245 | char *var; | ||
246 | } var_or_num_t; | ||
247 | |||
248 | typedef struct remembered_name { | ||
249 | struct remembered_name *next; | ||
247 | const char *var; | 250 | const char *var; |
248 | struct chk_var_recursive_looped_t *next; | 251 | } remembered_name; |
249 | } chk_var_recursive_looped_t; | ||
250 | 252 | ||
251 | static chk_var_recursive_looped_t *prev_chk_var_recursive; | ||
252 | 253 | ||
253 | static int | 254 | static arith_t FAST_FUNC |
254 | arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks) | 255 | evaluate_string(arith_state_t *math_state, const char *expr); |
256 | |||
257 | static const char* | ||
258 | arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) | ||
255 | { | 259 | { |
256 | if (t->var) { | 260 | if (t->var) { |
257 | const char *p = lookupvar(t->var); | 261 | const char *p = lookupvar(t->var); |
258 | |||
259 | if (p) { | 262 | if (p) { |
260 | int errcode; | 263 | remembered_name *cur; |
261 | 264 | remembered_name cur_save; | |
262 | /* recursive try as expression */ | ||
263 | chk_var_recursive_looped_t *cur; | ||
264 | chk_var_recursive_looped_t cur_save; | ||
265 | 265 | ||
266 | for (cur = prev_chk_var_recursive; cur; cur = cur->next) { | 266 | /* did we already see this name? |
267 | * testcase: a=b; b=a; echo $((a)) | ||
268 | */ | ||
269 | for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { | ||
267 | if (strcmp(cur->var, t->var) == 0) { | 270 | if (strcmp(cur->var, t->var) == 0) { |
268 | /* expression recursion loop detected */ | 271 | /* Yes */ |
269 | return -5; | 272 | return "expression recursion loop detected"; |
270 | } | 273 | } |
271 | } | 274 | } |
272 | /* save current lookuped var name */ | 275 | |
273 | cur = prev_chk_var_recursive; | 276 | /* push current var name */ |
277 | cur = math_state->list_of_recursed_names; | ||
274 | cur_save.var = t->var; | 278 | cur_save.var = t->var; |
275 | cur_save.next = cur; | 279 | cur_save.next = cur; |
276 | prev_chk_var_recursive = &cur_save; | 280 | math_state->list_of_recursed_names = &cur_save; |
281 | |||
282 | /* recursively evaluate p as expression */ | ||
283 | t->val = evaluate_string(math_state, p); | ||
277 | 284 | ||
278 | t->val = arith (p, &errcode, math_hooks); | 285 | /* pop current var name */ |
279 | /* restore previous ptr after recursiving */ | 286 | math_state->list_of_recursed_names = cur; |
280 | prev_chk_var_recursive = cur; | 287 | |
281 | return errcode; | 288 | return math_state->errmsg; |
282 | } | 289 | } |
283 | /* allow undefined var as 0 */ | 290 | /* treat undefined var as 0 */ |
284 | t->val = 0; | 291 | t->val = 0; |
285 | } | 292 | } |
286 | return 0; | 293 | return 0; |
287 | } | 294 | } |
288 | 295 | ||
289 | /* "applying" a token means performing it on the top elements on the integer | 296 | /* "Applying" a token means performing it on the top elements on the integer |
290 | * stack. For a unary operator it will only change the top element, but a | 297 | * stack. For an unary operator it will only change the top element, but a |
291 | * binary operator will pop two arguments and push a result */ | 298 | * binary operator will pop two arguments and push the result */ |
292 | static NOINLINE int | 299 | static NOINLINE const char* |
293 | arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks) | 300 | arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) |
294 | { | 301 | { |
295 | v_n_t *numptr_m1; | 302 | #define NUMPTR (*numstackptr) |
296 | arith_t numptr_val, rez; | 303 | |
297 | int ret_arith_lookup_val; | 304 | var_or_num_t *top_of_stack; |
305 | arith_t rez; | ||
306 | const char *err; | ||
298 | 307 | ||
299 | /* There is no operator that can work without arguments */ | 308 | /* There is no operator that can work without arguments */ |
300 | if (NUMPTR == numstack) goto err; | 309 | if (NUMPTR == numstack) |
301 | numptr_m1 = NUMPTR - 1; | 310 | goto err; |
311 | |||
312 | top_of_stack = NUMPTR - 1; | ||
302 | 313 | ||
303 | /* check operand is var with noninteger value */ | 314 | /* Resolve name to value, if needed */ |
304 | ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks); | 315 | err = arith_lookup_val(math_state, top_of_stack); |
305 | if (ret_arith_lookup_val) | 316 | if (err) |
306 | return ret_arith_lookup_val; | 317 | return err; |
307 | 318 | ||
308 | rez = numptr_m1->val; | 319 | rez = top_of_stack->val; |
309 | if (op == TOK_UMINUS) | 320 | if (op == TOK_UMINUS) |
310 | rez *= -1; | 321 | rez = -rez; |
311 | else if (op == TOK_NOT) | 322 | else if (op == TOK_NOT) |
312 | rez = !rez; | 323 | rez = !rez; |
313 | else if (op == TOK_BNOT) | 324 | else if (op == TOK_BNOT) |
@@ -318,118 +329,123 @@ arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hoo | |||
318 | rez--; | 329 | rez--; |
319 | else if (op != TOK_UPLUS) { | 330 | else if (op != TOK_UPLUS) { |
320 | /* Binary operators */ | 331 | /* Binary operators */ |
332 | arith_t right_side_val; | ||
333 | char bad_second_val; | ||
321 | 334 | ||
322 | /* check and binary operators need two arguments */ | 335 | /* Binary operators need two arguments */ |
323 | if (numptr_m1 == numstack) goto err; | 336 | if (top_of_stack == numstack) |
324 | |||
325 | /* ... and they pop one */ | ||
326 | --NUMPTR; | ||
327 | numptr_val = rez; | ||
328 | if (op == TOK_CONDITIONAL) { | ||
329 | if (!numptr_m1->contidional_second_val_initialized) { | ||
330 | /* protect $((expr1 ? expr2)) without ": expr" */ | ||
331 | goto err; | ||
332 | } | ||
333 | rez = numptr_m1->contidional_second_val; | ||
334 | } else if (numptr_m1->contidional_second_val_initialized) { | ||
335 | /* protect $((expr1 : expr2)) without "expr ? " */ | ||
336 | goto err; | 337 | goto err; |
338 | /* ...and they pop one */ | ||
339 | NUMPTR = top_of_stack; /* this decrements NUMPTR */ | ||
340 | |||
341 | bad_second_val = top_of_stack->second_val_present; | ||
342 | if (op == TOK_CONDITIONAL) { /* ? operation */ | ||
343 | /* Make next if (...) protect against | ||
344 | * $((expr1 ? expr2)) - that is, missing ": expr" */ | ||
345 | bad_second_val = !bad_second_val; | ||
346 | } | ||
347 | if (bad_second_val) { | ||
348 | /* Protect against $((expr <not_?_op> expr1 : expr2)) */ | ||
349 | return "malformed ?: operator"; | ||
337 | } | 350 | } |
338 | numptr_m1 = NUMPTR - 1; | 351 | |
352 | top_of_stack--; /* now points to left side */ | ||
353 | |||
339 | if (op != TOK_ASSIGN) { | 354 | if (op != TOK_ASSIGN) { |
340 | /* check operand is var with noninteger value for not '=' */ | 355 | /* Resolve left side value (unless the op is '=') */ |
341 | ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks); | 356 | err = arith_lookup_val(math_state, top_of_stack); |
342 | if (ret_arith_lookup_val) | 357 | if (err) |
343 | return ret_arith_lookup_val; | 358 | return err; |
344 | } | 359 | } |
345 | if (op == TOK_CONDITIONAL) { | 360 | |
346 | numptr_m1->contidional_second_val = rez; | 361 | right_side_val = rez; |
362 | rez = top_of_stack->val; | ||
363 | if (op == TOK_CONDITIONAL) /* ? operation */ | ||
364 | rez = (rez ? right_side_val : top_of_stack[1].second_val); | ||
365 | else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ | ||
366 | if (top_of_stack == numstack) { | ||
367 | /* Protect against $((expr : expr)) */ | ||
368 | return "malformed ?: operator"; | ||
369 | } | ||
370 | top_of_stack->second_val_present = op; | ||
371 | top_of_stack->second_val = right_side_val; | ||
347 | } | 372 | } |
348 | rez = numptr_m1->val; | 373 | else if (op == TOK_BOR || op == TOK_OR_ASSIGN) |
349 | if (op == TOK_BOR || op == TOK_OR_ASSIGN) | 374 | rez |= right_side_val; |
350 | rez |= numptr_val; | ||
351 | else if (op == TOK_OR) | 375 | else if (op == TOK_OR) |
352 | rez = numptr_val || rez; | 376 | rez = right_side_val || rez; |
353 | else if (op == TOK_BAND || op == TOK_AND_ASSIGN) | 377 | else if (op == TOK_BAND || op == TOK_AND_ASSIGN) |
354 | rez &= numptr_val; | 378 | rez &= right_side_val; |
355 | else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) | 379 | else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) |
356 | rez ^= numptr_val; | 380 | rez ^= right_side_val; |
357 | else if (op == TOK_AND) | 381 | else if (op == TOK_AND) |
358 | rez = rez && numptr_val; | 382 | rez = rez && right_side_val; |
359 | else if (op == TOK_EQ) | 383 | else if (op == TOK_EQ) |
360 | rez = (rez == numptr_val); | 384 | rez = (rez == right_side_val); |
361 | else if (op == TOK_NE) | 385 | else if (op == TOK_NE) |
362 | rez = (rez != numptr_val); | 386 | rez = (rez != right_side_val); |
363 | else if (op == TOK_GE) | 387 | else if (op == TOK_GE) |
364 | rez = (rez >= numptr_val); | 388 | rez = (rez >= right_side_val); |
365 | else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) | 389 | else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) |
366 | rez >>= numptr_val; | 390 | rez >>= right_side_val; |
367 | else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) | 391 | else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) |
368 | rez <<= numptr_val; | 392 | rez <<= right_side_val; |
369 | else if (op == TOK_GT) | 393 | else if (op == TOK_GT) |
370 | rez = (rez > numptr_val); | 394 | rez = (rez > right_side_val); |
371 | else if (op == TOK_LT) | 395 | else if (op == TOK_LT) |
372 | rez = (rez < numptr_val); | 396 | rez = (rez < right_side_val); |
373 | else if (op == TOK_LE) | 397 | else if (op == TOK_LE) |
374 | rez = (rez <= numptr_val); | 398 | rez = (rez <= right_side_val); |
375 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) | 399 | else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) |
376 | rez *= numptr_val; | 400 | rez *= right_side_val; |
377 | else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) | 401 | else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) |
378 | rez += numptr_val; | 402 | rez += right_side_val; |
379 | else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) | 403 | else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) |
380 | rez -= numptr_val; | 404 | rez -= right_side_val; |
381 | else if (op == TOK_ASSIGN || op == TOK_COMMA) | 405 | else if (op == TOK_ASSIGN || op == TOK_COMMA) |
382 | rez = numptr_val; | 406 | rez = right_side_val; |
383 | else if (op == TOK_CONDITIONAL_SEP) { | 407 | else if (op == TOK_EXPONENT) { |
384 | if (numptr_m1 == numstack) { | 408 | arith_t c; |
385 | /* protect $((expr : expr)) without "expr ? " */ | 409 | if (right_side_val < 0) |
386 | goto err; | 410 | return "exponent less than 0"; |
387 | } | 411 | c = 1; |
388 | numptr_m1->contidional_second_val_initialized = op; | 412 | while (--right_side_val >= 0) |
389 | numptr_m1->contidional_second_val = numptr_val; | 413 | c *= rez; |
390 | } else if (op == TOK_CONDITIONAL) { | 414 | rez = c; |
391 | rez = rez ? | 415 | } |
392 | numptr_val : numptr_m1->contidional_second_val; | 416 | else if (right_side_val == 0) |
393 | } else if (op == TOK_EXPONENT) { | 417 | return "divide by zero"; |
394 | if (numptr_val < 0) | ||
395 | return -3; /* exponent less than 0 */ | ||
396 | else { | ||
397 | arith_t c = 1; | ||
398 | |||
399 | if (numptr_val) | ||
400 | while (numptr_val--) | ||
401 | c *= rez; | ||
402 | rez = c; | ||
403 | } | ||
404 | } else if (numptr_val==0) /* zero divisor check */ | ||
405 | return -2; | ||
406 | else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) | 418 | else if (op == TOK_DIV || op == TOK_DIV_ASSIGN) |
407 | rez /= numptr_val; | 419 | rez /= right_side_val; |
408 | else if (op == TOK_REM || op == TOK_REM_ASSIGN) | 420 | else if (op == TOK_REM || op == TOK_REM_ASSIGN) |
409 | rez %= numptr_val; | 421 | rez %= right_side_val; |
410 | } | 422 | } |
411 | if (tok_have_assign(op)) { | 423 | |
424 | if (is_assign_op(op)) { | ||
412 | char buf[sizeof(arith_t)*3 + 2]; | 425 | char buf[sizeof(arith_t)*3 + 2]; |
413 | 426 | ||
414 | if (numptr_m1->var == NULL) { | 427 | if (top_of_stack->var == NULL) { |
415 | /* Hmm, 1=2 ? */ | 428 | /* Hmm, 1=2 ? */ |
429 | //TODO: actually, bash allows ++7 but for some reason it evals to 7, not 8 | ||
416 | goto err; | 430 | goto err; |
417 | } | 431 | } |
418 | /* save to shell variable */ | 432 | /* Save to shell variable */ |
419 | sprintf(buf, arith_t_fmt, rez); | 433 | sprintf(buf, ARITH_FMT, rez); |
420 | setvar(numptr_m1->var, buf); | 434 | setvar(top_of_stack->var, buf); |
421 | /* after saving, make previous value for v++ or v-- */ | 435 | /* After saving, make previous value for v++ or v-- */ |
422 | if (op == TOK_POST_INC) | 436 | if (op == TOK_POST_INC) |
423 | rez--; | 437 | rez--; |
424 | else if (op == TOK_POST_DEC) | 438 | else if (op == TOK_POST_DEC) |
425 | rez++; | 439 | rez++; |
426 | } | 440 | } |
427 | numptr_m1->val = rez; | 441 | |
428 | /* protect geting var value, is number now */ | 442 | top_of_stack->val = rez; |
429 | numptr_m1->var = NULL; | 443 | /* Erase var name, it is just a number now */ |
430 | return 0; | 444 | top_of_stack->var = NULL; |
445 | return NULL; | ||
431 | err: | 446 | err: |
432 | return -1; | 447 | return "arithmetic syntax error"; |
448 | #undef NUMPTR | ||
433 | } | 449 | } |
434 | 450 | ||
435 | /* longest must be first */ | 451 | /* longest must be first */ |
@@ -476,8 +492,7 @@ static const char op_tokens[] ALIGN1 = { | |||
476 | '(', 0, TOK_LPAREN, | 492 | '(', 0, TOK_LPAREN, |
477 | 0 | 493 | 0 |
478 | }; | 494 | }; |
479 | /* ptr to ")" */ | 495 | #define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) |
480 | #define endexpression (&op_tokens[sizeof(op_tokens)-7]) | ||
481 | 496 | ||
482 | const char* FAST_FUNC | 497 | const char* FAST_FUNC |
483 | endofname(const char *name) | 498 | endofname(const char *name) |
@@ -491,35 +506,40 @@ endofname(const char *name) | |||
491 | return name; | 506 | return name; |
492 | } | 507 | } |
493 | 508 | ||
494 | arith_t | 509 | static arith_t FAST_FUNC |
495 | arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | 510 | evaluate_string(arith_state_t *math_state, const char *expr) |
496 | { | 511 | { |
497 | char arithval; /* Current character under analysis */ | 512 | operator lasttok; |
498 | operator lasttok, op; | 513 | const char *errmsg; |
499 | operator prec; | 514 | const char *start_expr = expr = skip_whitespace(expr); |
500 | operator *stack, *stackptr; | 515 | unsigned expr_len = strlen(expr) + 2; |
501 | const char *p = endexpression; | ||
502 | int errcode; | ||
503 | v_n_t *numstack, *numstackptr; | ||
504 | unsigned datasizes = strlen(expr) + 2; | ||
505 | |||
506 | /* Stack of integers */ | 516 | /* Stack of integers */ |
507 | /* The proof that there can be no more than strlen(startbuf)/2+1 integers | 517 | /* The proof that there can be no more than strlen(startbuf)/2+1 |
508 | * in any given correct or incorrect expression is left as an exercise to | 518 | * integers in any given correct or incorrect expression |
509 | * the reader. */ | 519 | * is left as an exercise to the reader. */ |
510 | numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0])); | 520 | var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); |
521 | var_or_num_t *numstackptr = numstack; | ||
511 | /* Stack of operator tokens */ | 522 | /* Stack of operator tokens */ |
512 | stackptr = stack = alloca(datasizes * sizeof(stack[0])); | 523 | operator *const stack = alloca(expr_len * sizeof(stack[0])); |
524 | operator *stackptr = stack; | ||
513 | 525 | ||
514 | *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */ | 526 | /* Start with a left paren */ |
515 | *perrcode = errcode = 0; | 527 | *stackptr++ = lasttok = TOK_LPAREN; |
528 | errmsg = NULL; | ||
516 | 529 | ||
517 | while (1) { | 530 | while (1) { |
531 | const char *p; | ||
532 | operator op; | ||
533 | operator prec; | ||
534 | char arithval; | ||
535 | |||
536 | expr = skip_whitespace(expr); | ||
518 | arithval = *expr; | 537 | arithval = *expr; |
519 | if (arithval == 0) { | 538 | if (arithval == '\0') { |
520 | if (p == endexpression) { | 539 | if (expr == start_expr) { |
521 | /* Null expression. */ | 540 | /* Null expression */ |
522 | return 0; | 541 | numstack->val = 0; |
542 | goto ret; | ||
523 | } | 543 | } |
524 | 544 | ||
525 | /* This is only reached after all tokens have been extracted from the | 545 | /* This is only reached after all tokens have been extracted from the |
@@ -527,77 +547,80 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | |||
527 | * are to be applied in order. At the end, there should be a final | 547 | * are to be applied in order. At the end, there should be a final |
528 | * result on the integer stack */ | 548 | * result on the integer stack */ |
529 | 549 | ||
530 | if (expr != endexpression + 1) { | 550 | if (expr != ptr_to_rparen + 1) { |
531 | /* If we haven't done so already, */ | 551 | /* If we haven't done so already, |
532 | /* append a closing right paren */ | 552 | * append a closing right paren |
533 | expr = endexpression; | 553 | * and let the loop process it */ |
534 | /* and let the loop process it. */ | 554 | expr = ptr_to_rparen; |
535 | continue; | 555 | continue; |
536 | } | 556 | } |
537 | /* At this point, we're done with the expression. */ | 557 | /* At this point, we're done with the expression */ |
538 | if (numstackptr != numstack+1) { | 558 | if (numstackptr != numstack + 1) { |
539 | /* ... but if there isn't, it's bad */ | 559 | /* ...but if there isn't, it's bad */ |
540 | err: | 560 | goto err; |
541 | *perrcode = -1; | ||
542 | return *perrcode; | ||
543 | } | 561 | } |
544 | if (numstack->var) { | 562 | if (numstack->var) { |
545 | /* expression is $((var)) only, lookup now */ | 563 | /* expression is $((var)) only, lookup now */ |
546 | errcode = arith_lookup_val(numstack, math_hooks); | 564 | errmsg = arith_lookup_val(math_state, numstack); |
547 | } | 565 | } |
548 | ret: | 566 | goto ret; |
549 | *perrcode = errcode; | ||
550 | return numstack->val; | ||
551 | } | 567 | } |
552 | 568 | ||
553 | /* Continue processing the expression. */ | ||
554 | if (arith_isspace(arithval)) { | ||
555 | /* Skip whitespace */ | ||
556 | goto prologue; | ||
557 | } | ||
558 | p = endofname(expr); | 569 | p = endofname(expr); |
559 | if (p != expr) { | 570 | if (p != expr) { |
560 | size_t var_name_size = (p-expr) + 1; /* trailing zero */ | 571 | /* Name */ |
561 | 572 | size_t var_name_size = (p-expr) + 1; /* +1 for NUL */ | |
562 | numstackptr->var = alloca(var_name_size); | 573 | numstackptr->var = alloca(var_name_size); |
563 | safe_strncpy(numstackptr->var, expr, var_name_size); | 574 | safe_strncpy(numstackptr->var, expr, var_name_size); |
564 | expr = p; | 575 | expr = p; |
565 | num: | 576 | num: |
566 | numstackptr->contidional_second_val_initialized = 0; | 577 | numstackptr->second_val_present = 0; |
567 | numstackptr++; | 578 | numstackptr++; |
568 | lasttok = TOK_NUM; | 579 | lasttok = TOK_NUM; |
569 | continue; | 580 | continue; |
570 | } | 581 | } |
582 | |||
571 | if (isdigit(arithval)) { | 583 | if (isdigit(arithval)) { |
584 | /* Number */ | ||
572 | numstackptr->var = NULL; | 585 | numstackptr->var = NULL; |
573 | errno = 0; | 586 | errno = 0; |
574 | /* call strtoul[l]: */ | 587 | numstackptr->val = strto_arith_t(expr, (char**) &expr, 0); |
575 | numstackptr->val = strto_arith_t(expr, (char **) &expr, 0); | ||
576 | if (errno) | 588 | if (errno) |
577 | numstackptr->val = 0; /* bash compat */ | 589 | numstackptr->val = 0; /* bash compat */ |
578 | goto num; | 590 | goto num; |
579 | } | 591 | } |
580 | for (p = op_tokens; ; p++) { | ||
581 | const char *o; | ||
582 | 592 | ||
583 | if (*p == 0) { | 593 | /* Should be an operator */ |
584 | /* strange operator not found */ | 594 | p = op_tokens; |
585 | goto err; | 595 | while (1) { |
586 | } | 596 | // TODO: bash allows 7+++v, treats it as 7 + ++v |
587 | for (o = expr; *p && *o == *p; p++) | 597 | // we treat it as 7++ + v and reject |
588 | o++; | 598 | /* Compare expr to current op_tokens[] element */ |
589 | if (!*p) { | 599 | const char *e = expr; |
590 | /* found */ | 600 | while (1) { |
591 | expr = o - 1; | 601 | if (*p == '\0') { |
592 | break; | 602 | /* Match: operator is found */ |
603 | expr = e; | ||
604 | goto tok_found; | ||
605 | } | ||
606 | if (*p != *e) | ||
607 | break; | ||
608 | p++; | ||
609 | e++; | ||
593 | } | 610 | } |
594 | /* skip tail uncompared token */ | 611 | /* No match, go to next element of op_tokens[] */ |
595 | while (*p) | 612 | while (*p) |
596 | p++; | 613 | p++; |
597 | /* skip zero delim */ | 614 | p += 2; /* skip NUL and TOK_foo bytes */ |
598 | p++; | 615 | if (*p == '\0') { |
616 | /* No next element, operator not found */ | ||
617 | //math_state->syntax_error_at = expr; | ||
618 | goto err; | ||
619 | } | ||
599 | } | 620 | } |
600 | op = p[1]; | 621 | tok_found: |
622 | op = p[1]; /* fetch TOK_foo value */ | ||
623 | /* NB: expr now points past the operator */ | ||
601 | 624 | ||
602 | /* post grammar: a++ reduce to num */ | 625 | /* post grammar: a++ reduce to num */ |
603 | if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) | 626 | if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) |
@@ -626,13 +649,13 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | |||
626 | /* We don't want an unary operator to cause recursive descent on the | 649 | /* We don't want an unary operator to cause recursive descent on the |
627 | * stack, because there can be many in a row and it could cause an | 650 | * stack, because there can be many in a row and it could cause an |
628 | * operator to be evaluated before its argument is pushed onto the | 651 | * operator to be evaluated before its argument is pushed onto the |
629 | * integer stack. */ | 652 | * integer stack. |
630 | /* But for binary operators, "apply" everything on the operator | 653 | * But for binary operators, "apply" everything on the operator |
631 | * stack until we find an operator with a lesser priority than the | 654 | * stack until we find an operator with a lesser priority than the |
632 | * one we have just extracted. */ | 655 | * one we have just extracted. If op is right-associative, |
633 | /* Left paren is given the lowest priority so it will never be | 656 | * then stop "applying" on the equal priority too. |
657 | * Left paren is given the lowest priority so it will never be | ||
634 | * "applied" in this way. | 658 | * "applied" in this way. |
635 | * if associativity is right and priority eq, applied also skip | ||
636 | */ | 659 | */ |
637 | prec = PREC(op); | 660 | prec = PREC(op); |
638 | if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { | 661 | if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { |
@@ -642,41 +665,56 @@ arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) | |||
642 | goto err; | 665 | goto err; |
643 | } | 666 | } |
644 | while (stackptr != stack) { | 667 | while (stackptr != stack) { |
668 | operator prev_op = *--stackptr; | ||
645 | if (op == TOK_RPAREN) { | 669 | if (op == TOK_RPAREN) { |
646 | /* The algorithm employed here is simple: while we don't | 670 | /* The algorithm employed here is simple: while we don't |
647 | * hit an open paren nor the bottom of the stack, pop | 671 | * hit an open paren nor the bottom of the stack, pop |
648 | * tokens and apply them */ | 672 | * tokens and apply them */ |
649 | if (stackptr[-1] == TOK_LPAREN) { | 673 | if (prev_op == TOK_LPAREN) { |
650 | --stackptr; | 674 | /* Any operator directly after a |
651 | /* Any operator directly after a */ | 675 | * close paren should consider itself binary */ |
652 | lasttok = TOK_NUM; | 676 | lasttok = TOK_NUM; |
653 | /* close paren should consider itself binary */ | 677 | goto next; |
654 | goto prologue; | ||
655 | } | 678 | } |
656 | } else { | 679 | } else { |
657 | operator prev_prec = PREC(stackptr[-1]); | 680 | operator prev_prec = PREC(prev_op); |
658 | 681 | fix_assignment_prec(prec); | |
659 | convert_prec_is_assing(prec); | 682 | fix_assignment_prec(prev_prec); |
660 | convert_prec_is_assing(prev_prec); | 683 | if (prev_prec < prec |
661 | if (prev_prec < prec) | 684 | || (prev_prec == prec && is_right_associative(prec)) |
662 | break; | 685 | ) { |
663 | /* check right assoc */ | 686 | stackptr++; |
664 | if (prev_prec == prec && is_right_associativity(prec)) | ||
665 | break; | 687 | break; |
688 | } | ||
666 | } | 689 | } |
667 | errcode = arith_apply(*--stackptr, numstack, &numstackptr, math_hooks); | 690 | errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); |
668 | if (errcode) goto ret; | 691 | if (errmsg) |
692 | goto err_with_custom_msg; | ||
669 | } | 693 | } |
670 | if (op == TOK_RPAREN) { | 694 | if (op == TOK_RPAREN) |
671 | goto err; | 695 | goto err; |
672 | } | ||
673 | } | 696 | } |
674 | 697 | ||
675 | /* Push this operator to the stack and remember it. */ | 698 | /* Push this operator to the stack and remember it */ |
676 | *stackptr++ = lasttok = op; | 699 | *stackptr++ = lasttok = op; |
677 | prologue: | 700 | next: ; |
678 | ++expr; | 701 | } /* while (1) */ |
679 | } /* while */ | 702 | |
703 | err: | ||
704 | errmsg = "arithmetic syntax error"; | ||
705 | err_with_custom_msg: | ||
706 | numstack->val = -1; | ||
707 | ret: | ||
708 | math_state->errmsg = errmsg; | ||
709 | return numstack->val; | ||
710 | } | ||
711 | |||
712 | arith_t FAST_FUNC | ||
713 | arith(arith_state_t *math_state, const char *expr) | ||
714 | { | ||
715 | math_state->errmsg = NULL; | ||
716 | math_state->list_of_recursed_names = NULL; | ||
717 | return evaluate_string(math_state, expr); | ||
680 | } | 718 | } |
681 | 719 | ||
682 | /* | 720 | /* |
diff --git a/shell/math.h b/shell/math.h index 96088b4d2..2d305eb12 100644 --- a/shell/math.h +++ b/shell/math.h | |||
@@ -9,67 +9,53 @@ | |||
9 | 9 | ||
10 | /* The math library has just one function: | 10 | /* The math library has just one function: |
11 | * | 11 | * |
12 | * arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t *hooks); | 12 | * arith_t arith(arith_state_t *state, const char *expr); |
13 | * | 13 | * |
14 | * The first argument is the math string to parse. All normal expansions must | 14 | * The expr argument is the math string to parse. All normal expansions must |
15 | * be done already. i.e. no dollar symbols should be present. | 15 | * be done already. i.e. no dollar symbols should be present. |
16 | * | 16 | * |
17 | * The second argument is a semi-detailed error description in case something | 17 | * The state argument is a pointer to a struct of hooks for your shell (see below), |
18 | * goes wrong in the parsing steps. Currently, those values are (for | 18 | * and an error message string (NULL if no error). |
19 | * compatibility, you should assume all negative values are errors): | ||
20 | * 0 - no errors (yay!) | ||
21 | * -1 - unspecified problem | ||
22 | * -2 - divide by zero | ||
23 | * -3 - exponent less than 0 | ||
24 | * -5 - expression recursion loop detected | ||
25 | * | 19 | * |
26 | * The third argument is a struct pointer of hooks for your shell (see below). | 20 | * The function returns the answer to the expression. So if you called it |
27 | * | 21 | * with the expression: |
28 | * The function returns the answer to the expression. So if you called it | 22 | * "1 + 2 + 3" |
29 | * with the expression: | 23 | * you would obviously get back 6. |
30 | * "1 + 2 + 3" | ||
31 | * You would obviously get back 6. | ||
32 | */ | 24 | */ |
33 | 25 | ||
34 | /* To add support to a shell, you need to implement three functions: | 26 | /* To add support to a shell, you need to implement three functions: |
35 | * | 27 | * |
36 | * lookupvar() - look up and return the value of a variable | 28 | * lookupvar() - look up and return the value of a variable |
37 | * | 29 | * |
38 | * If the shell does: | 30 | * If the shell does: |
39 | * foo=123 | 31 | * foo=123 |
40 | * Then the code: | 32 | * Then the code: |
41 | * const char *val = lookupvar("foo"); | 33 | * const char *val = lookupvar("foo"); |
42 | * Will result in val pointing to "123" | 34 | * will result in val pointing to "123" |
43 | * | 35 | * |
44 | * setvar() - set a variable to some value | 36 | * setvar() - set a variable to some value |
45 | * | 37 | * |
46 | * If the arithmetic expansion does something like: | 38 | * If the arithmetic expansion does something like: |
47 | * $(( i = 1)) | 39 | * $(( i = 1)) |
48 | * Then the math code will make a call like so: | 40 | * then the math code will make a call like so: |
49 | * setvar("i", "1", 0); | 41 | * setvar("i", "1", 0); |
50 | * The storage for the first two parameters are not allocated, so your | 42 | * The storage for the first two parameters are not allocated, so your |
51 | * shell implementation will most likely need to strdup() them to save. | 43 | * shell implementation will most likely need to strdup() them to save. |
52 | * | 44 | * |
53 | * endofname() - return the end of a variable name from input | 45 | * endofname() - return the end of a variable name from input |
54 | * | 46 | * |
55 | * The arithmetic code does not know about variable naming conventions. | 47 | * The arithmetic code does not know about variable naming conventions. |
56 | * So when it is given an experession, it knows something is not numeric, | 48 | * So when it is given an experession, it knows something is not numeric, |
57 | * but it is up to the shell to dictate what is a valid identifiers. | 49 | * but it is up to the shell to dictate what is a valid identifiers. |
58 | * So when it encounters something like: | 50 | * So when it encounters something like: |
59 | * $(( some_var + 123 )) | 51 | * $(( some_var + 123 )) |
60 | * It will make a call like so: | 52 | * It will make a call like so: |
61 | * end = endofname("some_var + 123"); | 53 | * end = endofname("some_var + 123"); |
62 | * So the shell needs to scan the input string and return a pointer to the | 54 | * So the shell needs to scan the input string and return a pointer to the |
63 | * first non-identifier string. In this case, it should return the input | 55 | * first non-identifier string. In this case, it should return the input |
64 | * pointer with an offset pointing to the first space. The typical | 56 | * pointer with an offset pointing to the first space. The typical |
65 | * implementation will return the offset of first char that does not match | 57 | * implementation will return the offset of first char that does not match |
66 | * the regex (in C locale): ^[a-zA-Z_][a-zA-Z_0-9]* | 58 | * the regex (in C locale): ^[a-zA-Z_][a-zA-Z_0-9]* |
67 | */ | ||
68 | |||
69 | /* To make your life easier when dealing with optional 64bit math support, | ||
70 | * rather than assume that the type is "signed long" and you can always | ||
71 | * use "%ld" to scan/print the value, use the arith_t helper defines. See | ||
72 | * below for the exact things that are available. | ||
73 | */ | 59 | */ |
74 | 60 | ||
75 | #ifndef SHELL_MATH_H | 61 | #ifndef SHELL_MATH_H |
@@ -79,11 +65,11 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
79 | 65 | ||
80 | #if ENABLE_SH_MATH_SUPPORT_64 | 66 | #if ENABLE_SH_MATH_SUPPORT_64 |
81 | typedef long long arith_t; | 67 | typedef long long arith_t; |
82 | #define arith_t_fmt "%lld" | 68 | #define ARITH_FMT "%lld" |
83 | #define strto_arith_t strtoull | 69 | #define strto_arith_t strtoull |
84 | #else | 70 | #else |
85 | typedef long arith_t; | 71 | typedef long arith_t; |
86 | #define arith_t_fmt "%ld" | 72 | #define ARITH_FMT "%ld" |
87 | #define strto_arith_t strtoul | 73 | #define strto_arith_t strtoul |
88 | #endif | 74 | #endif |
89 | 75 | ||
@@ -96,13 +82,15 @@ typedef const char* FAST_FUNC (*arith_var_lookup_t)(const char *name); | |||
96 | typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); | 82 | typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); |
97 | //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); | 83 | //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); |
98 | 84 | ||
99 | typedef struct arith_eval_hooks { | 85 | typedef struct arith_state_t { |
86 | const char *errmsg; | ||
100 | arith_var_lookup_t lookupvar; | 87 | arith_var_lookup_t lookupvar; |
101 | arith_var_set_t setvar; | 88 | arith_var_set_t setvar; |
102 | // arith_var_endofname_t endofname; | 89 | // arith_var_endofname_t endofname; |
103 | } arith_eval_hooks_t; | 90 | void *list_of_recursed_names; |
91 | } arith_state_t; | ||
104 | 92 | ||
105 | arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*); | 93 | arith_t FAST_FUNC arith(arith_state_t *state, const char *expr); |
106 | 94 | ||
107 | POP_SAVED_FUNCTION_VISIBILITY | 95 | POP_SAVED_FUNCTION_VISIBILITY |
108 | 96 | ||