aboutsummaryrefslogtreecommitdiff
path: root/shell
diff options
context:
space:
mode:
authorNguyễn Thái Ngọc Duy <pclouds@gmail.com>2011-01-04 19:40:30 +0700
committerNguyễn Thái Ngọc Duy <pclouds@gmail.com>2011-01-04 19:40:30 +0700
commit3b5c308768d76298bb964814ecc34de47bcac0b4 (patch)
tree795340e9d8f5e5bf9e8d895641099af343eec2a0 /shell
parent2b9a0e715ec459198f486653023d963b79291da7 (diff)
parent5fe2f863b9cee5ab0e7ac873538bce48846dbad8 (diff)
downloadbusybox-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.c45
-rw-r--r--shell/ash_test/ash-arith/arith.right56
-rw-r--r--shell/ash_test/ash-signals/signal8.right3
-rwxr-xr-xshell/ash_test/ash-signals/signal8.tests18
-rw-r--r--shell/ash_test/ash-signals/signal9.right3
-rwxr-xr-xshell/ash_test/ash-signals/signal9.tests21
-rw-r--r--shell/hush.c183
-rw-r--r--shell/hush_test/hush-arith/arith.right54
-rw-r--r--shell/hush_test/hush-glob/bash_brace1.right4
-rwxr-xr-xshell/hush_test/hush-glob/bash_brace1.tests10
-rw-r--r--shell/math.c684
-rw-r--r--shell/math.h96
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)
5766static arith_t 5766static arith_t
5767ash_arith(const char *s) 5767ash_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'
5530 30 5530 30
5620 20 5620 20
5730 30 5730 30
58./arith.tests: line 117: syntax error: 1 ? 20 : x+=2 58./arith.tests: line 117: arithmetic syntax error
596 6 596 6
606,5,3 6,5,3 606,5,3 6,5,3
61263 263 61263 263
62255 255 62255 255
6340 40 6340 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
69abc 69abc
70def 70def
71ghi 71ghi
72./arith.tests: line 191: syntax error: ( 4 + A ) + 4 72./arith.tests: line 191: arithmetic syntax error
7316 16 7316 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
779 9 779 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
809 9 809 9
819 9 819 9
829 9 829 9
@@ -97,18 +97,18 @@ ghi
973 3 973 3
984 4 984 4
994 4 994 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
1054 4 1054 4
1067 7 1067 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
1126 6 1126 6
1133 3 1133 3
1147 7 1147 7
@@ -119,19 +119,19 @@ ghi
1192 2 1192 2
120-2 -2 120-2 -2
1211 1 1211 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
1275 5 1275 5
1281 1 1281 1
1294 4 1294 4
1300 0 1300 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
1338 12 1338 12
134./arith.tests: line 290: syntax error: a b 134./arith.tests: line 290: arithmetic syntax error
13542 13542
13642 13642
13742 13742
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 @@
1Removing traps
2End of exit_func
3Done: 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 '
2exit_func() {
3 echo "Removing traps"
4 trap - EXIT TERM INT
5 echo "End of exit_func"
6}
7set -e
8trap exit_func EXIT TERM INT
9sleep 2
10exit 77
11' &
12
13sleep 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.
16kill -TERM %1
17wait
18echo 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 @@
1Removing traps
2End of exit_func
3Done: 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 '
6exit_func() {
7 echo "Removing traps"
8 trap - EXIT TERM INT
9 echo "End of exit_func"
10}
11set -e
12trap exit_func EXIT TERM INT
13sleep 2
14exit 77
15' &
16
17child=$!
18sleep 1
19kill -TERM $child
20wait
21echo 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
394enum {
395 EXP_FLAG_GLOB = 0x200,
396 EXP_FLAG_ESC_GLOB_CHARS = 0x100,
397 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
398};
399
400typedef struct o_string { 402typedef 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;
413enum { 413enum {
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};
420enum {
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
2004static 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 */
2400static int glob_needed(const char *s) 2389static 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
4382static 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
4464static arith_t expand_and_evaluate_arith(const char *arg, int *errcode_p) 4484static 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
6120 20 6120 20
6230 30 6230 30
63check precedence of assignment vs. conditional operator 63check precedence of assignment vs. conditional operator
64hush: error in arithmetic 64hush: arithmetic syntax error
65check precedence of assignment vs. conditional operator 65check precedence of assignment vs. conditional operator
66associativity of assignment-operator operator 66associativity of assignment-operator operator
676 6 676 6
@@ -70,22 +70,22 @@ octal, hex
70263 263 70263 263
71255 255 71255 255
7240 40 7240 40
73hush: error in arithmetic 73hush: arithmetic syntax error
74hush: divide by 0 74hush: divide by zero
75hush: can't execute 'let': No such file or directory 75hush: can't execute 'let': No such file or directory
76hush: error in arithmetic 76hush: arithmetic syntax error
77hush: can't execute 'let': No such file or directory 77hush: can't execute 'let': No such file or directory
78abc 78abc
79def 79def
80ghi 80ghi
81hush: error in arithmetic 81hush: arithmetic syntax error
8216 16 8216 16
83hush: error in arithmetic 83hush: arithmetic syntax error
84hush: error in arithmetic 84hush: malformed ?: operator
85hush: error in arithmetic 85hush: arithmetic syntax error
869 9 869 9
87hush: error in arithmetic 87hush: arithmetic syntax error
88hush: error in arithmetic 88hush: arithmetic syntax error
899 9 899 9
909 9 909 9
919 9 919 9
@@ -106,18 +106,18 @@ hush: error in arithmetic
1063 3 1063 3
1074 4 1074 4
1084 4 1084 4
109hush: error in arithmetic 109hush: arithmetic syntax error
110hush: error in arithmetic 110hush: arithmetic syntax error
111hush: error in arithmetic 111hush: arithmetic syntax error
112hush: error in arithmetic 112hush: arithmetic syntax error
113hush: error in arithmetic 113hush: arithmetic syntax error
1144 4 1144 4
1157 7 1157 7
116-7 -7 116-7 -7
117hush: error in arithmetic 117hush: arithmetic syntax error
118hush: error in arithmetic 118hush: arithmetic syntax error
119hush: error in arithmetic 119hush: arithmetic syntax error
120hush: error in arithmetic 120hush: arithmetic syntax error
1216 6 1216 6
1223 3 1223 3
1237 7 1237 7
@@ -128,19 +128,19 @@ hush: error in arithmetic
1282 2 1282 2
129-2 -2 129-2 -2
1301 1 1301 1
131hush: error in arithmetic 131hush: arithmetic syntax error
132hush: error in arithmetic 132hush: arithmetic syntax error
133hush: error in arithmetic 133hush: arithmetic syntax error
134hush: error in arithmetic 134hush: arithmetic syntax error
135hush: error in arithmetic 135hush: arithmetic syntax error
1365 5 1365 5
1371 1 1371 1
1384 4 1384 4
1390 0 1390 0
140hush: error in arithmetic 140hush: arithmetic syntax error
141hush: error in arithmetic 141hush: arithmetic syntax error
1428 12 1428 12
143hush: error in arithmetic 143hush: arithmetic syntax error
14442 14442
14542 14542
14642 14642
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 @@
1bash_brace1.tests
2*{b,b}race1.t*
3bash_brace1.tests bash_brace1.tests
4Done: 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:
2v='*brace1.t*'; echo $v
3
4# ...but not brace expanded:
5v='*{b,b}race1.t*'; echo $v
6
7# whereas direct brces are expanded:
8echo *{b,b}race1.t*
9
10echo 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
130typedef unsigned char operator; 123typedef 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
221static int 215static int
222tok_have_assign(operator op) 216is_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
231static int 225static int
232is_right_associativity(operator prec) 226is_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
238typedef struct { 234typedef 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 */
246typedef 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
248typedef 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
251static chk_var_recursive_looped_t *prev_chk_var_recursive;
252 253
253static int 254static arith_t FAST_FUNC
254arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks) 255evaluate_string(arith_state_t *math_state, const char *expr);
256
257static const char*
258arith_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 */
292static NOINLINE int 299static NOINLINE const char*
293arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks) 300arith_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
482const char* FAST_FUNC 497const char* FAST_FUNC
483endofname(const char *name) 498endofname(const char *name)
@@ -491,35 +506,40 @@ endofname(const char *name)
491 return name; 506 return name;
492} 507}
493 508
494arith_t 509static arith_t FAST_FUNC
495arith(const char *expr, int *perrcode, a_e_h_t *math_hooks) 510evaluate_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
712arith_t FAST_FUNC
713arith(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
81typedef long long arith_t; 67typedef 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
85typedef long arith_t; 71typedef 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);
96typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *val); 82typedef 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
99typedef struct arith_eval_hooks { 85typedef 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
105arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*); 93arith_t FAST_FUNC arith(arith_state_t *state, const char *expr);
106 94
107POP_SAVED_FUNCTION_VISIBILITY 95POP_SAVED_FUNCTION_VISIBILITY
108 96