aboutsummaryrefslogtreecommitdiff
path: root/shell/ash.c
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 /shell/ash.c
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!)
Diffstat (limited to 'shell/ash.c')
-rw-r--r--shell/ash.c734
1 files changed, 35 insertions, 699 deletions
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);