From fac49481c6b4639e5a2fad6ea08ab6cd29b9304c Mon Sep 17 00:00:00 2001 From: Rob Landley Date: Mon, 22 Nov 2021 23:11:03 -0600 Subject: [PATCH] Fix $((math)) recursion order, handle "" as 0, and add more tests. --- tests/sh.test | 22 +++++++++++++++++++- toys/pending/sh.c | 52 +++++++++++++++++++++++++++++------------------ 2 files changed, 53 insertions(+), 21 deletions(-) diff --git a/tests/sh.test b/tests/sh.test index e1401cf4..53cf81b7 100644 --- a/tests/sh.test +++ b/tests/sh.test @@ -84,6 +84,8 @@ testing 'syntax' '$SH -c "if true; then echo hello | fi" 2>/dev/null || echo x'\ ln -s $(which $SH) bash testing 'non-absolute $_' "./bash -c 'echo \$_'" './bash\n' '' '' rm bash +testing '$_ with functions' 'true; x(){ echo $_;}; x abc; echo $_' \ + 'true\nabc\n' '' '' shxpect '$_ preserved on assignment error' I$'true hello; a=1 b=2 c=${}\n' \ E E"$P" I$'echo $_\n' O$'hello\n' @@ -223,8 +225,9 @@ testing 'quote' "echo \"'\"" "'\n" "" "" testing "math" 'echo $((1+2))' '3\n' '' '' testing "[math]" 'echo $[1+2]' '3\n' '' '' -testing "math prio" 'echo $((1+2*3))' '7\n' '' '' +testing "math prio" 'echo $((1+2*3**4))' '163\n' '' '' testing "math paren" 'echo $(((1+2)*3))' '9\n' '' '' +testing "math spaces" 'echo $(( ( 1 + 2 ) * 7 - 5 ** 2 ))' '-4\n' '' '' # Loops and flow control testing "case" 'for i in A C J B; do case "$i" in A) echo got A ;; B) echo and B ;; C) echo then C ;; *) echo default ;; esac; done' \ @@ -571,6 +574,9 @@ testing 'function nested parentheses' \ shxpect 'local creates a whiteout' \ I$'func() { local potato; echo ${potato?bang}; }; potato=123; func\n' \ E E"$P" I$'echo $?\n' O$'1\n' +testing 'local replaces/preserves magic type' \ + 'x() { local RANDOM=potato; echo $RANDOM;};x;echo -e "$RANDOM\n$RANDOM"|wc -l'\ + 'potato\n2\n' '' '' testing '$$ is parent shell' \ '{ echo $$; (echo $$) } | sort -u | wc -l' "1\n" "" "" @@ -579,6 +585,20 @@ testing '$PPID is parent shell' \ testing '$BASHPID is current PID' \ '{ echo $BASHPID; (echo $BASHPID) } | sort -u | wc -l' "2\n" "" "" +testing 'unexport supports +=' 'export -n ABC+=DEF; declare -p ABC' \ + 'declare -- ABC="DEF"\n' '' '' +testing 'unexport existing +=' \ + 'export ABC=XYZ; export -n ABC+=DEF; declare -p ABC' \ + 'declare -- ABC="XYZDEF"\n' '' '' + +testing '$!' '{ echo $BASHPID & echo $!; echo ${!};} | sort -u | wc -l' '1\n' \ + '' '' + +shxpect 'blank line preserves $?' \ + I$'false\n' E"$P" I$'\n' E"$P" I$'echo $?\n' O$'1\n' +testing 'NOP line clears $?' 'false;$NOTHING;echo $?' '0\n' '' '' +testing 'run "$@"' 'false;"$@";echo $?' '0\n' '' '' + # TODO finish variable list from shell init # $# $? $- $! $0 # $$ diff --git a/toys/pending/sh.c b/toys/pending/sh.c index 80050479..5ce255e7 100644 --- a/toys/pending/sh.c +++ b/toys/pending/sh.c @@ -480,30 +480,35 @@ static int recalculate(long long *dd, char **ss, int lvl) long long ee, ff; char cc = **nospace(ss); - // handle unary prefixes, parenthetical blocks, and constants - + // TODO: assignable (variable) + // Always start handling unary prefixes, parenthetical blocks, and constants if (cc=='+' || cc=='-') { ++*ss; - if (!recalculate(dd, ss, 0) || *ss) return 0; + if (!recalculate(dd, ss, 1) || **ss) return 0; if (cc=='-') *dd = -*dd; } else if (cc=='(') { ++*ss; - if (!recalculate(dd, ss, 0)) return 0; + if (!recalculate(dd, ss, 1)) return 0; if (**ss!=')') return 0; else ++*ss; } else if (isdigit(cc)) *dd = strtoll(*ss, ss, 0); //TODO overflow? - else return 0; - - if (lvl>1) if (strstart(nospace(ss), "**")) { - if (!recalculate(&ee, ss, 3)) return 0; + else if (!lvl && (!cc || cc==')')) { + *dd = 0; + return 1; + } else return 0; + + // x^y binds first + if (lvl<4) while (strstart(nospace(ss), "**")) { + if (!recalculate(&ee, ss, 4)) return 0; if (ee<0) perror_msg("** < 0"); for (ff = *dd, *dd = 1; ee; ee--) *dd *= ff; } - if (lvl>0) while ((cc = **nospace(ss))) { + // w*x/y%z bind next + if (lvl<3) while ((cc = **nospace(ss))) { if (cc=='*' || cc=='/' || cc=='%') { ++*ss; - if (!recalculate(&ee, ss, 2)) return 0; + if (!recalculate(&ee, ss, 3)) return 0; if (cc=='*') *dd *= ee; else if (cc=='%') *dd %= ee; else if (!ee) { @@ -513,10 +518,11 @@ static int recalculate(long long *dd, char **ss, int lvl) } else break; } - if (!lvl) while ((cc = **nospace(ss))) { + // x+y-z + if (lvl<2) while ((cc = **nospace(ss))) { if (cc=='+' || cc=='-') { ++*ss; - if (!recalculate(&ee, ss, 1)) return 0; + if (!recalculate(&ee, ss, 2)) return 0; if (cc=='+') *dd += ee; else *dd -= ee; } else break; @@ -531,8 +537,7 @@ static int calculate(long long *ll, char *equation) char *ss = equation; // TODO: error_msg->sherror_msg() with LINENO for scripts - if (!*equation) *ll = 0; - else if (!recalculate(ll, &ss, 0) || *ss) { + if (!recalculate(ll, &ss, 0) || *ss) { perror_msg("bad math: %s @ %d", equation, (int)(ss-equation)); return 0; @@ -898,8 +903,8 @@ static char *parse_word(char *start, int early, int quote) if (!*end || (*end=='\n' && !end[1])) return early ? end : 0; } else if (ii=='$' && -1!=(qq = stridx("({[", *end))) { if (strstart(&end, "((")) { + end--; toybuf[quote++] = 255; - end++; } else toybuf[quote++] = ")}]"[qq]; } else if (*end=='(' && strchr("?*+@!", ii)) toybuf[quote++] = ')'; else { @@ -1161,7 +1166,7 @@ static int run_subshell(char *str, int len) for (i = 0, vv = visible_vars(); vv[i]; i++) dprintf(pipes[1], "%u %lu\n%.*s", (unsigned)strlen(vv[i]->str), - vv[i]->flags, strlen(vv[i]->str), vv[i]->str); + vv[i]->flags, (int)strlen(vv[i]->str), vv[i]->str); free(vv); // send command @@ -1653,12 +1658,19 @@ static int expand_arg_nobrace(struct sh_arg *arg, char *str, unsigned flags, s = str+ii-1; kk = parse_word(s, 1, 0)-s; if (str[ii] == '[' || *toybuf == 255) { + struct sh_arg aa = {0}; long long ll; - ss = (s += 2+(str[ii]!='[')); - jj = kk - (3+2*(str[ii]!='[')); - if (!recalculate(&ll, &s, 0) || ss+jj != s) { - error_msg("math: %.*s @ %ld", jj, ss, (s-ss)); + // Expand $VARS in math string + ss = str+ii+1+(str[ii]=='('); + push_arg(delete, ss = xstrndup(ss, kk - (3+2*(str[ii]!='[')))); + expand_arg_nobrace(&aa, ss, NO_PATH|NO_SPLIT, delete, 0); + s = ss = (aa.v && *aa.v) ? *aa.v : ""; + free(aa.v); + + // Recursively calculate result + if (!recalculate(&ll, &s, 0) || *s) { + error_msg("bad math: %s @ %ld", ss, (s-ss)+1); goto fail; } ii += kk-1; -- 2.39.2