diff options
author | Mike Frysinger <vapier@gentoo.org> | 2009-04-02 10:02:37 +0000 |
---|---|---|
committer | Mike Frysinger <vapier@gentoo.org> | 2009-04-02 10:02:37 +0000 |
commit | 98c52645c02dacebccae7d68d6c2627f9318fcf7 (patch) | |
tree | e0c64b5b24206f95e72fd060336f74cb459f2109 | |
parent | 551ffdccea39a9223ad451954db40fd7a6e20e79 (diff) | |
download | busybox-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.in | 32 | ||||
-rw-r--r-- | shell/Kbuild | 1 | ||||
-rw-r--r-- | shell/ash.c | 734 | ||||
-rw-r--r-- | shell/hush.c | 102 | ||||
-rw-r--r-- | shell/math.c | 701 | ||||
-rw-r--r-- | shell/math.h | 101 |
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 | ||
87 | config 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 | |||
94 | config 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 | |||
103 | config ASH_GETOPTS | 87 | config 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 | |||
267 | comment "Bourne Shell Options" | 251 | comment "Bourne Shell Options" |
268 | depends on MSH || LASH || HUSH || ASH | 252 | depends on MSH || LASH || HUSH || ASH |
269 | 253 | ||
254 | config 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 | |||
261 | config 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 | |||
270 | config FEATURE_SH_EXTRA_QUIET | 270 | config 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 | |||
9 | lib-$(CONFIG_HUSH) += hush.o | 9 | lib-$(CONFIG_HUSH) += hush.o |
10 | lib-$(CONFIG_MSH) += msh.o | 10 | lib-$(CONFIG_MSH) += msh.o |
11 | lib-$(CONFIG_CTTYHACK) += cttyhack.o | 11 | lib-$(CONFIG_CTTYHACK) += cttyhack.o |
12 | lib-$(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 | */ |
2001 | static char * | 1994 | static const char * |
2002 | lookupvar(const char *name) | 1995 | lookupvar(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 | */ |
2027 | static char * | 2020 | static const char * |
2028 | bltinlookup(const char *name) | 2021 | bltinlookup(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 |
2641 | static const char S_I_T[][4] = { | 2634 | static 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 |
5262 | typedef int64_t arith_t; | ||
5263 | #define arith_t_type long long | ||
5264 | #else | ||
5265 | typedef long arith_t; | ||
5266 | #define arith_t_type long | ||
5267 | #endif | ||
5268 | |||
5269 | #if ENABLE_ASH_MATH_SUPPORT | ||
5270 | static arith_t dash_arith(const char *); | 5255 | static arith_t dash_arith(const char *); |
5271 | static 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 | |||
6291 | varvalue(char *name, int varflags, int flags, struct strlist *var_str_list) | 6271 | varvalue(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 |
8711 | static int helpcmd(int, char **); | 8690 | static int helpcmd(int, char **); |
8712 | #endif | 8691 | #endif |
8713 | #if ENABLE_ASH_MATH_SUPPORT | 8692 | #if ENABLE_SH_MATH_SUPPORT |
8714 | static int letcmd(int, char **); | 8693 | static int letcmd(int, char **); |
8715 | #endif | 8694 | #endif |
8716 | static int readcmd(int, char **); | 8695 | static 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 |
12388 | static arith_t | 12367 | static arith_t |
12389 | dash_arith(const char *s) | 12368 | dash_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 | |||
13056 | typedef 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 | |||
13147 | static int | ||
13148 | tok_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 | |||
13157 | static int | ||
13158 | is_right_associativity(operator prec) | ||
13159 | { | ||
13160 | return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) | ||
13161 | || prec == PREC(TOK_CONDITIONAL)); | ||
13162 | } | ||
13163 | |||
13164 | typedef 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 | |||
13172 | typedef 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 | |||
13177 | static chk_var_recursive_looped_t *prev_chk_var_recursive; | ||
13178 | |||
13179 | static int | ||
13180 | arith_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 */ | ||
13218 | static int | ||
13219 | arith_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 */ | ||
13366 | static 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 | |||
13412 | static arith_t | ||
13413 | arith(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(); | |||
13786 | int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 13117 | int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
13787 | int ash_main(int argc UNUSED_PARAM, char **argv) | 13118 | int 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))) | ||
1175 | static 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 | |||
1189 | static 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 | ||
1407 | static void o_addstrauto(o_string *o, const char *str) | ||
1408 | { | ||
1409 | o_addstr(o, str, strlen(str) + 1); | ||
1410 | } | ||
1411 | |||
1377 | static void o_addstr_duplicate_backslash(o_string *o, const char *str, int len) | 1412 | static 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 */ |
3759 | static void add_till_backquote(o_string *dest, struct in_str *input); | 3816 | static 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 | */ |
3837 | static void add_till_closing_curly_brace(o_string *dest, struct in_str *input) | 3894 | static 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 */ |
3872 | static int handle_dollar(o_string *dest, struct in_str *input) | 3935 | static 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 | |||
135 | typedef 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 | |||
226 | static int | ||
227 | tok_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 | |||
236 | static int | ||
237 | is_right_associativity(operator prec) | ||
238 | { | ||
239 | return (prec == PREC(TOK_ASSIGN) || prec == PREC(TOK_EXPONENT) | ||
240 | || prec == PREC(TOK_CONDITIONAL)); | ||
241 | } | ||
242 | |||
243 | typedef 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 | |||
251 | typedef 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 | |||
256 | static chk_var_recursive_looped_t *prev_chk_var_recursive; | ||
257 | |||
258 | static int | ||
259 | arith_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 */ | ||
297 | static int | ||
298 | arith_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 */ | ||
441 | static 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 | |||
487 | arith_t | ||
488 | arith(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 | ||
79 | typedef 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 | ||
84 | typedef long arith_t; | ||
85 | #define arith_t_type long | ||
86 | #define arith_t_fmt "%ld" | ||
87 | #define strto_arith_t strtol | ||
88 | #endif | ||
89 | |||
90 | typedef const char *(*arith_var_lookup_t)(const char *name); | ||
91 | typedef void (*arith_var_set_t)(const char *name, const char *val, int flags); | ||
92 | typedef char *(*arith_var_endofname_t)(const char *name); | ||
93 | typedef 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 | |||
99 | arith_t arith(const char *expr, int *perrcode, arith_eval_hooks_t*); | ||
100 | |||
101 | #endif | ||