aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Frysinger <vapier@gentoo.org>2009-04-02 10:02:37 +0000
committerMike Frysinger <vapier@gentoo.org>2009-04-02 10:02:37 +0000
commit98c52645c02dacebccae7d68d6c2627f9318fcf7 (patch)
treee0c64b5b24206f95e72fd060336f74cb459f2109
parent551ffdccea39a9223ad451954db40fd7a6e20e79 (diff)
downloadbusybox-w32-98c52645c02dacebccae7d68d6c2627f9318fcf7.tar.gz
busybox-w32-98c52645c02dacebccae7d68d6c2627f9318fcf7.tar.bz2
busybox-w32-98c52645c02dacebccae7d68d6c2627f9318fcf7.zip
split math code out of ash and into a standalone library so we can use it in any shell (like hush!)
-rw-r--r--shell/Config.in32
-rw-r--r--shell/Kbuild1
-rw-r--r--shell/ash.c734
-rw-r--r--shell/hush.c102
-rw-r--r--shell/math.c701
-rw-r--r--shell/math.h101
6 files changed, 945 insertions, 726 deletions
diff --git a/shell/Config.in b/shell/Config.in
index afc429667..7daf17ff6 100644
--- a/shell/Config.in
+++ b/shell/Config.in
@@ -84,22 +84,6 @@ config ASH_ALIAS
84 help 84 help
85 Enable alias support in the ash shell. 85 Enable alias support in the ash shell.
86 86
87config ASH_MATH_SUPPORT
88 bool "Posix math support"
89 default y
90 depends on ASH
91 help
92 Enable math support in the ash shell.
93
94config ASH_MATH_SUPPORT_64
95 bool "Extend Posix math support to 64 bit"
96 default n
97 depends on ASH_MATH_SUPPORT
98 help
99 Enable 64-bit math support in the ash shell. This will make
100 the shell slightly larger, but will allow computation with very
101 large numbers.
102
103config ASH_GETOPTS 87config ASH_GETOPTS
104 bool "Builtin getopt to parse positional parameters" 88 bool "Builtin getopt to parse positional parameters"
105 default n 89 default n
@@ -267,6 +251,22 @@ config MSH
267comment "Bourne Shell Options" 251comment "Bourne Shell Options"
268 depends on MSH || LASH || HUSH || ASH 252 depends on MSH || LASH || HUSH || ASH
269 253
254config SH_MATH_SUPPORT
255 bool "POSIX math support"
256 default y
257 depends on ASH || HUSH
258 help
259 Enable math support in the shell via $((...)) syntax.
260
261config SH_MATH_SUPPORT_64
262 bool "Extend POSIX math support to 64 bit"
263 default n
264 depends on SH_MATH_SUPPORT
265 help
266 Enable 64-bit math support in the shell. This will make the shell
267 slightly larger, but will allow computation with very large numbers.
268 This is not in POSIX, so do not rely on this in portable code.
269
270config FEATURE_SH_EXTRA_QUIET 270config FEATURE_SH_EXTRA_QUIET
271 bool "Hide message on interactive shell startup" 271 bool "Hide message on interactive shell startup"
272 default n 272 default n
diff --git a/shell/Kbuild b/shell/Kbuild
index deedc24f3..2b461b3bd 100644
--- a/shell/Kbuild
+++ b/shell/Kbuild
@@ -9,3 +9,4 @@ lib-$(CONFIG_ASH) += ash.o ash_ptr_hack.o
9lib-$(CONFIG_HUSH) += hush.o 9lib-$(CONFIG_HUSH) += hush.o
10lib-$(CONFIG_MSH) += msh.o 10lib-$(CONFIG_MSH) += msh.o
11lib-$(CONFIG_CTTYHACK) += cttyhack.o 11lib-$(CONFIG_CTTYHACK) += cttyhack.o
12lib-$(CONFIG_SH_MATH_SUPPORT) += math.o
diff --git a/shell/ash.c b/shell/ash.c
index 4f7f38f6d..0d3ab0ff5 100644
--- a/shell/ash.c
+++ b/shell/ash.c
@@ -17,19 +17,6 @@
17 */ 17 */
18 18
19/* 19/*
20 * rewrite arith.y to micro stack based cryptic algorithm by
21 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
22 *
23 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
24 * dynamic variables.
25 *
26 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
27 * used in busybox and size optimizations,
28 * rewrote arith (see notes to this), added locale support,
29 * rewrote dynamic variables.
30 */
31
32/*
33 * The following should be set to reflect the type of system you have: 20 * The following should be set to reflect the type of system you have:
34 * JOBS -> 1 if you have Berkeley job control, 0 otherwise. 21 * JOBS -> 1 if you have Berkeley job control, 0 otherwise.
35 * define SYSV if you are running under System V. 22 * define SYSV if you are running under System V.
@@ -63,6 +50,7 @@
63#include <paths.h> 50#include <paths.h>
64#include <setjmp.h> 51#include <setjmp.h>
65#include <fnmatch.h> 52#include <fnmatch.h>
53#include "math.h"
66 54
67#if defined SINGLE_APPLET_MAIN 55#if defined SINGLE_APPLET_MAIN
68/* STANDALONE does not make sense, and won't compile */ 56/* STANDALONE does not make sense, and won't compile */
@@ -197,6 +185,10 @@ struct globals_misc {
197#define debug optlist[15] 185#define debug optlist[15]
198#endif 186#endif
199 187
188#if ENABLE_SH_MATH_SUPPORT
189 arith_eval_hooks_t math_hooks;
190#endif
191
200 /* trap handler commands */ 192 /* trap handler commands */
201 /* 193 /*
202 * Sigmode records the current value of the signal handlers for the various 194 * Sigmode records the current value of the signal handlers for the various
@@ -246,6 +238,7 @@ extern struct globals_misc *const ash_ptr_to_globals_misc;
246#define random_LCG (G_misc.random_LCG ) 238#define random_LCG (G_misc.random_LCG )
247#define backgndpid (G_misc.backgndpid ) 239#define backgndpid (G_misc.backgndpid )
248#define job_warning (G_misc.job_warning) 240#define job_warning (G_misc.job_warning)
241#define math_hooks (G_misc.math_hooks )
249#define INIT_G_misc() do { \ 242#define INIT_G_misc() do { \
250 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \ 243 (*(struct globals_misc**)&ash_ptr_to_globals_misc) = xzalloc(sizeof(G_misc)); \
251 barrier(); \ 244 barrier(); \
@@ -1998,7 +1991,7 @@ findvar(struct var **vpp, const char *name)
1998/* 1991/*
1999 * Find the value of a variable. Returns NULL if not set. 1992 * Find the value of a variable. Returns NULL if not set.
2000 */ 1993 */
2001static char * 1994static const char *
2002lookupvar(const char *name) 1995lookupvar(const char *name)
2003{ 1996{
2004 struct var *v; 1997 struct var *v;
@@ -2024,7 +2017,7 @@ lookupvar(const char *name)
2024/* 2017/*
2025 * Search the environment of a builtin command. 2018 * Search the environment of a builtin command.
2026 */ 2019 */
2027static char * 2020static const char *
2028bltinlookup(const char *name) 2021bltinlookup(const char *name)
2029{ 2022{
2030 struct strlist *sp; 2023 struct strlist *sp;
@@ -2637,7 +2630,7 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2637#define USE_SIT_FUNCTION 2630#define USE_SIT_FUNCTION
2638#endif 2631#endif
2639 2632
2640#if ENABLE_ASH_MATH_SUPPORT 2633#if ENABLE_SH_MATH_SUPPORT
2641static const char S_I_T[][4] = { 2634static const char S_I_T[][4] = {
2642#if ENABLE_ASH_ALIAS 2635#if ENABLE_ASH_ALIAS
2643 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */ 2636 { CSPCL, CIGN, CIGN, CIGN }, /* 0, PEOA */
@@ -2681,7 +2674,7 @@ static const char S_I_T[][3] = {
2681 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */ 2674 { CCTL, CCTL, CCTL } /* 14, CTLESC ... */
2682#endif 2675#endif
2683}; 2676};
2684#endif /* ASH_MATH_SUPPORT */ 2677#endif /* SH_MATH_SUPPORT */
2685 2678
2686#ifdef USE_SIT_FUNCTION 2679#ifdef USE_SIT_FUNCTION
2687 2680
@@ -4273,7 +4266,7 @@ cmdputs(const char *s)
4273 case CTLBACKQ+CTLQUOTE: 4266 case CTLBACKQ+CTLQUOTE:
4274 str = "\"$(...)\""; 4267 str = "\"$(...)\"";
4275 goto dostr; 4268 goto dostr;
4276#if ENABLE_ASH_MATH_SUPPORT 4269#if ENABLE_SH_MATH_SUPPORT
4277 case CTLARI: 4270 case CTLARI:
4278 str = "$(("; 4271 str = "$((";
4279 goto dostr; 4272 goto dostr;
@@ -5258,17 +5251,8 @@ redirectsafe(union node *redir, int flags)
5258 * We have to deal with backquotes, shell variables, and file metacharacters. 5251 * We have to deal with backquotes, shell variables, and file metacharacters.
5259 */ 5252 */
5260 5253
5261#if ENABLE_ASH_MATH_SUPPORT_64 5254#if ENABLE_SH_MATH_SUPPORT
5262typedef int64_t arith_t;
5263#define arith_t_type long long
5264#else
5265typedef long arith_t;
5266#define arith_t_type long
5267#endif
5268
5269#if ENABLE_ASH_MATH_SUPPORT
5270static arith_t dash_arith(const char *); 5255static arith_t dash_arith(const char *);
5271static arith_t arith(const char *expr, int *perrcode);
5272#endif 5256#endif
5273 5257
5274/* 5258/*
@@ -5328,11 +5312,7 @@ cvtnum(arith_t num)
5328 int len; 5312 int len;
5329 5313
5330 expdest = makestrspace(32, expdest); 5314 expdest = makestrspace(32, expdest);
5331#if ENABLE_ASH_MATH_SUPPORT_64 5315 len = fmtstr(expdest, 32, arith_t_fmt, num);
5332 len = fmtstr(expdest, 32, "%lld", (long long) num);
5333#else
5334 len = fmtstr(expdest, 32, "%ld", num);
5335#endif
5336 STADJUST(len, expdest); 5316 STADJUST(len, expdest);
5337 return len; 5317 return len;
5338} 5318}
@@ -5696,7 +5676,7 @@ expbackq(union node *cmd, int quoted, int quotes)
5696 stackblock() + startloc)); 5676 stackblock() + startloc));
5697} 5677}
5698 5678
5699#if ENABLE_ASH_MATH_SUPPORT 5679#if ENABLE_SH_MATH_SUPPORT
5700/* 5680/*
5701 * Expand arithmetic expression. Backup to start of expression, 5681 * Expand arithmetic expression. Backup to start of expression,
5702 * evaluate, place result in (backed up) result, adjust string position. 5682 * evaluate, place result in (backed up) result, adjust string position.
@@ -5782,7 +5762,7 @@ argstr(char *p, int flag, struct strlist *var_str_list)
5782 CTLVAR, 5762 CTLVAR,
5783 CTLBACKQ, 5763 CTLBACKQ,
5784 CTLBACKQ | CTLQUOTE, 5764 CTLBACKQ | CTLQUOTE,
5785#if ENABLE_ASH_MATH_SUPPORT 5765#if ENABLE_SH_MATH_SUPPORT
5786 CTLENDARI, 5766 CTLENDARI,
5787#endif 5767#endif
5788 0 5768 0
@@ -5819,7 +5799,7 @@ argstr(char *p, int flag, struct strlist *var_str_list)
5819 length += strcspn(p + length, reject); 5799 length += strcspn(p + length, reject);
5820 c = p[length]; 5800 c = p[length];
5821 if (c && (!(c & 0x80) 5801 if (c && (!(c & 0x80)
5822#if ENABLE_ASH_MATH_SUPPORT 5802#if ENABLE_SH_MATH_SUPPORT
5823 || c == CTLENDARI 5803 || c == CTLENDARI
5824#endif 5804#endif
5825 )) { 5805 )) {
@@ -5897,7 +5877,7 @@ argstr(char *p, int flag, struct strlist *var_str_list)
5897 expbackq(argbackq->n, c, quotes); 5877 expbackq(argbackq->n, c, quotes);
5898 argbackq = argbackq->next; 5878 argbackq = argbackq->next;
5899 goto start; 5879 goto start;
5900#if ENABLE_ASH_MATH_SUPPORT 5880#if ENABLE_SH_MATH_SUPPORT
5901 case CTLENDARI: 5881 case CTLENDARI:
5902 p--; 5882 p--;
5903 expari(quotes); 5883 expari(quotes);
@@ -6291,7 +6271,7 @@ static ssize_t
6291varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) 6271varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6292{ 6272{
6293 int num; 6273 int num;
6294 char *p; 6274 const char *p;
6295 int i; 6275 int i;
6296 int sep = 0; 6276 int sep = 0;
6297 int sepq = 0; 6277 int sepq = 0;
@@ -6324,14 +6304,13 @@ varvalue(char *name, int varflags, int flags, struct strlist *var_str_list)
6324 len = cvtnum(num); 6304 len = cvtnum(num);
6325 break; 6305 break;
6326 case '-': 6306 case '-':
6327 p = makestrspace(NOPTS, expdest); 6307 expdest = makestrspace(NOPTS, expdest);
6328 for (i = NOPTS - 1; i >= 0; i--) { 6308 for (i = NOPTS - 1; i >= 0; i--) {
6329 if (optlist[i]) { 6309 if (optlist[i]) {
6330 USTPUTC(optletters(i), p); 6310 USTPUTC(optletters(i), expdest);
6331 len++; 6311 len++;
6332 } 6312 }
6333 } 6313 }
6334 expdest = p;
6335 break; 6314 break;
6336 case '@': 6315 case '@':
6337 if (sep) 6316 if (sep)
@@ -8710,7 +8689,7 @@ static int getoptscmd(int, char **);
8710#if !ENABLE_FEATURE_SH_EXTRA_QUIET 8689#if !ENABLE_FEATURE_SH_EXTRA_QUIET
8711static int helpcmd(int, char **); 8690static int helpcmd(int, char **);
8712#endif 8691#endif
8713#if ENABLE_ASH_MATH_SUPPORT 8692#if ENABLE_SH_MATH_SUPPORT
8714static int letcmd(int, char **); 8693static int letcmd(int, char **);
8715#endif 8694#endif
8716static int readcmd(int, char **); 8695static int readcmd(int, char **);
@@ -8792,7 +8771,7 @@ static const struct builtincmd builtintab[] = {
8792 { BUILTIN_REGULAR "jobs", jobscmd }, 8771 { BUILTIN_REGULAR "jobs", jobscmd },
8793 { BUILTIN_REGULAR "kill", killcmd }, 8772 { BUILTIN_REGULAR "kill", killcmd },
8794#endif 8773#endif
8795#if ENABLE_ASH_MATH_SUPPORT 8774#if ENABLE_SH_MATH_SUPPORT
8796 { BUILTIN_NOSPEC "let", letcmd }, 8775 { BUILTIN_NOSPEC "let", letcmd },
8797#endif 8776#endif
8798 { BUILTIN_ASSIGN "local", localcmd }, 8777 { BUILTIN_ASSIGN "local", localcmd },
@@ -10935,7 +10914,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
10935 USTPUTC(c, out); 10914 USTPUTC(c, out);
10936 } 10915 }
10937 break; 10916 break;
10938#if ENABLE_ASH_MATH_SUPPORT 10917#if ENABLE_SH_MATH_SUPPORT
10939 case CLP: /* '(' in arithmetic */ 10918 case CLP: /* '(' in arithmetic */
10940 parenlevel++; 10919 parenlevel++;
10941 USTPUTC(c, out); 10920 USTPUTC(c, out);
@@ -10991,7 +10970,7 @@ readtoken1(int firstc, int syntax, char *eofmark, int striptabs)
10991 } /* for (;;) */ 10970 } /* for (;;) */
10992 } 10971 }
10993 endword: 10972 endword:
10994#if ENABLE_ASH_MATH_SUPPORT 10973#if ENABLE_SH_MATH_SUPPORT
10995 if (syntax == ARISYNTAX) 10974 if (syntax == ARISYNTAX)
10996 raise_error_syntax("missing '))'"); 10975 raise_error_syntax("missing '))'");
10997#endif 10976#endif
@@ -11168,7 +11147,7 @@ parsesub: {
11168 pungetc(); 11147 pungetc();
11169 } else if (c == '(') { /* $(command) or $((arith)) */ 11148 } else if (c == '(') { /* $(command) or $((arith)) */
11170 if (pgetc() == '(') { 11149 if (pgetc() == '(') {
11171#if ENABLE_ASH_MATH_SUPPORT 11150#if ENABLE_SH_MATH_SUPPORT
11172 PARSEARITH(); 11151 PARSEARITH();
11173#else 11152#else
11174 raise_error_syntax("you disabled math support for $((arith)) syntax"); 11153 raise_error_syntax("you disabled math support for $((arith)) syntax");
@@ -11423,7 +11402,7 @@ parsebackq: {
11423 goto parsebackq_newreturn; 11402 goto parsebackq_newreturn;
11424} 11403}
11425 11404
11426#if ENABLE_ASH_MATH_SUPPORT 11405#if ENABLE_SH_MATH_SUPPORT
11427/* 11406/*
11428 * Parse an arithmetic expansion (indicate start of one and set state) 11407 * Parse an arithmetic expansion (indicate start of one and set state)
11429 */ 11408 */
@@ -12384,7 +12363,7 @@ timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12384 return 0; 12363 return 0;
12385} 12364}
12386 12365
12387#if ENABLE_ASH_MATH_SUPPORT 12366#if ENABLE_SH_MATH_SUPPORT
12388static arith_t 12367static arith_t
12389dash_arith(const char *s) 12368dash_arith(const char *s)
12390{ 12369{
@@ -12392,7 +12371,7 @@ dash_arith(const char *s)
12392 int errcode = 0; 12371 int errcode = 0;
12393 12372
12394 INT_OFF; 12373 INT_OFF;
12395 result = arith(s, &errcode); 12374 result = arith(s, &errcode, &math_hooks);
12396 if (errcode < 0) { 12375 if (errcode < 0) {
12397 if (errcode == -3) 12376 if (errcode == -3)
12398 ash_msg_and_raise_error("exponent less than 0"); 12377 ash_msg_and_raise_error("exponent less than 0");
@@ -12427,7 +12406,7 @@ letcmd(int argc UNUSED_PARAM, char **argv)
12427 12406
12428 return !i; 12407 return !i;
12429} 12408}
12430#endif /* ASH_MATH_SUPPORT */ 12409#endif /* SH_MATH_SUPPORT */
12431 12410
12432 12411
12433/* ============ miscbltin.c 12412/* ============ miscbltin.c
@@ -12951,654 +12930,6 @@ ulimitcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
12951 return 0; 12930 return 0;
12952} 12931}
12953 12932
12954
12955/* ============ Math support */
12956
12957#if ENABLE_ASH_MATH_SUPPORT
12958
12959/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
12960
12961 Permission is hereby granted, free of charge, to any person obtaining
12962 a copy of this software and associated documentation files (the
12963 "Software"), to deal in the Software without restriction, including
12964 without limitation the rights to use, copy, modify, merge, publish,
12965 distribute, sublicense, and/or sell copies of the Software, and to
12966 permit persons to whom the Software is furnished to do so, subject to
12967 the following conditions:
12968
12969 The above copyright notice and this permission notice shall be
12970 included in all copies or substantial portions of the Software.
12971
12972 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
12973 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
12974 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
12975 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
12976 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
12977 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
12978 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12979*/
12980
12981/* This is my infix parser/evaluator. It is optimized for size, intended
12982 * as a replacement for yacc-based parsers. However, it may well be faster
12983 * than a comparable parser written in yacc. The supported operators are
12984 * listed in #defines below. Parens, order of operations, and error handling
12985 * are supported. This code is thread safe. The exact expression format should
12986 * be that which POSIX specifies for shells. */
12987
12988/* The code uses a simple two-stack algorithm. See
12989 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
12990 * for a detailed explanation of the infix-to-postfix algorithm on which
12991 * this is based (this code differs in that it applies operators immediately
12992 * to the stack instead of adding them to a queue to end up with an
12993 * expression). */
12994
12995/* To use the routine, call it with an expression string and error return
12996 * pointer */
12997
12998/*
12999 * Aug 24, 2001 Manuel Novoa III
13000 *
13001 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
13002 *
13003 * 1) In arith_apply():
13004 * a) Cached values of *numptr and &(numptr[-1]).
13005 * b) Removed redundant test for zero denominator.
13006 *
13007 * 2) In arith():
13008 * a) Eliminated redundant code for processing operator tokens by moving
13009 * to a table-based implementation. Also folded handling of parens
13010 * into the table.
13011 * b) Combined all 3 loops which called arith_apply to reduce generated
13012 * code size at the cost of speed.
13013 *
13014 * 3) The following expressions were treated as valid by the original code:
13015 * 1() , 0! , 1 ( *3 ) .
13016 * These bugs have been fixed by internally enclosing the expression in
13017 * parens and then checking that all binary ops and right parens are
13018 * preceded by a valid expression (NUM_TOKEN).
13019 *
13020 * Note: It may be desirable to replace Aaron's test for whitespace with
13021 * ctype's isspace() if it is used by another busybox applet or if additional
13022 * whitespace chars should be considered. Look below the "#include"s for a
13023 * precompiler test.
13024 */
13025
13026/*
13027 * Aug 26, 2001 Manuel Novoa III
13028 *
13029 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
13030 *
13031 * Merge in Aaron's comments previously posted to the busybox list,
13032 * modified slightly to take account of my changes to the code.
13033 *
13034 */
13035
13036/*
13037 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
13038 *
13039 * - allow access to variable,
13040 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
13041 * - realize assign syntax (VAR=expr, +=, *= etc)
13042 * - realize exponentiation (** operator)
13043 * - realize comma separated - expr, expr
13044 * - realise ++expr --expr expr++ expr--
13045 * - realise expr ? expr : expr (but, second expr calculate always)
13046 * - allow hexadecimal and octal numbers
13047 * - was restored loses XOR operator
13048 * - remove one goto label, added three ;-)
13049 * - protect $((num num)) as true zero expr (Manuel`s error)
13050 * - always use special isspace(), see comment from bash ;-)
13051 */
13052
13053#define arith_isspace(arithval) \
13054 (arithval == ' ' || arithval == '\n' || arithval == '\t')
13055
13056typedef unsigned char operator;
13057
13058/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
13059 * precedence, and 3 high bits are an ID unique across operators of that
13060 * precedence. The ID portion is so that multiple operators can have the
13061 * same precedence, ensuring that the leftmost one is evaluated first.
13062 * Consider * and /. */
13063
13064#define tok_decl(prec,id) (((id)<<5)|(prec))
13065#define PREC(op) ((op) & 0x1F)
13066
13067#define TOK_LPAREN tok_decl(0,0)
13068
13069#define TOK_COMMA tok_decl(1,0)
13070
13071#define TOK_ASSIGN tok_decl(2,0)
13072#define TOK_AND_ASSIGN tok_decl(2,1)
13073#define TOK_OR_ASSIGN tok_decl(2,2)
13074#define TOK_XOR_ASSIGN tok_decl(2,3)
13075#define TOK_PLUS_ASSIGN tok_decl(2,4)
13076#define TOK_MINUS_ASSIGN tok_decl(2,5)
13077#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
13078#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
13079
13080#define TOK_MUL_ASSIGN tok_decl(3,0)
13081#define TOK_DIV_ASSIGN tok_decl(3,1)
13082#define TOK_REM_ASSIGN tok_decl(3,2)
13083
13084/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
13085#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
13086
13087/* conditional is right associativity too */
13088#define TOK_CONDITIONAL tok_decl(4,0)
13089#define TOK_CONDITIONAL_SEP tok_decl(4,1)
13090
13091#define TOK_OR tok_decl(5,0)
13092
13093#define TOK_AND tok_decl(6,0)
13094
13095#define TOK_BOR tok_decl(7,0)
13096
13097#define TOK_BXOR tok_decl(8,0)
13098
13099#define TOK_BAND tok_decl(9,0)
13100
13101#define TOK_EQ tok_decl(10,0)
13102#define TOK_NE tok_decl(10,1)
13103
13104#define TOK_LT tok_decl(11,0)
13105#define TOK_GT tok_decl(11,1)
13106#define TOK_GE tok_decl(11,2)
13107#define TOK_LE tok_decl(11,3)
13108
13109#define TOK_LSHIFT tok_decl(12,0)
13110#define TOK_RSHIFT tok_decl(12,1)
13111
13112#define TOK_ADD tok_decl(13,0)
13113#define TOK_SUB tok_decl(13,1)
13114
13115#define TOK_MUL tok_decl(14,0)
13116#define TOK_DIV tok_decl(14,1)
13117#define TOK_REM tok_decl(14,2)
13118
13119/* exponent is right associativity */
13120#define TOK_EXPONENT tok_decl(15,1)
13121
13122/* For now unary operators. */
13123#define UNARYPREC 16
13124#define TOK_BNOT tok_decl(UNARYPREC,0)
13125#define TOK_NOT tok_decl(UNARYPREC,1)
13126
13127#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
13128#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
13129
13130#define PREC_PRE (UNARYPREC+2)
13131
13132#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
13133#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
13134
13135#define PREC_POST (UNARYPREC+3)
13136
13137#define TOK_POST_INC tok_decl(PREC_POST, 0)
13138#define TOK_POST_DEC tok_decl(PREC_POST, 1)
13139
13140#define SPEC_PREC (UNARYPREC+4)
13141
13142#define TOK_NUM tok_decl(SPEC_PREC, 0)
13143#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
13144
13145#define NUMPTR (*numstackptr)
13146
13147static int
13148tok_have_assign(operator op)
13149{
13150 operator prec = PREC(op);
13151
13152 convert_prec_is_assing(prec);
13153 return (prec == PREC(TOK_ASSIGN) ||
13154 prec == PREC_PRE || prec == PREC_POST);
13155}
13156
13157static int
13158is_right_associativity(operator prec)
13159{
13160 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
13161 || prec == PREC(TOK_CONDITIONAL));
13162}
13163
13164typedef struct {
13165 arith_t val;
13166 arith_t contidional_second_val;
13167 char contidional_second_val_initialized;
13168 char *var; /* if NULL then is regular number,
13169 else is variable name */
13170} v_n_t;
13171
13172typedef struct chk_var_recursive_looped_t {
13173 const char *var;
13174 struct chk_var_recursive_looped_t *next;
13175} chk_var_recursive_looped_t;
13176
13177static chk_var_recursive_looped_t *prev_chk_var_recursive;
13178
13179static int
13180arith_lookup_val(v_n_t *t)
13181{
13182 if (t->var) {
13183 const char * p = lookupvar(t->var);
13184
13185 if (p) {
13186 int errcode;
13187
13188 /* recursive try as expression */
13189 chk_var_recursive_looped_t *cur;
13190 chk_var_recursive_looped_t cur_save;
13191
13192 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
13193 if (strcmp(cur->var, t->var) == 0) {
13194 /* expression recursion loop detected */
13195 return -5;
13196 }
13197 }
13198 /* save current lookuped var name */
13199 cur = prev_chk_var_recursive;
13200 cur_save.var = t->var;
13201 cur_save.next = cur;
13202 prev_chk_var_recursive = &cur_save;
13203
13204 t->val = arith (p, &errcode);
13205 /* restore previous ptr after recursiving */
13206 prev_chk_var_recursive = cur;
13207 return errcode;
13208 }
13209 /* allow undefined var as 0 */
13210 t->val = 0;
13211 }
13212 return 0;
13213}
13214
13215/* "applying" a token means performing it on the top elements on the integer
13216 * stack. For a unary operator it will only change the top element, but a
13217 * binary operator will pop two arguments and push a result */
13218static int
13219arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr)
13220{
13221 v_n_t *numptr_m1;
13222 arith_t numptr_val, rez;
13223 int ret_arith_lookup_val;
13224
13225 /* There is no operator that can work without arguments */
13226 if (NUMPTR == numstack) goto err;
13227 numptr_m1 = NUMPTR - 1;
13228
13229 /* check operand is var with noninteger value */
13230 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13231 if (ret_arith_lookup_val)
13232 return ret_arith_lookup_val;
13233
13234 rez = numptr_m1->val;
13235 if (op == TOK_UMINUS)
13236 rez *= -1;
13237 else if (op == TOK_NOT)
13238 rez = !rez;
13239 else if (op == TOK_BNOT)
13240 rez = ~rez;
13241 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
13242 rez++;
13243 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
13244 rez--;
13245 else if (op != TOK_UPLUS) {
13246 /* Binary operators */
13247
13248 /* check and binary operators need two arguments */
13249 if (numptr_m1 == numstack) goto err;
13250
13251 /* ... and they pop one */
13252 --NUMPTR;
13253 numptr_val = rez;
13254 if (op == TOK_CONDITIONAL) {
13255 if (!numptr_m1->contidional_second_val_initialized) {
13256 /* protect $((expr1 ? expr2)) without ": expr" */
13257 goto err;
13258 }
13259 rez = numptr_m1->contidional_second_val;
13260 } else if (numptr_m1->contidional_second_val_initialized) {
13261 /* protect $((expr1 : expr2)) without "expr ? " */
13262 goto err;
13263 }
13264 numptr_m1 = NUMPTR - 1;
13265 if (op != TOK_ASSIGN) {
13266 /* check operand is var with noninteger value for not '=' */
13267 ret_arith_lookup_val = arith_lookup_val(numptr_m1);
13268 if (ret_arith_lookup_val)
13269 return ret_arith_lookup_val;
13270 }
13271 if (op == TOK_CONDITIONAL) {
13272 numptr_m1->contidional_second_val = rez;
13273 }
13274 rez = numptr_m1->val;
13275 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
13276 rez |= numptr_val;
13277 else if (op == TOK_OR)
13278 rez = numptr_val || rez;
13279 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
13280 rez &= numptr_val;
13281 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
13282 rez ^= numptr_val;
13283 else if (op == TOK_AND)
13284 rez = rez && numptr_val;
13285 else if (op == TOK_EQ)
13286 rez = (rez == numptr_val);
13287 else if (op == TOK_NE)
13288 rez = (rez != numptr_val);
13289 else if (op == TOK_GE)
13290 rez = (rez >= numptr_val);
13291 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
13292 rez >>= numptr_val;
13293 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
13294 rez <<= numptr_val;
13295 else if (op == TOK_GT)
13296 rez = (rez > numptr_val);
13297 else if (op == TOK_LT)
13298 rez = (rez < numptr_val);
13299 else if (op == TOK_LE)
13300 rez = (rez <= numptr_val);
13301 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
13302 rez *= numptr_val;
13303 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
13304 rez += numptr_val;
13305 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
13306 rez -= numptr_val;
13307 else if (op == TOK_ASSIGN || op == TOK_COMMA)
13308 rez = numptr_val;
13309 else if (op == TOK_CONDITIONAL_SEP) {
13310 if (numptr_m1 == numstack) {
13311 /* protect $((expr : expr)) without "expr ? " */
13312 goto err;
13313 }
13314 numptr_m1->contidional_second_val_initialized = op;
13315 numptr_m1->contidional_second_val = numptr_val;
13316 } else if (op == TOK_CONDITIONAL) {
13317 rez = rez ?
13318 numptr_val : numptr_m1->contidional_second_val;
13319 } else if (op == TOK_EXPONENT) {
13320 if (numptr_val < 0)
13321 return -3; /* exponent less than 0 */
13322 else {
13323 arith_t c = 1;
13324
13325 if (numptr_val)
13326 while (numptr_val--)
13327 c *= rez;
13328 rez = c;
13329 }
13330 } else if (numptr_val==0) /* zero divisor check */
13331 return -2;
13332 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
13333 rez /= numptr_val;
13334 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
13335 rez %= numptr_val;
13336 }
13337 if (tok_have_assign(op)) {
13338 char buf[sizeof(arith_t_type)*3 + 2];
13339
13340 if (numptr_m1->var == NULL) {
13341 /* Hmm, 1=2 ? */
13342 goto err;
13343 }
13344 /* save to shell variable */
13345#if ENABLE_ASH_MATH_SUPPORT_64
13346 snprintf(buf, sizeof(buf), "%lld", (arith_t_type) rez);
13347#else
13348 snprintf(buf, sizeof(buf), "%ld", (arith_t_type) rez);
13349#endif
13350 setvar(numptr_m1->var, buf, 0);
13351 /* after saving, make previous value for v++ or v-- */
13352 if (op == TOK_POST_INC)
13353 rez--;
13354 else if (op == TOK_POST_DEC)
13355 rez++;
13356 }
13357 numptr_m1->val = rez;
13358 /* protect geting var value, is number now */
13359 numptr_m1->var = NULL;
13360 return 0;
13361 err:
13362 return -1;
13363}
13364
13365/* longest must be first */
13366static const char op_tokens[] ALIGN1 = {
13367 '<','<','=',0, TOK_LSHIFT_ASSIGN,
13368 '>','>','=',0, TOK_RSHIFT_ASSIGN,
13369 '<','<', 0, TOK_LSHIFT,
13370 '>','>', 0, TOK_RSHIFT,
13371 '|','|', 0, TOK_OR,
13372 '&','&', 0, TOK_AND,
13373 '!','=', 0, TOK_NE,
13374 '<','=', 0, TOK_LE,
13375 '>','=', 0, TOK_GE,
13376 '=','=', 0, TOK_EQ,
13377 '|','=', 0, TOK_OR_ASSIGN,
13378 '&','=', 0, TOK_AND_ASSIGN,
13379 '*','=', 0, TOK_MUL_ASSIGN,
13380 '/','=', 0, TOK_DIV_ASSIGN,
13381 '%','=', 0, TOK_REM_ASSIGN,
13382 '+','=', 0, TOK_PLUS_ASSIGN,
13383 '-','=', 0, TOK_MINUS_ASSIGN,
13384 '-','-', 0, TOK_POST_DEC,
13385 '^','=', 0, TOK_XOR_ASSIGN,
13386 '+','+', 0, TOK_POST_INC,
13387 '*','*', 0, TOK_EXPONENT,
13388 '!', 0, TOK_NOT,
13389 '<', 0, TOK_LT,
13390 '>', 0, TOK_GT,
13391 '=', 0, TOK_ASSIGN,
13392 '|', 0, TOK_BOR,
13393 '&', 0, TOK_BAND,
13394 '*', 0, TOK_MUL,
13395 '/', 0, TOK_DIV,
13396 '%', 0, TOK_REM,
13397 '+', 0, TOK_ADD,
13398 '-', 0, TOK_SUB,
13399 '^', 0, TOK_BXOR,
13400 /* uniq */
13401 '~', 0, TOK_BNOT,
13402 ',', 0, TOK_COMMA,
13403 '?', 0, TOK_CONDITIONAL,
13404 ':', 0, TOK_CONDITIONAL_SEP,
13405 ')', 0, TOK_RPAREN,
13406 '(', 0, TOK_LPAREN,
13407 0
13408};
13409/* ptr to ")" */
13410#define endexpression (&op_tokens[sizeof(op_tokens)-7])
13411
13412static arith_t
13413arith(const char *expr, int *perrcode)
13414{
13415 char arithval; /* Current character under analysis */
13416 operator lasttok, op;
13417 operator prec;
13418 operator *stack, *stackptr;
13419 const char *p = endexpression;
13420 int errcode;
13421 v_n_t *numstack, *numstackptr;
13422 unsigned datasizes = strlen(expr) + 2;
13423
13424 /* Stack of integers */
13425 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
13426 * in any given correct or incorrect expression is left as an exercise to
13427 * the reader. */
13428 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
13429 /* Stack of operator tokens */
13430 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
13431
13432 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
13433 *perrcode = errcode = 0;
13434
13435 while (1) {
13436 arithval = *expr;
13437 if (arithval == 0) {
13438 if (p == endexpression) {
13439 /* Null expression. */
13440 return 0;
13441 }
13442
13443 /* This is only reached after all tokens have been extracted from the
13444 * input stream. If there are still tokens on the operator stack, they
13445 * are to be applied in order. At the end, there should be a final
13446 * result on the integer stack */
13447
13448 if (expr != endexpression + 1) {
13449 /* If we haven't done so already, */
13450 /* append a closing right paren */
13451 expr = endexpression;
13452 /* and let the loop process it. */
13453 continue;
13454 }
13455 /* At this point, we're done with the expression. */
13456 if (numstackptr != numstack+1) {
13457 /* ... but if there isn't, it's bad */
13458 err:
13459 *perrcode = -1;
13460 return *perrcode;
13461 }
13462 if (numstack->var) {
13463 /* expression is $((var)) only, lookup now */
13464 errcode = arith_lookup_val(numstack);
13465 }
13466 ret:
13467 *perrcode = errcode;
13468 return numstack->val;
13469 }
13470
13471 /* Continue processing the expression. */
13472 if (arith_isspace(arithval)) {
13473 /* Skip whitespace */
13474 goto prologue;
13475 }
13476 p = endofname(expr);
13477 if (p != expr) {
13478 size_t var_name_size = (p-expr) + 1; /* trailing zero */
13479
13480 numstackptr->var = alloca(var_name_size);
13481 safe_strncpy(numstackptr->var, expr, var_name_size);
13482 expr = p;
13483 num:
13484 numstackptr->contidional_second_val_initialized = 0;
13485 numstackptr++;
13486 lasttok = TOK_NUM;
13487 continue;
13488 }
13489 if (isdigit(arithval)) {
13490 numstackptr->var = NULL;
13491#if ENABLE_ASH_MATH_SUPPORT_64
13492 numstackptr->val = strtoll(expr, (char **) &expr, 0);
13493#else
13494 numstackptr->val = strtol(expr, (char **) &expr, 0);
13495#endif
13496 goto num;
13497 }
13498 for (p = op_tokens; ; p++) {
13499 const char *o;
13500
13501 if (*p == 0) {
13502 /* strange operator not found */
13503 goto err;
13504 }
13505 for (o = expr; *p && *o == *p; p++)
13506 o++;
13507 if (!*p) {
13508 /* found */
13509 expr = o - 1;
13510 break;
13511 }
13512 /* skip tail uncompared token */
13513 while (*p)
13514 p++;
13515 /* skip zero delim */
13516 p++;
13517 }
13518 op = p[1];
13519
13520 /* post grammar: a++ reduce to num */
13521 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
13522 lasttok = TOK_NUM;
13523
13524 /* Plus and minus are binary (not unary) _only_ if the last
13525 * token was as number, or a right paren (which pretends to be
13526 * a number, since it evaluates to one). Think about it.
13527 * It makes sense. */
13528 if (lasttok != TOK_NUM) {
13529 switch (op) {
13530 case TOK_ADD:
13531 op = TOK_UPLUS;
13532 break;
13533 case TOK_SUB:
13534 op = TOK_UMINUS;
13535 break;
13536 case TOK_POST_INC:
13537 op = TOK_PRE_INC;
13538 break;
13539 case TOK_POST_DEC:
13540 op = TOK_PRE_DEC;
13541 break;
13542 }
13543 }
13544 /* We don't want a unary operator to cause recursive descent on the
13545 * stack, because there can be many in a row and it could cause an
13546 * operator to be evaluated before its argument is pushed onto the
13547 * integer stack. */
13548 /* But for binary operators, "apply" everything on the operator
13549 * stack until we find an operator with a lesser priority than the
13550 * one we have just extracted. */
13551 /* Left paren is given the lowest priority so it will never be
13552 * "applied" in this way.
13553 * if associativity is right and priority eq, applied also skip
13554 */
13555 prec = PREC(op);
13556 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
13557 /* not left paren or unary */
13558 if (lasttok != TOK_NUM) {
13559 /* binary op must be preceded by a num */
13560 goto err;
13561 }
13562 while (stackptr != stack) {
13563 if (op == TOK_RPAREN) {
13564 /* The algorithm employed here is simple: while we don't
13565 * hit an open paren nor the bottom of the stack, pop
13566 * tokens and apply them */
13567 if (stackptr[-1] == TOK_LPAREN) {
13568 --stackptr;
13569 /* Any operator directly after a */
13570 lasttok = TOK_NUM;
13571 /* close paren should consider itself binary */
13572 goto prologue;
13573 }
13574 } else {
13575 operator prev_prec = PREC(stackptr[-1]);
13576
13577 convert_prec_is_assing(prec);
13578 convert_prec_is_assing(prev_prec);
13579 if (prev_prec < prec)
13580 break;
13581 /* check right assoc */
13582 if (prev_prec == prec && is_right_associativity(prec))
13583 break;
13584 }
13585 errcode = arith_apply(*--stackptr, numstack, &numstackptr);
13586 if (errcode) goto ret;
13587 }
13588 if (op == TOK_RPAREN) {
13589 goto err;
13590 }
13591 }
13592
13593 /* Push this operator to the stack and remember it. */
13594 *stackptr++ = lasttok = op;
13595 prologue:
13596 ++expr;
13597 } /* while */
13598}
13599#endif /* ASH_MATH_SUPPORT */
13600
13601
13602/* ============ main() and helpers */ 12933/* ============ main() and helpers */
13603 12934
13604/* 12935/*
@@ -13786,7 +13117,7 @@ extern int etext();
13786int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 13117int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
13787int ash_main(int argc UNUSED_PARAM, char **argv) 13118int ash_main(int argc UNUSED_PARAM, char **argv)
13788{ 13119{
13789 char *shinit; 13120 const char *shinit;
13790 volatile smallint state; 13121 volatile smallint state;
13791 struct jmploc jmploc; 13122 struct jmploc jmploc;
13792 struct stackmark smark; 13123 struct stackmark smark;
@@ -13799,6 +13130,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
13799 INIT_G_alias(); 13130 INIT_G_alias();
13800#endif 13131#endif
13801 INIT_G_cmdtable(); 13132 INIT_G_cmdtable();
13133#if ENABLE_SH_MATH_SUPPORT
13134 math_hooks.lookupvar = lookupvar;
13135 math_hooks.setvar = setvar;
13136 math_hooks.endofname = endofname;
13137#endif
13802 13138
13803#if PROFILE 13139#if PROFILE
13804 monitor(4, etext, profile_buf, sizeof(profile_buf), 50); 13140 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
diff --git a/shell/hush.c b/shell/hush.c
index af6763517..e93e5a9a0 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -76,6 +76,8 @@
76#include <fnmatch.h> 76#include <fnmatch.h>
77#endif 77#endif
78 78
79#include "math.h"
80
79#define HUSH_VER_STR "0.92" 81#define HUSH_VER_STR "0.92"
80 82
81#if defined SINGLE_APPLET_MAIN 83#if defined SINGLE_APPLET_MAIN
@@ -452,6 +454,9 @@ struct globals {
452#if ENABLE_FEATURE_EDITING 454#if ENABLE_FEATURE_EDITING
453 line_input_t *line_input_state; 455 line_input_t *line_input_state;
454#endif 456#endif
457#if ENABLE_SH_MATH_SUPPORT
458 arith_eval_hooks_t hooks;
459#endif
455 pid_t root_pid; 460 pid_t root_pid;
456 pid_t last_bg_pid; 461 pid_t last_bg_pid;
457#if ENABLE_HUSH_JOB 462#if ENABLE_HUSH_JOB
@@ -1164,6 +1169,31 @@ static int unset_local_var(const char *name)
1164 return EXIT_SUCCESS; 1169 return EXIT_SUCCESS;
1165} 1170}
1166 1171
1172#if ENABLE_SH_MATH_SUPPORT
1173#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
1174#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
1175static char *endofname(const char *name)
1176{
1177 char *p;
1178
1179 p = (char *) name;
1180 if (!is_name(*p))
1181 return p;
1182 while (*++p) {
1183 if (!is_in_name(*p))
1184 break;
1185 }
1186 return p;
1187}
1188
1189static void arith_set_local_var(const char *name, const char *val, int flags)
1190{
1191 /* arith code doesnt malloc space, so do it for it */
1192 char *var = xmalloc(strlen(name) + 1 + strlen(val) + 1);
1193 sprintf(var, "%s=%s", name, val);
1194 set_local_var(var, flags);
1195}
1196#endif
1167 1197
1168/* 1198/*
1169 * in_str support 1199 * in_str support
@@ -1374,6 +1404,11 @@ static void o_addstr(o_string *o, const char *str, int len)
1374 o->data[o->length] = '\0'; 1404 o->data[o->length] = '\0';
1375} 1405}
1376 1406
1407static void o_addstrauto(o_string *o, const char *str)
1408{
1409 o_addstr(o, str, strlen(str) + 1);
1410}
1411
1377static void o_addstr_duplicate_backslash(o_string *o, const char *str, int len) 1412static void o_addstr_duplicate_backslash(o_string *o, const char *str, int len)
1378{ 1413{
1379 while (len) { 1414 while (len) {
@@ -1564,7 +1599,7 @@ static int o_glob(o_string *o, int n)
1564 char **argv = globdata.gl_pathv; 1599 char **argv = globdata.gl_pathv;
1565 o->length = pattern - o->data; /* "forget" pattern */ 1600 o->length = pattern - o->data; /* "forget" pattern */
1566 while (1) { 1601 while (1) {
1567 o_addstr(o, *argv, strlen(*argv) + 1); 1602 o_addstrauto(o, *argv);
1568 n = o_save_ptr_helper(o, n); 1603 n = o_save_ptr_helper(o, n);
1569 argv++; 1604 argv++;
1570 if (!*argv) 1605 if (!*argv)
@@ -1770,6 +1805,28 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1770 goto store_val; 1805 goto store_val;
1771 } 1806 }
1772#endif 1807#endif
1808#if ENABLE_SH_MATH_SUPPORT
1809 case '+': { /* <SPECIAL_VAR_SYMBOL>(cmd<SPECIAL_VAR_SYMBOL> */
1810 arith_t res;
1811 char buf[30];
1812 int errcode;
1813 *p = '\0';
1814 ++arg;
1815 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
1816 res = arith(arg, &errcode, &G.hooks);
1817 if (errcode < 0)
1818 switch (errcode) {
1819 case -3: maybe_die("arith", "exponent less than 0"); break;
1820 case -2: maybe_die("arith", "divide by zero"); break;
1821 case -5: maybe_die("arith", "expression recursion loop detected"); break;
1822 default: maybe_die("arith", "syntax error");
1823 }
1824 sprintf(buf, arith_t_fmt, res);
1825 o_addstrauto(output, buf);
1826 debug_printf_subst("ARITH RES '"arith_t_fmt"'\n", res);
1827 break;
1828 }
1829#endif
1773 default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */ 1830 default: /* <SPECIAL_VAR_SYMBOL>varname<SPECIAL_VAR_SYMBOL> */
1774 case_default: { 1831 case_default: {
1775 bool exp_len = false, exp_null = false; 1832 bool exp_len = false, exp_null = false;
@@ -1877,7 +1934,7 @@ static int expand_vars_to_list(o_string *output, int n, char *arg, char or_mask)
1877 debug_print_list("expand_vars_to_list[a]", output, n); 1934 debug_print_list("expand_vars_to_list[a]", output, n);
1878 /* this part is literal, and it was already pre-quoted 1935 /* this part is literal, and it was already pre-quoted
1879 * if needed (much earlier), do not use o_addQstr here! */ 1936 * if needed (much earlier), do not use o_addQstr here! */
1880 o_addstr(output, arg, strlen(arg) + 1); 1937 o_addstrauto(output, arg);
1881 debug_print_list("expand_vars_to_list[b]", output, n); 1938 debug_print_list("expand_vars_to_list[b]", output, n);
1882 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */ 1939 } else if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
1883 && !(ored_ch & 0x80) /* and all vars were not quoted. */ 1940 && !(ored_ch & 0x80) /* and all vars were not quoted. */
@@ -3754,7 +3811,7 @@ static int parse_group(o_string *dest, struct parse_context *ctx,
3754 /* command remains "open", available for possible redirects */ 3811 /* command remains "open", available for possible redirects */
3755} 3812}
3756 3813
3757#if ENABLE_HUSH_TICK 3814#if ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT
3758/* Subroutines for copying $(...) and `...` things */ 3815/* Subroutines for copying $(...) and `...` things */
3759static void add_till_backquote(o_string *dest, struct in_str *input); 3816static void add_till_backquote(o_string *dest, struct in_str *input);
3760/* '...' */ 3817/* '...' */
@@ -3834,7 +3891,7 @@ static void add_till_backquote(o_string *dest, struct in_str *input)
3834 * echo $(echo 'TEST)' BEST) TEST) BEST 3891 * echo $(echo 'TEST)' BEST) TEST) BEST
3835 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST 3892 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
3836 */ 3893 */
3837static void add_till_closing_curly_brace(o_string *dest, struct in_str *input) 3894static void add_till_closing_paren(o_string *dest, struct in_str *input, bool dbl)
3838{ 3895{
3839 int count = 0; 3896 int count = 0;
3840 while (1) { 3897 while (1) {
@@ -3844,8 +3901,14 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input)
3844 if (ch == '(') 3901 if (ch == '(')
3845 count++; 3902 count++;
3846 if (ch == ')') 3903 if (ch == ')')
3847 if (--count < 0) 3904 if (--count < 0) {
3848 break; 3905 if (!dbl)
3906 break;
3907 if (i_peek(input) == ')') {
3908 i_getch(input);
3909 break;
3910 }
3911 }
3849 o_addchr(dest, ch); 3912 o_addchr(dest, ch);
3850 if (ch == '\'') { 3913 if (ch == '\'') {
3851 add_till_single_quote(dest, input); 3914 add_till_single_quote(dest, input);
@@ -3866,7 +3929,7 @@ static void add_till_closing_curly_brace(o_string *dest, struct in_str *input)
3866 } 3929 }
3867 } 3930 }
3868} 3931}
3869#endif /* ENABLE_HUSH_TICK */ 3932#endif /* ENABLE_HUSH_TICK || ENABLE_SH_MATH_SUPPORT */
3870 3933
3871/* Return code: 0 for OK, 1 for syntax error */ 3934/* Return code: 0 for OK, 1 for syntax error */
3872static int handle_dollar(o_string *dest, struct in_str *input) 3935static int handle_dollar(o_string *dest, struct in_str *input)
@@ -3983,18 +4046,30 @@ static int handle_dollar(o_string *dest, struct in_str *input)
3983 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4046 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3984 break; 4047 break;
3985 } 4048 }
3986#if ENABLE_HUSH_TICK
3987 case '(': { 4049 case '(': {
3988 //int pos = dest->length;
3989 i_getch(input); 4050 i_getch(input);
4051
4052#if ENABLE_SH_MATH_SUPPORT
4053 if (i_peek(input) == '(') {
4054 i_getch(input);
4055 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4056 o_addchr(dest, quote_mask | '+');
4057 add_till_closing_paren(dest, input, true);
4058 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4059 break;
4060 }
4061#endif
4062
4063#if ENABLE_HUSH_TICK
4064 //int pos = dest->length;
3990 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4065 o_addchr(dest, SPECIAL_VAR_SYMBOL);
3991 o_addchr(dest, quote_mask | '`'); 4066 o_addchr(dest, quote_mask | '`');
3992 add_till_closing_curly_brace(dest, input); 4067 add_till_closing_paren(dest, input, false);
3993 //debug_printf_subst("SUBST RES2 '%s'\n", dest->data + pos); 4068 //debug_printf_subst("SUBST RES2 '%s'\n", dest->data + pos);
3994 o_addchr(dest, SPECIAL_VAR_SYMBOL); 4069 o_addchr(dest, SPECIAL_VAR_SYMBOL);
4070#endif
3995 break; 4071 break;
3996 } 4072 }
3997#endif
3998 case '_': 4073 case '_':
3999 i_getch(input); 4074 i_getch(input);
4000 ch = i_peek(input); 4075 ch = i_peek(input);
@@ -4526,6 +4601,11 @@ int hush_main(int argc, char **argv)
4526#if ENABLE_FEATURE_EDITING 4601#if ENABLE_FEATURE_EDITING
4527 G.line_input_state = new_line_input_t(FOR_SHELL); 4602 G.line_input_state = new_line_input_t(FOR_SHELL);
4528#endif 4603#endif
4604#if ENABLE_SH_MATH_SUPPORT
4605 G.hooks.lookupvar = lookup_param;
4606 G.hooks.setvar = arith_set_local_var;
4607 G.hooks.endofname = endofname;
4608#endif
4529 /* XXX what should these be while sourcing /etc/profile? */ 4609 /* XXX what should these be while sourcing /etc/profile? */
4530 G.global_argc = argc; 4610 G.global_argc = argc;
4531 G.global_argv = argv; 4611 G.global_argv = argv;
diff --git a/shell/math.c b/shell/math.c
new file mode 100644
index 000000000..9a46a937e
--- /dev/null
+++ b/shell/math.c
@@ -0,0 +1,701 @@
1/*
2 * arithmetic code ripped out of ash shell for code sharing
3 *
4 * Copyright (c) 1989, 1991, 1993, 1994
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
8 * was re-ported from NetBSD and debianized.
9 *
10 * This code is derived from software contributed to Berkeley by
11 * Kenneth Almquist.
12 *
13 * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
14 *
15 * Original BSD copyright notice is retained at the end of this file.
16 */
17/*
18 * rewrite arith.y to micro stack based cryptic algorithm by
19 * Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
20 *
21 * Modified by Paul Mundt <lethal@linux-sh.org> (c) 2004 to support
22 * dynamic variables.
23 *
24 * Modified by Vladimir Oleynik <dzo@simtreas.ru> (c) 2001-2005 to be
25 * used in busybox and size optimizations,
26 * rewrote arith (see notes to this), added locale support,
27 * rewrote dynamic variables.
28 */
29
30#include "busybox.h"
31#include "math.h"
32
33#define a_e_h_t arith_eval_hooks_t
34#define lookupvar (math_hooks->lookupvar)
35#define setvar (math_hooks->setvar)
36#define endofname (math_hooks->endofname)
37
38/* Copyright (c) 2001 Aaron Lehmann <aaronl@vitelus.com>
39
40 Permission is hereby granted, free of charge, to any person obtaining
41 a copy of this software and associated documentation files (the
42 "Software"), to deal in the Software without restriction, including
43 without limitation the rights to use, copy, modify, merge, publish,
44 distribute, sublicense, and/or sell copies of the Software, and to
45 permit persons to whom the Software is furnished to do so, subject to
46 the following conditions:
47
48 The above copyright notice and this permission notice shall be
49 included in all copies or substantial portions of the Software.
50
51 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
52 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
53 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
54 IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
55 CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
56 TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
57 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
58*/
59
60/* This is my infix parser/evaluator. It is optimized for size, intended
61 * as a replacement for yacc-based parsers. However, it may well be faster
62 * than a comparable parser written in yacc. The supported operators are
63 * listed in #defines below. Parens, order of operations, and error handling
64 * are supported. This code is thread safe. The exact expression format should
65 * be that which POSIX specifies for shells. */
66
67/* The code uses a simple two-stack algorithm. See
68 * http://www.onthenet.com.au/~grahamis/int2008/week02/lect02.html
69 * for a detailed explanation of the infix-to-postfix algorithm on which
70 * this is based (this code differs in that it applies operators immediately
71 * to the stack instead of adding them to a queue to end up with an
72 * expression). */
73
74/* To use the routine, call it with an expression string and error return
75 * pointer */
76
77/*
78 * Aug 24, 2001 Manuel Novoa III
79 *
80 * Reduced the generated code size by about 30% (i386) and fixed several bugs.
81 *
82 * 1) In arith_apply():
83 * a) Cached values of *numptr and &(numptr[-1]).
84 * b) Removed redundant test for zero denominator.
85 *
86 * 2) In arith():
87 * a) Eliminated redundant code for processing operator tokens by moving
88 * to a table-based implementation. Also folded handling of parens
89 * into the table.
90 * b) Combined all 3 loops which called arith_apply to reduce generated
91 * code size at the cost of speed.
92 *
93 * 3) The following expressions were treated as valid by the original code:
94 * 1() , 0! , 1 ( *3 ) .
95 * These bugs have been fixed by internally enclosing the expression in
96 * parens and then checking that all binary ops and right parens are
97 * preceded by a valid expression (NUM_TOKEN).
98 *
99 * Note: It may be desirable to replace Aaron's test for whitespace with
100 * ctype's isspace() if it is used by another busybox applet or if additional
101 * whitespace chars should be considered. Look below the "#include"s for a
102 * precompiler test.
103 */
104
105/*
106 * Aug 26, 2001 Manuel Novoa III
107 *
108 * Return 0 for null expressions. Pointed out by Vladimir Oleynik.
109 *
110 * Merge in Aaron's comments previously posted to the busybox list,
111 * modified slightly to take account of my changes to the code.
112 *
113 */
114
115/*
116 * (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
117 *
118 * - allow access to variable,
119 * used recursive find value indirection (c=2*2; a="c"; $((a+=2)) produce 6)
120 * - realize assign syntax (VAR=expr, +=, *= etc)
121 * - realize exponentiation (** operator)
122 * - realize comma separated - expr, expr
123 * - realise ++expr --expr expr++ expr--
124 * - realise expr ? expr : expr (but, second expr calculate always)
125 * - allow hexadecimal and octal numbers
126 * - was restored loses XOR operator
127 * - remove one goto label, added three ;-)
128 * - protect $((num num)) as true zero expr (Manuel`s error)
129 * - always use special isspace(), see comment from bash ;-)
130 */
131
132#define arith_isspace(arithval) \
133 (arithval == ' ' || arithval == '\n' || arithval == '\t')
134
135typedef unsigned char operator;
136
137/* An operator's token id is a bit of a bitfield. The lower 5 bits are the
138 * precedence, and 3 high bits are an ID unique across operators of that
139 * precedence. The ID portion is so that multiple operators can have the
140 * same precedence, ensuring that the leftmost one is evaluated first.
141 * Consider * and /. */
142
143#define tok_decl(prec,id) (((id)<<5)|(prec))
144#define PREC(op) ((op) & 0x1F)
145
146#define TOK_LPAREN tok_decl(0,0)
147
148#define TOK_COMMA tok_decl(1,0)
149
150#define TOK_ASSIGN tok_decl(2,0)
151#define TOK_AND_ASSIGN tok_decl(2,1)
152#define TOK_OR_ASSIGN tok_decl(2,2)
153#define TOK_XOR_ASSIGN tok_decl(2,3)
154#define TOK_PLUS_ASSIGN tok_decl(2,4)
155#define TOK_MINUS_ASSIGN tok_decl(2,5)
156#define TOK_LSHIFT_ASSIGN tok_decl(2,6)
157#define TOK_RSHIFT_ASSIGN tok_decl(2,7)
158
159#define TOK_MUL_ASSIGN tok_decl(3,0)
160#define TOK_DIV_ASSIGN tok_decl(3,1)
161#define TOK_REM_ASSIGN tok_decl(3,2)
162
163/* all assign is right associativity and precedence eq, but (7+3)<<5 > 256 */
164#define convert_prec_is_assing(prec) do { if (prec == 3) prec = 2; } while (0)
165
166/* conditional is right associativity too */
167#define TOK_CONDITIONAL tok_decl(4,0)
168#define TOK_CONDITIONAL_SEP tok_decl(4,1)
169
170#define TOK_OR tok_decl(5,0)
171
172#define TOK_AND tok_decl(6,0)
173
174#define TOK_BOR tok_decl(7,0)
175
176#define TOK_BXOR tok_decl(8,0)
177
178#define TOK_BAND tok_decl(9,0)
179
180#define TOK_EQ tok_decl(10,0)
181#define TOK_NE tok_decl(10,1)
182
183#define TOK_LT tok_decl(11,0)
184#define TOK_GT tok_decl(11,1)
185#define TOK_GE tok_decl(11,2)
186#define TOK_LE tok_decl(11,3)
187
188#define TOK_LSHIFT tok_decl(12,0)
189#define TOK_RSHIFT tok_decl(12,1)
190
191#define TOK_ADD tok_decl(13,0)
192#define TOK_SUB tok_decl(13,1)
193
194#define TOK_MUL tok_decl(14,0)
195#define TOK_DIV tok_decl(14,1)
196#define TOK_REM tok_decl(14,2)
197
198/* exponent is right associativity */
199#define TOK_EXPONENT tok_decl(15,1)
200
201/* For now unary operators. */
202#define UNARYPREC 16
203#define TOK_BNOT tok_decl(UNARYPREC,0)
204#define TOK_NOT tok_decl(UNARYPREC,1)
205
206#define TOK_UMINUS tok_decl(UNARYPREC+1,0)
207#define TOK_UPLUS tok_decl(UNARYPREC+1,1)
208
209#define PREC_PRE (UNARYPREC+2)
210
211#define TOK_PRE_INC tok_decl(PREC_PRE, 0)
212#define TOK_PRE_DEC tok_decl(PREC_PRE, 1)
213
214#define PREC_POST (UNARYPREC+3)
215
216#define TOK_POST_INC tok_decl(PREC_POST, 0)
217#define TOK_POST_DEC tok_decl(PREC_POST, 1)
218
219#define SPEC_PREC (UNARYPREC+4)
220
221#define TOK_NUM tok_decl(SPEC_PREC, 0)
222#define TOK_RPAREN tok_decl(SPEC_PREC, 1)
223
224#define NUMPTR (*numstackptr)
225
226static int
227tok_have_assign(operator op)
228{
229 operator prec = PREC(op);
230
231 convert_prec_is_assing(prec);
232 return (prec == PREC(TOK_ASSIGN) ||
233 prec == PREC_PRE || prec == PREC_POST);
234}
235
236static int
237is_right_associativity(operator prec)
238{
239 return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT)
240 || prec == PREC(TOK_CONDITIONAL));
241}
242
243typedef struct {
244 arith_t val;
245 arith_t contidional_second_val;
246 char contidional_second_val_initialized;
247 char *var; /* if NULL then is regular number,
248 else is variable name */
249} v_n_t;
250
251typedef struct chk_var_recursive_looped_t {
252 const char *var;
253 struct chk_var_recursive_looped_t *next;
254} chk_var_recursive_looped_t;
255
256static chk_var_recursive_looped_t *prev_chk_var_recursive;
257
258static int
259arith_lookup_val(v_n_t *t, a_e_h_t *math_hooks)
260{
261 if (t->var) {
262 const char * p = lookupvar(t->var);
263
264 if (p) {
265 int errcode;
266
267 /* recursive try as expression */
268 chk_var_recursive_looped_t *cur;
269 chk_var_recursive_looped_t cur_save;
270
271 for (cur = prev_chk_var_recursive; cur; cur = cur->next) {
272 if (strcmp(cur->var, t->var) == 0) {
273 /* expression recursion loop detected */
274 return -5;
275 }
276 }
277 /* save current lookuped var name */
278 cur = prev_chk_var_recursive;
279 cur_save.var = t->var;
280 cur_save.next = cur;
281 prev_chk_var_recursive = &cur_save;
282
283 t->val = arith (p, &errcode, math_hooks);
284 /* restore previous ptr after recursiving */
285 prev_chk_var_recursive = cur;
286 return errcode;
287 }
288 /* allow undefined var as 0 */
289 t->val = 0;
290 }
291 return 0;
292}
293
294/* "applying" a token means performing it on the top elements on the integer
295 * stack. For a unary operator it will only change the top element, but a
296 * binary operator will pop two arguments and push a result */
297static int
298arith_apply(operator op, v_n_t *numstack, v_n_t **numstackptr, a_e_h_t *math_hooks)
299{
300 v_n_t *numptr_m1;
301 arith_t numptr_val, rez;
302 int ret_arith_lookup_val;
303
304 /* There is no operator that can work without arguments */
305 if (NUMPTR == numstack) goto err;
306 numptr_m1 = NUMPTR - 1;
307
308 /* check operand is var with noninteger value */
309 ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks);
310 if (ret_arith_lookup_val)
311 return ret_arith_lookup_val;
312
313 rez = numptr_m1->val;
314 if (op == TOK_UMINUS)
315 rez *= -1;
316 else if (op == TOK_NOT)
317 rez = !rez;
318 else if (op == TOK_BNOT)
319 rez = ~rez;
320 else if (op == TOK_POST_INC || op == TOK_PRE_INC)
321 rez++;
322 else if (op == TOK_POST_DEC || op == TOK_PRE_DEC)
323 rez--;
324 else if (op != TOK_UPLUS) {
325 /* Binary operators */
326
327 /* check and binary operators need two arguments */
328 if (numptr_m1 == numstack) goto err;
329
330 /* ... and they pop one */
331 --NUMPTR;
332 numptr_val = rez;
333 if (op == TOK_CONDITIONAL) {
334 if (!numptr_m1->contidional_second_val_initialized) {
335 /* protect $((expr1 ? expr2)) without ": expr" */
336 goto err;
337 }
338 rez = numptr_m1->contidional_second_val;
339 } else if (numptr_m1->contidional_second_val_initialized) {
340 /* protect $((expr1 : expr2)) without "expr ? " */
341 goto err;
342 }
343 numptr_m1 = NUMPTR - 1;
344 if (op != TOK_ASSIGN) {
345 /* check operand is var with noninteger value for not '=' */
346 ret_arith_lookup_val = arith_lookup_val(numptr_m1, math_hooks);
347 if (ret_arith_lookup_val)
348 return ret_arith_lookup_val;
349 }
350 if (op == TOK_CONDITIONAL) {
351 numptr_m1->contidional_second_val = rez;
352 }
353 rez = numptr_m1->val;
354 if (op == TOK_BOR || op == TOK_OR_ASSIGN)
355 rez |= numptr_val;
356 else if (op == TOK_OR)
357 rez = numptr_val || rez;
358 else if (op == TOK_BAND || op == TOK_AND_ASSIGN)
359 rez &= numptr_val;
360 else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN)
361 rez ^= numptr_val;
362 else if (op == TOK_AND)
363 rez = rez && numptr_val;
364 else if (op == TOK_EQ)
365 rez = (rez == numptr_val);
366 else if (op == TOK_NE)
367 rez = (rez != numptr_val);
368 else if (op == TOK_GE)
369 rez = (rez >= numptr_val);
370 else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN)
371 rez >>= numptr_val;
372 else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN)
373 rez <<= numptr_val;
374 else if (op == TOK_GT)
375 rez = (rez > numptr_val);
376 else if (op == TOK_LT)
377 rez = (rez < numptr_val);
378 else if (op == TOK_LE)
379 rez = (rez <= numptr_val);
380 else if (op == TOK_MUL || op == TOK_MUL_ASSIGN)
381 rez *= numptr_val;
382 else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN)
383 rez += numptr_val;
384 else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN)
385 rez -= numptr_val;
386 else if (op == TOK_ASSIGN || op == TOK_COMMA)
387 rez = numptr_val;
388 else if (op == TOK_CONDITIONAL_SEP) {
389 if (numptr_m1 == numstack) {
390 /* protect $((expr : expr)) without "expr ? " */
391 goto err;
392 }
393 numptr_m1->contidional_second_val_initialized = op;
394 numptr_m1->contidional_second_val = numptr_val;
395 } else if (op == TOK_CONDITIONAL) {
396 rez = rez ?
397 numptr_val : numptr_m1->contidional_second_val;
398 } else if (op == TOK_EXPONENT) {
399 if (numptr_val < 0)
400 return -3; /* exponent less than 0 */
401 else {
402 arith_t c = 1;
403
404 if (numptr_val)
405 while (numptr_val--)
406 c *= rez;
407 rez = c;
408 }
409 } else if (numptr_val==0) /* zero divisor check */
410 return -2;
411 else if (op == TOK_DIV || op == TOK_DIV_ASSIGN)
412 rez /= numptr_val;
413 else if (op == TOK_REM || op == TOK_REM_ASSIGN)
414 rez %= numptr_val;
415 }
416 if (tok_have_assign(op)) {
417 char buf[sizeof(arith_t_type)*3 + 2];
418
419 if (numptr_m1->var == NULL) {
420 /* Hmm, 1=2 ? */
421 goto err;
422 }
423 /* save to shell variable */
424 snprintf(buf, sizeof(buf), arith_t_fmt, (arith_t_type) rez);
425 setvar(numptr_m1->var, buf, 0);
426 /* after saving, make previous value for v++ or v-- */
427 if (op == TOK_POST_INC)
428 rez--;
429 else if (op == TOK_POST_DEC)
430 rez++;
431 }
432 numptr_m1->val = rez;
433 /* protect geting var value, is number now */
434 numptr_m1->var = NULL;
435 return 0;
436 err:
437 return -1;
438}
439
440/* longest must be first */
441static const char op_tokens[] ALIGN1 = {
442 '<','<','=',0, TOK_LSHIFT_ASSIGN,
443 '>','>','=',0, TOK_RSHIFT_ASSIGN,
444 '<','<', 0, TOK_LSHIFT,
445 '>','>', 0, TOK_RSHIFT,
446 '|','|', 0, TOK_OR,
447 '&','&', 0, TOK_AND,
448 '!','=', 0, TOK_NE,
449 '<','=', 0, TOK_LE,
450 '>','=', 0, TOK_GE,
451 '=','=', 0, TOK_EQ,
452 '|','=', 0, TOK_OR_ASSIGN,
453 '&','=', 0, TOK_AND_ASSIGN,
454 '*','=', 0, TOK_MUL_ASSIGN,
455 '/','=', 0, TOK_DIV_ASSIGN,
456 '%','=', 0, TOK_REM_ASSIGN,
457 '+','=', 0, TOK_PLUS_ASSIGN,
458 '-','=', 0, TOK_MINUS_ASSIGN,
459 '-','-', 0, TOK_POST_DEC,
460 '^','=', 0, TOK_XOR_ASSIGN,
461 '+','+', 0, TOK_POST_INC,
462 '*','*', 0, TOK_EXPONENT,
463 '!', 0, TOK_NOT,
464 '<', 0, TOK_LT,
465 '>', 0, TOK_GT,
466 '=', 0, TOK_ASSIGN,
467 '|', 0, TOK_BOR,
468 '&', 0, TOK_BAND,
469 '*', 0, TOK_MUL,
470 '/', 0, TOK_DIV,
471 '%', 0, TOK_REM,
472 '+', 0, TOK_ADD,
473 '-', 0, TOK_SUB,
474 '^', 0, TOK_BXOR,
475 /* uniq */
476 '~', 0, TOK_BNOT,
477 ',', 0, TOK_COMMA,
478 '?', 0, TOK_CONDITIONAL,
479 ':', 0, TOK_CONDITIONAL_SEP,
480 ')', 0, TOK_RPAREN,
481 '(', 0, TOK_LPAREN,
482 0
483};
484/* ptr to ")" */
485#define endexpression (&op_tokens[sizeof(op_tokens)-7])
486
487arith_t
488arith(const char *expr, int *perrcode, a_e_h_t *math_hooks)
489{
490 char arithval; /* Current character under analysis */
491 operator lasttok, op;
492 operator prec;
493 operator *stack, *stackptr;
494 const char *p = endexpression;
495 int errcode;
496 v_n_t *numstack, *numstackptr;
497 unsigned datasizes = strlen(expr) + 2;
498
499 /* Stack of integers */
500 /* The proof that there can be no more than strlen(startbuf)/2+1 integers
501 * in any given correct or incorrect expression is left as an exercise to
502 * the reader. */
503 numstackptr = numstack = alloca((datasizes / 2) * sizeof(numstack[0]));
504 /* Stack of operator tokens */
505 stackptr = stack = alloca(datasizes * sizeof(stack[0]));
506
507 *stackptr++ = lasttok = TOK_LPAREN; /* start off with a left paren */
508 *perrcode = errcode = 0;
509
510 while (1) {
511 arithval = *expr;
512 if (arithval == 0) {
513 if (p == endexpression) {
514 /* Null expression. */
515 return 0;
516 }
517
518 /* This is only reached after all tokens have been extracted from the
519 * input stream. If there are still tokens on the operator stack, they
520 * are to be applied in order. At the end, there should be a final
521 * result on the integer stack */
522
523 if (expr != endexpression + 1) {
524 /* If we haven't done so already, */
525 /* append a closing right paren */
526 expr = endexpression;
527 /* and let the loop process it. */
528 continue;
529 }
530 /* At this point, we're done with the expression. */
531 if (numstackptr != numstack+1) {
532 /* ... but if there isn't, it's bad */
533 err:
534 *perrcode = -1;
535 return *perrcode;
536 }
537 if (numstack->var) {
538 /* expression is $((var)) only, lookup now */
539 errcode = arith_lookup_val(numstack, math_hooks);
540 }
541 ret:
542 *perrcode = errcode;
543 return numstack->val;
544 }
545
546 /* Continue processing the expression. */
547 if (arith_isspace(arithval)) {
548 /* Skip whitespace */
549 goto prologue;
550 }
551 p = endofname(expr);
552 if (p != expr) {
553 size_t var_name_size = (p-expr) + 1; /* trailing zero */
554
555 numstackptr->var = alloca(var_name_size);
556 safe_strncpy(numstackptr->var, expr, var_name_size);
557 expr = p;
558 num:
559 numstackptr->contidional_second_val_initialized = 0;
560 numstackptr++;
561 lasttok = TOK_NUM;
562 continue;
563 }
564 if (isdigit(arithval)) {
565 numstackptr->var = NULL;
566 numstackptr->val = strto_arith_t(expr, (char **) &expr, 0);
567 goto num;
568 }
569 for (p = op_tokens; ; p++) {
570 const char *o;
571
572 if (*p == 0) {
573 /* strange operator not found */
574 goto err;
575 }
576 for (o = expr; *p && *o == *p; p++)
577 o++;
578 if (!*p) {
579 /* found */
580 expr = o - 1;
581 break;
582 }
583 /* skip tail uncompared token */
584 while (*p)
585 p++;
586 /* skip zero delim */
587 p++;
588 }
589 op = p[1];
590
591 /* post grammar: a++ reduce to num */
592 if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC)
593 lasttok = TOK_NUM;
594
595 /* Plus and minus are binary (not unary) _only_ if the last
596 * token was as number, or a right paren (which pretends to be
597 * a number, since it evaluates to one). Think about it.
598 * It makes sense. */
599 if (lasttok != TOK_NUM) {
600 switch (op) {
601 case TOK_ADD:
602 op = TOK_UPLUS;
603 break;
604 case TOK_SUB:
605 op = TOK_UMINUS;
606 break;
607 case TOK_POST_INC:
608 op = TOK_PRE_INC;
609 break;
610 case TOK_POST_DEC:
611 op = TOK_PRE_DEC;
612 break;
613 }
614 }
615 /* We don't want a unary operator to cause recursive descent on the
616 * stack, because there can be many in a row and it could cause an
617 * operator to be evaluated before its argument is pushed onto the
618 * integer stack. */
619 /* But for binary operators, "apply" everything on the operator
620 * stack until we find an operator with a lesser priority than the
621 * one we have just extracted. */
622 /* Left paren is given the lowest priority so it will never be
623 * "applied" in this way.
624 * if associativity is right and priority eq, applied also skip
625 */
626 prec = PREC(op);
627 if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) {
628 /* not left paren or unary */
629 if (lasttok != TOK_NUM) {
630 /* binary op must be preceded by a num */
631 goto err;
632 }
633 while (stackptr != stack) {
634 if (op == TOK_RPAREN) {
635 /* The algorithm employed here is simple: while we don't
636 * hit an open paren nor the bottom of the stack, pop
637 * tokens and apply them */
638 if (stackptr[-1] == TOK_LPAREN) {
639 --stackptr;
640 /* Any operator directly after a */
641 lasttok = TOK_NUM;
642 /* close paren should consider itself binary */
643 goto prologue;
644 }
645 } else {
646 operator prev_prec = PREC(stackptr[-1]);
647
648 convert_prec_is_assing(prec);
649 convert_prec_is_assing(prev_prec);
650 if (prev_prec < prec)
651 break;
652 /* check right assoc */
653 if (prev_prec == prec && is_right_associativity(prec))
654 break;
655 }
656 errcode = arith_apply(*--stackptr, numstack, &numstackptr, math_hooks);
657 if (errcode) goto ret;
658 }
659 if (op == TOK_RPAREN) {
660 goto err;
661 }
662 }
663
664 /* Push this operator to the stack and remember it. */
665 *stackptr++ = lasttok = op;
666 prologue:
667 ++expr;
668 } /* while */
669}
670
671/*-
672 * Copyright (c) 1989, 1991, 1993, 1994
673 * The Regents of the University of California. All rights reserved.
674 *
675 * This code is derived from software contributed to Berkeley by
676 * Kenneth Almquist.
677 *
678 * Redistribution and use in source and binary forms, with or without
679 * modification, are permitted provided that the following conditions
680 * are met:
681 * 1. Redistributions of source code must retain the above copyright
682 * notice, this list of conditions and the following disclaimer.
683 * 2. Redistributions in binary form must reproduce the above copyright
684 * notice, this list of conditions and the following disclaimer in the
685 * documentation and/or other materials provided with the distribution.
686 * 3. Neither the name of the University nor the names of its contributors
687 * may be used to endorse or promote products derived from this software
688 * without specific prior written permission.
689 *
690 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
691 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
692 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
693 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
694 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
695 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
696 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
697 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
698 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
699 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
700 * SUCH DAMAGE.
701 */
diff --git a/shell/math.h b/shell/math.h
new file mode 100644
index 000000000..a52680923
--- /dev/null
+++ b/shell/math.h
@@ -0,0 +1,101 @@
1/* math.h - interface to shell math "library" -- this allows shells to share
2 * the implementation of arithmetic $((...)) expansions.
3 *
4 * This aims to be a POSIX shell math library as documented here:
5 * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_04
6 *
7 * See math.c for internal documentation.
8 */
9
10/* The math library has just one function:
11 *
12 * arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t *hooks);
13 *
14 * The first argument is the math string to parse. All normal expansions must
15 * be done already. i.e. no dollar symbols should be present.
16 *
17 * The second argument is a semi-detailed error description in case something
18 * goes wrong in the parsing steps. Currently, those values are (for
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 *
26 * The third argument is a struct pointer of hooks for your shell (see below).
27 *
28 * The function returns the answer to the expression. So if you called it
29 * with the expression:
30 * "1 + 2 + 3"
31 * You would obviously get back 6.
32 */
33
34/* To add support to a shell, you need to implement three functions:
35 *
36 * lookupvar() - look up and return the value of a variable
37 *
38 * If the shell does:
39 * foo=123
40 * Then the code:
41 * const char *val = lookupvar("foo");
42 * Will result in val pointing to "123"
43 *
44 * setvar() - set a variable to some value
45 *
46 * If the arithmetic expansion does something like:
47 * $(( i = 1))
48 * Then the math code will make a call like so:
49 * setvar("i", "1", 0);
50 * The storage for the first two parameters are not allocated, so your
51 * shell implementation will most likely need to strdup() them to save.
52 *
53 * endofname() - return the end of a variable name from input
54 *
55 * The arithmetic code does not know about variable naming conventions.
56 * 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.
58 * So when it encounters something like:
59 * $(( some_var + 123 ))
60 * It will make a call like so:
61 * end = endofname("some_var + 123");
62 * 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
64 * pointer with an offset pointing to the first space. The typical
65 * 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]*
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 */
74
75#ifndef _SHELL_MATH_
76#define _SHELL_MATH_
77
78#if ENABLE_SH_MATH_SUPPORT_64
79typedef int64_t arith_t;
80#define arith_t_type long long
81#define arith_t_fmt "%lld"
82#define strto_arith_t strtoll
83#else
84typedef long arith_t;
85#define arith_t_type long
86#define arith_t_fmt "%ld"
87#define strto_arith_t strtol
88#endif
89
90typedef const char *(*arith_var_lookup_t)(const char *name);
91typedef void (*arith_var_set_t)(const char *name, const char *val, int flags);
92typedef char *(*arith_var_endofname_t)(const char *name);
93typedef struct arith_eval_hooks {
94 arith_var_lookup_t lookupvar;
95 arith_var_set_t setvar;
96 arith_var_endofname_t endofname;
97} arith_eval_hooks_t;
98
99arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*);
100
101#endif