From henrique.rodrigues at ist.utl.pt Mon Aug 1 12:16:25 2022 From: henrique.rodrigues at ist.utl.pt (Henrique Rodrigues) Date: Mon, 1 Aug 2022 13:16:25 +0100 Subject: [PATCH] ping: fix typo Message-ID: <85ed73d9-0083-e928-b491-0cfdbe431ddf@ist.utl.pt> Hi, Please finx attached a patch that fixes two typos on networking/ping.c. "recevied" should be "received". Thank you, Henrique Rodrigues -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-Fix-typo-on-ping.patch Type: text/x-patch Size: 1337 bytes Desc: not available URL: From vda.linux at googlemail.com Tue Aug 2 09:18:59 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 2 Aug 2022 11:18:59 +0200 Subject: [PATCHv3]ash: Add ifsfree to varunset and varvalue function to fix a buffer over-read In-Reply-To: References: Message-ID: Applied, thank you! On Mon, Jun 20, 2022 at 8:23 PM Alex Gorinson wrote: > > Due to a logic error in the ifsbreakup function in ash.c when a > heredoc and normal command is run one after the other by means of a > semi-colon, when the second command drops into ifsbreakup the command > will be evaluated with the ifslastp/ifsfirst struct that was set when > the heredoc was evaluated. This results in a buffer over-read that > can leak the program's heap, stack, and arena addresses which can be > used to beat ASLR. > > Steps to Reproduce: > First bug: > cmd args: ~/exampleDir/example> busybox ash > $ M='AAAAAAAAAAAAAAAAA' > $ q00(){ > $ <<000;echo > $ ${D?$M$M$M$M$M$M} > $ 000 > $ } > $ q00 should be echo'd out; this works with ash, busybox ash, and dash and > all options.> > > Patch: > Adding the following to ash.c will fix the bug. > ================================ > --- a/shell/ash.c > +++ b/shell/ash.c > @@ -7030,6 +7030,7 @@ > msg = umsg; > } > } > +ifsfree(); > ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail); > } > > @@ -7445,6 +7446,7 @@ > if (discard) > return -1; > +ifsfree(); > raise_error_syntax("bad substitution"); > } > ================================ From vda.linux at googlemail.com Tue Aug 2 12:27:48 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 2 Aug 2022 14:27:48 +0200 Subject: [PATCH v2] ash: Fix use-after-free on idx variable In-Reply-To: <20220601141720.1709-1-soeren@soeren-tempel.net> References: <20220601131153.30200-1-soeren@soeren-tempel.net> <20220601141720.1709-1-soeren@soeren-tempel.net> Message-ID: Applied, thank you. On Wed, Jun 1, 2022 at 4:18 PM wrote: > > From: S?ren Tempel > > Consider the following code from ash.c: > > STPUTC(*idx, expdest); > if (quotes && (unsigned char)*idx == CTLESC) { > > The idx variable points to a value in the stack string (as managed > by STPUTC). STPUTC may resize this stack string via realloc(3). If > this happens, the idx pointer needs to be updated. Otherwise, > dereferencing idx may result in a use-after free. > > The valgrind output for this edge case looks as follows: > > Invalid read of size 1 > at 0x113AD7: subevalvar (ash.c:7326) > by 0x112EC7: evalvar (ash.c:7674) > by 0x113219: argstr (ash.c:6891) > by 0x113D10: expandarg (ash.c:8098) > by 0x118989: evalcommand (ash.c:10377) > by 0x116744: evaltree (ash.c:9373) > by 0x1170DC: cmdloop (ash.c:13577) > by 0x1191E4: ash_main (ash.c:14756) > by 0x10CB3B: run_applet_no_and_exit (appletlib.c:967) > by 0x10CBCA: run_applet_and_exit (appletlib.c:986) > by 0x10CBCA: main (appletlib.c:1126) > Address 0x48b4099 is 857 bytes inside a block of size 2,736 free'd > at 0x48A6FC9: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) > by 0x125B03: xrealloc (xfuncs_printf.c:61) > by 0x10F9D2: growstackblock (ash.c:1736) > by 0x10FA4E: growstackstr (ash.c:1775) > by 0x10FA71: _STPUTC (ash.c:1816) > by 0x113A94: subevalvar (ash.c:7325) > by 0x112EC7: evalvar (ash.c:7674) > by 0x113219: argstr (ash.c:6891) > by 0x113D10: expandarg (ash.c:8098) > by 0x118989: evalcommand (ash.c:10377) > by 0x116744: evaltree (ash.c:9373) > by 0x1170DC: cmdloop (ash.c:13577) > Block was alloc'd at > at 0x48A26D5: malloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) > by 0x125AE9: xmalloc (xfuncs_printf.c:50) > by 0x10ED56: stalloc (ash.c:1622) > by 0x10F9FF: growstackblock (ash.c:1746) > by 0x10FB2A: growstackto (ash.c:1783) > by 0x10FB47: makestrspace (ash.c:1795) > by 0x10FDE7: memtodest (ash.c:6390) > by 0x10FE91: strtodest (ash.c:6417) > by 0x112CC5: varvalue (ash.c:7558) > by 0x112D80: evalvar (ash.c:7603) > by 0x113219: argstr (ash.c:6891) > by 0x113D10: expandarg (ash.c:8098) > > This patch fixes this issue by updating the pointers again via > the restart label if STPUTC re-sized the stack. This issue > has been reported to us at Alpine Linux downstream. > > Also: Move the second realloc-check inside the if statement > that follows so it isn't done twice if the condition evaluates > to false. > > See also: > > * https://gitlab.alpinelinux.org/alpine/aports/-/issues/13900 > * http://lists.busybox.net/pipermail/busybox/2022-April/089655.html > --- > Changes since v1: Don't check for restart twice in a row if > `quotes && (unsigned char)*idx == CTLESC` evaluates to false. > > shell/ash.c | 6 ++++-- > 1 file changed, 4 insertions(+), 2 deletions(-) > > diff --git a/shell/ash.c b/shell/ash.c > index ef4a47afe..cbc50eefe 100644 > --- a/shell/ash.c > +++ b/shell/ash.c > @@ -7323,13 +7323,15 @@ subevalvar(char *start, char *str, int strloc, > if (idx >= end) > break; > STPUTC(*idx, expdest); > + if (stackblock() != restart_detect) > + goto restart; > if (quotes && (unsigned char)*idx == CTLESC) { > idx++; > len++; > STPUTC(*idx, expdest); > + if (stackblock() != restart_detect) > + goto restart; > } > - if (stackblock() != restart_detect) > - goto restart; > idx++; > len++; > rmesc++; > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From vda.linux at googlemail.com Tue Aug 2 12:35:44 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 2 Aug 2022 14:35:44 +0200 Subject: [PATCH] ping: fix typo In-Reply-To: <85ed73d9-0083-e928-b491-0cfdbe431ddf@ist.utl.pt> References: <85ed73d9-0083-e928-b491-0cfdbe431ddf@ist.utl.pt> Message-ID: Applied, thank you On Mon, Aug 1, 2022 at 2:22 PM Henrique Rodrigues wrote: > > Hi, > > Please finx attached a patch that fixes two typos on networking/ping.c. > "recevied" should be "received". > > Thank you, > Henrique Rodrigues > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From vda.linux at googlemail.com Tue Aug 2 13:07:58 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 2 Aug 2022 15:07:58 +0200 Subject: [PATCH] vi: add 'ZQ' quitting command In-Reply-To: References: Message-ID: Applied, thank you On Wed, Jun 8, 2022 at 9:52 PM Grob Grobmann wrote: > > Busybox vi provides the 'ZZ' command to save and close > the similar 'ZQ' command just exits without saving > I propose this small patch to add that command. > > diff --git a/editors/vi.c b/editors/vi.c > index 3dbe5b471..2357caa5d 100644 > --- a/editors/vi.c > +++ b/editors/vi.c > @@ -4290,8 +4290,14 @@ static void do_cmd(int c) > goto dc_i; // start inserting > break; > case 'Z': // Z- if modified, {write}; exit > - // ZZ means to save file (if necessary), then exit > c1 = get_one_char(); > + // ZQ means to exit without saving > + if (c1 == 'Q') { > + editing=0; > + optind = cmdline_filecnt; > + break; > + } > + // ZZ means to save file (if necessary), then exit > if (c1 != 'Z') { > indicate_error(); > break; > > Cheers > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From vda.linux at googlemail.com Tue Aug 2 13:35:01 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 2 Aug 2022 15:35:01 +0200 Subject: udhcpc6 kernel listen mode is broken In-Reply-To: References: Message-ID: Fixed, thank you. Can you try current git? On Sat, Jun 4, 2022 at 2:58 AM Danomi Manchego wrote: > > Sorry all, I did not realize that there was a function called > d6_listen_socket() in d6_socket.c - so no new function is needed to > fix DHCPv6 Renew reply processing. The correct message ID is needed > and use of d6_listen_socket() rather than udhcp_listen_socket(). > > Regards, > Danomi - > > On Tue, May 10, 2022 at 9:34 PM Danomi Manchego > wrote: > > > > Hello, > > > > On April 1, I sent "udhcpc6 renew message copy/paste error" email > > about udhcpc6 sends the wrong message ID for Renew message due to > > copy/paste error from IPv4 dhcpc.c. (Was DHCPREQUEST, should be > > D6_MSG_RENEW.) After fixing that, I found that the Renew is sent > > correctly, and the DHCPv6 server replies, but udhcpc6 fails to get the > > reply. Because there is no reply, the lease does not get extended by > > Renew. I found that the issue is that the udhcp_listen_socket() in > > socket.c (used for kernel listen mode in d6_dhcpc.c) is somewhat > > hard-coded for IPv4. I was able to get kernel listen mode to work by > > adding a new function like this to socket.c and using it in d6_dhcp.c. > > My udhcp6_listen_socket() differs from udhcp_listen_socket() as > > follows: > > > > * Use PF_INET6 instead of PF_INET. > > > > * Set IPPROTO_IPV6 / IPV6_V6ONLY socket option rather than broadcast option. > > > > * Use `struct sockaddr_in6` instead of `struct sockaddr_in`. > > > > * Use AF_INET6 instead of AF_INET. > > > > (Maybe SOCK_CLOEXEC should also be set in *both* functions when > > calling xsocket since udhcpc/udhcpc6 invoke external udhcpc.script, > > but I did not try it.) > > > > My function is pasted below. > > > > int FAST_FUNC udhcp6_listen_socket(/*uint32_t ip,*/ int port, const char *inf) > > { > > int fd; > > struct sockaddr_in6 addr; > > char *colon; > > > > log2("opening listen socket on *:%d %s", port, inf); > > fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); > > > > setsockopt_reuseaddr(fd); > > > > if (setsockopt_1(fd, IPPROTO_IPV6, IPV6_V6ONLY) < 0) > > bb_simple_perror_msg_and_die("IPPROTO_IPV6"); > > > > /* SO_BINDTODEVICE doesn't work on ethernet aliases (ethN:M) */ > > colon = strrchr(inf, ':'); > > if (colon) > > *colon = '\0'; > > > > if (setsockopt_bindtodevice(fd, inf)) > > xfunc_die(); /* warning is already printed */ > > > > if (colon) > > *colon = ':'; > > > > memset(&addr, 0, sizeof(addr)); > > addr.sin6_family = AF_INET6; > > addr.sin6_port = htons(port); > > /* addr.sin_addr.s_addr = ip; - all-zeros is INADDR_ANY */ > > xbind(fd, (struct sockaddr *)&addr, sizeof(addr)); > > > > return fd; > > } > > > > Regards, > > Danomi - > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From soeren at soeren-tempel.net Tue Aug 2 15:58:15 2022 From: soeren at soeren-tempel.net (=?UTF-8?Q?S=C3=B6ren?= Tempel) Date: Tue, 02 Aug 2022 17:58:15 +0200 Subject: [PATCH] ash: fix use-after-free in bash pattern substitution In-Reply-To: <2Q7K46KHIOP6J.2M2KADVWIF3Q0@8pit.net> References: <20220129102232.25914-1-soeren@soeren-tempel.net> <2Q7K46KHIOP6J.2M2KADVWIF3Q0@8pit.net> Message-ID: <2BOIA616XR39Z.2TNV6SX4Y9CQ6@8pit.net> PING again :) S?ren Tempel wrote: > PING. > > This has been applied at Alpine Linux downstream for the past few months > and we didn't encounter into any issues with this particular patch so far. > > soeren at soeren-tempel.net wrote: > > From: S?ren Tempel > > > > At Alpine Linux downstream, we were made aware of a segmentation fault > > occurring during string replacement in BusyBox ash [0]. Further > > debugging revealed that the segmentation fault occurs due to a > > use-after-free in BusyBox's bash pattern substitution implementation. > > Specially, the problem is that the repl variable (pointing to the > > replacement string) points to a value in the stack string. However, when > > accessing the repl pointer in Line 7350 it is possible that the stack > > has been moved since the last repl assignment due to the STPUTC > > invocations in Line 7317 and 7321 (since STPUTC may grow the stack via > > realloc(3)). > > > > For this reason, the code in Line 7350 may access an unmapped memory > > region and therefore causes a segmentation fault if prior STPUTC > > invocations moved the stack via realloc(3). The valgrind output > > for this edge case looks as follows: > > > > Invalid read of size 1 > > at 0x15D8DD: subevalvar (ash.c:7350) > > by 0x15DC43: evalvar (ash.c:7666) > > by 0x15B717: argstr (ash.c:6893) > > by 0x15BAEC: expandarg (ash.c:8090) > > by 0x15F4CC: evalcommand (ash.c:10429) > > by 0x15B26C: evaltree (ash.c:9365) > > by 0x15E4FC: cmdloop (ash.c:13569) > > by 0x15FD8B: ash_main (ash.c:14748) > > by 0x115BF2: run_applet_no_and_exit (appletlib.c:967) > > by 0x115F16: run_applet_and_exit (appletlib.c:986) > > by 0x115EF9: busybox_main (appletlib.c:917) > > by 0x115EF9: run_applet_and_exit (appletlib.c:979) > > by 0x115F8F: main (appletlib.c:1126) > > Address 0x48b8646 is 2,054 bytes inside a block of size 4,776 free'd > > at 0x48A6FC9: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) > > by 0x116E86: xrealloc (xfuncs_printf.c:61) > > by 0x1565DB: growstackblock (ash.c:1736) > > by 0x156EF7: growstackstr (ash.c:1775) > > by 0x156F1A: _STPUTC (ash.c:1816) > > by 0x15D843: subevalvar (ash.c:7317) > > by 0x15DC43: evalvar (ash.c:7666) > > by 0x15B717: argstr (ash.c:6893) > > by 0x15BAEC: expandarg (ash.c:8090) > > by 0x15F4CC: evalcommand (ash.c:10429) > > by 0x15B26C: evaltree (ash.c:9365) > > by 0x15E4FC: cmdloop (ash.c:13569) > > > > A testcase for reproducing this edge case is provided in the downstream > > bug report [1]. This commit fixes the issue by reconstructing the repl > > pointer relative to stackblock() via strloc and slash_pos. > > > > [0]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13469 > > [1]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13469#note_210530 > > > > Signed-off-by: S?ren Tempel > > --- > > Discussion: I am not familiar with the ash code base. For this reason, > > it is presently unclear to me if there is a path where slash_pos < 0, > > STPUTC is invoked, and repl points to the stack. If so, handling for the > > case that slash_pos < 0 also needs to be added to the proposed path. > > Furthermore, I haven't tested this patch extensively so please review > > with extra care. > > > > shell/ash.c | 6 ++++++ > > 1 file changed, 6 insertions(+) > > > > diff --git a/shell/ash.c b/shell/ash.c > > index 55df54bd0..24f9a8270 100644 > > --- a/shell/ash.c > > +++ b/shell/ash.c > > @@ -7346,6 +7346,12 @@ subevalvar(char *start, char *str, int strloc, > > idx = loc; > > } > > > > + // The STPUTC invocations above may resize and move the > > + // stack via realloc(3). Since repl is a pointer into the > > + // stack, we need to reconstruct it relative to stackblock(). > > + if (slash_pos >= 0) > > + repl = (char *)stackblock() + strloc + slash_pos + 1; > > + > > //bb_error_msg("repl:'%s'", repl); > > for (loc = (char*)repl; *loc; loc++) { > > char *restart_detect = stackblock(); > > _______________________________________________ > > busybox mailing list > > busybox at busybox.net > > http://lists.busybox.net/mailman/listinfo/busybox > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From vda.linux at googlemail.com Tue Aug 2 16:28:47 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 2 Aug 2022 18:28:47 +0200 Subject: [PATCH] ash: fix use-after-free in bash pattern substitution In-Reply-To: <2BOIA616XR39Z.2TNV6SX4Y9CQ6@8pit.net> References: <20220129102232.25914-1-soeren@soeren-tempel.net> <2Q7K46KHIOP6J.2M2KADVWIF3Q0@8pit.net> <2BOIA616XR39Z.2TNV6SX4Y9CQ6@8pit.net> Message-ID: Applied, thank you. On Tue, Aug 2, 2022 at 5:58 PM S?ren Tempel wrote: > > PING again :) > > S?ren Tempel wrote: > > PING. > > > > This has been applied at Alpine Linux downstream for the past few months > > and we didn't encounter into any issues with this particular patch so far. > > > > soeren at soeren-tempel.net wrote: > > > From: S?ren Tempel > > > > > > At Alpine Linux downstream, we were made aware of a segmentation fault > > > occurring during string replacement in BusyBox ash [0]. Further > > > debugging revealed that the segmentation fault occurs due to a > > > use-after-free in BusyBox's bash pattern substitution implementation. > > > Specially, the problem is that the repl variable (pointing to the > > > replacement string) points to a value in the stack string. However, when > > > accessing the repl pointer in Line 7350 it is possible that the stack > > > has been moved since the last repl assignment due to the STPUTC > > > invocations in Line 7317 and 7321 (since STPUTC may grow the stack via > > > realloc(3)). > > > > > > For this reason, the code in Line 7350 may access an unmapped memory > > > region and therefore causes a segmentation fault if prior STPUTC > > > invocations moved the stack via realloc(3). The valgrind output > > > for this edge case looks as follows: > > > > > > Invalid read of size 1 > > > at 0x15D8DD: subevalvar (ash.c:7350) > > > by 0x15DC43: evalvar (ash.c:7666) > > > by 0x15B717: argstr (ash.c:6893) > > > by 0x15BAEC: expandarg (ash.c:8090) > > > by 0x15F4CC: evalcommand (ash.c:10429) > > > by 0x15B26C: evaltree (ash.c:9365) > > > by 0x15E4FC: cmdloop (ash.c:13569) > > > by 0x15FD8B: ash_main (ash.c:14748) > > > by 0x115BF2: run_applet_no_and_exit (appletlib.c:967) > > > by 0x115F16: run_applet_and_exit (appletlib.c:986) > > > by 0x115EF9: busybox_main (appletlib.c:917) > > > by 0x115EF9: run_applet_and_exit (appletlib.c:979) > > > by 0x115F8F: main (appletlib.c:1126) > > > Address 0x48b8646 is 2,054 bytes inside a block of size 4,776 free'd > > > at 0x48A6FC9: realloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) > > > by 0x116E86: xrealloc (xfuncs_printf.c:61) > > > by 0x1565DB: growstackblock (ash.c:1736) > > > by 0x156EF7: growstackstr (ash.c:1775) > > > by 0x156F1A: _STPUTC (ash.c:1816) > > > by 0x15D843: subevalvar (ash.c:7317) > > > by 0x15DC43: evalvar (ash.c:7666) > > > by 0x15B717: argstr (ash.c:6893) > > > by 0x15BAEC: expandarg (ash.c:8090) > > > by 0x15F4CC: evalcommand (ash.c:10429) > > > by 0x15B26C: evaltree (ash.c:9365) > > > by 0x15E4FC: cmdloop (ash.c:13569) > > > > > > A testcase for reproducing this edge case is provided in the downstream > > > bug report [1]. This commit fixes the issue by reconstructing the repl > > > pointer relative to stackblock() via strloc and slash_pos. > > > > > > [0]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13469 > > > [1]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13469#note_210530 > > > > > > Signed-off-by: S?ren Tempel > > > --- > > > Discussion: I am not familiar with the ash code base. For this reason, > > > it is presently unclear to me if there is a path where slash_pos < 0, > > > STPUTC is invoked, and repl points to the stack. If so, handling for the > > > case that slash_pos < 0 also needs to be added to the proposed path. > > > Furthermore, I haven't tested this patch extensively so please review > > > with extra care. > > > > > > shell/ash.c | 6 ++++++ > > > 1 file changed, 6 insertions(+) > > > > > > diff --git a/shell/ash.c b/shell/ash.c > > > index 55df54bd0..24f9a8270 100644 > > > --- a/shell/ash.c > > > +++ b/shell/ash.c > > > @@ -7346,6 +7346,12 @@ subevalvar(char *start, char *str, int strloc, > > > idx = loc; > > > } > > > > > > + // The STPUTC invocations above may resize and move the > > > + // stack via realloc(3). Since repl is a pointer into the > > > + // stack, we need to reconstruct it relative to stackblock(). > > > + if (slash_pos >= 0) > > > + repl = (char *)stackblock() + strloc + slash_pos + 1; > > > + > > > //bb_error_msg("repl:'%s'", repl); > > > for (loc = (char*)repl; *loc; loc++) { > > > char *restart_detect = stackblock(); > > > _______________________________________________ > > > busybox mailing list > > > busybox at busybox.net > > > http://lists.busybox.net/mailman/listinfo/busybox > > _______________________________________________ > > busybox mailing list > > busybox at busybox.net > > http://lists.busybox.net/mailman/listinfo/busybox > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From soeren at soeren-tempel.net Wed Aug 3 06:30:26 2022 From: soeren at soeren-tempel.net (=?UTF-8?Q?S=C3=B6ren?= Tempel) Date: Wed, 03 Aug 2022 08:30:26 +0200 Subject: [PATCH] ed: don't use memcpy with overlapping memory regions In-Reply-To: <21PHGKH89PGCJ.3P3652PG9R9MG@8pit.net> References: <20220208192930.15089-1-soeren@soeren-tempel.net> <21PHGKH89PGCJ.3P3652PG9R9MG@8pit.net> Message-ID: <34KG665IX3UJS.3FW56Z09RA3AK@8pit.net> Pinging again as this is still unfixed and the proposed fix is rather trivial. S?ren Tempel wrote: > Ping. > > soeren at soeren-tempel.net wrote: > > From: S?ren Tempel > > > > The memcpy invocations in the subCommand function, modified by this > > commit, previously used memcpy with overlapping memory regions. This is > > undefined behavior. On Alpine Linux, it causes BusyBox ed to crash since > > we compile BusyBox with -D_FORTIFY_SOURCE=2 and our fortify-headers > > implementation catches this source of undefined behavior [0]. The issue > > can only be triggered if the replacement string is the same size or > > shorter than the old string. > > > > Looking at the code, it seems to me that a memmove(3) is what was > > actually intended here, this commit modifies the code accordingly. > > > > [0]: https://gitlab.alpinelinux.org/alpine/aports/-/issues/13504 > > --- > > editors/ed.c | 2 +- > > 1 file changed, 1 insertion(+), 1 deletion(-) > > > > diff --git a/editors/ed.c b/editors/ed.c > > index 209ce9942..4a84f7433 100644 > > --- a/editors/ed.c > > +++ b/editors/ed.c > > @@ -720,7 +720,7 @@ static void subCommand(const char *cmd, int num1, int num2) > > if (deltaLen <= 0) { > > memcpy(&lp->data[offset], newStr, newLen); > > if (deltaLen) { > > - memcpy(&lp->data[offset + newLen], > > + memmove(&lp->data[offset + newLen], > > &lp->data[offset + oldLen], > > lp->len - offset - oldLen); > > > > _______________________________________________ > > busybox mailing list > > busybox at busybox.net > > http://lists.busybox.net/mailman/listinfo/busybox > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From steffen at sdaoden.eu Fri Aug 5 17:00:32 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 05 Aug 2022 19:00:32 +0200 Subject: [PATCH] shell: exchange Dijkstra $(( )) evaluator.. In-Reply-To: References: Message-ID: <20220805170032.jtZku%steffen@sdaoden.eu> Here is the test script. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) -------------- next part -------------- command -v shopt && shopt -s expand_aliases alias p=printf;alias e=echo;alias s=export # s I=10 J=33 # e '== BASE' e "<$(())>" e "<$(( ))>" e "<$((1))>" e "<$((0))>" e "<$((0x0))>" e "<$((0X0))>" e "<$((000))>" e "<$((000000000000001))>" e "<$((2#00000000000000000000000000000000000001))>" e "<$((0X00000000000000000000000000000000000000000001))>" e "<$((999999999999999999999999999999999999999999999))>" e "<$(( 10 ))>" e "<$((9191919191919))>" e "<$((0xD))>" e "<$((013))>" e "<$((32#VV))>" e "<$((36#ZZ))>" e "<$((36#zz))>" e "<$(( 64#zzZZ ))>" e "<$((64#ZZzz))>" e "<$((I))>" e "<$((J))>" e "<$(( I ))>" e "<$(( J ))>" e "<$(( (1) ))>" e "<$((((1))))>" e "<$(((((1)))))>" e "<$(( (J) ))>" e "<$((((J))))>" e "<$(((((J)))))>" e "<$(( ( ( ( J ) ) ) ))>" # e '== UNA PLUS/MINUS' e "<$((+0))>" e "<$(( + 0 ))>" e "<$(( +1))>" e "<$((+ 1 ))>" e "<$(( + 4221 ))>" e "<$(( +0x4221 ))>" e "<$(( + 64#ZZzz ))>" e "<$(( +64#ZZzz ))>" e "<$((+ (1) ))>" e "<$((+((1))))>" e "<$((+(((1)))))>" e "<$((-0))>" e "<$(( - 0 ))>" e "<$(( -1))>" e "<$((- 1 ))>" e "<$(( - 4221 ))>" e "<$(( -0x4221 ))>" e "<$(( - 64#ZZzz ))>" e "<$(( -64#ZZzz ))>" e "<$((- (1) ))>" e "<$((-((1))))>" e "<$((-(((1)))))>" e "<$((+ -(1) ))>" e "<$((+(-(-1))))>" e "<$((+(-(-(-1)))))>" # e '== UNA !' e "<$((!0))>" e "<$((! 00000000))>" e "<$((!1))>" e "<$((! 0x00001))>" e "<$((! - 0))>" e "<$((!-1))>" # e '== UNA ~' e "<$((~0))>" e "<$((~ 00000000))>" e "<$((~1))>" e "<$((~ 0x00001))>" e "<$((~ 64#zz))>" e "<$((~-1))>" e "<$((~ - 1))>" e "<$((~-0))>" e "<$((~ - 0))>" e "<$((~(-0)))>" e "<$((~((- 0))))>" # e '== BIN +' e "<$((0+0))>" e "<$(( 0 + 0 ))>" e "<$((0+1))>" e "<$(( 0 + 1 ))>" e "<$((1+0))>" e "<$(( 1 + 0 ))>" e "<$((1+1))>" e "<$(( 1 + 1 ))>" e "<$(( (1 + 1) ))>" e "<$(((((((-1)))) + (((-1))))))>" e "<$((1111+2222))>" e "<$((2222+1111))>" e "<$(( +0x10 + +0x11 ))>" e "<$(( -0x10 + -0x11 ))>" e "<$(( -0x10 + -0x11 ))>" e "<$(( +64#10 + -64#11 ))>" e "<$(( +0x11 + +0x10 ))>" e "<$(( -0x11 + -0x10 ))>" e "<$(( -0x11 + -0x10 ))>" e "<$(( +64#11 + -64#10 ))>" e "<$((0x8000000000000000+-1))>" e "<$((0x8000000000000000+1))>" e "<$((0x7FFFFFFFFFFFFFFF+-1))>" e "<$((0x7FFFFFFFFFFFFFFF+1))>" e "<$((0xFFFFFFFFFFFFFFFF+-1))>" e "<$((0xFFFFFFFFFFFFFFFF+1))>" e "<$((0x8000000000000000+-11))>" e "<$((0x8000000000000000+11))>" e "<$((0x7FFFFFFFFFFFFFFF+-11))>" e "<$((0x7FFFFFFFFFFFFFFF+11))>" e "<$((0xFFFFFFFFFFFFFFFF+-11))>" e "<$((0xFFFFFFFFFFFFFFFF+11))>" # e '== BIN -' e "<$((0-0))>" e "<$(( 0 - 0 ))>" e "<$((0-1))>" e "<$(( 0 - 1 ))>" e "<$((1-0))>" e "<$(( 1 - 0 ))>" e "<$((1-1))>" e "<$(( 1 - 1 ))>" e "<$(( (1 - 1) ))>" e "<$(((((((+1)))) - (((+1))))))>" e "<$((1111-2222))>" e "<$((2222-1111))>" e "<$(( +0x10 - +0x11 ))>" e "<$(( -0x10 - -0x11 ))>" e "<$(( -0x10 - -0x11 ))>" e "<$(( +64#10 - -64#11 ))>" e "<$(( +0x11 - +0x10 ))>" e "<$(( -0x11 - -0x10 ))>" e "<$(( -0x11 - -0x10 ))>" e "<$(( +64#11 - -64#10 ))>" e "<$((0x8000000000000000--1))>" e "<$((0x8000000000000000-1))>" e "<$((0x7FFFFFFFFFFFFFFF--1))>" e "<$((0x7FFFFFFFFFFFFFFF-1))>" e "<$((0xFFFFFFFFFFFFFFFF--1))>" e "<$((0xFFFFFFFFFFFFFFFF-1))>" e "<$((0x8000000000000000--11))>" e "<$((0x8000000000000000-11))>" e "<$((0x7FFFFFFFFFFFFFFF--11))>" e "<$((0x7FFFFFFFFFFFFFFF-11))>" e "<$((0xFFFFFFFFFFFFFFFF--11))>" e "<$((0xFFFFFFFFFFFFFFFF-11))>" # e '== BIN *' e "<$((0*0))>" e "<$(( 0 * 0 ))>" e "<$((0*1))>" e "<$(( 0 * 1 ))>" e "<$((1*0))>" e "<$(( 1 * 0 ))>" e "<$((1*1))>" e "<$(( 1 * 1 ))>" e "<$((1111*2222))>" e "<$((2222*1111))>" e "<$(( +0x10 * +0x11 ))>" e "<$(( -0x10 * -0x11 ))>" e "<$(( -0x10 * -0x11 ))>" e "<$(( +64#10 * -64#11 ))>" e "<$(( +0x11 * +0x10 ))>" e "<$(( -0x11 * -0x10 ))>" e "<$(( -0x11 * -0x10 ))>" e "<$(( +64#11 * -64#10 ))>" e "<$((0x8000000000000000*-1))>" e "<$((0x8000000000000000*1))>" e "<$((0x7FFFFFFFFFFFFFFF*-1))>" e "<$((0x7FFFFFFFFFFFFFFF*1))>" e "<$((0xFFFFFFFFFFFFFFFF*-1))>" e "<$((0xFFFFFFFFFFFFFFFF*1))>" e "<$((0x8000000000000000*-11))>" e "<$((0x8000000000000000*11))>" e "<$((0x7FFFFFFFFFFFFFFF*-11))>" e "<$((0x7FFFFFFFFFFFFFFF*11))>" e "<$((0xFFFFFFFFFFFFFFFF*-11))>" e "<$((0xFFFFFFFFFFFFFFFF*11))>" # e '== BIN /' e "<$(( 0 / 1 ))>" e "<$((1/1))>" e "<$(( 1 / 1 ))>" e "<$((1111/2222))>" e "<$((2222/1111))>" e "<$(( +0x10 / +0x11 ))>" e "<$(( -0x10 / -0x11 ))>" e "<$(( -0x10 / -0x11 ))>" e "<$(( +64#10 / -64#11 ))>" e "<$(( +0x11 / +0x10 ))>" e "<$(( -0x11 / -0x10 ))>" e "<$(( -0x11 / -0x10 ))>" e "<$(( +64#11 / -64#10 ))>" e "<$((2/1))>" e "<$((3/1))>" e "<$((3/2))>" e "<$((3/3))>" e "<$((3/4))>" e "<$((-1/4))>" e "<$((0x8000000000000000/-1))>" e "<$((0x8000000000000000/1))>" e "<$((0x7FFFFFFFFFFFFFFF/-1))>" e "<$((0x7FFFFFFFFFFFFFFF/1))>" e "<$((0xFFFFFFFFFFFFFFFF/-1))>" e "<$((0xFFFFFFFFFFFFFFFF/1))>" e "<$((0x8000000000000000/-11))>" e "<$((0x8000000000000000/11))>" e "<$((0x7FFFFFFFFFFFFFFF/-11))>" e "<$((0x7FFFFFFFFFFFFFFF/11))>" e "<$((0xFFFFFFFFFFFFFFFF/-11))>" e "<$((0xFFFFFFFFFFFFFFFF/11))>" # e '== BIN %' e "<$(( 0 % 1 ))>" e "<$((1%1))>" e "<$(( 1 % 1 ))>" e "<$((1111%2222))>" e "<$((2222%1111))>" e "<$(( +0x10 % +0x11 ))>" e "<$(( -0x10 % -0x11 ))>" e "<$(( -0x10 % -0x11 ))>" e "<$(( +64#10 % -64#11 ))>" e "<$(( +0x11 % +0x10 ))>" e "<$(( -0x11 % -0x10 ))>" e "<$(( -0x11 % -0x10 ))>" e "<$(( +64#11 % -64#10 ))>" e "<$((2%1))>" e "<$((3%1))>" e "<$((3%2))>" e "<$((3%3))>" e "<$((3%4))>" e "<$((-1%4))>" e "<$((0x8000000000000000%-1))>" e "<$((0x8000000000000000%1))>" e "<$((0x7FFFFFFFFFFFFFFF%-1))>" e "<$((0x7FFFFFFFFFFFFFFF%1))>" e "<$((0xFFFFFFFFFFFFFFFF%-1))>" e "<$((0xFFFFFFFFFFFFFFFF%1))>" e "<$((0x8000000000000000%-11))>" e "<$((0x8000000000000000%11))>" e "<$((0x7FFFFFFFFFFFFFFF%-11))>" e "<$((0x7FFFFFFFFFFFFFFF%11))>" e "<$((0xFFFFFFFFFFFFFFFF%-11))>" e "<$((0xFFFFFFFFFFFFFFFF%11))>" # e '== BIN <<' e "<$((0<<0))>" e "<$(( 0 << 0 ))>" e "<$((0<<1))>" e "<$(( 0 << 1 ))>" e "<$((1<<0))>" e "<$(( 1 << 0 ))>" e "<$((1<<1))>" e "<$(( 1 << 1 ))>" e "<$((1111<<2222))>" e "<$((2222<<1111))>" e "<$(( +0x10 << +0x11 ))>" e "<$(( -0x10 << -0x11 ))>" e "<$(( -0x10 << -0x11 ))>" e "<$(( +64#10 << -64#11 ))>" e "<$(( +0x11 << +0x10 ))>" e "<$(( -0x11 << -0x10 ))>" e "<$(( -0x11 << -0x10 ))>" e "<$(( +64#11 << -64#10 ))>" e "<$(( +64 << +1024 ))>" e "<$((0x8000000000000000<<-1))>" e "<$((0x8000000000000000<<1))>" e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" e "<$((0x7FFFFFFFFFFFFFFF<<1))>" e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" e "<$((0xFFFFFFFFFFFFFFFF<<1))>" e "<$((0x8000000000000000<<-11))>" e "<$((0x8000000000000000<<11))>" e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" e "<$((0x7FFFFFFFFFFFFFFF<<11))>" e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" e "<$((0xFFFFFFFFFFFFFFFF<<11))>" # e '== BIN >>' e "<$((0>>0))>" e "<$(( 0 >> 0 ))>" e "<$((0>>1))>" e "<$(( 0 >> 1 ))>" e "<$((1>>0))>" e "<$(( 1 >> 0 ))>" e "<$((1>>1))>" e "<$(( 1 >> 1 ))>" e "<$((1>>>1))>" e "<$(( 1 >>> 1 ))>" e "<$((1111>>2222))>" e "<$((2222>>1111))>" e "<$((1111>>>2222))>" e "<$((2222>>>1111))>" e "<$(( +0x10 >> +0x11 ))>" e "<$(( -0x10 >> -0x11 ))>" e "<$(( -0x10 >> -0x11 ))>" e "<$(( -0x10 >>> -0x11 ))>" e "<$(( +64#10 >> -64#11 ))>" e "<$(( +0x11 >> +0x10 ))>" e "<$(( -0x11 >> -0x10 ))>" e "<$(( -0x11 >> -0x10 ))>" e "<$(( +64#11 >> -64#10 ))>" e "<$(( +64 >> +1024 ))>" e "<$((0x8000000000000000>>-1))>" e "<$((0x8000000000000000>>1))>" e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" e "<$((0x7FFFFFFFFFFFFFFF>>1))>" e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" e "<$((0xFFFFFFFFFFFFFFFF>>1))>" e "<$((0x8000000000000000>>-11))>" e "<$((0x8000000000000000>>11))>" e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" e "<$((0x7FFFFFFFFFFFFFFF>>11))>" e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" e "<$((0xFFFFFFFFFFFFFFFF>>11))>" e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" # e '== BIN **' e "<$((0**1))>" e "<$((2**1))>" e "<$((2**2))>" e "<$((2**3))>" e "<$((2**4))>" e "<$((10**4))>" e "<$((10**10))>" e "<$((10**5+5))>" e "<$((10**(5+5)))>" # e '== LOG OR' e "<$((0||0))>" e "<$(( 000 || 0X0 ))>" e "<$((01 || 64#1))>" e "<$((01 || 64#1))>" e "<$((0x1234 || 4660))>" e "<$((0x1234 || 011064))>" s I=33 J=33;e "<$((I||J))>" s I=33 J=33;e "<$(( I || J ))>" e "<$((0||1))>" e "<$((0||0000000000000000000000001))>" e "<$((1||2))>" e "<$((0x1234 || 04660))>" e "<$((0x1234 || 0x11064))>" s I=10 J=33;e "<$((I||J))>" s I=-10 J=-33;e "<$((I||J))>" s I=-33 J=-33;e "<$((I||J))>" s I=0 J=-33;e "<$((I||J))>" s I=33 J=0;e "<$((I||J))>" # e '== LOG AND' e "<$((0&&0))>" e "<$(( 000 && 0X0 ))>" e "<$((01 && 64#1))>" e "<$((01 && 64#1))>" e "<$((0x1234 && 4660))>" e "<$((0x1234 && 011064))>" s I=33 J=33;e "<$((I&&J))>" s I=33 J=33;e "<$(( I && J ))>" e "<$((0&&1))>" e "<$((0&&0000000000000000000000001))>" e "<$((1&&2))>" e "<$((0x1234 && 04660))>" e "<$((0x1234 && 0x11064))>" s I=10 J=33;e "<$((I&&J))>" s I=-10 J=-33;e "<$((I&&J))>" s I=-33 J=-33;e "<$((I&&J))>" s I=0 J=-33;e "<$((I&&J))>" s I=33 J=0;e "<$((I&&J))>" # e '== BIN BIT_OR' e "<$((0|0))>" e "<$(( 0 | 0 ))>" e "<$((0|1))>" e "<$(( 0 | 1 ))>" e "<$((1|0))>" e "<$(( 1 | 0 ))>" e "<$((1|1))>" e "<$(( 1 | 1 ))>" e "<$((1111|2222))>" e "<$((2222|1111))>" e "<$(( +0x10 | +0x11 ))>" e "<$(( -0x10 | -0x11 ))>" e "<$(( -0x10 | -0x11 ))>" e "<$(( +64#10 | -64#11 ))>" e "<$(( +0x11 | +0x10 ))>" e "<$(( -0x11 | -0x10 ))>" e "<$(( -0x11 | -0x10 ))>" e "<$(( +64#11 | -64#10 ))>" e "<$(( +64 | +1024 ))>" e "<$((0x8000000000000000|-1))>" e "<$((0x8000000000000000|1))>" e "<$((0x7FFFFFFFFFFFFFFF|-1))>" e "<$((0x7FFFFFFFFFFFFFFF|1))>" e "<$((0xFFFFFFFFFFFFFFFF|-1))>" e "<$((0xFFFFFFFFFFFFFFFF|1))>" e "<$((0x8000000000000000|-11))>" e "<$((0x8000000000000000|11))>" e "<$((0x7FFFFFFFFFFFFFFF|-11))>" e "<$((0x7FFFFFFFFFFFFFFF|11))>" e "<$((0xFFFFFFFFFFFFFFFF|-11))>" e "<$((0xFFFFFFFFFFFFFFFF|11))>" # e '== BIN BIT_XOR' e "<$((0^0))>" e "<$(( 0 ^ 0 ))>" e "<$((0^1))>" e "<$(( 0 ^ 1 ))>" e "<$((1^0))>" e "<$(( 1 ^ 0 ))>" e "<$((1^1))>" e "<$(( 1 ^ 1 ))>" e "<$((1111^2222))>" e "<$((2222^1111))>" e "<$(( +0x10 ^ +0x11 ))>" e "<$(( -0x10 ^ -0x11 ))>" e "<$(( -0x10 ^ -0x11 ))>" e "<$(( +64#10 ^ -64#11 ))>" e "<$(( +0x11 ^ +0x10 ))>" e "<$(( -0x11 ^ -0x10 ))>" e "<$(( -0x11 ^ -0x10 ))>" e "<$(( +64#11 ^ -64#10 ))>" e "<$(( +64 ^ +1024 ))>" e "<$((0x8000000000000000^-1))>" e "<$((0x8000000000000000^1))>" e "<$((0x7FFFFFFFFFFFFFFF^-1))>" e "<$((0x7FFFFFFFFFFFFFFF^1))>" e "<$((0xFFFFFFFFFFFFFFFF^-1))>" e "<$((0xFFFFFFFFFFFFFFFF^1))>" e "<$((0x8000000000000000^-11))>" e "<$((0x8000000000000000^11))>" e "<$((0x7FFFFFFFFFFFFFFF^-11))>" e "<$((0x7FFFFFFFFFFFFFFF^11))>" e "<$((0xFFFFFFFFFFFFFFFF^-11))>" e "<$((0xFFFFFFFFFFFFFFFF^11))>" # e '== BIN BIT_AND' e "<$((0&0))>" e "<$(( 0 & 0 ))>" e "<$((0&1))>" e "<$(( 0 & 1 ))>" e "<$((1&0))>" e "<$(( 1 & 0 ))>" e "<$((1&1))>" e "<$(( 1 & 1 ))>" e "<$((1111&2222))>" e "<$((2222&1111))>" e "<$(( +0x10 & +0x11 ))>" e "<$(( -0x10 & -0x11 ))>" e "<$(( -0x10 & -0x11 ))>" e "<$(( +64#10 & -64#11 ))>" e "<$(( +0x11 & +0x10 ))>" e "<$(( -0x11 & -0x10 ))>" e "<$(( -0x11 & -0x10 ))>" e "<$(( +64#11 & -64#10 ))>" e "<$(( +64 & +1024 ))>" e "<$((0x8000000000000000&-1))>" e "<$((0x8000000000000000&1))>" e "<$((0x7FFFFFFFFFFFFFFF&-1))>" e "<$((0x7FFFFFFFFFFFFFFF&1))>" e "<$((0xFFFFFFFFFFFFFFFF&-1))>" e "<$((0xFFFFFFFFFFFFFFFF&1))>" e "<$((0x8000000000000000&-11))>" e "<$((0x8000000000000000&11))>" e "<$((0x7FFFFFFFFFFFFFFF&-11))>" e "<$((0x7FFFFFFFFFFFFFFF&11))>" e "<$((0xFFFFFFFFFFFFFFFF&-11))>" e "<$((0xFFFFFFFFFFFFFFFF&11))>" # e '== BIN EQ' e "<$((0==0))>" e "<$(( 000 == 0X0 ))>" e "<$((01 == 64#1))>" e "<$((01 == 64#1))>" e "<$((0x1234 == 4660))>" e "<$((0x1234 == 011064))>" s I=33 J=33;e "<$((I==J))>" s I=33 J=33;e "<$(( I == J ))>" e "<$((0==1))>" e "<$((0==0000000000000000000000001))>" e "<$((1==2))>" e "<$((0x1234 == 04660))>" e "<$((0x1234 == 0x11064))>" s I=10 J=33;e "<$((I==J))>" s I=-10 J=-33;e "<$((I==J))>" s I=-33 J=-33;e "<$((I==J))>" # e '== BIN NE' e "<$((0!=0))>" e "<$(( 000 != 0X0 ))>" e "<$((01 != 64#1))>" e "<$((01 != 64#1))>" e "<$((0x1234 != 4660))>" e "<$((0x1234 != 011064))>" s I=33 J=33;e "<$((I!=J))>" s I=33 J=33;e "<$(( I != J ))>" e "<$((0!=1))>" e "<$((0!=0000000000000000000000001))>" e "<$((1!=2))>" e "<$((0x1234 != 04660))>" e "<$((0x1234 != 0x11064))>" s I=10 J=33;e "<$((I!=J))>" s I=-10 J=-33;e "<$((I!=J))>" s I=-33 J=-33;e "<$((I!=J))>" # e '== BIN LE' e "<$((0<=0))>" e "<$(( 000 <= 0X0 ))>" e "<$((01 <= 64#1))>" e "<$((01 <= 64#2))>" e "<$((02 <= 64#1))>" e "<$((0x1234 <= 4660))>" e "<$((0x1234 <= 011064))>" e "<$((0x1233 <= 011064))>" e "<$((0x1235 <= 011064))>" s I=33 J=33;e "<$((I<=J))>" s I=33 J=33;e "<$((I<=J))>" s I=32 J=33;e "<$((I<=J))>" s I=34 J=33;e "<$((I<=J))>" s I=-33 J=-33;e "<$((I<=J))>" s I=-33 J=-33;e "<$((I<=J))>" s I=-32 J=-33;e "<$((I<=J))>" s I=-34 J=-33;e "<$((I<=J))>" # e '== BIN GE' e "<$((0>=0))>" e "<$(( 000 >= 0X0 ))>" e "<$((01 >= 64#1))>" e "<$((01 >= 64#2))>" e "<$((02 >= 64#1))>" e "<$((0x1234 >= 4660))>" e "<$((0x1234 >= 011064))>" e "<$((0x1233 >= 011064))>" e "<$((0x1235 >= 011064))>" s I=33 J=33;e "<$((I>=J))>" s I=33 J=33;e "<$((I>=J))>" s I=32 J=33;e "<$((I>=J))>" s I=34 J=33;e "<$((I>=J))>" s I=-33 J=-33;e "<$((I>=J))>" s I=-33 J=-33;e "<$((I>=J))>" s I=-32 J=-33;e "<$((I>=J))>" s I=-34 J=-33;e "<$((I>=J))>" # e '== BIN LT' e "<$((0<0))>" e "<$(( 000 < 0X0 ))>" e "<$((01 < 64#1))>" e "<$((01 < 64#2))>" e "<$((02 < 64#1))>" e "<$((0x1234 < 4660))>" e "<$((0x1234 < 011064))>" e "<$((0x1233 < 011064))>" e "<$((0x1235 < 011064))>" s I=33 J=33;e "<$((I" s I=33 J=33;e "<$((I" s I=32 J=33;e "<$((I" s I=34 J=33;e "<$((I" s I=-33 J=-33;e "<$((I" s I=-33 J=-33;e "<$((I" s I=-32 J=-33;e "<$((I" s I=-34 J=-33;e "<$((I" # e '== BIN GT' e "<$((0>0))>" e "<$(( 000 > 0X0 ))>" e "<$((01 > 64#1))>" e "<$((01 > 64#2))>" e "<$((02 > 64#1))>" e "<$((0x1234 > 4660))>" e "<$((0x1234 > 011064))>" e "<$((0x1233 > 011064))>" e "<$((0x1235 > 011064))>" s I=33 J=33;e "<$((I>J))>" s I=33 J=33;e "<$((I>J))>" s I=32 J=33;e "<$((I>J))>" s I=34 J=33;e "<$((I>J))>" s I=-33 J=-33;e "<$((I>J))>" s I=-33 J=-33;e "<$((I>J))>" s I=-32 J=-33;e "<$((I>J))>" s I=-34 J=-33;e "<$((I>J))>" # # COMMA below # e '== PRECEDENCE I' e "<$(( 1 + 2 + 3 ))>" e "<$(( 1 - 2 + 3 ))>" e "<$(( 3 - 2 - 1 ))>" e "<$(( 3 - 2 + 1 ))>" e "<$(( - 2 + 1 ))>" e "<$(( 2 + -1 ))>" e "<$(( ! 2 + 1 ))>" e "<$(( 2 + !1 ))>" e "<$(( 3 * 2 + 2 ))>" e "<$(( 3 + 2 * 2 ))>" e "<$(( 3 * 2 * 2 ))>" e "<$(( 9 / 3 + 2 ))>" e "<$(( 9 + 3 / 2 ))>" e "<$(( 9 / 3 / 2 ))>" e "<$(( 9 << 1 + 2 ))>" e "<$(( 9 + 3 << 2 ))>" e "<$(( 9 << 3 << 2 ))>" e "<$(( 9 >> 1 + 2 ))>" e "<$(( 9 + 3 >> 2 ))>" e "<$(( 19 >> 3 >> 1 ))>" e "<$(( 19 >> 3 << 1 ))>" e "<$(( 19 << 3 >> 1 ))>" e "<$(( 2 + 3 < 3 * 2 ))>" e "<$(( 2 << 3 >= 3 << 2 ))>" e "<$(( 0xfD & 0xF == 0xF ))>" e "<$((0xfD&0xF==0xF))>" e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" e "<$((3*7,2<<8,9-7))>" # e '== PARENS' e "<$(((1 + 2) + 3))>" e "<$(((1+2)+3))>" e "<$((1 - (2 + 3)))>" e "<$((1-(2+3)))>" e "<$((3 - (2 - 1)))>" e "<$((3-(2-1)))>" e "<$((3 - ( 2 + 1 )))>" e "<$((3-(2+1)))>" e "<$((- (2 + 1)))>" e "<$((-(2+1)))>" e "<$((! (2 + 1)))>" e "<$((!(2+1)))>" e "<$((3 * (2 + 2)))>" e "<$((3*(2+2)))>" e "<$(((3 + 2) * 2))>" e "<$(((3+2)*2))>" e "<$((3 * (2 * 2)))>" e "<$((3*(2*8)))>" e "<$((9 / (3 + 2)))>" e "<$((9/(3+2)))>" e "<$((( 9 + 3 ) / 2))>" e "<$(((9+3)/2))>" e "<$((9 / ( 3 / 2 )))>" e "<$((9/(3/2)))>" e "<$((( 9 << 1 ) + 2))>" e "<$(((9<<1)+2))>" e "<$((9 + (3 << 2)))>" e "<$((9+(3<<2)))>" e "<$((9 << (3 << 2)))>" e "<$((9<<(3<<2)))>" e "<$(((9 >> 1) + 2))>" e "<$(((9>>1)+2))>" e "<$((9 + (3 >> 2)))>" e "<$((9+(3>>2)))>" e "<$((19 >> (3 >> 1)))>" e "<$((19>>(3>>1)))>" e "<$((19 >> (3 << 1)))>" e "<$((19>>(3<<1)))>" e "<$((19 << (3 >> 1)))>" e "<$((19<<(3>>1)))>" e "<$((2 + (3 < 3) * 2))>" e "<$((2+(3<3)*2))>" e "<$((2 << ((3 >= 3) << 2)))>" e "<$((2<<((3>=3)<<2)))>" e "<$(((0xfD & 0xF) == 0xF))>" e "<$(((0xfD&0xF)==0xF))>" e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" e "<$((3*(7,2)<<(8,9-7)))>" # # COND BELOW # e '== ASSIGN I' unset I;p "<$(( I = 3 ))>";e "<$I>" unset I;p "<$((I=3))>";e "<$I>" s I=10;p "<$((I=3))>";e "<$I>" s I=10;p "<$((I+=1))>";e "<$I>" s I=10;p "<$((I-=1))>";e "<$I>" s I=10;p "<$((I*=1))>";e "<$I>" s I=10;p "<$((I*=2))>";e "<$I>" s I=10;p "<$((I/=1))>";e "<$I>" s I=10;p "<$((I/=2))>";e "<$I>" s I=10;p "<$((I%=1))>";e "<$I>" s I=10;p "<$((I%=2))>";e "<$I>" s I=10;p "<$((I**=1))>";e "<$I>" s I=10;p "<$((I**=2))>";e "<$I>" s I=10;p "<$((I**=1+1))>";e "<$I>" s I=10;p "<$((I|=1))>";e "<$I>" s I=10;p "<$((I^=1))>";e "<$I>" ; p "<$((I^=1))>";e "<$I>" s I=10;p "<$((I&=2))>";e "<$I>" s I=10;p "<$((I>>=1))>";e "<$I>" s I=10;p "<$((I<<=1))>";e "<$I>" s I=-1;p "<$((I>>>=1))>";e "<$I>" # e '== ASSIGN II' s I=2;p "<$(((I+=1)-1))>";e "<$I>" s I=4;p "<$(((I-=1)+1))>";e "<$I>" s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" s I=10;p "<$((I=2,I|=1))>";e "<$I>" s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" # e '== POSTFIX' s I=1;p "<$((I++))>";e "<$I>" s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" s I=1;p "<$((I--))>";e "<$I>" s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" # e '== PREFIX' s I=1;p "<$((++I))>";e "<$I>" s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" s I=1;p "<$((--I))>";e "<$I>" s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" # e '== VAR RECUR' s I='1 + 1';p "<$((I))>";e "<$I>" s I='1 + 1';p "<$((+I))>";e "<$I>" s I='1 + 1';p "<$((++I))>";e "<$I>" s I='1 + 1';p "<$((I++))>";e "<$I>" s I='1 + 1';p "<$((1+I))>";e "<$I>" s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" s I='1 + 1';p "<$((I=I))>";e "<$I>" s I='1 + 1';p "<$((I=+I))>";e "<$I>" s I='1 + 1';p "<$((I=1+I))>";e "<$I>" s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" # e '== COMMA' e "<$(( 1 , 2 ))>" e "<$(( 1 , 2 , 3 ))>" e "<$(( 1 , 2 , 3 , 4 ))>" e "<$((1,2,3,4))>" s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" # e '== COND ' e "<$(( +0 ? 2 : 3 ))>" e "<$((-0?2:3))>" e "<$(( +1 ? 2 : 3 ))>" e "<$(( 1-1 ? 2 : 3 ))>" e "<$(( 1-0 ? 2 : 3 ))>" e "<$((-1?2:3))>" e "<$(( 0x1234 ? 111 : 222 ))>" e "<$((1**2 ? 5 : 7))>" e "<$((0**2 ? 5 : 7))>" e "<$((0**2>=0?5:7))>" e "<$((-1<=0**2?5:7))>" e "<$((1<=0**2?5:7))>" e "<$((1>2||1*0?5:7))>" e "<$((1>2&&1*0?5:7))>" e "<$((1<2&&1*0?5:7))>" e "<$((1<2&&1*0+1?5:7))>" e '-- COND .2' e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" e "<$((2<1?-1:2>1?1:0))>" e "<$((4<5 ? 1 : 32))>" e "<$((4>5 ? 1 : 32))>" e "<$((4>(2+3) ? 1 : 32))>" e "<$((4<(2+3) ? 1 : 32))>" e "<$(((2+2)<(2+3) ? 1 : 32))>" e "<$(((2+2)>(2+3) ? 1 : 32))>" ## grouping protects precedence in : parts (syntax error tests below) e '-- COND .3' e "<$((1-1 < 1 ? 2,4 : 1,3))>" e "<$((0<1?2,4:(1,3)))>" e "<$((0,1,2,0?2,4:1,3))>" e "<$((0,1,2,1?2,4:1,3))>" e "<$((0,1,2,0?2,4:(1,3)))>" e "<$((0,1,2,1?2,4:(1,3)))>" e "<$((0,1,2,0?(2,4):1,3))>" e "<$((0,1,2,1?(2,4):1,3))>" e "<$((0,1,2,0?(2,4):(1,3)))>" e "<$((0,1,2,1?(2,4):(1,3)))>" e "<$((0?2:((0,3)?1:4)))>" e "<$((1?2:3,0?1:4))>" e "<$((1?2:3,0?1:4?5:6))>" e "<$((1?2:(3,0)?1:4?5:6))>" e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" e '-- COND .4' e "<$((1?2?3?4?5:6:7:8:9))>" e "<$((1?2?3?0?5:6:7:8:9))>" e "<$((1?2?0?0?5:6:7:8:9))>" e "<$((1?0?0?0?5:6:7:8:9))>" e "<$((0?0?0?0?5:6:7:8:9))>" e "<$((0?3+4?10:11:5+6?12:13))>" e "<$((1?3+4?10:11:5+6?12:13))>" e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" e '-- COND .5' e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" e '-- COND .6' e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" e '-- COND .7' s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$((((I1";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1";\ e "<$I1><$I2><$I3><$I4><$I5>" # only first s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ e "<$I1><$I2><$I3><$I4>" # last not etc. s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I3)?(I3";\ e "<$I1><$I2><$I3><$I4><$I5>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1>I2)?(I2";\ e "<$I1><$I2><$I3><$I4><$I5>" e '-- COND .8' s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" s I=0;p "<$((1?20:(I+=2)))>"; e "<$I>" s I=0;p "<$((1?I+=10:(I+=2)))>"; e "<$I>" s I=0;p "<$((0?I+=2:20))>"; e "<$I>" s I=0;p "<$((0?I+=2:(I+=10)))>"; e "<$I>" s I=0;p "<$((0?(I+=2):(20)))>"; e "<$I>" s I=0;p "<$((0?(I+=2):(I+=20)))>"; e "<$I>" # e '== WILD I' e "<$(( 3 + ( 11 ) ))>" e "<$((1 + (2 - 2)))>" e "<$((1 + (2 - 2)))>" e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" e "<$(( 3+((2 * 2))/6 ))>" e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" # e '== WILD II' s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" s I=10;p "<$((3+(3*(I++))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" s I=10;p "<$(( +10 + + +I ))>";e "<$I>" s I=10;p "<$(( +10 + ++I ))>";e "<$I>" s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" s I=10;p "<$(( +10 +++ I ))>";e "<$I>" s I=10;p "<$(( +10+++I ))>";e "<$I>" s I=10;p "<$((+10++I))>";e "<$I>" s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" e "<$(( +10 + + + ++++ +11 ))>" e "<$(( +10 + + + ++++ ++11 ))>" e "<$((+10++++++++11))>" From steffen at sdaoden.eu Fri Aug 5 16:57:36 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 05 Aug 2022 18:57:36 +0200 Subject: [PATCH] shell: exchange Dijkstra $(( )) evaluator.. Message-ID: The former implementation was not correct regarding whiteouts in ?: conditional branches. The new one also parses a bit better, in effect on equal level than bash with its recursive descendent parser. --- Hello! I implemented that one heavily inspired from the current busybox implementation, so thanks to everyone who worked on that. I have to admit that the size increasement is quite large, the error msg stuff in arith() alone is ~400 bytes: 2938 0 0 2938 b7a math.o 4668 0 0 4668 123c math.o For that you get an evaluator that is en par with bash, and it even gets +10++VAR -> +10 + +VAR. (Since we do handle +10++11 correctly via "prefix split", we should also handle this) In advance i will post a test script (i also have a "should fail" one, but this does not work fine here, it is ok for bash and my one), and if i run this i get --- .good.x 2022-08-05 17:54:42.453434049 +0200 +++ .good.sh 2022-08-05 17:50:08.990105909 +0200 @@ -9,7 +9,7 @@ <1> <1> <1> -<9223372036854775807> +<0> <10> <9191919191919> <13> compared to my one (and bash). All the integer conversion is up to the current code, i did not touch it. The evaluator also supports unsigned right shift >>> and exponent-assignment **=, which makes for the other differences to bash. "For now" i simply copied over that shexp-arith.h unchanged, and added compatibility shims so that arith() can use it. The evaluator is quite fresh, maybe i can optimize it, and the way it is done for now allows easy-most comparison. You surely could make the object smaller if the compatibility shims would be replaced with inline code. I hope you like it! Thanks for busybox! shell/ash.c | 4 +- shell/hush.c | 13 +- shell/math.c | 740 +++++----------------------- shell/math.h | 3 +- shell/shexp-arith.h | 1134 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1261 insertions(+), 633 deletions(-) create mode 100644 shell/shexp-arith.h diff --git a/shell/ash.c b/shell/ash.c index 105edd4c8d..bcb5161117 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6029,8 +6029,10 @@ ash_arith(const char *s) INT_OFF; result = arith(&math_state, s); - if (math_state.errmsg) + if (math_state.errmsg) { ash_msg_and_raise_error(math_state.errmsg); + free(math_state.errmsg); + } INT_ON; return result; diff --git a/shell/hush.c b/shell/hush.c index 051b123e78..f59bb57b3b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6475,7 +6475,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, } #if ENABLE_FEATURE_SH_MATH -static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) +static arith_t expand_and_evaluate_arith(const char *arg, char **errmsg_p) { arith_state_t math_state; arith_t res; @@ -6489,8 +6489,11 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) free(exp_str); if (errmsg_p) *errmsg_p = math_state.errmsg; - if (math_state.errmsg) + if (math_state.errmsg) { msg_and_die_if_script(math_state.errmsg); + if (errmsg_p == NULL) + free(math_state.errmsg); + } return res; } #endif @@ -6814,11 +6817,13 @@ static NOINLINE int expand_one_var(o_string *output, int n, */ arith_t beg, len; unsigned vallen; - const char *errmsg; + char *errmsg; beg = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { + free(errmsg); goto empty_result; + } debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); *p++ = SPECIAL_VAR_SYMBOL; exp_word = p; diff --git a/shell/math.c b/shell/math.c index 76d22c9bd5..a20596089d 100644 --- a/shell/math.c +++ b/shell/math.c @@ -116,398 +116,6 @@ #include "libbb.h" #include "math.h" -typedef unsigned char operator; - -/* An operator's token id is a bit of a bitfield. The lower 5 bits are the - * precedence, and 3 high bits are an ID unique across operators of that - * precedence. The ID portion is so that multiple operators can have the - * same precedence, ensuring that the leftmost one is evaluated first. - * Consider * and / - */ -#define tok_decl(prec,id) (((id)<<5) | (prec)) -#define PREC(op) ((op) & 0x1F) - -#define TOK_LPAREN tok_decl(0,0) - -#define TOK_COMMA tok_decl(1,0) - -/* All assignments are right associative and have the same precedence, - * but there are 11 of them, which doesn't fit into 3 bits for unique id. - * Abusing another precedence level: - */ -#define TOK_ASSIGN tok_decl(2,0) -#define TOK_AND_ASSIGN tok_decl(2,1) -#define TOK_OR_ASSIGN tok_decl(2,2) -#define TOK_XOR_ASSIGN tok_decl(2,3) -#define TOK_PLUS_ASSIGN tok_decl(2,4) -#define TOK_MINUS_ASSIGN tok_decl(2,5) -#define TOK_LSHIFT_ASSIGN tok_decl(2,6) -#define TOK_RSHIFT_ASSIGN tok_decl(2,7) - -#define TOK_MUL_ASSIGN tok_decl(3,0) -#define TOK_DIV_ASSIGN tok_decl(3,1) -#define TOK_REM_ASSIGN tok_decl(3,2) - -#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) - -/* Ternary conditional operator is right associative too */ -#define TOK_CONDITIONAL tok_decl(4,0) -#define TOK_CONDITIONAL_SEP tok_decl(4,1) - -#define TOK_OR tok_decl(5,0) - -#define TOK_AND tok_decl(6,0) - -#define TOK_BOR tok_decl(7,0) - -#define TOK_BXOR tok_decl(8,0) - -#define TOK_BAND tok_decl(9,0) - -#define TOK_EQ tok_decl(10,0) -#define TOK_NE tok_decl(10,1) - -#define TOK_LT tok_decl(11,0) -#define TOK_GT tok_decl(11,1) -#define TOK_GE tok_decl(11,2) -#define TOK_LE tok_decl(11,3) - -#define TOK_LSHIFT tok_decl(12,0) -#define TOK_RSHIFT tok_decl(12,1) - -#define TOK_ADD tok_decl(13,0) -#define TOK_SUB tok_decl(13,1) - -#define TOK_MUL tok_decl(14,0) -#define TOK_DIV tok_decl(14,1) -#define TOK_REM tok_decl(14,2) - -/* Exponent is right associative */ -#define TOK_EXPONENT tok_decl(15,1) - -/* Unary operators */ -#define UNARYPREC 16 -#define TOK_BNOT tok_decl(UNARYPREC,0) -#define TOK_NOT tok_decl(UNARYPREC,1) - -#define TOK_UMINUS tok_decl(UNARYPREC+1,0) -#define TOK_UPLUS tok_decl(UNARYPREC+1,1) - -#define PREC_PRE (UNARYPREC+2) - -#define TOK_PRE_INC tok_decl(PREC_PRE, 0) -#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) - -#define PREC_POST (UNARYPREC+3) - -#define TOK_POST_INC tok_decl(PREC_POST, 0) -#define TOK_POST_DEC tok_decl(PREC_POST, 1) - -#define SPEC_PREC (UNARYPREC+4) - -#define TOK_NUM tok_decl(SPEC_PREC, 0) -#define TOK_RPAREN tok_decl(SPEC_PREC, 1) - -static int -is_assign_op(operator op) -{ - operator prec = PREC(op); - fix_assignment_prec(prec); - return prec == PREC(TOK_ASSIGN) - || prec == PREC_PRE - || prec == PREC_POST; -} - -static int -is_right_associative(operator prec) -{ - return prec == PREC(TOK_ASSIGN) - || prec == PREC(TOK_EXPONENT) - || prec == PREC(TOK_CONDITIONAL); -} - - -typedef struct { - arith_t val; - /* We acquire second_val only when "expr1 : expr2" part - * of ternary ?: op is evaluated. - * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). - * ':' produces a new value which has two parts, val and second_val; - * then '?' selects one of them based on its left side. - */ - arith_t second_val; - char second_val_present; - /* If NULL then it's just a number, else it's a named variable */ - char *var; -} var_or_num_t; - -typedef struct remembered_name { - struct remembered_name *next; - const char *var; -} remembered_name; - - -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr); - -static const char* -arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) -{ - if (t->var) { - const char *p = math_state->lookupvar(t->var); - if (p) { - remembered_name *cur; - remembered_name cur_save; - - /* did we already see this name? - * testcase: a=b; b=a; echo $((a)) - */ - for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { - if (strcmp(cur->var, t->var) == 0) { - /* Yes */ - return "expression recursion loop detected"; - } - } - - /* push current var name */ - cur = math_state->list_of_recursed_names; - cur_save.var = t->var; - cur_save.next = cur; - math_state->list_of_recursed_names = &cur_save; - - /* recursively evaluate p as expression */ - t->val = evaluate_string(math_state, p); - - /* pop current var name */ - math_state->list_of_recursed_names = cur; - - return math_state->errmsg; - } - /* treat undefined var as 0 */ - t->val = 0; - } - return 0; -} - -/* "Applying" a token means performing it on the top elements on the integer - * stack. For an unary operator it will only change the top element, but a - * binary operator will pop two arguments and push the result */ -static NOINLINE const char* -arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) -{ -#define NUMPTR (*numstackptr) - - var_or_num_t *top_of_stack; - arith_t rez; - const char *err; - - /* There is no operator that can work without arguments */ - if (NUMPTR == numstack) - goto err; - - top_of_stack = NUMPTR - 1; - - /* Resolve name to value, if needed */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - - rez = top_of_stack->val; - if (op == TOK_UMINUS) - rez = -rez; - else if (op == TOK_NOT) - rez = !rez; - else if (op == TOK_BNOT) - rez = ~rez; - else if (op == TOK_POST_INC || op == TOK_PRE_INC) - rez++; - else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) - rez--; - else if (op != TOK_UPLUS) { - /* Binary operators */ - arith_t right_side_val; - char bad_second_val; - - /* Binary operators need two arguments */ - if (top_of_stack == numstack) - goto err; - /* ...and they pop one */ - NUMPTR = top_of_stack; /* this decrements NUMPTR */ - - bad_second_val = top_of_stack->second_val_present; - if (op == TOK_CONDITIONAL) { /* ? operation */ - /* Make next if (...) protect against - * $((expr1 ? expr2)) - that is, missing ": expr" */ - bad_second_val = !bad_second_val; - } - if (bad_second_val) { - /* Protect against $((expr expr1 : expr2)) */ - return "malformed ?: operator"; - } - - top_of_stack--; /* now points to left side */ - - if (op != TOK_ASSIGN) { - /* Resolve left side value (unless the op is '=') */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - } - - right_side_val = rez; - rez = top_of_stack->val; - if (op == TOK_CONDITIONAL) /* ? operation */ - rez = (rez ? right_side_val : top_of_stack[1].second_val); - else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ - if (top_of_stack == numstack) { - /* Protect against $((expr : expr)) */ - return "malformed ?: operator"; - } - top_of_stack->second_val_present = op; - top_of_stack->second_val = right_side_val; - } - else if (op == TOK_BOR || op == TOK_OR_ASSIGN) - rez |= right_side_val; - else if (op == TOK_OR) - rez = right_side_val || rez; - else if (op == TOK_BAND || op == TOK_AND_ASSIGN) - rez &= right_side_val; - else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) - rez ^= right_side_val; - else if (op == TOK_AND) - rez = rez && right_side_val; - else if (op == TOK_EQ) - rez = (rez == right_side_val); - else if (op == TOK_NE) - rez = (rez != right_side_val); - else if (op == TOK_GE) - rez = (rez >= right_side_val); - else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) - rez >>= right_side_val; - else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) - rez <<= right_side_val; - else if (op == TOK_GT) - rez = (rez > right_side_val); - else if (op == TOK_LT) - rez = (rez < right_side_val); - else if (op == TOK_LE) - rez = (rez <= right_side_val); - else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) - rez *= right_side_val; - else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) - rez += right_side_val; - else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) - rez -= right_side_val; - else if (op == TOK_ASSIGN || op == TOK_COMMA) - rez = right_side_val; - else if (op == TOK_EXPONENT) { - arith_t c; - if (right_side_val < 0) - return "exponent less than 0"; - c = 1; - while (--right_side_val >= 0) - c *= rez; - rez = c; - } - else if (right_side_val == 0) - return "divide by zero"; - else if (op == TOK_DIV || op == TOK_DIV_ASSIGN - || op == TOK_REM || op == TOK_REM_ASSIGN) { - /* - * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' - * - * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1 - * and thus is not representable. - * Some CPUs segfault trying such op. - * Others overflow MAX_POSITIVE_INT+1 to - * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000). - * Make sure to at least not SEGV here: - */ - if (right_side_val == -1 - && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */ - ) { - right_side_val = 1; - } - if (op == TOK_DIV || op == TOK_DIV_ASSIGN) - rez /= right_side_val; - else { - rez %= right_side_val; - } - } - } - - if (is_assign_op(op)) { - char buf[sizeof(arith_t)*3 + 2]; - - if (top_of_stack->var == NULL) { - /* Hmm, 1=2 ? */ - goto err; - } - /* Save to shell variable */ - sprintf(buf, ARITH_FMT, rez); - math_state->setvar(top_of_stack->var, buf); - /* After saving, make previous value for v++ or v-- */ - if (op == TOK_POST_INC) - rez--; - if (op == TOK_POST_DEC) - rez++; - } - - top_of_stack->val = rez; - /* Erase var name, it is just a number now */ - top_of_stack->var = NULL; - return NULL; - err: - return "arithmetic syntax error"; -#undef NUMPTR -} - -/* longest must be first */ -static const char op_tokens[] ALIGN1 = { - '<','<','=',0, TOK_LSHIFT_ASSIGN, - '>','>','=',0, TOK_RSHIFT_ASSIGN, - '<','<', 0, TOK_LSHIFT, - '>','>', 0, TOK_RSHIFT, - '|','|', 0, TOK_OR, - '&','&', 0, TOK_AND, - '!','=', 0, TOK_NE, - '<','=', 0, TOK_LE, - '>','=', 0, TOK_GE, - '=','=', 0, TOK_EQ, - '|','=', 0, TOK_OR_ASSIGN, - '&','=', 0, TOK_AND_ASSIGN, - '*','=', 0, TOK_MUL_ASSIGN, - '/','=', 0, TOK_DIV_ASSIGN, - '%','=', 0, TOK_REM_ASSIGN, - '+','=', 0, TOK_PLUS_ASSIGN, - '-','=', 0, TOK_MINUS_ASSIGN, - '-','-', 0, TOK_POST_DEC, - '^','=', 0, TOK_XOR_ASSIGN, - '+','+', 0, TOK_POST_INC, - '*','*', 0, TOK_EXPONENT, - '!', 0, TOK_NOT, - '<', 0, TOK_LT, - '>', 0, TOK_GT, - '=', 0, TOK_ASSIGN, - '|', 0, TOK_BOR, - '&', 0, TOK_BAND, - '*', 0, TOK_MUL, - '/', 0, TOK_DIV, - '%', 0, TOK_REM, - '+', 0, TOK_ADD, - '-', 0, TOK_SUB, - '^', 0, TOK_BXOR, - /* uniq */ - '~', 0, TOK_BNOT, - ',', 0, TOK_COMMA, - '?', 0, TOK_CONDITIONAL, - ':', 0, TOK_CONDITIONAL_SEP, - ')', 0, TOK_RPAREN, - '(', 0, TOK_LPAREN, - 0 -}; -#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) - #if ENABLE_FEATURE_SH_MATH_BASE static arith_t strto_arith_t(const char *nptr, char **endptr) { @@ -577,250 +185,130 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) # endif #endif -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr) -{ - operator lasttok; - const char *errmsg; - const char *start_expr = expr = skip_whitespace(expr); - unsigned expr_len = strlen(expr) + 2; - /* Stack of integers */ - /* The proof that there can be no more than strlen(startbuf)/2+1 - * integers in any given correct or incorrect expression - * is left as an exercise to the reader. */ - var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); - var_or_num_t *numstackptr = numstack; - /* Stack of operator tokens */ - operator *const stack = alloca(expr_len * sizeof(stack[0])); - operator *stackptr = stack; - - /* Start with a left paren */ - *stackptr++ = lasttok = TOK_LPAREN; - errmsg = NULL; - - while (1) { - const char *p; - operator op; - operator prec; - - expr = skip_whitespace(expr); - if (*expr == '\0') { - if (expr == start_expr) { - /* Null expression */ - numstack->val = 0; - goto ret; - } - - /* This is only reached after all tokens have been extracted from the - * input stream. If there are still tokens on the operator stack, they - * are to be applied in order. At the end, there should be a final - * result on the integer stack */ - - if (expr != ptr_to_rparen + 1) { - /* If we haven't done so already, - * append a closing right paren - * and let the loop process it */ - expr = ptr_to_rparen; -//bb_error_msg("expr=')'"); - continue; - } - /* At this point, we're done with the expression */ - if (numstackptr != numstack + 1) { - /* ...but if there isn't, it's bad */ - goto err; - } - goto ret; - } - - p = endofname(expr); - if (p != expr) { - /* Name */ - size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ - numstackptr->var = alloca(var_name_size); - safe_strncpy(numstackptr->var, expr, var_name_size); -//bb_error_msg("var:'%s'", numstackptr->var); - expr = p; - num: - numstackptr->second_val_present = 0; - numstackptr++; - lasttok = TOK_NUM; - continue; - } - - if (isdigit(*expr)) { - /* Number */ - numstackptr->var = NULL; - errno = 0; - numstackptr->val = strto_arith_t(expr, (char**) &expr); -//bb_error_msg("val:%lld", numstackptr->val); - if (errno) - numstackptr->val = 0; /* bash compat */ - goto num; - } - - /* Should be an operator */ - - /* Special case: XYZ--, XYZ++, --XYZ, ++XYZ are recognized - * only if XYZ is a variable name, not a number or EXPR. IOW: - * "a+++v" is a++ + v. - * "(a)+++7" is ( a ) + + + 7. - * "7+++v" is 7 + ++v, not 7++ + v. - * "--7" is - - 7, not --7. - * "++++a" is + + ++a, not ++ ++a. - */ - if ((expr[0] == '+' || expr[0] == '-') - && (expr[1] == expr[0]) - ) { - if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ - char next = skip_whitespace(expr + 2)[0]; - if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ - //bb_error_msg("special %c%c", expr[0], expr[0]); - op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); - expr++; - goto tok_found1; - } - } - } - - p = op_tokens; - while (1) { - /* Compare expr to current op_tokens[] element */ - const char *e = expr; - while (1) { - if (*p == '\0') { - /* Match: operator is found */ - expr = e; - goto tok_found; - } - if (*p != *e) - break; - p++; - e++; - } - /* No match, go to next element of op_tokens[] */ - while (*p) - p++; - p += 2; /* skip NUL and TOK_foo bytes */ - if (*p == '\0') { - /* No next element, operator not found */ - //math_state->syntax_error_at = expr; - goto err; - } - } - tok_found: - op = p[1]; /* fetch TOK_foo value */ - tok_found1: - /* NB: expr now points past the operator */ +#define boole bool +# define FAL0 false +# define TRU1 true +#define s64 arith_t +#if ENABLE_FEATURE_SH_MATH_64 +# define S64_MIN LLONG_MIN +# define u64 unsigned long long +#else +# define S64_MIN LONG_MIN +# define u64 unsigned long +#endif +#define u8 uint8_t +#define u16 uint16_t +#define u32 uint32_t +#define U32_MAX UINT32_MAX +#define ul unsigned long +#define up uintptr_t +#define UZ_MAX SIZE_MAX +#define uz size_t + +#define a_SHEXP_ISVARC(C) ((C) == '_' || isalnum(S(unsigned char,C))) +#define a_SHEXP_ISVARC_BAD1ST(C) su_cs_is_digit(C) +#define a_SHEXP_ISVARC_BADNST(C) FAL0 +#define ASSERT(X) +#define ASSERT_NYD_EXEC(X,Y) +#define BITENUM_IS(X,Y) X +#define CONCAT(S1,S2) su__CONCAT_1(S1, S2) +# define su__CONCAT_1(S1,S2) su__CONCAT_2(S1, S2) +# define su__CONCAT_2(S1,S2) S1 ## S2 +#define DBG(X) +#define FALLTHRU +#define N_(X) X +#define NIL NULL +#define NYD_IN S(void,0) +#define NYD2_IN S(void,0) +#define NYD_OU S(void,0) +#define NYD2_OU S(void,0) +#define P2UZ(X) S(size_t,X) +#define S(X,Y) ((X)(Y)) +#define savestr(X) xstrdup(X) +#define su_ALIGNOF(X) ((sizeof(X) + 15) & ~15) +#define su_cs_cmp(X,Y) strcmp(X, Y) +#define su_cs_is_digit(X) isdigit(S(unsigned char,X)) +#define su_cs_is_space(X) isspace(S(unsigned char,X)) +#define su_empty "" +#define su_IDEC_STATE_EBASE 0 /* (could case $CC optimiz.) */ +#define su_IDEC_STATE_EMASK (1u<<0) +#define su_IDEC_STATE_CONSUMED (1u<<1) +#define su_IENC_BUFFER_SIZE 80u +#define su_LOFI_ALLOC(X) alloca(X) +#define su_LOFI_FREE(X) +#define su_mem_move(X,Y,Z) memmove(X, Y, Z) +#define STRUCT_ZERO(X,Y) memset(Y, 0, sizeof(X)) +#define UNLIKELY(X) X +#define UNUSED(X) S(void,X) + +#if LONG_MAX - 1 > 0x7FFFFFFFl - 1 +# define su_64(X) X +#else +# define su_64(X) +#endif - /* post grammar: a++ reduce to num */ - if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) - lasttok = TOK_NUM; +// +#define a_SHEXP_ARITH_COOKIE arith_state_t * - /* Plus and minus are binary (not unary) _only_ if the last - * token was a number, or a right paren (which pretends to be - * a number, since it evaluates to one). Think about it. - * It makes sense. */ - if (lasttok != TOK_NUM) { - switch (op) { - case TOK_ADD: - op = TOK_UPLUS; - break; - case TOK_SUB: - op = TOK_UMINUS; - break; - case TOK_POST_INC: - op = TOK_PRE_INC; - break; - case TOK_POST_DEC: - op = TOK_PRE_DEC; - break; - } - } - /* We don't want an unary operator to cause recursive descent on the - * stack, because there can be many in a row and it could cause an - * operator to be evaluated before its argument is pushed onto the - * integer stack. - * But for binary operators, "apply" everything on the operator - * stack until we find an operator with a lesser priority than the - * one we have just extracted. If op is right-associative, - * then stop "applying" on the equal priority too. - * Left paren is given the lowest priority so it will never be - * "applied" in this way. - */ - prec = PREC(op); -//bb_error_msg("prec:%02x", prec); - if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { - /* not left paren or unary */ - if (lasttok != TOK_NUM) { - /* binary op must be preceded by a num */ - goto err; - } - /* The algorithm employed here is simple: while we don't - * hit an open paren nor the bottom of the stack, pop - * tokens and apply them */ - while (stackptr != stack) { - operator prev_op = *--stackptr; - if (op == TOK_RPAREN) { -//bb_error_msg("op == TOK_RPAREN"); - if (prev_op == TOK_LPAREN) { -//bb_error_msg("prev_op == TOK_LPAREN"); -//bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); - if (numstackptr[-1].var) { - /* Expression is (var), lookup now */ - errmsg = arith_lookup_val(math_state, &numstackptr[-1]); - if (errmsg) - goto err_with_custom_msg; - /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ - numstackptr[-1].var = NULL; - } - /* Any operator directly after a - * close paren should consider itself binary */ - lasttok = TOK_NUM; - goto next; - } -//bb_error_msg("prev_op != TOK_LPAREN"); - } else { - operator prev_prec = PREC(prev_op); -//bb_error_msg("op != TOK_RPAREN"); - fix_assignment_prec(prec); - fix_assignment_prec(prev_prec); - if (prev_prec < prec - || (prev_prec == prec && is_right_associative(prec)) - ) { - stackptr++; - break; - } - } -//bb_error_msg("arith_apply(prev_op:%02x)", prev_op); - errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); - if (errmsg) - goto err_with_custom_msg; - } - if (op == TOK_RPAREN) - goto err; - } +static inline u32 a_idec_x(void *resp, char const *cbuf, + char const **endptr_or_nil){ + u32 rv; + arith_t res; +#if ENABLE_FEATURE_SH_MATH_BASE + char const *eptr; - /* Push this operator to the stack and remember it */ -//bb_error_msg("push op:%02x", op); - *stackptr++ = lasttok = op; - next: ; - } /* while (1) */ + if(endptr_or_nil == NIL) + endptr_or_nil = &eptr; +#endif - err: - errmsg = "arithmetic syntax error"; - err_with_custom_msg: - numstack->val = -1; - ret: - math_state->errmsg = errmsg; - return numstack->val; + errno = 0; + res = strto_arith_t(cbuf, (char**)endptr_or_nil); + rv = 0; + if(errno == 0){ + if(**endptr_or_nil == '\0') + rv = su_IDEC_STATE_CONSUMED; + }else{ + rv = su_IDEC_STATE_EMASK; + res = 0; + } + *S(s64*,resp) = res; + return rv; } +#define su_idec_cp(A,B,C,D,E) a_idec_x(A, B, E) +#define su_ienc_s64(X,Y,Z) (sprintf(X, ARITH_FMT, Y), X) +#define n_var_vlook(X,Y) (*self->sac_cookie->lookupvar)(X) +#define n_var_vset(X,Y,Z) (*self->sac_cookie->setvar)(X, (char*)(Y)) + +#include "shexp-arith.h" arith_t FAST_FUNC arith(arith_state_t *math_state, const char *expr) { + char const *err_rest, *emsg; + s64 res; + math_state->errmsg = NULL; - math_state->list_of_recursed_names = NULL; - return evaluate_string(math_state, expr); + + switch(a_shexp_arith_eval(math_state, &res, expr, UZ_MAX, &err_rest)){ + default: + return res; +#undef a_X +#define a_X(X,N) case CONCAT(a_SHEXP_ARITH_ERR_,X): emsg = N_(N); break + a_X(NOMEM, "out of memory"); + a_X(SYNTAX, "syntax error"); + a_X(ASSIGN_NO_VAR, "assignment without variable"); + a_X(DIV_BY_ZERO, "division by zero"); + a_X(EXP_INVALID, "invalid exponent"); + a_X(NO_OPERAND, "syntax error, expected operand"); + a_X(COND_NO_COLON, "syntax error, incomplete ?: condition"); + a_X(NAME_LOOP, "recursive variable name reference"); + a_X(OP_INVALID, "unknown operator"); + } +#undef a_X + + math_state->errmsg = xasprintf("%s (rest: %s)", emsg, err_rest); + + return -1; } /* diff --git a/shell/math.h b/shell/math.h index 41ef6e8dfa..6d54201086 100644 --- a/shell/math.h +++ b/shell/math.h @@ -76,11 +76,10 @@ typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *v //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); typedef struct arith_state_t { - const char *errmsg; + char *errmsg; arith_var_lookup_t lookupvar; arith_var_set_t setvar; // arith_var_endofname_t endofname; - void *list_of_recursed_names; } arith_state_t; arith_t FAST_FUNC arith(arith_state_t *state, const char *expr); diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h new file mode 100644 index 0000000000..96a63bd2b9 --- /dev/null +++ b/shell/shexp-arith.h @@ -0,0 +1,1134 @@ +/*@ S-nail - a mail user agent derived from Berkeley Mail. + *@ Signed 64-bit sh(1)ell-style $(( ARITH ))metic expression evaluator. + *@ POW2 bases are parsed as unsigned, operation overflow is not handled, + *@ saturated mode is not supported, division by zero is handled via error. + *@ The maximum expression length is ~100.000.000 on 32-bit, U32_MAX otherwise. + *@ After reading on Dijkstra's two stack algorithm; found in the internet: + *@ + *@ We can use Dijkstra's two stack algorithm to solve an equation. + *@ You need two stacks, a value stack (operands), and an operator stack. + *@ Numbers will be double values, operators will be char values. + *@ The whole of the expression is made up of tokens, ignoring whitespace. + *@ + *@ While there are still tokens to read + *@ Get the next item + *@ If the item is: + *@ A number: push it onto the value stack. + *@ A left parenthesis: push it onto the operator stack. + *@ A right parenthesis: + *@ While the top of the operator stack is not a left parenthesis + *@ Pop the operator from the operator stack. + *@ Pop the value stack twice, getting two operands. + *@ Apply the operator to the operands, in the correct order. + *@ Push the result onto the value stack. + *@ Pop the left parenthesis from the operator stack + *@ An operator op: + *@ While the operator stack is not empty, and the top of the + *@ operator stack has the same or greater precedence as op, + *@ Pop the operator from the operator stack. + *@ Pop the value stack twice, getting two operands. + *@ Apply the operator to the operands, in the correct order. + *@ Push the result onto the value stack. + *@ Push op onto the operator stack. + *@ While the operator stack is not empty... [less push op] + *@ + *@ At this point the operator stack should be empty, and the value stack + *@ should have only one value in it, which is the final result. + *@ + *@ as well as bash:expr.c, a bit. Most heavily inspired by busybox. + *@ Conclusion: the algorithm scales badly to ternary and whiteouts; in order + *@ XXX to provide good error feedback the operator stack would need to contain + *@ XXX structs that remember string positions. + * + * Copyright (c) 2022 Steffen Nurpmeso . + * SPDX-License-Identifier: ISC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if 1 +# define a_SHEXP_ARITH_DBG 0 +# define a_SHEXP_ARITH_L(X) +#else +# define a_SHEXP_ARITH_DBG 1 +# define a_SHEXP_ARITH_L(X) a_shexp__arith_log X +#endif + +/* We parse with base 0: set _RESCAN to allow "I=' -10';$((10#$I))" */ +#define a_SHEXP_ARITH_IDEC_MODE (su_IDEC_MODE_SIGNED_TYPE |\ + su_IDEC_MODE_POW2BASE_UNSIGNED | su_IDEC_MODE_LIMIT_NOERROR |\ + su_IDEC_MODE_BASE0_NUMBER_SIGN_RESCAN) + +enum a_shexp_arith_error{ + a_SHEXP_ARITH_ERR_NONE, + a_SHEXP_ARITH_ERR_NOMEM, /* Out of memory */ + a_SHEXP_ARITH_ERR_SYNTAX, /* General syntax error */ + a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR, /* Assignment without variable */ + a_SHEXP_ARITH_ERR_DIV_BY_ZERO, + a_SHEXP_ARITH_ERR_EXP_INVALID, /* Invalid exponent */ + a_SHEXP_ARITH_ERR_NO_OPERAND, /* Expected an argument here */ + a_SHEXP_ARITH_ERR_COND_NO_COLON, /* Incomplete ?: condition */ + a_SHEXP_ARITH_ERR_NAME_LOOP, /* Variable self-reference loop */ + a_SHEXP_ARITH_ERR_OP_INVALID /* Unknown operator */ +}; + +/* Operators and precedences in increasing precedence order. + * (The operator stack as such is u16: [OP_FLAGS |] (OP<<8) | PREC.) + * bash's expr.c says: + * Sub-expressions within parentheses have a precedence level greater than + * all of these levels and are evaluated first. Within a single precedence + * group, evaluation is left-to-right, except for arithmetic assignment, + * which is evaluated right-to-left (as in C). */ +enum a_shexp_arith_ops{ +#undef a_X +#define a_X(N,P,O) \ + CONCAT(a_SHEXP_ARITH_PREC_,N) = CONCAT(P,u),\ + CONCAT(a_SHEXP_ARITH_OP_,N) =\ + (CONCAT(O,u) << 8) | CONCAT(a_SHEXP_ARITH_PREC_,N) + + a_X(PAREN_LEFT, 0, 0), + + a_X(COMMA, 1, 0), + + a_X(ASSIGN, 2, 0), + a_X(ASSIGN_BIT_OR, 2, 1), + a_X(ASSIGN_BIT_XOR, 2, 2), + a_X(ASSIGN_BIT_AND, 2, 3), + a_X(ASSIGN_SHIFT_LEFT, 2, 4), a_X(ASSIGN_SHIFT_RIGHT, 2, 5), + a_X(ASSIGN_SHIFT_RIGHTU, 2, 6), + a_X(ASSIGN_ADD, 2, 7), a_X(ASSIGN_SUB, 2, 8), + a_X(ASSIGN_MUL, 2, 9), a_X(ASSIGN_DIV, 2, 10), a_X(ASSIGN_MOD, 2, 11), + a_X(ASSIGN_EXP, 2, 12), + + a_X(COND, 3, 0), + a_X(COND_COLON, 3, 1), + + a_X(OR, 4, 0), + a_X(AND, 5, 0), + a_X(BIT_OR, 6, 0), + a_X(BIT_XOR, 7, 0), + a_X(BIT_AND, 8, 0), + a_X(EQ, 9, 0), a_X(NE, 9, 1), + a_X(LE, 10, 0), a_X(GE, 10, 1), a_X(LT, 10, 2), a_X(GT, 10, 3), + a_X(SHIFT_LEFT, 11, 0), a_X(SHIFT_RIGHT, 11, 1), a_X(SHIFT_RIGHTU, 11, 2), + a_X(ADD, 12, 0), a_X(SUB, 12, 1), + a_X(MUL, 13, 0), a_X(DIV, 13, 1), a_X(MOD, 13, 2), + a_X(EXP, 14, 0), + + /* Further operators are unary, pre- or postfix */ + a_SHEXP_ARITH_PREC_UNARY = 15, + a_SHEXP_ARITH_PREC_PREFIX = 16, + a_SHEXP_ARITH_PREC_POSTFIX = 18, + + a_X(UNARY_NOT, 15, 0), a_X(UNARY_BIT_NOT, 15, 1), + a_X(PREFIX_INC, 16, 0), a_X(PREFIX_DEC, 16, 1), + a_X(UNARY_PLUS, 17, 1), a_X(UNARY_MINUS, 17, 0), + a_X(POSTFIX_INC, 18, 0), a_X(POSTFIX_DEC, 18, 1), + + /* Beyond operator profanity; the first "is a number" */ + a_SHEXP_ARITH_PREC_SKY = 19, + a_X(NUM, 19, 0), a_X(PAREN_RIGHT, 19, 1), + +#undef a_X +}; + +enum arith_op_flags{ + /* Mask off operator and precision */ + a_SHEXP_ARITH_OP_MASK = 0x1FFF, + a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON = 1u<<13, + a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT = 1u<<14, + a_SHEXP_ARITH_OP_FLAG_WHITEOUT = 1u<<15, + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK = a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT | + a_SHEXP_ARITH_OP_FLAG_WHITEOUT, + a_SHEXP_ARITH_OP_FLAG_MASK = a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON | + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK +}; + +struct a_shexp_arith_name_stack{ + struct a_shexp_arith_name_stack *sans_last; + char const *sans_var; +}; + +struct a_shexp_arith_val{ + s64 sav_val; + char *sav_var; /* Named variable or NIL */ +}; + +struct a_shexp_arith_stack{ + u16 *sas_ops; + u16 *sas_ops_top; + struct a_shexp_arith_val *sas_nums; + struct a_shexp_arith_val *sas_nums_top; +}; + +struct a_shexp_arith_ctx{ + enum a_shexp_arith_error sac_error; + su_64( u8 sac__pad[4]; ) + char const *sac_error_rest; + s64 sac_rv; + struct a_shexp_arith_stack *sac_stack; + struct a_shexp_arith_name_stack *sac_name_stack; +#undef a_SHEXP_ARITH_IFS +#ifdef mx_SOURCE +# define a_SHEXP_ARITH_IFS(X) X + char const *sac_ifs_ws; /* IFS whitespace */ +#else +# define a_SHEXP_ARITH_IFS(X) +#endif +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE sac_cookie; +#endif +}; + +/* Sort by ~expected usage -- however, longest first if ambiguous! + * Follow busybox, save space by compressing data in char[] not struct[]! + * (XXX Instead use 1-st byte jump table like for commands) */ +static char const a_shexp_arith_op_toks[] = { +#undef a_X +#define a_X(X) \ + S(char,(CONCAT(a_SHEXP_ARITH_OP_,X) & 0xFF00u) >> 8),\ + S(char,CONCAT(a_SHEXP_ARITH_PREC_,X)) + + '+','+','\0', a_X(POSTFIX_INC), + '+','=','\0', a_X(ASSIGN_ADD), + '+','\0', a_X(ADD), + '-','-','\0', a_X(POSTFIX_DEC), + '-','=','\0', a_X(ASSIGN_SUB), + '-','\0', a_X(SUB), + '*','*','=','\0', a_X(ASSIGN_EXP), + '*','*','\0', a_X(EXP), + '*','=','\0', a_X(ASSIGN_MUL), + '*','\0', a_X(MUL), + '/','=','\0', a_X(ASSIGN_DIV), + '/','\0', a_X(DIV), + '%','=','\0', a_X(ASSIGN_MOD), + '%','\0', a_X(MOD), + '|','|','\0', a_X(OR), + '|','=','\0', a_X(ASSIGN_BIT_OR), + '|','\0', a_X(BIT_OR), + '^','=','\0', a_X(ASSIGN_BIT_XOR), + '^','\0', a_X(BIT_XOR), + '&','&','\0', a_X(AND), + '&','=','\0', a_X(ASSIGN_BIT_AND), + '&','\0', a_X(BIT_AND), + '<','<','=',0, a_X(ASSIGN_SHIFT_LEFT), + '<','<','\0', a_X(SHIFT_LEFT), + '>','>','>','=',0, a_X(ASSIGN_SHIFT_RIGHTU), + '>','>','>','\0', a_X(SHIFT_RIGHTU), + '>','>','=',0, a_X(ASSIGN_SHIFT_RIGHT), + '>','>','\0', a_X(SHIFT_RIGHT), + + '~','\0', a_X(UNARY_BIT_NOT), + '!','=','\0', a_X(NE), + '!','\0', a_X(UNARY_NOT), + + ')','\0', a_X(PAREN_RIGHT), + '(','\0', a_X(PAREN_LEFT), + ',','\0', a_X(COMMA), + + '<','=','\0', a_X(LE), + '>','=','\0', a_X(GE), + '=','=','\0', a_X(EQ), + '<','\0', a_X(LT), + '>','\0', a_X(GT), + '=','\0', a_X(ASSIGN), + + '?','\0', a_X(COND), + ':','\0', a_X(COND_COLON), + + '\0' +#undef a_X +}; + +/* Our "public" entry point. exp_buf can be NIL if exp_len is 0, it need not + * be NUL terminated (stop for NUL or out of length). + * Upon error *err_rest is set to a "newly allocated" string that points to + * where parse stopped (which could be at EOS if error happens in trailer). */ +static enum a_shexp_arith_error a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len, char const **err_rest); + +static void a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len); + +/* Count non-WS as well as normalized WS ([:"space":]+ -> ' ') in exp_buf, + * return count. If store!=NIL, also copy normalization. + * An all-WS exp_buf returns 0 */ +static uz a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil); + +/* Resolve and evaluate the "self-contained string" savp->sav_var. + * Take care to avoid name lookup loops */ +static boole a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp); + +/* Work top of the stack, which may pop & push etc */ +static boole a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self); + +static boole a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self); + +#if a_SHEXP_ARITH_DBG +static void a_shexp__arith_log(char const *fmt, ...); +#endif + +static enum a_shexp_arith_error +a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len, char const **err_rest){ + struct a_shexp_arith_stack sas_stack; + struct a_shexp_arith_ctx self; + NYD_IN; + + a_SHEXP_ARITH_L(("> arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + STRUCT_ZERO(struct a_shexp_arith_ctx, &self); +#ifdef a_SHEXP_ARITH_COOKIE + self.sac_cookie = cookie; +#endif + + ASSERT_NYD_EXEC(resp != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND); + DBG( *resp = 0; ) + ASSERT_NYD_EXEC(exp_len == 0 || exp_buf != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND); + + a_SHEXP_ARITH_IFS( self.sac_ifs_ws = ok_vlook(ifs_ws); ) + self.sac_stack = &sas_stack; + a_shexp__arith_eval(&self, exp_buf, exp_len); + + *resp = self.sac_rv; + if(self.sac_error != a_SHEXP_ARITH_ERR_NONE) + *err_rest = self.sac_error_rest; + + a_SHEXP_ARITH_L(("< arith_eval %zu <%.*s> -> <%lld> ERR<%d>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf, self.sac_rv, self.sac_error)); + + NYD_OU; + return self.sac_error; +} + +static void +a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len){ + char *ep, *cp, c; + u16 lop; + struct a_shexp_arith_stack *sasp; + void *mem_p; + NYD2_IN; + + a_SHEXP_ARITH_L((" > _arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + self->sac_error_rest = su_empty; + mem_p = NIL; + + sasp = self->sac_stack; + + /* Create single contiguous allocation for anything. + * Since we need to keep pointers to variable names along the way, simply + * NUL terminate in this large buffer (move backward by one first) */ + /* C99 */{ + union {void *v; char *c;} p; + uz i, j, a; + + /* Done for empty expression */ + if((i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, NIL)) == 0) + goto jleave; + + /* Overflow check: since arithmetic expressions are rarely long enough + * to come near this limit, laxe & fuzzy, not exact; max U32_MAX! */ + if(su_64( i > U32_MAX || ) + i >= ((UZ_MAX - i) / (su_ALIGNOF(*sasp->sas_nums) + + sizeof(*sasp->sas_ops) + 1))){ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + + j = su_ALIGNOF(*sasp->sas_nums) * ((i + 1) >> 1); + a = j + (sizeof(*sasp->sas_ops) * (i + 1)) + i + 1 +1; + mem_p = p.v = su_LOFI_ALLOC(a); + if(p.v == NIL){ + /* (For MX LOFI has _MUSTFAIL set though) */ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + sasp->sas_nums = sasp->sas_nums_top = S(struct a_shexp_arith_val*,p.v); + p.c += j; + sasp->sas_ops = sasp->sas_ops_top = S(u16*,p.v); + p.c += sizeof(*sasp->sas_ops) * i; + /* (room for moving back vars by one, to NUL terminate 'em) */ + a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, ep = ++p.c); + + a_SHEXP_ARITH_L((" ! _arith_eval ALLOC <%lu> " + "nums=%p (%lu) ops=%p %lu <%s>\n", + S(ul,a), sasp->sas_nums, S(ul,j / su_ALIGNOF(*sasp->sas_nums)), + sasp->sas_ops, S(ul,i), ep)); + } + + /* Start with a left paren */ + *sasp->sas_ops_top++ = lop = a_SHEXP_ARITH_OP_PAREN_LEFT; + + for(;;) Jouter:{ + u16 op; + + a_SHEXP_ARITH_L((" = _arith_eval TICK LOP <0x%02X %u> " + "nums=%lu ops=%lu DATA %lu <%s>\n", + lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + self->sac_error_rest = ep; + + if(*ep == '\0'){ + /* At the end of the expression pop anything left. + * Assume we have read PAREN_RIGHT */ + if(exp_buf != NIL){ + exp_buf = NIL; + op = a_SHEXP_ARITH_OP_PAREN_RIGHT; + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + goto jtok_go; + } + + /* After PAREN_RIGHT, we must be finished */ + if(sasp->sas_nums_top != &sasp->sas_nums[1]) + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } + + /* Skip (normalized) WS now */ + if(*ep == ' ') + ++ep; + ASSERT(!su_cs_is_space(*ep)); + + /* A number? */ + if(su_cs_is_digit(*ep)){ + BITENUM_IS(u32,su_idec_state) is; + + is = su_idec_cp(&sasp->sas_nums_top->sav_val, ep, 0, + a_SHEXP_ARITH_IDEC_MODE, S(char const**,&ep)); + if((is &= su_IDEC_STATE_EMASK) && is != su_IDEC_STATE_EBASE) + sasp->sas_nums_top->sav_val = 0; + sasp->sas_nums_top->sav_var = NIL; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval NUM <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + continue; + } + + /* Is it a variable name? */ + for(cp = ep; (c = *cp, a_SHEXP_ISVARC(c)); ++cp) + if(cp == ep && a_SHEXP_ISVARC_BAD1ST(c)) + break; + + if(cp != ep){ + for(;;){ + c = cp[-1]; + if(!a_SHEXP_ISVARC_BADNST(c)) + break; + if(--cp == ep){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* We reserved one byte at the front, so we can simply move back + * the variable name by one, and then NUL terminate it. + * (Unfortunately we do _have_ to copy; even more weird: move!) */ + su_mem_move(sasp->sas_nums_top->sav_var = &ep[-1], ep, P2UZ(cp - ep)); + cp[-1] = '\0'; + ep = cp; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval VAR <%s>\n", + sasp->sas_nums_top[-1].sav_var)); + continue; + } + + /* An operator. + * We turn prefix operators to multiple unary plus/minus if + * not attached to a variable name (++10 -> + + 10). + * (We adjust postfix to prefix below) */ + if((ep[0] == '+' || ep[0] == '-') && (ep[1] == ep[0])){ + if(sasp->sas_nums_top == sasp->sas_nums || + sasp->sas_nums_top[-1].sav_var == NIL){ + if((c = ep[2]) == ' ') + c = ep[3]; + + if(c != '\0' && (!a_SHEXP_ISVARC(c) || a_SHEXP_ISVARC_BAD1ST(c))){ + op = (ep[0] == '+') ? a_SHEXP_ARITH_OP_ADD + : a_SHEXP_ARITH_OP_SUB; + ++ep; + a_SHEXP_ARITH_L((" + _arith_eval OP PREFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", ep[0], ep[0], ep[0])); + goto jtok_go; + } + } + } + + /* Operator search */ + /* C99 */{ + char const *tokp; + + /* 3=NUL+OP+PREC */ + for(tokp = a_shexp_arith_op_toks; *tokp != '\0'; tokp += 3){ + for(cp = ep;; ++tokp, ++cp){ + if(*tokp == '\0'){ + ep = cp; + op = (S(u16,tokp[1]) << 8) | S(u8,tokp[2]); + goto jtok_go; + }else if(*tokp != *cp) + break; + } + + while(*tokp != '\0') + ++tokp; + } + self->sac_error = a_SHEXP_ARITH_ERR_OP_INVALID; + goto jleave; + } + +jtok_go:/* C99 */{ + u8 prec; + + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP <0x%02X %u> LOP <0x%02X %u> " + "nums=%lu ops=%lu %lu <%s>\n", + op, prec, lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(op == a_SHEXP_ARITH_OP_UNARY_PLUS){ + a_SHEXP_ARITH_L((" + _arith_eval IGNORE UNARY PLUS\n")); + continue; + } + + /* Correct our understanding of what there is. + * Post grammar: a++ reduce to num */ + if((lop & 0xFF) == a_SHEXP_ARITH_PREC_POSTFIX){ + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval LOP POSTFIX REDUCED to NUM\n")); + } + /* Adjust some binary/postfix operators */ + else if(lop != a_SHEXP_ARITH_OP_NUM){ + switch(op){ + case a_SHEXP_ARITH_OP_ADD: + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST: IGNORE UNARY PLUS\n")); + continue; + case a_SHEXP_ARITH_OP_SUB: + op = a_SHEXP_ARITH_OP_UNARY_MINUS; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_INC: + op = a_SHEXP_ARITH_OP_PREFIX_INC; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_DEC: + op = a_SHEXP_ARITH_OP_PREFIX_DEC; +junapre: + prec = a_SHEXP_ARITH_PREC_PREFIX; + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST TO UNARY/PREFIX\n")); + break; + } + } + /* Special: +10++VAR -> +10 + +VAR. (Since we do handle +10++11 + * correctly via "prefix split", we should also handle this) */ + else if(prec == a_SHEXP_ARITH_PREC_POSTFIX){ + ASSERT(lop == a_SHEXP_ARITH_OP_NUM); + if((c = ep[0]) == ' ') + c = ep[1]; + if(c != '\0' && (a_SHEXP_ISVARC(c) && !a_SHEXP_ISVARC_BAD1ST(c))){ + c = *--ep; + op = (c == '+') ? a_SHEXP_ARITH_OP_ADD : a_SHEXP_ARITH_OP_SUB; + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP POSTFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", c, c, c)); + } + } + + if((prec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + prec < a_SHEXP_ARITH_PREC_UNARY) || + prec >= a_SHEXP_ARITH_PREC_SKY){ + if(lop != a_SHEXP_ARITH_OP_NUM){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + goto jleave; + } + + /* Pop as much as possible */ + while(sasp->sas_ops_top != sasp->sas_ops){ + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + + a_SHEXP_ARITH_L((" + _arith_eval TRY POP - OP " + "<0x%02X %u>, NEW LOP <0x%02X %u 0x%X> nums=%lu ops=%lu\n", + op, op & 0xFF, lop, lop & 0xFF, + (*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK), + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* Special-case parenthesis groups */ + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + if(lop == a_SHEXP_ARITH_OP_PAREN_LEFT){ + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + if(!(*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK)){ + /* Resolve VAR to NUM */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + if(!a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + } + a_SHEXP_ARITH_L((" + _arith_eval OP () RESOLVED <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + } + sasp->sas_nums_top[-1].sav_var = NIL; + lop = a_SHEXP_ARITH_OP_NUM; + goto Jouter; + } + }else{ + u8 lprec; + + lprec = lop & 0xFF; + + /* */ + if(op == a_SHEXP_ARITH_OP_COND){ + u16 x; + + x = *sasp->sas_ops_top; + x &= a_SHEXP_ARITH_OP_FLAG_MASK; + if(x & a_SHEXP_ARITH_OP_FLAG_WHITEOUT){ + x ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + x |= a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT; + } + op |= x; + + /* Resolve as much as possible */ + while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + lprec != a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + if((--sasp->sas_nums_top)->sav_val == 0) + op |= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + + op |= *sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK; + /* Delay ternary */ + ++sasp->sas_ops_top; + break; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + uz recur; + u16 *opsp, x; + boole delay; + + delay = TRU1; + + /* Find our counterpart ? so we can toggle whiteout */ + opsp = sasp->sas_ops_top; + for(recur = 1;; --opsp){ + if(opsp == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + x = *opsp & a_SHEXP_ARITH_OP_MASK; + if(x == a_SHEXP_ARITH_OP_COND_COLON) + ++recur; + else if(x == a_SHEXP_ARITH_OP_COND && --recur == 0){ + *opsp |= a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON; + break; + } + } + op |= *opsp & a_SHEXP_ARITH_OP_FLAG_MASK; + op ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + + /* Resolve innermost condition asap. + * In "1 ? 0 ? 5 : 6 : 3", resolve innermost upon :3 */ + while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + lprec != a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + /* If we now see a COLON, we have to resolve further! + * This is because of code flow restrictions of the Dijkstra + * algorithm, which fits ternary (as well as visual user + * error feedback) badly (the way we do): pop as pop can! */ + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + delay = FAL0; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + lop = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + } + + if(lop != a_SHEXP_ARITH_OP_COND){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + if(delay) + ++sasp->sas_ops_top; + a_SHEXP_ARITH_L((" + _arith_eval DELAY TERNARY ?:%s\n", + ((op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) + ? " WHITEOUT" : su_empty))); + break; + } + /* Is this a right-associative operation? */ + else if(lprec < prec){ + ++sasp->sas_ops_top; + a_SHEXP_ARITH_L((" + _arith_eval DELAY PRECEDENCE\n")); + break; + }else if(lprec == prec && prec == a_SHEXP_ARITH_PREC_ASSIGN){ + ++sasp->sas_ops_top; + a_SHEXP_ARITH_L((" + _arith_eval DELAY RIGHT ASSOC\n")); + break; + }else if(lop == a_SHEXP_ARITH_OP_COND){ + ++sasp->sas_ops_top; + a_SHEXP_ARITH_L((" + _arith_eval DELAY CONDITION\n")); + break; + } + } + + /* */ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops && + &sasp->sas_ops_top[-1] > sasp->sas_ops); + ASSERT((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND); + --sasp->sas_ops_top; + *sasp->sas_ops_top ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + } + } + + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* Push this operator to the stack and remember it */ + if(sasp->sas_ops_top > sasp->sas_ops && + (op & 0xFF) != a_SHEXP_ARITH_PREC_COND) + op |= sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_FLAG_MASK; + *sasp->sas_ops_top++ = op; + lop = op & a_SHEXP_ARITH_OP_MASK; + a_SHEXP_ARITH_L((" + _arith_eval OP PUSH <0x%02X %u> nums=%lu ops=%lu\n", + op, (op & 0xFF), S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + } + } + + self->sac_rv = sasp->sas_nums->sav_val; + +jleave: + if(self->sac_error != a_SHEXP_ARITH_ERR_NONE) + self->sac_error_rest = savestr(self->sac_error_rest); + + if(mem_p != NIL) + su_LOFI_FREE(mem_p); + + a_SHEXP_ARITH_L((" < _arith_eval <%lld> ERR<%d>\n", + self->sac_rv, self->sac_error)); + + NYD2_OU; +} + +static uz +a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil){ + a_SHEXP_ARITH_IFS( char const *ifs_ws; ) + char c; + boole last_ws, ws; + uz rv; + NYD2_IN; + UNUSED(self); + + rv = 0; + a_SHEXP_ARITH_IFS( ifs_ws = self->sac_ifs_ws; ) + + for(;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + goto jleave; + if(!(su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + )) + break; + } + + for(last_ws = FAL0;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + break; + + ws = (su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + ); + if(ws){ + if(last_ws) + continue; + c = ' '; + } + last_ws = ws; + + ++rv; + if(store_or_nil != NIL) + *store_or_nil++ = c; + } + + if(last_ws){ + --rv; + if(store_or_nil != NIL) + --store_or_nil; + } + +jleave: + if(store_or_nil != NIL) + *store_or_nil = '\0'; + + NYD2_OU; + return rv; +} + +static boole +a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp){ + struct a_shexp_arith_name_stack sans_stack, *sansp; + struct a_shexp_arith_stack sas_stack, *sasp; + char const *cp; + NYD_IN; + ASSERT(savp->sav_var != NIL); + + a_SHEXP_ARITH_L(("> _arith_val_eval %p <%s>\n", savp, savp->sav_var)); + + savp->sav_val = 0; + + /* Also look in program environment XXX configurable? */ + cp = n_var_vlook(savp->sav_var, TRU1); + if(cp == NIL) + goto jleave; + + for(sansp = self->sac_name_stack; sansp != NIL; sansp = sansp->sans_last){ + if(!su_cs_cmp(sansp->sans_var, savp->sav_var)){ + self->sac_error = a_SHEXP_ARITH_ERR_NAME_LOOP; + goto jleave; + } + } + + /* cp must be a self-contained expression. + * However, in most cases it solely consists of an integer, shortcut it! */ + if(su_cs_is_digit(*cp) && (su_idec_cp(&savp->sav_val, cp, 0, + a_SHEXP_ARITH_IDEC_MODE, NIL) & su_IDEC_STATE_CONSUMED)){ + a_SHEXP_ARITH_L((" + _arith_val_eval NUM DIRECT <%lld>\n", + savp->sav_val)); + }else{ + sasp = self->sac_stack; + self->sac_stack = &sas_stack; + + sans_stack.sans_last = sansp = self->sac_name_stack; + sans_stack.sans_var = savp->sav_var; + self->sac_name_stack = &sans_stack; + + a_shexp__arith_eval(self, cp, UZ_MAX); + savp->sav_val = self->sac_rv; + /* .sav_var may be needed further on for updating purposes */ + + self->sac_stack = sasp; + self->sac_name_stack = sansp; + } + + cp = NIL; +jleave: + a_SHEXP_ARITH_L(("< _arith_val_eval %p <%s> <%lld> -> OK <%d>\n", + savp, savp->sav_var, savp->sav_val, + (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE))); + + NYD_OU; + return (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE); +} + +static boole +a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ + struct a_shexp_arith_val *nums_top; + u8 prec; + u16 op; + struct a_shexp_arith_stack *sasp; + s64 val; + boole rv, ign; + NYD_IN; + + rv = FAL0; + val = 0; + sasp = self->sac_stack; + op = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + ign = ((*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) != 0); + + a_SHEXP_ARITH_L((" > _arith_op_apply %s<0x%02X %u> " + "nums_top=%p (%lu) ops_top=%p (%lu)\n", + (ign ? "WHITEOUT " : su_empty), op, (op & 0xFF), sasp->sas_nums_top, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + sasp->sas_ops_top, S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* At least one argument is always needed */ + if((nums_top = sasp->sas_nums_top) == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + goto jleave; + } + --nums_top; + + /* Resolve name to value as necessary */ + if(!ign && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + val = nums_top->sav_val; + prec = op & 0xFF; + + /* Not a binary operator? */ + if(prec >= a_SHEXP_ARITH_PREC_UNARY && prec < a_SHEXP_ARITH_PREC_SKY){ + if(ign) + goto jquick; + + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_UNARY_NOT: val = !val; break; + case a_SHEXP_ARITH_OP_UNARY_BIT_NOT: val = ~val; break; + case a_SHEXP_ARITH_OP_UNARY_MINUS: val = -val; break; + case a_SHEXP_ARITH_OP_PREFIX_INC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_INC: ++val; break; + case a_SHEXP_ARITH_OP_PREFIX_DEC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_DEC: --val; break; + } + }else if(op == a_SHEXP_ARITH_OP_COND){ + if(!(*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON)){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_NO_COLON; + goto jleave; + } + goto jquick; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(nums_top > sasp->sas_nums); + if(!ign) + nums_top[-1].sav_val = nums_top[0].sav_val; + else + val = nums_top[-1].sav_val; + sasp->sas_nums_top = nums_top; + + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + if((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND_COLON){ + --sasp->sas_ops_top; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + if(!ign) + sasp->sas_nums_top[-1].sav_val = val; + } + }else{ + /* Binaries need two numbers: one is popped, the other replaced */ + s64 rval; + + if(nums_top == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + goto jleave; + } + sasp->sas_nums_top = nums_top--; + + if(ign) + goto jquick; + + /* Resolve LHV as necessary */ + if(op != a_SHEXP_ARITH_OP_COMMA && op != a_SHEXP_ARITH_OP_ASSIGN && + nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + rval = val; + val = nums_top->sav_val; /* (may be bogus for assign, fixed soon) */ + + /* In precedence order (excluding assignments) */ + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_COMMA: FALLTHRU + + case a_SHEXP_ARITH_OP_ASSIGN: val = rval; break; + + case a_SHEXP_ARITH_OP_OR: val = (val != 0 || rval != 0); break; + case a_SHEXP_ARITH_OP_AND: val = (val != 0 && rval != 0); break; + + case a_SHEXP_ARITH_OP_BIT_OR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_OR: val |= rval; break; + case a_SHEXP_ARITH_OP_BIT_XOR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_XOR: val ^= rval; break; + case a_SHEXP_ARITH_OP_BIT_AND: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_AND: val &= rval; break; + + case a_SHEXP_ARITH_OP_EQ: val = (val == rval); break; + case a_SHEXP_ARITH_OP_NE: val = (val != rval); break; + + case a_SHEXP_ARITH_OP_LE: val = (val <= rval); break; + case a_SHEXP_ARITH_OP_GE: val = (val >= rval); break; + case a_SHEXP_ARITH_OP_LT: val = (val < rval); break; + case a_SHEXP_ARITH_OP_GT: val = (val > rval); break; + + case a_SHEXP_ARITH_OP_SHIFT_LEFT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_LEFT: val <<= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHT: val >>= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHTU: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHTU: + val = S(s64,S(u64,val) >> rval); + break; + + case a_SHEXP_ARITH_OP_ADD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_ADD: val += rval; break; + case a_SHEXP_ARITH_OP_SUB: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SUB: val -= rval; break; + + case a_SHEXP_ARITH_OP_MUL: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MUL: val *= rval; break; + /* For /,%, avoid lvh=S64_MIN, rhv=-1: + * CHANGES, bash 4.3 [ac50fbac377e32b98d2de396f016ea81e8ee9961]: + * Fixed a bug that caused floating-point exceptions and + * overflow errors for the / and % arithmetic operators when + * using INTMAX_MIN and -1. */ + case a_SHEXP_ARITH_OP_DIV: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_DIV: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val != S64_MIN || rval != -1) + val /= rval; + break; + case a_SHEXP_ARITH_OP_MOD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MOD: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val == S64_MIN && rval == -1) + val = 0; + else + val %= rval; + break; + + case a_SHEXP_ARITH_OP_EXP: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_EXP: + if(rval < 0){ + self->sac_error = a_SHEXP_ARITH_ERR_EXP_INVALID; + goto jleave; + }else{ + s64 i; + + for(i = 1; rval > 0; --rval) + i *= val; + val = i; + } + break; + } + } + + /* Assignment updates a variable, which must exist. + * For prefix and postfix operators, too: we already turned them into + * multiple unary plus/minus unless we had seen a variable name */ +jquick: + if(prec == a_SHEXP_ARITH_PREC_ASSIGN || prec == a_SHEXP_ARITH_PREC_PREFIX || + prec == a_SHEXP_ARITH_PREC_POSTFIX){ + char buf[su_IENC_BUFFER_SIZE], *bp; + + if(nums_top->sav_var == NIL){ + self->sac_error = a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR; + goto jleave; + } + + if(!ign){ + bp = su_ienc_s64(buf, val, 10); + n_var_vset(nums_top->sav_var, S(up,bp), FAL0); + } + + /* And restore the stack value again for postfix operators */ + if(op == a_SHEXP_ARITH_OP_POSTFIX_INC) + --val; + else if(op == a_SHEXP_ARITH_OP_POSTFIX_DEC) + ++val; + + if(!ign) + a_SHEXP_ARITH_L((" + _arith_op_apply VAR <%s> SET <%s> VAL <%lld>\n", + nums_top->sav_var, bp, val)); + } + + nums_top->sav_val = val; + nums_top->sav_var = NIL; + + rv = TRU1; +jleave: + a_SHEXP_ARITH_L((" < _arith_op_apply RV %d <0x%02X %u> RES<%lld> ERR<%d> " + "nums=%lu ops=%lu\n", + rv, op, op & 0xFF, val, self->sac_error, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + NYD_OU; + return rv; +} + +static boole +a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self){ + u16 lop, lprec; + boole next_stop; + NYD_IN; + + for(next_stop = FAL0;;){ + if(!a_shexp__arith_op_apply(self)){ + next_stop = FAL0; + break; + } + if(next_stop) + break; + lop = *--self->sac_stack->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + next_stop = (lprec == a_SHEXP_ARITH_PREC_PAREN_LEFT || + lop == a_SHEXP_ARITH_OP_COND); + } + + NYD_OU; + return next_stop; +} + +#if a_SHEXP_ARITH_DBG +static void +a_shexp__arith_log(char const *fmt, ...){ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +#endif + +#undef a_SHEXP_ARITH_IFS + +#undef a_SHEXP_ARITH_DBG +#undef a_SHEXP_ARITH_L +#undef a_SHEXP_ARITH_IDEC_MODE + +/* s-it-mode */ -- 2.37.1 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Fri Aug 5 20:20:26 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 05 Aug 2022 22:20:26 +0200 Subject: [PATCH] shell: shexp-arith: also bring "I1=I2=10 I2=3; echo $(( I1, I2 ))" to bash In-Reply-To: References: Message-ID: <8629f1bb0471e00284eac7c61b06f9eea999ec1a.1659730455.git.steffen@sdaoden.eu> --- Hello! You will not believe it, but now that i was reading over the mail i sent to busybox@ i saw a comma related bug in _op_apply(), and that lead me to another test, and that then to another fix even! shell/shexp-arith.h | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h index 96a63bd2b9..2fb867f1c2 100644 --- a/shell/shexp-arith.h +++ b/shell/shexp-arith.h @@ -687,13 +687,23 @@ junapre: if(delay) ++sasp->sas_ops_top; - a_SHEXP_ARITH_L((" + _arith_eval DELAY TERNARY ?:%s\n", + a_SHEXP_ARITH_L((" + _arith_eval %sTERNARY ?:%s\n", + (delay ? "DELAY " : su_empty), ((op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) ? " WHITEOUT" : su_empty))); break; } /* Is this a right-associative operation? */ else if(lprec < prec){ + /* There is a special case though: if we are about to delay + * a comma sequence, and the LHV is a variable, then we need + * to expand that immediately, in order to correctly get + * I1=I2=10 I2=3; echo $(( I1, I2 )) */ + if(op == a_SHEXP_ARITH_OP_COMMA && + sasp->sas_nums_top[-1].sav_var != NIL && + !a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; ++sasp->sas_ops_top; a_SHEXP_ARITH_L((" + _arith_eval DELAY PRECEDENCE\n")); break; @@ -837,8 +847,8 @@ a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, /* cp must be a self-contained expression. * However, in most cases it solely consists of an integer, shortcut it! */ - if(su_cs_is_digit(*cp) && (su_idec_cp(&savp->sav_val, cp, 0, - a_SHEXP_ARITH_IDEC_MODE, NIL) & su_IDEC_STATE_CONSUMED)){ + if(su_idec_cp(&savp->sav_val, cp, 0, a_SHEXP_ARITH_IDEC_MODE, NIL + ) & su_IDEC_STATE_CONSUMED){ a_SHEXP_ARITH_L((" + _arith_val_eval NUM DIRECT <%lld>\n", savp->sav_val)); }else{ @@ -956,8 +966,7 @@ a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ goto jquick; /* Resolve LHV as necessary */ - if(op != a_SHEXP_ARITH_OP_COMMA && op != a_SHEXP_ARITH_OP_ASSIGN && - nums_top->sav_var != NIL && + if(op != a_SHEXP_ARITH_OP_ASSIGN && nums_top->sav_var != NIL && !a_shexp__arith_val_eval(self, nums_top)) goto jleave; -- 2.37.1 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Fri Aug 5 20:49:40 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 05 Aug 2022 22:49:40 +0200 Subject: [PATCH] shell: tweak latter: apply to all operators In-Reply-To: References: <8629f1bb0471e00284eac7c61b06f9eea999ec1a.1659730455.git.steffen@sdaoden.eu> Message-ID: <4ff570add066cb91596361cd2d200cf758bf3bf6.1659731750.git.steffen@sdaoden.eu> --- Arriba Arriba Andale Andale! P.S.: i also have written a graylister for postfix, in C. 94003 bytes, works just smooth for months; is a package in Alpine, and has a focus-sender mode that i now use and that is in no way worse than long delays. In fact better to me, because i also have private correspondance from the server, and many german companies and such use pressing service providers which hammer you from (the original and) multiple (other) IPs if the first mail is delayed. 4-mask=24 6-mask=64 delay-max=300 delay-min=0 delay-progressive gc-rebalance=3 gc-timeout=10080 limit=242000 limit-delay=221000 server-queue=64 #server-timeout=30 server-timeout=0 store-path=/var/lib/pg # ll /var/lib/|grep pg drwxr-x--- 2 smtpd smtpd 4096 Aug 5 21:25 pg/ focus-sender # ! --focus-sender #count=2 count=1 msg-allow=permit msg-block=REJECT msg-defer=DEFER 4.2.0 Service temporarily faded to Gray allow .alpinelinux.org allow .osuosl.org allow sdaoden.eu Just in case; i drive it with 0.00 CPU time per week in less than a megabyte of RAM; it could make the box less busy. shell/shexp-arith.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h index 2fb867f1c2..d4b723b5ec 100644 --- a/shell/shexp-arith.h +++ b/shell/shexp-arith.h @@ -696,11 +696,10 @@ junapre: /* Is this a right-associative operation? */ else if(lprec < prec){ /* There is a special case though: if we are about to delay - * a comma sequence, and the LHV is a variable, then we need - * to expand that immediately, in order to correctly get - * I1=I2=10 I2=3; echo $(( I1, I2 )) */ - if(op == a_SHEXP_ARITH_OP_COMMA && - sasp->sas_nums_top[-1].sav_var != NIL && + * and the LHV is a variable, then we need to expand that + * immediately, in order to correctly get + * I1=I2=10 I2=3; echo $(( I1 [,+..] I2 )) */ + if(sasp->sas_nums_top[-1].sav_var != NIL && !a_shexp__arith_val_eval(self, &sasp->sas_nums_top[-1])) goto jleave; -- 2.37.1 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From danomimanchego123 at gmail.com Fri Aug 5 23:15:57 2022 From: danomimanchego123 at gmail.com (Danomi Manchego) Date: Fri, 5 Aug 2022 19:15:57 -0400 Subject: udhcpc6 kernel listen mode is broken In-Reply-To: References: Message-ID: Yes, these changes work - they match what I patched locally. Thanks for getting back to this. On Tue, Aug 2, 2022 at 9:35 AM Denys Vlasenko wrote: > > Fixed, thank you. Can you try current git? > > On Sat, Jun 4, 2022 at 2:58 AM Danomi Manchego > wrote: > > > > Sorry all, I did not realize that there was a function called > > d6_listen_socket() in d6_socket.c - so no new function is needed to > > fix DHCPv6 Renew reply processing. The correct message ID is needed > > and use of d6_listen_socket() rather than udhcp_listen_socket(). > > > > Regards, > > Danomi - > > > > On Tue, May 10, 2022 at 9:34 PM Danomi Manchego > > wrote: > > > > > > Hello, > > > > > > On April 1, I sent "udhcpc6 renew message copy/paste error" email > > > about udhcpc6 sends the wrong message ID for Renew message due to > > > copy/paste error from IPv4 dhcpc.c. (Was DHCPREQUEST, should be > > > D6_MSG_RENEW.) After fixing that, I found that the Renew is sent > > > correctly, and the DHCPv6 server replies, but udhcpc6 fails to get the > > > reply. Because there is no reply, the lease does not get extended by > > > Renew. I found that the issue is that the udhcp_listen_socket() in > > > socket.c (used for kernel listen mode in d6_dhcpc.c) is somewhat > > > hard-coded for IPv4. I was able to get kernel listen mode to work by > > > adding a new function like this to socket.c and using it in d6_dhcp.c. > > > My udhcp6_listen_socket() differs from udhcp_listen_socket() as > > > follows: > > > > > > * Use PF_INET6 instead of PF_INET. > > > > > > * Set IPPROTO_IPV6 / IPV6_V6ONLY socket option rather than broadcast option. > > > > > > * Use `struct sockaddr_in6` instead of `struct sockaddr_in`. > > > > > > * Use AF_INET6 instead of AF_INET. > > > > > > (Maybe SOCK_CLOEXEC should also be set in *both* functions when > > > calling xsocket since udhcpc/udhcpc6 invoke external udhcpc.script, > > > but I did not try it.) > > > > > > My function is pasted below. > > > > > > int FAST_FUNC udhcp6_listen_socket(/*uint32_t ip,*/ int port, const char *inf) > > > { > > > int fd; > > > struct sockaddr_in6 addr; > > > char *colon; > > > > > > log2("opening listen socket on *:%d %s", port, inf); > > > fd = xsocket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP); > > > > > > setsockopt_reuseaddr(fd); > > > > > > if (setsockopt_1(fd, IPPROTO_IPV6, IPV6_V6ONLY) < 0) > > > bb_simple_perror_msg_and_die("IPPROTO_IPV6"); > > > > > > /* SO_BINDTODEVICE doesn't work on ethernet aliases (ethN:M) */ > > > colon = strrchr(inf, ':'); > > > if (colon) > > > *colon = '\0'; > > > > > > if (setsockopt_bindtodevice(fd, inf)) > > > xfunc_die(); /* warning is already printed */ > > > > > > if (colon) > > > *colon = ':'; > > > > > > memset(&addr, 0, sizeof(addr)); > > > addr.sin6_family = AF_INET6; > > > addr.sin6_port = htons(port); > > > /* addr.sin_addr.s_addr = ip; - all-zeros is INADDR_ANY */ > > > xbind(fd, (struct sockaddr *)&addr, sizeof(addr)); > > > > > > return fd; > > > } > > > > > > Regards, > > > Danomi - > > _______________________________________________ > > busybox mailing list > > busybox at busybox.net > > http://lists.busybox.net/mailman/listinfo/busybox From steffen at sdaoden.eu Mon Aug 8 21:31:10 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Mon, 08 Aug 2022 23:31:10 +0200 Subject: [PATCH] shell: shexp-arith: also bring "I1=I2=10 I2=3; echo $(( I1, I2 ))" to bash In-Reply-To: <8629f1bb0471e00284eac7c61b06f9eea999ec1a.1659730455.git.steffen@sdaoden.eu> References: <8629f1bb0471e00284eac7c61b06f9eea999ec1a.1659730455.git.steffen@sdaoden.eu> Message-ID: <20220808213110.VRCLl%steffen@sdaoden.eu> Hello! Steffen Nurpmeso wrote in <8629f1bb0471e00284eac7c61b06f9eea999ec1a.1659730455.git.steffen at sdaoden\ .eu>: ... |You will not believe it, but now that i was reading over the mail |i sent to busybox@ i saw a comma related bug in _op_apply(), and |that lead me to another test, and that then to another fix even! So please wait with that one, shall you already look at it. On Sunday i thought about it and have to say it still has issues for some of those conditions, for example 2 and 3 of these four, s I1=I2=10 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" s I1=I2=10 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" s I1=I2=10 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" s I1=I2=10 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" and compared to how bash evaluates these constructs ( == WILD RECUR <10><10> -<11><11> -<21><11> +<11><10> +<21><10> <10><10> ): i will add more such recursive-evaluation-that-modifies- following-constructs tests (of course: POSIX makes it unspecified if a veriable contains anything else but solely a number, but still), and once i have it going i will post a single changeset again, ok? Sorry for the noise, the problem just came to my mind when i was reading the email that came back from busybox@, i had not seen it before. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Wed Aug 10 15:36:40 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Wed, 10 Aug 2022 17:36:40 +0200 Subject: [PATCHv2] shell: exchange Dijkstra $(( )) evaluator.. In-Reply-To: References: Message-ID: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> The former implementation was not correct regarding whiteouts in ?: conditional branches. The new one also parses a bit better, in effect on equal level than bash with its recursive descendent parser. --- Hello again. Ok i am out of ideas on further tests, and it works. Mostly a take-over miss it was, and a bit. The diff is a bit larger because i changed some comments, too. Unfortunately the necessary changes add another 100 bytes exactly. 2938 0 0 2938 b7a math.o 4668 0 0 4668 123c math.o ... 4768 0 0 4768 12a0 shell/math.o This can surely be stripped by a couple of hundred bytes if the compatibility shims are replaced with busybox-only code, which i have not (yet) done. I am (also still) hoping for some optimizations regarding regarding resolving of ?: conditionals, but my hope is vanishing (for major ones, anyway). What can be made better is tracking of where errors happen; this requires a different type for the operator stack: it surely will add some more bytes, too. For busybox instead removing all this error location tracking will surely save some bytes. I found out that the Dijkstra algorithm is not the best fit if anything but unary and binary is to be handled, but it was an interesting experience. And the result is still a pretty compact piece of code, in file size bytes almost en par with bash's (stack) recursive descendant one, and also the results match. Ciao! shell/ash.c | 4 +- shell/hush.c | 13 +- shell/math.c | 740 +++++----------------------- shell/math.h | 3 +- shell/shexp-arith.h | 1128 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 1255 insertions(+), 633 deletions(-) create mode 100644 shell/shexp-arith.h diff --git a/shell/ash.c b/shell/ash.c index 55c1034f55..7c5449f497 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6029,8 +6029,10 @@ ash_arith(const char *s) INT_OFF; result = arith(&math_state, s); - if (math_state.errmsg) + if (math_state.errmsg) { ash_msg_and_raise_error(math_state.errmsg); + free(math_state.errmsg); + } INT_ON; return result; diff --git a/shell/hush.c b/shell/hush.c index 051b123e78..f59bb57b3b 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6475,7 +6475,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, } #if ENABLE_FEATURE_SH_MATH -static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) +static arith_t expand_and_evaluate_arith(const char *arg, char **errmsg_p) { arith_state_t math_state; arith_t res; @@ -6489,8 +6489,11 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) free(exp_str); if (errmsg_p) *errmsg_p = math_state.errmsg; - if (math_state.errmsg) + if (math_state.errmsg) { msg_and_die_if_script(math_state.errmsg); + if (errmsg_p == NULL) + free(math_state.errmsg); + } return res; } #endif @@ -6814,11 +6817,13 @@ static NOINLINE int expand_one_var(o_string *output, int n, */ arith_t beg, len; unsigned vallen; - const char *errmsg; + char *errmsg; beg = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { + free(errmsg); goto empty_result; + } debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); *p++ = SPECIAL_VAR_SYMBOL; exp_word = p; diff --git a/shell/math.c b/shell/math.c index 76d22c9bd5..a20596089d 100644 --- a/shell/math.c +++ b/shell/math.c @@ -116,398 +116,6 @@ #include "libbb.h" #include "math.h" -typedef unsigned char operator; - -/* An operator's token id is a bit of a bitfield. The lower 5 bits are the - * precedence, and 3 high bits are an ID unique across operators of that - * precedence. The ID portion is so that multiple operators can have the - * same precedence, ensuring that the leftmost one is evaluated first. - * Consider * and / - */ -#define tok_decl(prec,id) (((id)<<5) | (prec)) -#define PREC(op) ((op) & 0x1F) - -#define TOK_LPAREN tok_decl(0,0) - -#define TOK_COMMA tok_decl(1,0) - -/* All assignments are right associative and have the same precedence, - * but there are 11 of them, which doesn't fit into 3 bits for unique id. - * Abusing another precedence level: - */ -#define TOK_ASSIGN tok_decl(2,0) -#define TOK_AND_ASSIGN tok_decl(2,1) -#define TOK_OR_ASSIGN tok_decl(2,2) -#define TOK_XOR_ASSIGN tok_decl(2,3) -#define TOK_PLUS_ASSIGN tok_decl(2,4) -#define TOK_MINUS_ASSIGN tok_decl(2,5) -#define TOK_LSHIFT_ASSIGN tok_decl(2,6) -#define TOK_RSHIFT_ASSIGN tok_decl(2,7) - -#define TOK_MUL_ASSIGN tok_decl(3,0) -#define TOK_DIV_ASSIGN tok_decl(3,1) -#define TOK_REM_ASSIGN tok_decl(3,2) - -#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) - -/* Ternary conditional operator is right associative too */ -#define TOK_CONDITIONAL tok_decl(4,0) -#define TOK_CONDITIONAL_SEP tok_decl(4,1) - -#define TOK_OR tok_decl(5,0) - -#define TOK_AND tok_decl(6,0) - -#define TOK_BOR tok_decl(7,0) - -#define TOK_BXOR tok_decl(8,0) - -#define TOK_BAND tok_decl(9,0) - -#define TOK_EQ tok_decl(10,0) -#define TOK_NE tok_decl(10,1) - -#define TOK_LT tok_decl(11,0) -#define TOK_GT tok_decl(11,1) -#define TOK_GE tok_decl(11,2) -#define TOK_LE tok_decl(11,3) - -#define TOK_LSHIFT tok_decl(12,0) -#define TOK_RSHIFT tok_decl(12,1) - -#define TOK_ADD tok_decl(13,0) -#define TOK_SUB tok_decl(13,1) - -#define TOK_MUL tok_decl(14,0) -#define TOK_DIV tok_decl(14,1) -#define TOK_REM tok_decl(14,2) - -/* Exponent is right associative */ -#define TOK_EXPONENT tok_decl(15,1) - -/* Unary operators */ -#define UNARYPREC 16 -#define TOK_BNOT tok_decl(UNARYPREC,0) -#define TOK_NOT tok_decl(UNARYPREC,1) - -#define TOK_UMINUS tok_decl(UNARYPREC+1,0) -#define TOK_UPLUS tok_decl(UNARYPREC+1,1) - -#define PREC_PRE (UNARYPREC+2) - -#define TOK_PRE_INC tok_decl(PREC_PRE, 0) -#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) - -#define PREC_POST (UNARYPREC+3) - -#define TOK_POST_INC tok_decl(PREC_POST, 0) -#define TOK_POST_DEC tok_decl(PREC_POST, 1) - -#define SPEC_PREC (UNARYPREC+4) - -#define TOK_NUM tok_decl(SPEC_PREC, 0) -#define TOK_RPAREN tok_decl(SPEC_PREC, 1) - -static int -is_assign_op(operator op) -{ - operator prec = PREC(op); - fix_assignment_prec(prec); - return prec == PREC(TOK_ASSIGN) - || prec == PREC_PRE - || prec == PREC_POST; -} - -static int -is_right_associative(operator prec) -{ - return prec == PREC(TOK_ASSIGN) - || prec == PREC(TOK_EXPONENT) - || prec == PREC(TOK_CONDITIONAL); -} - - -typedef struct { - arith_t val; - /* We acquire second_val only when "expr1 : expr2" part - * of ternary ?: op is evaluated. - * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). - * ':' produces a new value which has two parts, val and second_val; - * then '?' selects one of them based on its left side. - */ - arith_t second_val; - char second_val_present; - /* If NULL then it's just a number, else it's a named variable */ - char *var; -} var_or_num_t; - -typedef struct remembered_name { - struct remembered_name *next; - const char *var; -} remembered_name; - - -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr); - -static const char* -arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) -{ - if (t->var) { - const char *p = math_state->lookupvar(t->var); - if (p) { - remembered_name *cur; - remembered_name cur_save; - - /* did we already see this name? - * testcase: a=b; b=a; echo $((a)) - */ - for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { - if (strcmp(cur->var, t->var) == 0) { - /* Yes */ - return "expression recursion loop detected"; - } - } - - /* push current var name */ - cur = math_state->list_of_recursed_names; - cur_save.var = t->var; - cur_save.next = cur; - math_state->list_of_recursed_names = &cur_save; - - /* recursively evaluate p as expression */ - t->val = evaluate_string(math_state, p); - - /* pop current var name */ - math_state->list_of_recursed_names = cur; - - return math_state->errmsg; - } - /* treat undefined var as 0 */ - t->val = 0; - } - return 0; -} - -/* "Applying" a token means performing it on the top elements on the integer - * stack. For an unary operator it will only change the top element, but a - * binary operator will pop two arguments and push the result */ -static NOINLINE const char* -arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) -{ -#define NUMPTR (*numstackptr) - - var_or_num_t *top_of_stack; - arith_t rez; - const char *err; - - /* There is no operator that can work without arguments */ - if (NUMPTR == numstack) - goto err; - - top_of_stack = NUMPTR - 1; - - /* Resolve name to value, if needed */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - - rez = top_of_stack->val; - if (op == TOK_UMINUS) - rez = -rez; - else if (op == TOK_NOT) - rez = !rez; - else if (op == TOK_BNOT) - rez = ~rez; - else if (op == TOK_POST_INC || op == TOK_PRE_INC) - rez++; - else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) - rez--; - else if (op != TOK_UPLUS) { - /* Binary operators */ - arith_t right_side_val; - char bad_second_val; - - /* Binary operators need two arguments */ - if (top_of_stack == numstack) - goto err; - /* ...and they pop one */ - NUMPTR = top_of_stack; /* this decrements NUMPTR */ - - bad_second_val = top_of_stack->second_val_present; - if (op == TOK_CONDITIONAL) { /* ? operation */ - /* Make next if (...) protect against - * $((expr1 ? expr2)) - that is, missing ": expr" */ - bad_second_val = !bad_second_val; - } - if (bad_second_val) { - /* Protect against $((expr expr1 : expr2)) */ - return "malformed ?: operator"; - } - - top_of_stack--; /* now points to left side */ - - if (op != TOK_ASSIGN) { - /* Resolve left side value (unless the op is '=') */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - } - - right_side_val = rez; - rez = top_of_stack->val; - if (op == TOK_CONDITIONAL) /* ? operation */ - rez = (rez ? right_side_val : top_of_stack[1].second_val); - else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ - if (top_of_stack == numstack) { - /* Protect against $((expr : expr)) */ - return "malformed ?: operator"; - } - top_of_stack->second_val_present = op; - top_of_stack->second_val = right_side_val; - } - else if (op == TOK_BOR || op == TOK_OR_ASSIGN) - rez |= right_side_val; - else if (op == TOK_OR) - rez = right_side_val || rez; - else if (op == TOK_BAND || op == TOK_AND_ASSIGN) - rez &= right_side_val; - else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) - rez ^= right_side_val; - else if (op == TOK_AND) - rez = rez && right_side_val; - else if (op == TOK_EQ) - rez = (rez == right_side_val); - else if (op == TOK_NE) - rez = (rez != right_side_val); - else if (op == TOK_GE) - rez = (rez >= right_side_val); - else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) - rez >>= right_side_val; - else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) - rez <<= right_side_val; - else if (op == TOK_GT) - rez = (rez > right_side_val); - else if (op == TOK_LT) - rez = (rez < right_side_val); - else if (op == TOK_LE) - rez = (rez <= right_side_val); - else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) - rez *= right_side_val; - else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) - rez += right_side_val; - else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) - rez -= right_side_val; - else if (op == TOK_ASSIGN || op == TOK_COMMA) - rez = right_side_val; - else if (op == TOK_EXPONENT) { - arith_t c; - if (right_side_val < 0) - return "exponent less than 0"; - c = 1; - while (--right_side_val >= 0) - c *= rez; - rez = c; - } - else if (right_side_val == 0) - return "divide by zero"; - else if (op == TOK_DIV || op == TOK_DIV_ASSIGN - || op == TOK_REM || op == TOK_REM_ASSIGN) { - /* - * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' - * - * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1 - * and thus is not representable. - * Some CPUs segfault trying such op. - * Others overflow MAX_POSITIVE_INT+1 to - * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000). - * Make sure to at least not SEGV here: - */ - if (right_side_val == -1 - && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */ - ) { - right_side_val = 1; - } - if (op == TOK_DIV || op == TOK_DIV_ASSIGN) - rez /= right_side_val; - else { - rez %= right_side_val; - } - } - } - - if (is_assign_op(op)) { - char buf[sizeof(arith_t)*3 + 2]; - - if (top_of_stack->var == NULL) { - /* Hmm, 1=2 ? */ - goto err; - } - /* Save to shell variable */ - sprintf(buf, ARITH_FMT, rez); - math_state->setvar(top_of_stack->var, buf); - /* After saving, make previous value for v++ or v-- */ - if (op == TOK_POST_INC) - rez--; - if (op == TOK_POST_DEC) - rez++; - } - - top_of_stack->val = rez; - /* Erase var name, it is just a number now */ - top_of_stack->var = NULL; - return NULL; - err: - return "arithmetic syntax error"; -#undef NUMPTR -} - -/* longest must be first */ -static const char op_tokens[] ALIGN1 = { - '<','<','=',0, TOK_LSHIFT_ASSIGN, - '>','>','=',0, TOK_RSHIFT_ASSIGN, - '<','<', 0, TOK_LSHIFT, - '>','>', 0, TOK_RSHIFT, - '|','|', 0, TOK_OR, - '&','&', 0, TOK_AND, - '!','=', 0, TOK_NE, - '<','=', 0, TOK_LE, - '>','=', 0, TOK_GE, - '=','=', 0, TOK_EQ, - '|','=', 0, TOK_OR_ASSIGN, - '&','=', 0, TOK_AND_ASSIGN, - '*','=', 0, TOK_MUL_ASSIGN, - '/','=', 0, TOK_DIV_ASSIGN, - '%','=', 0, TOK_REM_ASSIGN, - '+','=', 0, TOK_PLUS_ASSIGN, - '-','=', 0, TOK_MINUS_ASSIGN, - '-','-', 0, TOK_POST_DEC, - '^','=', 0, TOK_XOR_ASSIGN, - '+','+', 0, TOK_POST_INC, - '*','*', 0, TOK_EXPONENT, - '!', 0, TOK_NOT, - '<', 0, TOK_LT, - '>', 0, TOK_GT, - '=', 0, TOK_ASSIGN, - '|', 0, TOK_BOR, - '&', 0, TOK_BAND, - '*', 0, TOK_MUL, - '/', 0, TOK_DIV, - '%', 0, TOK_REM, - '+', 0, TOK_ADD, - '-', 0, TOK_SUB, - '^', 0, TOK_BXOR, - /* uniq */ - '~', 0, TOK_BNOT, - ',', 0, TOK_COMMA, - '?', 0, TOK_CONDITIONAL, - ':', 0, TOK_CONDITIONAL_SEP, - ')', 0, TOK_RPAREN, - '(', 0, TOK_LPAREN, - 0 -}; -#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) - #if ENABLE_FEATURE_SH_MATH_BASE static arith_t strto_arith_t(const char *nptr, char **endptr) { @@ -577,250 +185,130 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) # endif #endif -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr) -{ - operator lasttok; - const char *errmsg; - const char *start_expr = expr = skip_whitespace(expr); - unsigned expr_len = strlen(expr) + 2; - /* Stack of integers */ - /* The proof that there can be no more than strlen(startbuf)/2+1 - * integers in any given correct or incorrect expression - * is left as an exercise to the reader. */ - var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); - var_or_num_t *numstackptr = numstack; - /* Stack of operator tokens */ - operator *const stack = alloca(expr_len * sizeof(stack[0])); - operator *stackptr = stack; - - /* Start with a left paren */ - *stackptr++ = lasttok = TOK_LPAREN; - errmsg = NULL; - - while (1) { - const char *p; - operator op; - operator prec; - - expr = skip_whitespace(expr); - if (*expr == '\0') { - if (expr == start_expr) { - /* Null expression */ - numstack->val = 0; - goto ret; - } - - /* This is only reached after all tokens have been extracted from the - * input stream. If there are still tokens on the operator stack, they - * are to be applied in order. At the end, there should be a final - * result on the integer stack */ - - if (expr != ptr_to_rparen + 1) { - /* If we haven't done so already, - * append a closing right paren - * and let the loop process it */ - expr = ptr_to_rparen; -//bb_error_msg("expr=')'"); - continue; - } - /* At this point, we're done with the expression */ - if (numstackptr != numstack + 1) { - /* ...but if there isn't, it's bad */ - goto err; - } - goto ret; - } - - p = endofname(expr); - if (p != expr) { - /* Name */ - size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ - numstackptr->var = alloca(var_name_size); - safe_strncpy(numstackptr->var, expr, var_name_size); -//bb_error_msg("var:'%s'", numstackptr->var); - expr = p; - num: - numstackptr->second_val_present = 0; - numstackptr++; - lasttok = TOK_NUM; - continue; - } - - if (isdigit(*expr)) { - /* Number */ - numstackptr->var = NULL; - errno = 0; - numstackptr->val = strto_arith_t(expr, (char**) &expr); -//bb_error_msg("val:%lld", numstackptr->val); - if (errno) - numstackptr->val = 0; /* bash compat */ - goto num; - } - - /* Should be an operator */ - - /* Special case: XYZ--, XYZ++, --XYZ, ++XYZ are recognized - * only if XYZ is a variable name, not a number or EXPR. IOW: - * "a+++v" is a++ + v. - * "(a)+++7" is ( a ) + + + 7. - * "7+++v" is 7 + ++v, not 7++ + v. - * "--7" is - - 7, not --7. - * "++++a" is + + ++a, not ++ ++a. - */ - if ((expr[0] == '+' || expr[0] == '-') - && (expr[1] == expr[0]) - ) { - if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ - char next = skip_whitespace(expr + 2)[0]; - if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ - //bb_error_msg("special %c%c", expr[0], expr[0]); - op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); - expr++; - goto tok_found1; - } - } - } - - p = op_tokens; - while (1) { - /* Compare expr to current op_tokens[] element */ - const char *e = expr; - while (1) { - if (*p == '\0') { - /* Match: operator is found */ - expr = e; - goto tok_found; - } - if (*p != *e) - break; - p++; - e++; - } - /* No match, go to next element of op_tokens[] */ - while (*p) - p++; - p += 2; /* skip NUL and TOK_foo bytes */ - if (*p == '\0') { - /* No next element, operator not found */ - //math_state->syntax_error_at = expr; - goto err; - } - } - tok_found: - op = p[1]; /* fetch TOK_foo value */ - tok_found1: - /* NB: expr now points past the operator */ +#define boole bool +# define FAL0 false +# define TRU1 true +#define s64 arith_t +#if ENABLE_FEATURE_SH_MATH_64 +# define S64_MIN LLONG_MIN +# define u64 unsigned long long +#else +# define S64_MIN LONG_MIN +# define u64 unsigned long +#endif +#define u8 uint8_t +#define u16 uint16_t +#define u32 uint32_t +#define U32_MAX UINT32_MAX +#define ul unsigned long +#define up uintptr_t +#define UZ_MAX SIZE_MAX +#define uz size_t + +#define a_SHEXP_ISVARC(C) ((C) == '_' || isalnum(S(unsigned char,C))) +#define a_SHEXP_ISVARC_BAD1ST(C) su_cs_is_digit(C) +#define a_SHEXP_ISVARC_BADNST(C) FAL0 +#define ASSERT(X) +#define ASSERT_NYD_EXEC(X,Y) +#define BITENUM_IS(X,Y) X +#define CONCAT(S1,S2) su__CONCAT_1(S1, S2) +# define su__CONCAT_1(S1,S2) su__CONCAT_2(S1, S2) +# define su__CONCAT_2(S1,S2) S1 ## S2 +#define DBG(X) +#define FALLTHRU +#define N_(X) X +#define NIL NULL +#define NYD_IN S(void,0) +#define NYD2_IN S(void,0) +#define NYD_OU S(void,0) +#define NYD2_OU S(void,0) +#define P2UZ(X) S(size_t,X) +#define S(X,Y) ((X)(Y)) +#define savestr(X) xstrdup(X) +#define su_ALIGNOF(X) ((sizeof(X) + 15) & ~15) +#define su_cs_cmp(X,Y) strcmp(X, Y) +#define su_cs_is_digit(X) isdigit(S(unsigned char,X)) +#define su_cs_is_space(X) isspace(S(unsigned char,X)) +#define su_empty "" +#define su_IDEC_STATE_EBASE 0 /* (could case $CC optimiz.) */ +#define su_IDEC_STATE_EMASK (1u<<0) +#define su_IDEC_STATE_CONSUMED (1u<<1) +#define su_IENC_BUFFER_SIZE 80u +#define su_LOFI_ALLOC(X) alloca(X) +#define su_LOFI_FREE(X) +#define su_mem_move(X,Y,Z) memmove(X, Y, Z) +#define STRUCT_ZERO(X,Y) memset(Y, 0, sizeof(X)) +#define UNLIKELY(X) X +#define UNUSED(X) S(void,X) + +#if LONG_MAX - 1 > 0x7FFFFFFFl - 1 +# define su_64(X) X +#else +# define su_64(X) +#endif - /* post grammar: a++ reduce to num */ - if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) - lasttok = TOK_NUM; +// +#define a_SHEXP_ARITH_COOKIE arith_state_t * - /* Plus and minus are binary (not unary) _only_ if the last - * token was a number, or a right paren (which pretends to be - * a number, since it evaluates to one). Think about it. - * It makes sense. */ - if (lasttok != TOK_NUM) { - switch (op) { - case TOK_ADD: - op = TOK_UPLUS; - break; - case TOK_SUB: - op = TOK_UMINUS; - break; - case TOK_POST_INC: - op = TOK_PRE_INC; - break; - case TOK_POST_DEC: - op = TOK_PRE_DEC; - break; - } - } - /* We don't want an unary operator to cause recursive descent on the - * stack, because there can be many in a row and it could cause an - * operator to be evaluated before its argument is pushed onto the - * integer stack. - * But for binary operators, "apply" everything on the operator - * stack until we find an operator with a lesser priority than the - * one we have just extracted. If op is right-associative, - * then stop "applying" on the equal priority too. - * Left paren is given the lowest priority so it will never be - * "applied" in this way. - */ - prec = PREC(op); -//bb_error_msg("prec:%02x", prec); - if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { - /* not left paren or unary */ - if (lasttok != TOK_NUM) { - /* binary op must be preceded by a num */ - goto err; - } - /* The algorithm employed here is simple: while we don't - * hit an open paren nor the bottom of the stack, pop - * tokens and apply them */ - while (stackptr != stack) { - operator prev_op = *--stackptr; - if (op == TOK_RPAREN) { -//bb_error_msg("op == TOK_RPAREN"); - if (prev_op == TOK_LPAREN) { -//bb_error_msg("prev_op == TOK_LPAREN"); -//bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); - if (numstackptr[-1].var) { - /* Expression is (var), lookup now */ - errmsg = arith_lookup_val(math_state, &numstackptr[-1]); - if (errmsg) - goto err_with_custom_msg; - /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ - numstackptr[-1].var = NULL; - } - /* Any operator directly after a - * close paren should consider itself binary */ - lasttok = TOK_NUM; - goto next; - } -//bb_error_msg("prev_op != TOK_LPAREN"); - } else { - operator prev_prec = PREC(prev_op); -//bb_error_msg("op != TOK_RPAREN"); - fix_assignment_prec(prec); - fix_assignment_prec(prev_prec); - if (prev_prec < prec - || (prev_prec == prec && is_right_associative(prec)) - ) { - stackptr++; - break; - } - } -//bb_error_msg("arith_apply(prev_op:%02x)", prev_op); - errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); - if (errmsg) - goto err_with_custom_msg; - } - if (op == TOK_RPAREN) - goto err; - } +static inline u32 a_idec_x(void *resp, char const *cbuf, + char const **endptr_or_nil){ + u32 rv; + arith_t res; +#if ENABLE_FEATURE_SH_MATH_BASE + char const *eptr; - /* Push this operator to the stack and remember it */ -//bb_error_msg("push op:%02x", op); - *stackptr++ = lasttok = op; - next: ; - } /* while (1) */ + if(endptr_or_nil == NIL) + endptr_or_nil = &eptr; +#endif - err: - errmsg = "arithmetic syntax error"; - err_with_custom_msg: - numstack->val = -1; - ret: - math_state->errmsg = errmsg; - return numstack->val; + errno = 0; + res = strto_arith_t(cbuf, (char**)endptr_or_nil); + rv = 0; + if(errno == 0){ + if(**endptr_or_nil == '\0') + rv = su_IDEC_STATE_CONSUMED; + }else{ + rv = su_IDEC_STATE_EMASK; + res = 0; + } + *S(s64*,resp) = res; + return rv; } +#define su_idec_cp(A,B,C,D,E) a_idec_x(A, B, E) +#define su_ienc_s64(X,Y,Z) (sprintf(X, ARITH_FMT, Y), X) +#define n_var_vlook(X,Y) (*self->sac_cookie->lookupvar)(X) +#define n_var_vset(X,Y,Z) (*self->sac_cookie->setvar)(X, (char*)(Y)) + +#include "shexp-arith.h" arith_t FAST_FUNC arith(arith_state_t *math_state, const char *expr) { + char const *err_rest, *emsg; + s64 res; + math_state->errmsg = NULL; - math_state->list_of_recursed_names = NULL; - return evaluate_string(math_state, expr); + + switch(a_shexp_arith_eval(math_state, &res, expr, UZ_MAX, &err_rest)){ + default: + return res; +#undef a_X +#define a_X(X,N) case CONCAT(a_SHEXP_ARITH_ERR_,X): emsg = N_(N); break + a_X(NOMEM, "out of memory"); + a_X(SYNTAX, "syntax error"); + a_X(ASSIGN_NO_VAR, "assignment without variable"); + a_X(DIV_BY_ZERO, "division by zero"); + a_X(EXP_INVALID, "invalid exponent"); + a_X(NO_OPERAND, "syntax error, expected operand"); + a_X(COND_NO_COLON, "syntax error, incomplete ?: condition"); + a_X(NAME_LOOP, "recursive variable name reference"); + a_X(OP_INVALID, "unknown operator"); + } +#undef a_X + + math_state->errmsg = xasprintf("%s (rest: %s)", emsg, err_rest); + + return -1; } /* diff --git a/shell/math.h b/shell/math.h index 41ef6e8dfa..6d54201086 100644 --- a/shell/math.h +++ b/shell/math.h @@ -76,11 +76,10 @@ typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *v //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); typedef struct arith_state_t { - const char *errmsg; + char *errmsg; arith_var_lookup_t lookupvar; arith_var_set_t setvar; // arith_var_endofname_t endofname; - void *list_of_recursed_names; } arith_state_t; arith_t FAST_FUNC arith(arith_state_t *state, const char *expr); diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h new file mode 100644 index 0000000000..4b23746df1 --- /dev/null +++ b/shell/shexp-arith.h @@ -0,0 +1,1128 @@ +/*@ S-nail - a mail user agent derived from Berkeley Mail. + *@ Signed 64-bit sh(1)ell-style $(( ARITH ))metic expression evaluator. + *@ POW2 bases are parsed as unsigned, operation overflow is not handled, + *@ saturated mode is not supported, division by zero is handled via error. + *@ The expression length limit is ~100.000.000 on 32-bit, U32_MAX otherwise. + *@ After reading on Dijkstra's two stack algorithm, as well as bash:expr.c. + *@ Most heavily inspired by busybox. + *@ Conclusion: + *@ - The Dijkstra algorithm scales very badly to ternary as are used to + *@ implement conditionals and their ignored sub-expressions. + *@ XXX - To provide good error feedback the operator stack would need to + *@ XXX contain structs that remember string positions. + * + * Copyright (c) 2022 Steffen Nurpmeso . + * SPDX-License-Identifier: ISC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#if 1 +# define a_SHEXP_ARITH_DBG 0 +# define a_SHEXP_ARITH_L(X) +#else +# define a_SHEXP_ARITH_DBG 1 +# define a_SHEXP_ARITH_L(X) a_shexp__arith_log X +#endif + +/* We parse with base 0: set _RESCAN to allow "I=' -10';$((10#$I))" */ +#define a_SHEXP_ARITH_IDEC_MODE (su_IDEC_MODE_SIGNED_TYPE |\ + su_IDEC_MODE_POW2BASE_UNSIGNED | su_IDEC_MODE_LIMIT_NOERROR |\ + su_IDEC_MODE_BASE0_NUMBER_SIGN_RESCAN) + +enum a_shexp_arith_error{ + a_SHEXP_ARITH_ERR_NONE, + a_SHEXP_ARITH_ERR_NOMEM, /* Out of memory */ + a_SHEXP_ARITH_ERR_SYNTAX, /* General syntax error */ + a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR, /* Assignment without variable */ + a_SHEXP_ARITH_ERR_DIV_BY_ZERO, + a_SHEXP_ARITH_ERR_EXP_INVALID, /* Invalid exponent */ + a_SHEXP_ARITH_ERR_NO_OPERAND, /* Expected an argument here */ + a_SHEXP_ARITH_ERR_COND_NO_COLON, /* Incomplete ?: condition */ + a_SHEXP_ARITH_ERR_NAME_LOOP, /* Variable self-reference loop */ + a_SHEXP_ARITH_ERR_OP_INVALID /* Unknown operator */ +}; + +/* Operators and precedences in increasing precedence order. + * (The operator stack as such is u16: [OP_FLAGS |] (OP<<8) | PREC.) */ +enum a_shexp_arith_ops{ +#undef a_X +#define a_X(N,P,O) \ + CONCAT(a_SHEXP_ARITH_PREC_,N) = CONCAT(P,u),\ + CONCAT(a_SHEXP_ARITH_OP_,N) =\ + (CONCAT(O,u) << 8) | CONCAT(a_SHEXP_ARITH_PREC_,N) + + a_X(PAREN_LEFT, 0, 0), + + a_X(COMMA, 1, 0), + + a_X(ASSIGN, 2, 0), + a_X(ASSIGN_BIT_OR, 2, 1), + a_X(ASSIGN_BIT_XOR, 2, 2), + a_X(ASSIGN_BIT_AND, 2, 3), + a_X(ASSIGN_SHIFT_LEFT, 2, 4), a_X(ASSIGN_SHIFT_RIGHT, 2, 5), + a_X(ASSIGN_SHIFT_RIGHTU, 2, 6), + a_X(ASSIGN_ADD, 2, 7), a_X(ASSIGN_SUB, 2, 8), + a_X(ASSIGN_MUL, 2, 9), a_X(ASSIGN_DIV, 2, 10), a_X(ASSIGN_MOD, 2, 11), + a_X(ASSIGN_EXP, 2, 12), + + a_X(COND, 3, 0), + a_X(COND_COLON, 3, 1), + + a_X(OR, 4, 0), + a_X(AND, 5, 0), + a_X(BIT_OR, 6, 0), + a_X(BIT_XOR, 7, 0), + a_X(BIT_AND, 8, 0), + a_X(EQ, 9, 0), a_X(NE, 9, 1), + a_X(LE, 10, 0), a_X(GE, 10, 1), a_X(LT, 10, 2), a_X(GT, 10, 3), + a_X(SHIFT_LEFT, 11, 0), a_X(SHIFT_RIGHT, 11, 1), a_X(SHIFT_RIGHTU, 11, 2), + a_X(ADD, 12, 0), a_X(SUB, 12, 1), + a_X(MUL, 13, 0), a_X(DIV, 13, 1), a_X(MOD, 13, 2), + a_X(EXP, 14, 0), + + /* Further operators are unary, pre- or postfix */ + a_SHEXP_ARITH_PREC_UNARY = 15, + a_SHEXP_ARITH_PREC_PREFIX = 16, + a_SHEXP_ARITH_PREC_POSTFIX = 18, + + a_X(UNARY_NOT, 15, 0), a_X(UNARY_BIT_NOT, 15, 1), + a_X(PREFIX_INC, 16, 0), a_X(PREFIX_DEC, 16, 1), + a_X(UNARY_PLUS, 17, 1), a_X(UNARY_MINUS, 17, 0), + a_X(POSTFIX_INC, 18, 0), a_X(POSTFIX_DEC, 18, 1), + + /* Beyond operator profanity; the first "is a number" */ + a_SHEXP_ARITH_PREC_SKY = 19, + a_X(NUM, 19, 0), a_X(PAREN_RIGHT, 19, 1), + +#undef a_X +}; + +enum arith_op_flags{ + /* Mask off operator and precision */ + a_SHEXP_ARITH_OP_MASK = 0x1FFF, + a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON = 1u<<13, + a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT = 1u<<14, + a_SHEXP_ARITH_OP_FLAG_WHITEOUT = 1u<<15, + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK = a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT | + a_SHEXP_ARITH_OP_FLAG_WHITEOUT, + a_SHEXP_ARITH_OP_FLAG_MASK = a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON | + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK +}; + +struct a_shexp_arith_name_stack{ + struct a_shexp_arith_name_stack *sans_last; + char const *sans_var; +}; + +struct a_shexp_arith_val{ + s64 sav_val; + char *sav_var; /* Named variable or NIL */ +}; + +struct a_shexp_arith_stack{ + u16 *sas_ops; + u16 *sas_ops_top; + struct a_shexp_arith_val *sas_nums; + struct a_shexp_arith_val *sas_nums_top; +}; + +struct a_shexp_arith_ctx{ + enum a_shexp_arith_error sac_error; + su_64( u8 sac__pad[4]; ) + char const *sac_error_rest; + s64 sac_rv; + struct a_shexp_arith_stack *sac_stack; + struct a_shexp_arith_name_stack *sac_name_stack; +#undef a_SHEXP_ARITH_IFS +#ifdef mx_SOURCE +# define a_SHEXP_ARITH_IFS(X) X + char const *sac_ifs_ws; /* IFS whitespace */ +#else +# define a_SHEXP_ARITH_IFS(X) +#endif +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE sac_cookie; +#endif +}; + +/* Sort by ~expected usage -- however, longest first if ambiguous! + * Follow busybox, save space by compressing data in char[] not struct[]! + * (XXX Instead use 1-st byte jump table like for commands) */ +static char const a_shexp_arith_op_toks[] = { +#undef a_X +#define a_X(X) \ + S(char,(CONCAT(a_SHEXP_ARITH_OP_,X) & 0xFF00u) >> 8),\ + S(char,CONCAT(a_SHEXP_ARITH_PREC_,X)) + + '+','+','\0', a_X(POSTFIX_INC), + '+','=','\0', a_X(ASSIGN_ADD), + '+','\0', a_X(ADD), + '-','-','\0', a_X(POSTFIX_DEC), + '-','=','\0', a_X(ASSIGN_SUB), + '-','\0', a_X(SUB), + '*','*','=','\0', a_X(ASSIGN_EXP), + '*','*','\0', a_X(EXP), + '*','=','\0', a_X(ASSIGN_MUL), + '*','\0', a_X(MUL), + '/','=','\0', a_X(ASSIGN_DIV), + '/','\0', a_X(DIV), + '%','=','\0', a_X(ASSIGN_MOD), + '%','\0', a_X(MOD), + '|','|','\0', a_X(OR), + '|','=','\0', a_X(ASSIGN_BIT_OR), + '|','\0', a_X(BIT_OR), + '^','=','\0', a_X(ASSIGN_BIT_XOR), + '^','\0', a_X(BIT_XOR), + '&','&','\0', a_X(AND), + '&','=','\0', a_X(ASSIGN_BIT_AND), + '&','\0', a_X(BIT_AND), + '<','<','=',0, a_X(ASSIGN_SHIFT_LEFT), + '<','<','\0', a_X(SHIFT_LEFT), + '>','>','>','=',0, a_X(ASSIGN_SHIFT_RIGHTU), + '>','>','>','\0', a_X(SHIFT_RIGHTU), + '>','>','=',0, a_X(ASSIGN_SHIFT_RIGHT), + '>','>','\0', a_X(SHIFT_RIGHT), + + '~','\0', a_X(UNARY_BIT_NOT), + '!','=','\0', a_X(NE), + '!','\0', a_X(UNARY_NOT), + + ')','\0', a_X(PAREN_RIGHT), + '(','\0', a_X(PAREN_LEFT), + ',','\0', a_X(COMMA), + + '<','=','\0', a_X(LE), + '>','=','\0', a_X(GE), + '=','=','\0', a_X(EQ), + '<','\0', a_X(LT), + '>','\0', a_X(GT), + '=','\0', a_X(ASSIGN), + + '?','\0', a_X(COND), + ':','\0', a_X(COND_COLON), + + '\0' +#undef a_X +}; + +/* Our "public" entry point. exp_buf can be NIL if exp_len is 0, it need not + * be NUL terminated (stop for NUL or out of length). + * Upon error *err_rest is set to a "newly allocated" string that points to + * where parse stopped (which could be at EOS if error happens in trailer). */ +static enum a_shexp_arith_error a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len, char const **err_rest); + +static void a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len); + +/* Count non-WS as well as normalized WS ([:"space":]+ -> ' ') in exp_buf, + * return count. If store!=NIL, also copy normalization. + * An all-WS exp_buf returns 0 */ +static uz a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil); + +/* Resolve and evaluate the "self-contained string" savp->sav_var. + * Take care to avoid name lookup loops */ +static boole a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp); + +/* Work top of the stack, which may pop & push etc */ +static boole a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self); + +static boole a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self); + +#if a_SHEXP_ARITH_DBG +static void a_shexp__arith_log(char const *fmt, ...); +#endif + +static enum a_shexp_arith_error +a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len, char const **err_rest){ + struct a_shexp_arith_stack sas_stack; + struct a_shexp_arith_ctx self; + NYD_IN; + + a_SHEXP_ARITH_L(("> arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + STRUCT_ZERO(struct a_shexp_arith_ctx, &self); +#ifdef a_SHEXP_ARITH_COOKIE + self.sac_cookie = cookie; +#endif + + ASSERT_NYD_EXEC(resp != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND); + DBG( *resp = 0; ) + ASSERT_NYD_EXEC(exp_len == 0 || exp_buf != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND); + + a_SHEXP_ARITH_IFS( self.sac_ifs_ws = ok_vlook(ifs_ws); ) + self.sac_stack = &sas_stack; + a_shexp__arith_eval(&self, exp_buf, exp_len); + + *resp = self.sac_rv; + if(self.sac_error != a_SHEXP_ARITH_ERR_NONE) + *err_rest = self.sac_error_rest; + + a_SHEXP_ARITH_L(("< arith_eval %zu <%.*s> -> <%lld> ERR<%d>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf, self.sac_rv, self.sac_error)); + + NYD_OU; + return self.sac_error; +} + +static void +a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len){ + char *ep, *cp, c; + u16 lop; + struct a_shexp_arith_stack *sasp; + void *mem_p; + NYD2_IN; + + a_SHEXP_ARITH_L((" > _arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + self->sac_error_rest = su_empty; + mem_p = NIL; + + sasp = self->sac_stack; + + /* Create a single continuous allocation for anything. + * Since we need to keep pointers to variable names along the way, simply + * NUL terminate in this large buffer (move backward by one first) */ + /* C99 */{ + union {void *v; char *c;} p; + uz i, j, a; + + /* Done for empty expression */ + if((i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, NIL)) == 0) + goto jleave; + + /* Overflow check: since arithmetic expressions are rarely long enough + * to come near this limit, laxe & fuzzy, not exact; max U32_MAX! */ + if(su_64( i > U32_MAX || ) + i >= ((UZ_MAX - i) / (su_ALIGNOF(*sasp->sas_nums) + + sizeof(*sasp->sas_ops) + 1))){ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + + j = su_ALIGNOF(*sasp->sas_nums) * ((i + 1) >> 1); + a = j + (sizeof(*sasp->sas_ops) * (i + 1)) + i + 1 +1; + mem_p = p.v = su_LOFI_ALLOC(a); + if(p.v == NIL){ + /* (For MX LOFI has _MUSTFAIL set though) */ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + sasp->sas_nums = sasp->sas_nums_top = S(struct a_shexp_arith_val*,p.v); + p.c += j; + sasp->sas_ops = sasp->sas_ops_top = S(u16*,p.v); + p.c += sizeof(*sasp->sas_ops) * i; + /* (room for moving back vars by one, to NUL terminate 'em) */ + a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, ep = ++p.c); + + a_SHEXP_ARITH_L((" ! _arith_eval ALLOC <%lu> " + "nums=%p (%lu) ops=%p %lu <%s>\n", + S(ul,a), sasp->sas_nums, S(ul,j / su_ALIGNOF(*sasp->sas_nums)), + sasp->sas_ops, S(ul,i), ep)); + } + + /* Start with a left paren */ + *sasp->sas_ops_top++ = lop = a_SHEXP_ARITH_OP_PAREN_LEFT; + + for(;;) Jouter:{ + u16 op; + + a_SHEXP_ARITH_L((" = _arith_eval TICK LOP <0x%02X %u> " + "nums=%lu ops=%lu DATA %lu <%s>\n", + lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + self->sac_error_rest = ep; + + if(*ep == '\0'){ + /* At the end of the expression pop anything left. + * Assume we have read PAREN_RIGHT */ + if(exp_buf != NIL){ + exp_buf = NIL; + op = a_SHEXP_ARITH_OP_PAREN_RIGHT; + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + goto jtok_go; + } + + /* After PAREN_RIGHT, we must be finished */ + if(sasp->sas_nums_top != &sasp->sas_nums[1]) + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } + + /* Skip (normalized) WS now */ + if(*ep == ' ') + ++ep; + ASSERT(!su_cs_is_space(*ep)); + + /* A number? */ + if(su_cs_is_digit(*ep)){ + BITENUM_IS(u32,su_idec_state) is; + + is = su_idec_cp(&sasp->sas_nums_top->sav_val, ep, 0, + a_SHEXP_ARITH_IDEC_MODE, S(char const**,&ep)); + if((is &= su_IDEC_STATE_EMASK) && is != su_IDEC_STATE_EBASE) + sasp->sas_nums_top->sav_val = 0; + sasp->sas_nums_top->sav_var = NIL; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval NUM <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + continue; + } + + /* Is it a variable name? */ + for(cp = ep; (c = *cp, a_SHEXP_ISVARC(c)); ++cp) + if(cp == ep && a_SHEXP_ISVARC_BAD1ST(c)) + break; + + if(cp != ep){ + for(;;){ + c = cp[-1]; + if(!a_SHEXP_ISVARC_BADNST(c)) + break; + if(--cp == ep){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* We reserved one byte at the front, so we can simply move back + * the variable name by one, and then NUL terminate it. + * (Unfortunately we do _have_ to copy; even more weird: move!) */ + su_mem_move(sasp->sas_nums_top->sav_var = &ep[-1], ep, P2UZ(cp - ep)); + cp[-1] = '\0'; + ep = cp; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval VAR <%s>\n", + sasp->sas_nums_top[-1].sav_var)); + continue; + } + + /* An operator. + * We turn prefix operators to multiple unary plus/minus if + * not attached to a variable name (++10 -> + + 10). + * (We adjust postfix to prefix below) */ + if((ep[0] == '+' || ep[0] == '-') && (ep[1] == ep[0])){ + if(sasp->sas_nums_top == sasp->sas_nums || + sasp->sas_nums_top[-1].sav_var == NIL){ + if((c = ep[2]) == ' ') + c = ep[3]; + + if(c != '\0' && (!a_SHEXP_ISVARC(c) || a_SHEXP_ISVARC_BAD1ST(c))){ + op = (ep[0] == '+') ? a_SHEXP_ARITH_OP_ADD + : a_SHEXP_ARITH_OP_SUB; + ++ep; + a_SHEXP_ARITH_L((" + _arith_eval OP PREFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", ep[0], ep[0], ep[0])); + goto jtok_go; + } + } + } + + /* Operator search */ + /* C99 */{ + char const *tokp; + + /* 3=NUL+OP+PREC */ + for(tokp = a_shexp_arith_op_toks; *tokp != '\0'; tokp += 3){ + for(cp = ep;; ++tokp, ++cp){ + if(*tokp == '\0'){ + ep = cp; + op = (S(u16,tokp[1]) << 8) | S(u8,tokp[2]); + goto jtok_go; + }else if(*tokp != *cp) + break; + } + + while(*tokp != '\0') + ++tokp; + } + self->sac_error = a_SHEXP_ARITH_ERR_OP_INVALID; + goto jleave; + } + +jtok_go:/* C99 */{ + u8 prec; + + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP <0x%02X %u> LOP <0x%02X %u> " + "nums=%lu ops=%lu %lu <%s>\n", + op, prec, lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(op == a_SHEXP_ARITH_OP_UNARY_PLUS){ + a_SHEXP_ARITH_L((" + _arith_eval IGNORE UNARY PLUS\n")); + continue; + } + + /* Correct our understanding of what there is. + * Post grammar: VAR++ reduces to num */ + if((lop & 0xFF) == a_SHEXP_ARITH_PREC_POSTFIX){ + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval LOP POSTFIX REDUCED to NUM\n")); + } + /* Adjust some binary/postfix operators to make them flow */ + else if(lop != a_SHEXP_ARITH_OP_NUM){ + switch(op){ + case a_SHEXP_ARITH_OP_ADD: + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST: IGNORE UNARY PLUS\n")); + continue; + case a_SHEXP_ARITH_OP_SUB: + op = a_SHEXP_ARITH_OP_UNARY_MINUS; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_INC: + op = a_SHEXP_ARITH_OP_PREFIX_INC; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_DEC: + op = a_SHEXP_ARITH_OP_PREFIX_DEC; +junapre: + prec = a_SHEXP_ARITH_PREC_PREFIX; + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST TO UNARY/PREFIX\n")); + break; + } + } + /* Special: +10++VAR -> +10 + +VAR. (Since we do handle +10++11 + * correctly via "prefix split", we should also handle this) */ + else if(prec == a_SHEXP_ARITH_PREC_POSTFIX){ + ASSERT(lop == a_SHEXP_ARITH_OP_NUM); + if((c = ep[0]) == ' ') + c = ep[1]; + if(c != '\0' && (a_SHEXP_ISVARC(c) && !a_SHEXP_ISVARC_BAD1ST(c))){ + c = *--ep; + op = (c == '+') ? a_SHEXP_ARITH_OP_ADD : a_SHEXP_ARITH_OP_SUB; + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP POSTFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", c, c, c)); + } + } + + /* Check whether we can work it a bit */ + if((prec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + prec < a_SHEXP_ARITH_PREC_UNARY) || + prec >= a_SHEXP_ARITH_PREC_SKY){ + if(lop != a_SHEXP_ARITH_OP_NUM){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + goto jleave; + } + + /* Pop as much as possible */ + while(sasp->sas_ops_top != sasp->sas_ops){ + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + + a_SHEXP_ARITH_L((" + _arith_eval TRY POP - OP " + "<0x%02X %u>, NEW LOP <0x%02X %u 0x%X> nums=%lu ops=%lu\n", + op, op & 0xFF, lop, lop & 0xFF, + (*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK), + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* Special-case parenthesis groups */ + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + if(lop == a_SHEXP_ARITH_OP_PAREN_LEFT){ + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + /* Resolve VAR to NUM */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + ASSERT(!(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK)); + if(!a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + } + sasp->sas_nums_top[-1].sav_var = NIL; + a_SHEXP_ARITH_L((" + _arith_eval OP () RESOLVED <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + lop = a_SHEXP_ARITH_OP_NUM; + goto Jouter; + } + }else{ + u8 lprec; + + lprec = lop & 0xFF; + + /* */ + if(op == a_SHEXP_ARITH_OP_COND){ + u16 x; + + x = *sasp->sas_ops_top; + x &= a_SHEXP_ARITH_OP_FLAG_MASK; + if(x & a_SHEXP_ARITH_OP_FLAG_WHITEOUT){ + x ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + x |= a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT; + } + op |= x; + + /* Resolve as much as possible */ + while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + lprec != a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + if((--sasp->sas_nums_top)->sav_val == 0) + op |= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + + op |= *sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK; + /* Delay ternary */ + ++sasp->sas_ops_top; + break; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + uz recur; + u16 *opsp, x; + boole delay; + + delay = TRU1; + + /* Find our counterpart ? so we can toggle whiteout */ + opsp = sasp->sas_ops_top; + for(recur = 1;; --opsp){ + if(opsp == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + x = *opsp & a_SHEXP_ARITH_OP_MASK; + if(x == a_SHEXP_ARITH_OP_COND_COLON) + ++recur; + else if(x == a_SHEXP_ARITH_OP_COND && --recur == 0){ + *opsp |= a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON; + break; + } + } + op |= *opsp & a_SHEXP_ARITH_OP_FLAG_MASK; + op ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + + /* Resolve innermost condition asap. + * In "1 ? 0 ? 5 : 6 : 3", resolve innermost upon :3 */ + while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + lprec != a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + /* If we now see a COLON, we have to resolve further! + * Code flow restrictions of the Dijkstra algorithm!, which + * fits ternary badly (the way we do): pop as pop can! */ + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + delay = FAL0; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + lop = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + } + + if(lop != a_SHEXP_ARITH_OP_COND){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + if(delay) + ++sasp->sas_ops_top; + a_SHEXP_ARITH_L((" + _arith_eval %sTERNARY ?:%s\n", + (delay ? "DELAY " : su_empty), + ((op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) + ? " WHITEOUT" : su_empty))); + break; + } + /* Is this a right-associative operation? */ + else{ + boole doit=FAL0; + + if(lprec < prec){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY PRECEDENCE\n")); + }else if(lprec == prec && prec == a_SHEXP_ARITH_PREC_ASSIGN){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY RIGHT ASSOC\n")); + }else if(lop == a_SHEXP_ARITH_OP_COND){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY CONDITION\n")); + } + + if(doit){ + /* If we are about to delay and LHV is a VAR, expand that + * immediately to expand in correct order things like + * I1=I2=10 I2=3; echo $((I1,I2)) + * I1=I2=10 I2=3; echo $((I1+=I2)) */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + if(op != a_SHEXP_ARITH_OP_ASSIGN && + !(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + !a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + if(prec != a_SHEXP_ARITH_PREC_ASSIGN) + sasp->sas_nums_top[-1].sav_var = NIL; + } + + ++sasp->sas_ops_top; + break; + } + } + } + + /* */ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops && + &sasp->sas_ops_top[-1] > sasp->sas_ops); + ASSERT((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND); + --sasp->sas_ops_top; + *sasp->sas_ops_top ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + } + } + + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* Push this operator to the stack and remember it */ + if(sasp->sas_ops_top > sasp->sas_ops && + (op & 0xFF) != a_SHEXP_ARITH_PREC_COND) + op |= sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_FLAG_MASK; + *sasp->sas_ops_top++ = op; + lop = op & a_SHEXP_ARITH_OP_MASK; + a_SHEXP_ARITH_L((" + _arith_eval OP PUSH <0x%02X %u> nums=%lu ops=%lu\n", + op, (op & 0xFF), S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + } + } + + self->sac_rv = sasp->sas_nums->sav_val; + +jleave: + if(self->sac_error != a_SHEXP_ARITH_ERR_NONE) + self->sac_error_rest = savestr(self->sac_error_rest); + + if(mem_p != NIL) + su_LOFI_FREE(mem_p); + + a_SHEXP_ARITH_L((" < _arith_eval <%lld> ERR<%d>\n", + self->sac_rv, self->sac_error)); + + NYD2_OU; +} + +static uz +a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil){ + a_SHEXP_ARITH_IFS( char const *ifs_ws; ) + char c; + boole last_ws, ws; + uz rv; + NYD2_IN; + UNUSED(self); + + rv = 0; + a_SHEXP_ARITH_IFS( ifs_ws = self->sac_ifs_ws; ) + + for(;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + goto jleave; + if(!(su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + )) + break; + } + + for(last_ws = FAL0;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + break; + + ws = (su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + ); + if(ws){ + if(last_ws) + continue; + c = ' '; + } + last_ws = ws; + + ++rv; + if(store_or_nil != NIL) + *store_or_nil++ = c; + } + + if(last_ws){ + --rv; + if(store_or_nil != NIL) + --store_or_nil; + } + +jleave: + if(store_or_nil != NIL) + *store_or_nil = '\0'; + + NYD2_OU; + return rv; +} + +static boole +a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp){ + struct a_shexp_arith_name_stack sans_stack, *sansp; + struct a_shexp_arith_stack sas_stack, *sasp; + char const *cp; + NYD_IN; + ASSERT(savp->sav_var != NIL); + + a_SHEXP_ARITH_L(("> _arith_val_eval %p <%s>\n", savp, savp->sav_var)); + + savp->sav_val = 0; + + /* Also look in program environment XXX configurable? */ + cp = n_var_vlook(savp->sav_var, TRU1); + if(cp == NIL) + goto jleave; + + for(sansp = self->sac_name_stack; sansp != NIL; sansp = sansp->sans_last){ + if(!su_cs_cmp(sansp->sans_var, savp->sav_var)){ + self->sac_error = a_SHEXP_ARITH_ERR_NAME_LOOP; + goto jleave; + } + } + + /* cp must be a self-contained expression. + * However, in most cases it solely consists of an integer, shortcut that */ + if(su_idec_cp(&savp->sav_val, cp, 0, a_SHEXP_ARITH_IDEC_MODE, NIL + ) & su_IDEC_STATE_CONSUMED){ + a_SHEXP_ARITH_L((" + _arith_val_eval NUM DIRECT <%lld>\n", + savp->sav_val)); + }else{ + sasp = self->sac_stack; + self->sac_stack = &sas_stack; + + sans_stack.sans_last = sansp = self->sac_name_stack; + sans_stack.sans_var = savp->sav_var; + self->sac_name_stack = &sans_stack; + + a_shexp__arith_eval(self, cp, UZ_MAX); + savp->sav_val = self->sac_rv; + /* .sav_var may be needed further on for updating purposes */ + + self->sac_stack = sasp; + self->sac_name_stack = sansp; + } + + cp = NIL; +jleave: + a_SHEXP_ARITH_L(("< _arith_val_eval %p <%s> <%lld> -> OK <%d>\n", + savp, savp->sav_var, savp->sav_val, + (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE))); + + NYD_OU; + return (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE); +} + +static boole +a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ + struct a_shexp_arith_val *nums_top; + u8 prec; + u16 op; + struct a_shexp_arith_stack *sasp; + s64 val; + boole rv, ign; + NYD_IN; + + rv = FAL0; + val = 0; + sasp = self->sac_stack; + op = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + ign = ((*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) != 0); + + a_SHEXP_ARITH_L((" > _arith_op_apply %s<0x%02X %u> " + "nums_top=%p (%lu) ops_top=%p (%lu)\n", + (ign ? "WHITEOUT " : su_empty), op, (op & 0xFF), sasp->sas_nums_top, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + sasp->sas_ops_top, S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* At least one argument is always needed */ + if((nums_top = sasp->sas_nums_top) == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + goto jleave; + } + --nums_top; + + /* Resolve name ([R]VAL) to value as necessary */ + if(!ign && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + val = nums_top->sav_val; + prec = op & 0xFF; + + /* Not a binary operator? */ + if(prec >= a_SHEXP_ARITH_PREC_UNARY && prec < a_SHEXP_ARITH_PREC_SKY){ + if(ign) + goto jquick; + + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_UNARY_NOT: val = !val; break; + case a_SHEXP_ARITH_OP_UNARY_BIT_NOT: val = ~val; break; + case a_SHEXP_ARITH_OP_UNARY_MINUS: val = -val; break; + case a_SHEXP_ARITH_OP_PREFIX_INC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_INC: ++val; break; + case a_SHEXP_ARITH_OP_PREFIX_DEC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_DEC: --val; break; + } + }else if(op == a_SHEXP_ARITH_OP_COND){ + if(!(*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON)){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_NO_COLON; + goto jleave; + } + goto jquick; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(nums_top > sasp->sas_nums); + if(!ign){ + /* Move the ternary value over to LHV where we find it as a result, + * and ensure LHV's name is forgotten so not to evaluate it (for + * example in 0?I1:I2 I1 would be evaluated when resolving the virtual + * outer group, because it still exists on number stack) */ + nums_top[-1].sav_val = nums_top[0].sav_val; + nums_top[-1].sav_var = NIL; + }else{ + val = nums_top[-1].sav_val; + /*nums_top[0].sav_var = NIL;*/ + } + sasp->sas_nums_top = nums_top; + + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + if((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND_COLON){ + --sasp->sas_ops_top; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + if(!ign) + sasp->sas_nums_top[-1].sav_val = val; + } + }else{ + /* Binaries need two numbers: one is popped, the other replaced */ + s64 rval; + + if(nums_top == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + goto jleave; + } + sasp->sas_nums_top = nums_top--; + + if(ign) + goto jquick; + + /* Resolve LHV as necessary */ + if(op != a_SHEXP_ARITH_OP_ASSIGN && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + rval = val; + val = nums_top->sav_val; /* (may be bogus for assign, fixed soon) */ + + /* In precedence order (excluding assignments) */ + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_COMMA: FALLTHRU + + case a_SHEXP_ARITH_OP_ASSIGN: val = rval; break; + + case a_SHEXP_ARITH_OP_OR: val = (val != 0 || rval != 0); break; + case a_SHEXP_ARITH_OP_AND: val = (val != 0 && rval != 0); break; + + case a_SHEXP_ARITH_OP_BIT_OR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_OR: val |= rval; break; + case a_SHEXP_ARITH_OP_BIT_XOR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_XOR: val ^= rval; break; + case a_SHEXP_ARITH_OP_BIT_AND: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_AND: val &= rval; break; + + case a_SHEXP_ARITH_OP_EQ: val = (val == rval); break; + case a_SHEXP_ARITH_OP_NE: val = (val != rval); break; + + case a_SHEXP_ARITH_OP_LE: val = (val <= rval); break; + case a_SHEXP_ARITH_OP_GE: val = (val >= rval); break; + case a_SHEXP_ARITH_OP_LT: val = (val < rval); break; + case a_SHEXP_ARITH_OP_GT: val = (val > rval); break; + + case a_SHEXP_ARITH_OP_SHIFT_LEFT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_LEFT: val <<= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHT: val >>= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHTU: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHTU: + val = S(s64,S(u64,val) >> rval); + break; + + case a_SHEXP_ARITH_OP_ADD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_ADD: val += rval; break; + case a_SHEXP_ARITH_OP_SUB: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SUB: val -= rval; break; + + case a_SHEXP_ARITH_OP_MUL: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MUL: val *= rval; break; + /* For /,%, avoid lvh=S64_MIN, rhv=-1: + * CHANGES, bash 4.3 [ac50fbac377e32b98d2de396f016ea81e8ee9961]: + * Fixed a bug that caused floating-point exceptions and + * overflow errors for the / and % arithmetic operators when + * using INTMAX_MIN and -1. */ + case a_SHEXP_ARITH_OP_DIV: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_DIV: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val != S64_MIN || rval != -1) + val /= rval; + break; + case a_SHEXP_ARITH_OP_MOD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MOD: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val == S64_MIN && rval == -1) + val = 0; + else + val %= rval; + break; + + case a_SHEXP_ARITH_OP_EXP: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_EXP: + if(rval < 0){ + self->sac_error = a_SHEXP_ARITH_ERR_EXP_INVALID; + goto jleave; + }else{ + s64 i; + + for(i = 1; rval > 0; --rval) + i *= val; + val = i; + } + break; + } + } + + /* Assignment updates a variable, which must exist. + * For prefix and postfix operators, too: we already turned them into + * multiple unary plus/minus unless we had seen a variable name */ +jquick: + if(prec == a_SHEXP_ARITH_PREC_ASSIGN || prec == a_SHEXP_ARITH_PREC_PREFIX || + prec == a_SHEXP_ARITH_PREC_POSTFIX){ + char buf[su_IENC_BUFFER_SIZE], *bp; + + if(nums_top->sav_var == NIL){ + self->sac_error = a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR; + goto jleave; + } + + if(!ign){ + bp = su_ienc_s64(buf, val, 10); + n_var_vset(nums_top->sav_var, S(up,bp), FAL0); + } + + /* And restore the stack value again for postfix operators */ + if(op == a_SHEXP_ARITH_OP_POSTFIX_INC) + --val; + else if(op == a_SHEXP_ARITH_OP_POSTFIX_DEC) + ++val; + + if(!ign) + a_SHEXP_ARITH_L((" + _arith_op_apply VAR <%s> SET <%s> VAL <%lld>\n", + nums_top->sav_var, bp, val)); + } + + nums_top->sav_val = val; + nums_top->sav_var = NIL; + + rv = TRU1; +jleave: + a_SHEXP_ARITH_L((" < _arith_op_apply RV %d <0x%02X %u> RES<%lld> ERR<%d> " + "nums=%lu ops=%lu\n", + rv, op, op & 0xFF, val, self->sac_error, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + NYD_OU; + return rv; +} + +static boole +a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self){ + u16 lop, lprec; + boole next_stop; + NYD_IN; + + for(next_stop = FAL0;;){ + if(!a_shexp__arith_op_apply(self)){ + next_stop = FAL0; + break; + } + if(next_stop) + break; + lop = *--self->sac_stack->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + next_stop = (lprec == a_SHEXP_ARITH_PREC_PAREN_LEFT || + lop == a_SHEXP_ARITH_OP_COND); + } + + NYD_OU; + return next_stop; +} + +#if a_SHEXP_ARITH_DBG +static void +a_shexp__arith_log(char const *fmt, ...){ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +#endif + +#undef a_SHEXP_ARITH_IFS + +#undef a_SHEXP_ARITH_DBG +#undef a_SHEXP_ARITH_L +#undef a_SHEXP_ARITH_IDEC_MODE + +/* s-it-mode */ -- 2.37.1 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Wed Aug 10 15:37:52 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Wed, 10 Aug 2022 17:37:52 +0200 Subject: [PATCHv2] shell: exchange Dijkstra $(( )) evaluator.. In-Reply-To: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> References: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> Message-ID: <20220810153752.Dw-eh%steffen@sdaoden.eu> Steffen Nurpmeso wrote in <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen at sdaoden\ .eu>: ... |Ok i am out of ideas on further tests, and it works. And attached is the new test (not that much different). --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) -------------- next part -------------- # make this work with (ba)sh \ command -v shopt && shopt -s expand_aliases;\ alias p=printf;alias e=echo;alias s=export s I=10 J=33 e '= BASE' e "<$(())>" e "<$(( ))>" e "<$((1))>" e "<$((0))>" e "<$((0x0))>" e "<$((0X0))>" e "<$((000))>" e "<$((000000000000001))>" e "<$((2#00000000000000000000000000000000000001))>" e "<$((0X00000000000000000000000000000000000000000001))>" e "<$((999999999999999999999999999999999999999999999))>" e "<$(( 10 ))>" e "<$((9191919191919))>" e "<$((0xD))>" e "<$((013))>" e "<$((32#VV))>" e "<$((36#ZZ))>" e "<$((36#zz))>" e "<$(( 64#zzZZ ))>" e "<$((64#ZZzz))>" e "<$((I))>" e "<$((J))>" e "<$(( I ))>" e "<$(( J ))>" e "<$(( (1) ))>" e "<$((((1))))>" e "<$(((((1)))))>" e "<$(( (J) ))>" e "<$((((J))))>" e "<$(((((J)))))>" e "<$(( ( ( ( J ) ) ) ))>" e '= UNA PLUS/MINUS' e "<$((+0))>" e "<$(( + 0 ))>" e "<$(( +1))>" e "<$((+ 1 ))>" e "<$(( + 4221 ))>" e "<$(( +0x4221 ))>" e "<$(( + 64#ZZzz ))>" e "<$(( +64#ZZzz ))>" e "<$((+ (1) ))>" e "<$((+((1))))>" e "<$((+(((1)))))>" e "<$((-0))>" e "<$(( - 0 ))>" e "<$(( -1))>" e "<$((- 1 ))>" e "<$(( - 4221 ))>" e "<$(( -0x4221 ))>" e "<$(( - 64#ZZzz ))>" e "<$(( -64#ZZzz ))>" e "<$((- (1) ))>" e "<$((-((1))))>" e "<$((-(((1)))))>" e "<$((+ -(1) ))>" e "<$((+(-(-1))))>" e "<$((+(-(-(-1)))))>" e '= UNA !' e "<$((!0))>" e "<$((! 00000000))>" e "<$((!1))>" e "<$((! 0x00001))>" e "<$((! - 0))>" e "<$((!-1))>" e '= UNA ~' e "<$((~0))>" e "<$((~ 00000000))>" e "<$((~1))>" e "<$((~ 0x00001))>" e "<$((~ 64#zz))>" e "<$((~-1))>" e "<$((~ - 1))>" e "<$((~-0))>" e "<$((~ - 0))>" e "<$((~(-0)))>" e "<$((~((- 0))))>" e '= BIN +' e "<$((0+0))>" e "<$(( 0 + 0 ))>" e "<$((0+1))>" e "<$(( 0 + 1 ))>" e "<$((1+0))>" e "<$(( 1 + 0 ))>" e "<$((1+1))>" e "<$(( 1 + 1 ))>" e "<$(( (1 + 1) ))>" e "<$(((((((-1)))) + (((-1))))))>" e "<$((1111+2222))>" e "<$((2222+1111))>" e "<$(( +0x10 + +0x11 ))>" e "<$(( -0x10 + -0x11 ))>" e "<$(( -0x10 + -0x11 ))>" e "<$(( +64#10 + -64#11 ))>" e "<$(( +0x11 + +0x10 ))>" e "<$(( -0x11 + -0x10 ))>" e "<$(( -0x11 + -0x10 ))>" e "<$(( +64#11 + -64#10 ))>" e "<$((0x8000000000000000+-1))>" e "<$((0x8000000000000000+1))>" e "<$((0x7FFFFFFFFFFFFFFF+-1))>" e "<$((0x7FFFFFFFFFFFFFFF+1))>" e "<$((0xFFFFFFFFFFFFFFFF+-1))>" e "<$((0xFFFFFFFFFFFFFFFF+1))>" e "<$((0x8000000000000000+-11))>" e "<$((0x8000000000000000+11))>" e "<$((0x7FFFFFFFFFFFFFFF+-11))>" e "<$((0x7FFFFFFFFFFFFFFF+11))>" e "<$((0xFFFFFFFFFFFFFFFF+-11))>" e "<$((0xFFFFFFFFFFFFFFFF+11))>" e '= BIN -' e "<$((0-0))>" e "<$(( 0 - 0 ))>" e "<$((0-1))>" e "<$(( 0 - 1 ))>" e "<$((1-0))>" e "<$(( 1 - 0 ))>" e "<$((1-1))>" e "<$(( 1 - 1 ))>" e "<$(( (1 - 1) ))>" e "<$(((((((+1)))) - (((+1))))))>" e "<$((1111-2222))>" e "<$((2222-1111))>" e "<$(( +0x10 - +0x11 ))>" e "<$(( -0x10 - -0x11 ))>" e "<$(( -0x10 - -0x11 ))>" e "<$(( +64#10 - -64#11 ))>" e "<$(( +0x11 - +0x10 ))>" e "<$(( -0x11 - -0x10 ))>" e "<$(( -0x11 - -0x10 ))>" e "<$(( +64#11 - -64#10 ))>" e "<$((0x8000000000000000--1))>" e "<$((0x8000000000000000-1))>" e "<$((0x7FFFFFFFFFFFFFFF--1))>" e "<$((0x7FFFFFFFFFFFFFFF-1))>" e "<$((0xFFFFFFFFFFFFFFFF--1))>" e "<$((0xFFFFFFFFFFFFFFFF-1))>" e "<$((0x8000000000000000--11))>" e "<$((0x8000000000000000-11))>" e "<$((0x7FFFFFFFFFFFFFFF--11))>" e "<$((0x7FFFFFFFFFFFFFFF-11))>" e "<$((0xFFFFFFFFFFFFFFFF--11))>" e "<$((0xFFFFFFFFFFFFFFFF-11))>" e '= BIN *' e "<$((0*0))>" e "<$(( 0 * 0 ))>" e "<$((0*1))>" e "<$(( 0 * 1 ))>" e "<$((1*0))>" e "<$(( 1 * 0 ))>" e "<$((1*1))>" e "<$(( 1 * 1 ))>" e "<$((1111*2222))>" e "<$((2222*1111))>" e "<$(( +0x10 * +0x11 ))>" e "<$(( -0x10 * -0x11 ))>" e "<$(( -0x10 * -0x11 ))>" e "<$(( +64#10 * -64#11 ))>" e "<$(( +0x11 * +0x10 ))>" e "<$(( -0x11 * -0x10 ))>" e "<$(( -0x11 * -0x10 ))>" e "<$(( +64#11 * -64#10 ))>" e "<$((0x8000000000000000*-1))>" e "<$((0x8000000000000000*1))>" e "<$((0x7FFFFFFFFFFFFFFF*-1))>" e "<$((0x7FFFFFFFFFFFFFFF*1))>" e "<$((0xFFFFFFFFFFFFFFFF*-1))>" e "<$((0xFFFFFFFFFFFFFFFF*1))>" e "<$((0x8000000000000000*-11))>" e "<$((0x8000000000000000*11))>" e "<$((0x7FFFFFFFFFFFFFFF*-11))>" e "<$((0x7FFFFFFFFFFFFFFF*11))>" e "<$((0xFFFFFFFFFFFFFFFF*-11))>" e "<$((0xFFFFFFFFFFFFFFFF*11))>" e '= BIN /' e "<$(( 0 / 1 ))>" e "<$((1/1))>" e "<$(( 1 / 1 ))>" e "<$((1111/2222))>" e "<$((2222/1111))>" e "<$(( +0x10 / +0x11 ))>" e "<$(( -0x10 / -0x11 ))>" e "<$(( -0x10 / -0x11 ))>" e "<$(( +64#10 / -64#11 ))>" e "<$(( +0x11 / +0x10 ))>" e "<$(( -0x11 / -0x10 ))>" e "<$(( -0x11 / -0x10 ))>" e "<$(( +64#11 / -64#10 ))>" e "<$((2/1))>" e "<$((3/1))>" e "<$((3/2))>" e "<$((3/3))>" e "<$((3/4))>" e "<$((-1/4))>" e "<$((0x8000000000000000/-1))>" e "<$((0x8000000000000000/1))>" e "<$((0x7FFFFFFFFFFFFFFF/-1))>" e "<$((0x7FFFFFFFFFFFFFFF/1))>" e "<$((0xFFFFFFFFFFFFFFFF/-1))>" e "<$((0xFFFFFFFFFFFFFFFF/1))>" e "<$((0x8000000000000000/-11))>" e "<$((0x8000000000000000/11))>" e "<$((0x7FFFFFFFFFFFFFFF/-11))>" e "<$((0x7FFFFFFFFFFFFFFF/11))>" e "<$((0xFFFFFFFFFFFFFFFF/-11))>" e "<$((0xFFFFFFFFFFFFFFFF/11))>" e '= BIN %' e "<$(( 0 % 1 ))>" e "<$((1%1))>" e "<$(( 1 % 1 ))>" e "<$((1111%2222))>" e "<$((2222%1111))>" e "<$(( +0x10 % +0x11 ))>" e "<$(( -0x10 % -0x11 ))>" e "<$(( -0x10 % -0x11 ))>" e "<$(( +64#10 % -64#11 ))>" e "<$(( +0x11 % +0x10 ))>" e "<$(( -0x11 % -0x10 ))>" e "<$(( -0x11 % -0x10 ))>" e "<$(( +64#11 % -64#10 ))>" e "<$((2%1))>" e "<$((3%1))>" e "<$((3%2))>" e "<$((3%3))>" e "<$((3%4))>" e "<$((-1%4))>" e "<$((0x8000000000000000%-1))>" e "<$((0x8000000000000000%1))>" e "<$((0x7FFFFFFFFFFFFFFF%-1))>" e "<$((0x7FFFFFFFFFFFFFFF%1))>" e "<$((0xFFFFFFFFFFFFFFFF%-1))>" e "<$((0xFFFFFFFFFFFFFFFF%1))>" e "<$((0x8000000000000000%-11))>" e "<$((0x8000000000000000%11))>" e "<$((0x7FFFFFFFFFFFFFFF%-11))>" e "<$((0x7FFFFFFFFFFFFFFF%11))>" e "<$((0xFFFFFFFFFFFFFFFF%-11))>" e "<$((0xFFFFFFFFFFFFFFFF%11))>" e '= BIN <<' e "<$((0<<0))>" e "<$(( 0 << 0 ))>" e "<$((0<<1))>" e "<$(( 0 << 1 ))>" e "<$((1<<0))>" e "<$(( 1 << 0 ))>" e "<$((1<<1))>" e "<$(( 1 << 1 ))>" e "<$((1111<<2222))>" e "<$((2222<<1111))>" e "<$(( +0x10 << +0x11 ))>" e "<$(( -0x10 << -0x11 ))>" e "<$(( -0x10 << -0x11 ))>" e "<$(( +64#10 << -64#11 ))>" e "<$(( +0x11 << +0x10 ))>" e "<$(( -0x11 << -0x10 ))>" e "<$(( -0x11 << -0x10 ))>" e "<$(( +64#11 << -64#10 ))>" e "<$(( +64 << +1024 ))>" e "<$((0x8000000000000000<<-1))>" e "<$((0x8000000000000000<<1))>" e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" e "<$((0x7FFFFFFFFFFFFFFF<<1))>" e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" e "<$((0xFFFFFFFFFFFFFFFF<<1))>" e "<$((0x8000000000000000<<-11))>" e "<$((0x8000000000000000<<11))>" e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" e "<$((0x7FFFFFFFFFFFFFFF<<11))>" e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" e "<$((0xFFFFFFFFFFFFFFFF<<11))>" e '= BIN >>' e "<$((0>>0))>" e "<$(( 0 >> 0 ))>" e "<$((0>>1))>" e "<$(( 0 >> 1 ))>" e "<$((1>>0))>" e "<$(( 1 >> 0 ))>" e "<$((1>>1))>" e "<$(( 1 >> 1 ))>" e "<$((1>>>1))>" e "<$(( 1 >>> 1 ))>" e "<$((1111>>2222))>" e "<$((2222>>1111))>" e "<$((1111>>>2222))>" e "<$((2222>>>1111))>" e "<$(( +0x10 >> +0x11 ))>" e "<$(( -0x10 >> -0x11 ))>" e "<$(( -0x10 >> -0x11 ))>" e "<$(( -0x10 >>> -0x11 ))>" e "<$(( +64#10 >> -64#11 ))>" e "<$(( +0x11 >> +0x10 ))>" e "<$(( -0x11 >> -0x10 ))>" e "<$(( -0x11 >> -0x10 ))>" e "<$(( +64#11 >> -64#10 ))>" e "<$(( +64 >> +1024 ))>" e "<$((0x8000000000000000>>-1))>" e "<$((0x8000000000000000>>1))>" e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" e "<$((0x7FFFFFFFFFFFFFFF>>1))>" e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" e "<$((0xFFFFFFFFFFFFFFFF>>1))>" e "<$((0x8000000000000000>>-11))>" e "<$((0x8000000000000000>>11))>" e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" e "<$((0x7FFFFFFFFFFFFFFF>>11))>" e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" e "<$((0xFFFFFFFFFFFFFFFF>>11))>" e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" e '= BIN **' e "<$((0**1))>" e "<$((2**1))>" e "<$((2**2))>" e "<$((2**3))>" e "<$((2**4))>" e "<$((10**4))>" e "<$((10**10))>" e "<$((10**5+5))>" e "<$((10**(5+5)))>" e '= LOG OR' e "<$((0||0))>" e "<$(( 000 || 0X0 ))>" e "<$((01 || 64#1))>" e "<$((01 || 64#1))>" e "<$((0x1234 || 4660))>" e "<$((0x1234 || 011064))>" s I=33 J=33;e "<$((I||J))>" s I=33 J=33;e "<$(( I || J ))>" e "<$((0||1))>" e "<$((0||0000000000000000000000001))>" e "<$((1||2))>" e "<$((0x1234 || 04660))>" e "<$((0x1234 || 0x11064))>" s I=10 J=33;e "<$((I||J))>" s I=-10 J=-33;e "<$((I||J))>" s I=-33 J=-33;e "<$((I||J))>" s I=0 J=-33;e "<$((I||J))>" s I=33 J=0;e "<$((I||J))>" e '= LOG AND' e "<$((0&&0))>" e "<$(( 000 && 0X0 ))>" e "<$((01 && 64#1))>" e "<$((01 && 64#1))>" e "<$((0x1234 && 4660))>" e "<$((0x1234 && 011064))>" s I=33 J=33;e "<$((I&&J))>" s I=33 J=33;e "<$(( I && J ))>" e "<$((0&&1))>" e "<$((0&&0000000000000000000000001))>" e "<$((1&&2))>" e "<$((0x1234 && 04660))>" e "<$((0x1234 && 0x11064))>" s I=10 J=33;e "<$((I&&J))>" s I=-10 J=-33;e "<$((I&&J))>" s I=-33 J=-33;e "<$((I&&J))>" s I=0 J=-33;e "<$((I&&J))>" s I=33 J=0;e "<$((I&&J))>" e '= BIN BIT_OR' e "<$((0|0))>" e "<$(( 0 | 0 ))>" e "<$((0|1))>" e "<$(( 0 | 1 ))>" e "<$((1|0))>" e "<$(( 1 | 0 ))>" e "<$((1|1))>" e "<$(( 1 | 1 ))>" e "<$((1111|2222))>" e "<$((2222|1111))>" e "<$(( +0x10 | +0x11 ))>" e "<$(( -0x10 | -0x11 ))>" e "<$(( -0x10 | -0x11 ))>" e "<$(( +64#10 | -64#11 ))>" e "<$(( +0x11 | +0x10 ))>" e "<$(( -0x11 | -0x10 ))>" e "<$(( -0x11 | -0x10 ))>" e "<$(( +64#11 | -64#10 ))>" e "<$(( +64 | +1024 ))>" e "<$((0x8000000000000000|-1))>" e "<$((0x8000000000000000|1))>" e "<$((0x7FFFFFFFFFFFFFFF|-1))>" e "<$((0x7FFFFFFFFFFFFFFF|1))>" e "<$((0xFFFFFFFFFFFFFFFF|-1))>" e "<$((0xFFFFFFFFFFFFFFFF|1))>" e "<$((0x8000000000000000|-11))>" e "<$((0x8000000000000000|11))>" e "<$((0x7FFFFFFFFFFFFFFF|-11))>" e "<$((0x7FFFFFFFFFFFFFFF|11))>" e "<$((0xFFFFFFFFFFFFFFFF|-11))>" e "<$((0xFFFFFFFFFFFFFFFF|11))>" e '= BIN BIT_XOR' e "<$((0^0))>" e "<$(( 0 ^ 0 ))>" e "<$((0^1))>" e "<$(( 0 ^ 1 ))>" e "<$((1^0))>" e "<$(( 1 ^ 0 ))>" e "<$((1^1))>" e "<$(( 1 ^ 1 ))>" e "<$((1111^2222))>" e "<$((2222^1111))>" e "<$(( +0x10 ^ +0x11 ))>" e "<$(( -0x10 ^ -0x11 ))>" e "<$(( -0x10 ^ -0x11 ))>" e "<$(( +64#10 ^ -64#11 ))>" e "<$(( +0x11 ^ +0x10 ))>" e "<$(( -0x11 ^ -0x10 ))>" e "<$(( -0x11 ^ -0x10 ))>" e "<$(( +64#11 ^ -64#10 ))>" e "<$(( +64 ^ +1024 ))>" e "<$((0x8000000000000000^-1))>" e "<$((0x8000000000000000^1))>" e "<$((0x7FFFFFFFFFFFFFFF^-1))>" e "<$((0x7FFFFFFFFFFFFFFF^1))>" e "<$((0xFFFFFFFFFFFFFFFF^-1))>" e "<$((0xFFFFFFFFFFFFFFFF^1))>" e "<$((0x8000000000000000^-11))>" e "<$((0x8000000000000000^11))>" e "<$((0x7FFFFFFFFFFFFFFF^-11))>" e "<$((0x7FFFFFFFFFFFFFFF^11))>" e "<$((0xFFFFFFFFFFFFFFFF^-11))>" e "<$((0xFFFFFFFFFFFFFFFF^11))>" e '= BIN BIT_AND' e "<$((0&0))>" e "<$(( 0 & 0 ))>" e "<$((0&1))>" e "<$(( 0 & 1 ))>" e "<$((1&0))>" e "<$(( 1 & 0 ))>" e "<$((1&1))>" e "<$(( 1 & 1 ))>" e "<$((1111&2222))>" e "<$((2222&1111))>" e "<$(( +0x10 & +0x11 ))>" e "<$(( -0x10 & -0x11 ))>" e "<$(( -0x10 & -0x11 ))>" e "<$(( +64#10 & -64#11 ))>" e "<$(( +0x11 & +0x10 ))>" e "<$(( -0x11 & -0x10 ))>" e "<$(( -0x11 & -0x10 ))>" e "<$(( +64#11 & -64#10 ))>" e "<$(( +64 & +1024 ))>" e "<$((0x8000000000000000&-1))>" e "<$((0x8000000000000000&1))>" e "<$((0x7FFFFFFFFFFFFFFF&-1))>" e "<$((0x7FFFFFFFFFFFFFFF&1))>" e "<$((0xFFFFFFFFFFFFFFFF&-1))>" e "<$((0xFFFFFFFFFFFFFFFF&1))>" e "<$((0x8000000000000000&-11))>" e "<$((0x8000000000000000&11))>" e "<$((0x7FFFFFFFFFFFFFFF&-11))>" e "<$((0x7FFFFFFFFFFFFFFF&11))>" e "<$((0xFFFFFFFFFFFFFFFF&-11))>" e "<$((0xFFFFFFFFFFFFFFFF&11))>" e '= BIN EQ' e "<$((0==0))>" e "<$(( 000 == 0X0 ))>" e "<$((01 == 64#1))>" e "<$((01 == 64#1))>" e "<$((0x1234 == 4660))>" e "<$((0x1234 == 011064))>" s I=33 J=33;e "<$((I==J))>" s I=33 J=33;e "<$(( I == J ))>" e "<$((0==1))>" e "<$((0==0000000000000000000000001))>" e "<$((1==2))>" e "<$((0x1234 == 04660))>" e "<$((0x1234 == 0x11064))>" s I=10 J=33;e "<$((I==J))>" s I=-10 J=-33;e "<$((I==J))>" s I=-33 J=-33;e "<$((I==J))>" e '= BIN NE' e "<$((0!=0))>" e "<$(( 000 != 0X0 ))>" e "<$((01 != 64#1))>" e "<$((01 != 64#1))>" e "<$((0x1234 != 4660))>" e "<$((0x1234 != 011064))>" s I=33 J=33;e "<$((I!=J))>" s I=33 J=33;e "<$(( I != J ))>" e "<$((0!=1))>" e "<$((0!=0000000000000000000000001))>" e "<$((1!=2))>" e "<$((0x1234 != 04660))>" e "<$((0x1234 != 0x11064))>" s I=10 J=33;e "<$((I!=J))>" s I=-10 J=-33;e "<$((I!=J))>" s I=-33 J=-33;e "<$((I!=J))>" e '= BIN LE' e "<$((0<=0))>" e "<$(( 000 <= 0X0 ))>" e "<$((01 <= 64#1))>" e "<$((01 <= 64#2))>" e "<$((02 <= 64#1))>" e "<$((0x1234 <= 4660))>" e "<$((0x1234 <= 011064))>" e "<$((0x1233 <= 011064))>" e "<$((0x1235 <= 011064))>" s I=33 J=33;e "<$((I<=J))>" s I=33 J=33;e "<$((I<=J))>" s I=32 J=33;e "<$((I<=J))>" s I=34 J=33;e "<$((I<=J))>" s I=-33 J=-33;e "<$((I<=J))>" s I=-33 J=-33;e "<$((I<=J))>" s I=-32 J=-33;e "<$((I<=J))>" s I=-34 J=-33;e "<$((I<=J))>" e '= BIN GE' e "<$((0>=0))>" e "<$(( 000 >= 0X0 ))>" e "<$((01 >= 64#1))>" e "<$((01 >= 64#2))>" e "<$((02 >= 64#1))>" e "<$((0x1234 >= 4660))>" e "<$((0x1234 >= 011064))>" e "<$((0x1233 >= 011064))>" e "<$((0x1235 >= 011064))>" s I=33 J=33;e "<$((I>=J))>" s I=33 J=33;e "<$((I>=J))>" s I=32 J=33;e "<$((I>=J))>" s I=34 J=33;e "<$((I>=J))>" s I=-33 J=-33;e "<$((I>=J))>" s I=-33 J=-33;e "<$((I>=J))>" s I=-32 J=-33;e "<$((I>=J))>" s I=-34 J=-33;e "<$((I>=J))>" e '= BIN LT' e "<$((0<0))>" e "<$(( 000 < 0X0 ))>" e "<$((01 < 64#1))>" e "<$((01 < 64#2))>" e "<$((02 < 64#1))>" e "<$((0x1234 < 4660))>" e "<$((0x1234 < 011064))>" e "<$((0x1233 < 011064))>" e "<$((0x1235 < 011064))>" s I=33 J=33;e "<$((I" s I=33 J=33;e "<$((I" s I=32 J=33;e "<$((I" s I=34 J=33;e "<$((I" s I=-33 J=-33;e "<$((I" s I=-33 J=-33;e "<$((I" s I=-32 J=-33;e "<$((I" s I=-34 J=-33;e "<$((I" e '= BIN GT' e "<$((0>0))>" e "<$(( 000 > 0X0 ))>" e "<$((01 > 64#1))>" e "<$((01 > 64#2))>" e "<$((02 > 64#1))>" e "<$((0x1234 > 4660))>" e "<$((0x1234 > 011064))>" e "<$((0x1233 > 011064))>" e "<$((0x1235 > 011064))>" s I=33 J=33;e "<$((I>J))>" s I=33 J=33;e "<$((I>J))>" s I=32 J=33;e "<$((I>J))>" s I=34 J=33;e "<$((I>J))>" s I=-33 J=-33;e "<$((I>J))>" s I=-33 J=-33;e "<$((I>J))>" s I=-32 J=-33;e "<$((I>J))>" s I=-34 J=-33;e "<$((I>J))>" # # COMMA below e '= PRECEDENCE I' e "<$(( 1 + 2 + 3 ))>" e "<$(( 1 - 2 + 3 ))>" e "<$(( 3 - 2 - 1 ))>" e "<$(( 3 - 2 + 1 ))>" e "<$(( - 2 + 1 ))>" e "<$(( 2 + -1 ))>" e "<$(( ! 2 + 1 ))>" e "<$(( 2 + !1 ))>" e "<$(( 3 * 2 + 2 ))>" e "<$(( 3 + 2 * 2 ))>" e "<$(( 3 * 2 * 2 ))>" e "<$(( 9 / 3 + 2 ))>" e "<$(( 9 + 3 / 2 ))>" e "<$(( 9 / 3 / 2 ))>" e "<$(( 9 << 1 + 2 ))>" e "<$(( 9 + 3 << 2 ))>" e "<$(( 9 << 3 << 2 ))>" e "<$(( 9 >> 1 + 2 ))>" e "<$(( 9 + 3 >> 2 ))>" e "<$(( 19 >> 3 >> 1 ))>" e "<$(( 19 >> 3 << 1 ))>" e "<$(( 19 << 3 >> 1 ))>" e "<$(( 2 + 3 < 3 * 2 ))>" e "<$(( 2 << 3 >= 3 << 2 ))>" e "<$(( 0xfD & 0xF == 0xF ))>" e "<$((0xfD&0xF==0xF))>" e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" e "<$((3*7,2<<8,9-7))>" e '= PARENS' e "<$(((1 + 2) + 3))>" e "<$(((1+2)+3))>" e "<$((1 - (2 + 3)))>" e "<$((1-(2+3)))>" e "<$((3 - (2 - 1)))>" e "<$((3-(2-1)))>" e "<$((3 - ( 2 + 1 )))>" e "<$((3-(2+1)))>" e "<$((- (2 + 1)))>" e "<$((-(2+1)))>" e "<$((! (2 + 1)))>" e "<$((!(2+1)))>" e "<$((3 * (2 + 2)))>" e "<$((3*(2+2)))>" e "<$(((3 + 2) * 2))>" e "<$(((3+2)*2))>" e "<$((3 * (2 * 2)))>" e "<$((3*(2*8)))>" e "<$((9 / (3 + 2)))>" e "<$((9/(3+2)))>" e "<$((( 9 + 3 ) / 2))>" e "<$(((9+3)/2))>" e "<$((9 / ( 3 / 2 )))>" e "<$((9/(3/2)))>" e "<$((( 9 << 1 ) + 2))>" e "<$(((9<<1)+2))>" e "<$((9 + (3 << 2)))>" e "<$((9+(3<<2)))>" e "<$((9 << (3 << 2)))>" e "<$((9<<(3<<2)))>" e "<$(((9 >> 1) + 2))>" e "<$(((9>>1)+2))>" e "<$((9 + (3 >> 2)))>" e "<$((9+(3>>2)))>" e "<$((19 >> (3 >> 1)))>" e "<$((19>>(3>>1)))>" e "<$((19 >> (3 << 1)))>" e "<$((19>>(3<<1)))>" e "<$((19 << (3 >> 1)))>" e "<$((19<<(3>>1)))>" e "<$((2 + (3 < 3) * 2))>" e "<$((2+(3<3)*2))>" e "<$((2 << ((3 >= 3) << 2)))>" e "<$((2<<((3>=3)<<2)))>" e "<$(((0xfD & 0xF) == 0xF))>" e "<$(((0xfD&0xF)==0xF))>" e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" e "<$((3*(7,2)<<(8,9-7)))>" # # COND BELOW e '= ASSIGN I' unset I;p "<$(( I = 3 ))>";e "<$I>" unset I;p "<$((I=3))>";e "<$I>" s I=10;p "<$((I=3))>";e "<$I>" s I=10;p "<$((I+=1))>";e "<$I>" s I=10;p "<$((I-=1))>";e "<$I>" s I=10;p "<$((I*=1))>";e "<$I>" s I=10;p "<$((I*=2))>";e "<$I>" s I=10;p "<$((I/=1))>";e "<$I>" s I=10;p "<$((I/=2))>";e "<$I>" s I=10;p "<$((I%=1))>";e "<$I>" s I=10;p "<$((I%=2))>";e "<$I>" s I=10;p "<$((I**=1))>";e "<$I>" s I=10;p "<$((I**=2))>";e "<$I>" s I=10;p "<$((I**=1+1))>";e "<$I>" s I=10;p "<$((I|=1))>";e "<$I>" s I=10;p "<$((I^=1))>";e "<$I>" ; p "<$((I^=1))>";e "<$I>" s I=10;p "<$((I&=2))>";e "<$I>" s I=10;p "<$((I>>=1))>";e "<$I>" s I=10;p "<$((I<<=1))>";e "<$I>" s I=-1;p "<$((I>>>=1))>";e "<$I>" e '= ASSIGN II' s I=2;p "<$(((I+=1)-1))>";e "<$I>" s I=4;p "<$(((I-=1)+1))>";e "<$I>" s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" s I=10;p "<$((I=2,I|=1))>";e "<$I>" s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" e '= POSTFIX' s I=1;p "<$((I++))>";e "<$I>" s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" s I=1;p "<$((I--))>";e "<$I>" s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" e '= PREFIX' s I=1;p "<$((++I))>";e "<$I>" s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" s I=1;p "<$((--I))>";e "<$I>" s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" e '= VAR RECUR' s I='1 + 1';p "<$((I))>";e "<$I>" s I='1 + 1';p "<$((+I))>";e "<$I>" s I='1 + 1';p "<$((++I))>";e "<$I>" s I='1 + 1';p "<$((I++))>";e "<$I>" s I='1 + 1';p "<$((1+I))>";e "<$I>" s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" s I='1 + 1';p "<$((I=I))>";e "<$I>" s I='1 + 1';p "<$((I=+I))>";e "<$I>" s I='1 + 1';p "<$((I=1+I))>";e "<$I>" s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" e '= COMMA' e "<$(( 1 , 2 ))>" e "<$(( 1 , 2 , 3 ))>" e "<$(( 1 , 2 , 3 , 4 ))>" e "<$((1,2,3,4))>" s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" s I1=I2=10 I2=3; p "<$((I1,I2))>"; e "<$I1><$I2>" e '= COND ' e "<$(( +0 ? 2 : 3 ))>" e "<$((-0?2:3))>" e "<$(( +1 ? 2 : 3 ))>" e "<$(( 1-1 ? 2 : 3 ))>" e "<$(( 1-0 ? 2 : 3 ))>" e "<$((-1?2:3))>" e "<$(( 0x1234 ? 111 : 222 ))>" e "<$((1**2 ? 5 : 7))>" e "<$((0**2 ? 5 : 7))>" e "<$((0**2>=0?5:7))>" e "<$((-1<=0**2?5:7))>" e "<$((1<=0**2?5:7))>" e "<$((1>2||1*0?5:7))>" e "<$((1>2&&1*0?5:7))>" e "<$((1<2&&1*0?5:7))>" e "<$((1<2&&1*0+1?5:7))>" e '-- COND .2' e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" e "<$((2<1?-1:2>1?1:0))>" e "<$((4<5 ? 1 : 32))>" e "<$((4>5 ? 1 : 32))>" e "<$((4>(2+3) ? 1 : 32))>" e "<$((4<(2+3) ? 1 : 32))>" e "<$(((2+2)<(2+3) ? 1 : 32))>" e "<$(((2+2)>(2+3) ? 1 : 32))>" ## grouping protects precedence in : parts (syntax error tests below) e '-- COND .3' e "<$((1-1 < 1 ? 2,4 : 1,3))>" e "<$((0<1?2,4:(1,3)))>" e "<$((0,1,2,0?2,4:1,3))>" e "<$((0,1,2,1?2,4:1,3))>" e "<$((0,1,2,0?2,4:(1,3)))>" e "<$((0,1,2,1?2,4:(1,3)))>" e "<$((0,1,2,0?(2,4):1,3))>" e "<$((0,1,2,1?(2,4):1,3))>" e "<$((0,1,2,0?(2,4):(1,3)))>" e "<$((0,1,2,1?(2,4):(1,3)))>" e "<$((0?2:((0,3)?1:4)))>" e "<$((1?2:3,0?1:4))>" e "<$((1?2:3,0?1:4?5:6))>" e "<$((1?2:(3,0)?1:4?5:6))>" e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" e '-- COND .4' e "<$((1?2?3?4?5:6:7:8:9))>" e "<$((1?2?3?0?5:6:7:8:9))>" e "<$((1?2?0?0?5:6:7:8:9))>" e "<$((1?0?0?0?5:6:7:8:9))>" e "<$((0?0?0?0?5:6:7:8:9))>" e "<$((0?3+4?10:11:5+6?12:13))>" e "<$((1?3+4?10:11:5+6?12:13))>" e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" e '-- COND .5' e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" e '-- COND .6' e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" e '-- COND .7' s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$((((I1";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1";\ e "<$I1><$I2><$I3><$I4><$I5>" # only first s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ e "<$I1><$I2><$I3><$I4>" # last not etc. s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ e "<$I1><$I2><$I3><$I4>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1I3)?(I3";\ e "<$I1><$I2><$I3><$I4><$I5>" s I1=2 I2=3 I3=4 I4=5;\ p "<$(((I1>I2)?(I2";\ e "<$I1><$I2><$I3><$I4><$I5>" e '-- COND .8' s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" s I=0;p "<$((1?20:(I+=2)))>"; e "<$I>" s I=0;p "<$((1?I+=10:(I+=2)))>"; e "<$I>" s I=0;p "<$((0?I+=2:20))>"; e "<$I>" s I=0;p "<$((0?I+=2:(I+=10)))>"; e "<$I>" s I=0;p "<$((0?(I+=2):(20)))>"; e "<$I>" s I=0;p "<$((0?(I+=2):(I+=20)))>"; e "<$I>" e '-- COND .9' s I1=+E+ I2=1+1; p "<$((0?I1:I2))>"; e "<$I1><$I2>" s I1=1+1 I2=+E+; p "<$((1?I1:I2))>"; e "<$I1><$I2>" s I1=+E+ I2=1+1; p "<$((0?I1=1:(I2=2)))>"; e "<$I1><$I2>" s I1=1+1 I2=+E+; p "<$((1?I1=1:(I2=2)))>"; e "<$I1><$I2>" s I1=+E+ I2=1+1; p "<$((0?I1*=I1:(I2*=I2)))>"; e "<$I1><$I2>" s I1=1+1 I2=+E+; p "<$((1?I1*=I1:(I2*=I2)))>"; e "<$I1><$I2>" e '= WILD I' e "<$(( 3 + ( 11 ) ))>" e "<$((1 + (2 - 2)))>" e "<$((1 + (2 - 2)))>" e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" e "<$(( 3+((2 * 2))/6 ))>" e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" s I1=I2=10 I2=3; p "<$((I1 + I2))>"; e "<$I1><$I2>" s I1=I2=10 I2=3; p "<$((I1 * I2))>"; e "<$I1><$I2>" s I1=I2=10 I2=3; p "<$((I1 % I2))>"; e "<$I1><$I2>" e '= WILD II' s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" s I=10;p "<$((3+(3*(I++))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" s I=10;p "<$(( +10 + + +I ))>";e "<$I>" s I=10;p "<$(( +10 + ++I ))>";e "<$I>" s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" s I=10;p "<$(( +10 +++ I ))>";e "<$I>" s I=10;p "<$(( +10+++I ))>";e "<$I>" s I=10;p "<$((+10++I))>";e "<$I>" s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" e "<$(( +10 + + + ++++ +11 ))>" e "<$(( +10 + + + ++++ ++11 ))>" e "<$((+10++++++++11))>" e '= WILD RECUR' # (some yet) s I1=I2=10 I2=5;p "<$((I1+=I2))>";e "<$I1><$I2>" s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" s I1=I2='(I2=10)+1' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" s I1=I2='(I2=(I2=10)+1)' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" s I1=I2=+E+ I2=5;p "<$((I1=10))>";e "<$I1><$I2>" s I1=I2=+E+ I2=5;p "<$((0?I1:++I2))>";e "<$I1><$I2>" s I1=I2=10 I2=5;p "<$((I2,(1?I1:++I2)))>";e "<$I1><$I2>" From steffen at sdaoden.eu Wed Aug 10 22:55:12 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Thu, 11 Aug 2022 00:55:12 +0200 Subject: [PATCHv2] shell: exchange Dijkstra $(( )) evaluator.. In-Reply-To: <20220810153752.Dw-eh%steffen@sdaoden.eu> References: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <20220810153752.Dw-eh%steffen@sdaoden.eu> Message-ID: <20220810225512.rRIy1%steffen@sdaoden.eu> Hm. Sorry to cause more distress, but i ran into this: $ bash -c ' I1=I2=10 I2=5 I3=I2+=1; echo "<$(( I1?I1:I3 ))>";echo "<$I1><$I2><$I3>"' <10> <10> $ s-nail -#:/ -Y 'set I1=I2=10 I2=5 I3=I2+=1;echo "<$(( I1?I1:I3 ))>";echo "<$I1><$I2><$I3>"' -Yx <10> <10> $ ../busybox sh -c ' I1=I2=10 I2=5 I3=I2+=1; echo "<$(( I1?I1:I3 ))>";echo "<$I1><$I2><$I3>"' <6> <6> $ diff- src/mx/shexp-arith.h /x/src/busybox.git/shell/shexp-arith.h $ echo $? 0 That is interesting. I will investigate. (I have not run the test with busybox again, actually. Why should i have done so?) I hope this thread falls silent again soon. Ciao, and a good night from Germany. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Thu Aug 11 00:15:24 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Thu, 11 Aug 2022 02:15:24 +0200 Subject: [PATCH] shell: fill in tremendous $(()) evaluator miss In-Reply-To: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> References: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> Message-ID: <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> --- I found the hopefully last problem. It is a mystery how _that_ miss was not caused by any of the tests yet; only because of busybox different stack pattern it was catched at all! The three new tests s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" however would have catched it. Interestingly we now have a divergence to the bash $(()) implementation, and i have opened a bash bug report because of it, as i think we are correct: given this file # make this work with (ba)sh \ command -v shopt && shopt -s expand_aliases;\ alias p=printf;alias e=echo;alias s=export s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" we get -<6><6><6> -<10><10><5> +<6><0><6> +<1><1><5> compared to bash, and i think the latter two are correct. Hey! May it now rest in peace, finally. And sorry for all the noise! I hope you like it, if you want to include it in busybox i would be willing to embed it better, which surely nibbles quite a few bytes off the thing. Ciao from Germany. shell/shexp-arith.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h index 4b23746df1..17582f7d49 100644 --- a/shell/shexp-arith.h +++ b/shell/shexp-arith.h @@ -593,7 +593,16 @@ junapre: } ASSERT(sasp->sas_nums_top > sasp->sas_nums); - if((--sasp->sas_nums_top)->sav_val == 0) + --sasp->sas_nums_top; + + if((op & a_SHEXP_ARITH_OP_MASK) != a_SHEXP_ARITH_OP_ASSIGN && + sasp->sas_nums_top->sav_var != NIL){ + if(!a_shexp__arith_val_eval(self, sasp->sas_nums_top)) + goto jleave; + sasp->sas_nums_top->sav_var = NIL; + } + + if((sasp->sas_nums_top)->sav_val == 0) op |= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; op |= *sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK; -- 2.37.1 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From nv at vosn.de Tue Aug 9 14:01:00 2022 From: nv at vosn.de (Nikolaus Voss) Date: Tue, 9 Aug 2022 16:01:00 +0200 Subject: [PATCH] ip link: support "type can bitrate NUM" argument Message-ID: <20220811072513.8E70ED18@mail.steuer-voss.de> This is the minimum needed to set up SocketCAN interfaces. Setting the bitrate is usually required before upping the device, e.g. ip link set can0 type can bitrate 1000000 ip link set up can0 iproute2 supports more options to "type can" but these are not mandatory for using the device. Signed-off-by: Nikolaus Voss --- networking/ip.c | 2 +- networking/libiproute/iplink.c | 57 ++++++++++++++++++++++++++++-- networking/libiproute/libnetlink.c | 14 ++++++++ networking/libiproute/libnetlink.h | 5 +++ 4 files changed, 75 insertions(+), 3 deletions(-) diff --git a/networking/ip.c b/networking/ip.c index 7c3208699..f0159912a 100644 --- a/networking/ip.c +++ b/networking/ip.c @@ -152,7 +152,7 @@ //usage:#define iplink_trivial_usage //usage: /*Usage:iplink*/"set IFACE [up|down] [arp on|off] [multicast on|off]\n" //usage: " [promisc on|off] [mtu NUM] [name NAME] [qlen NUM] [address MAC]\n" -//usage: " [master IFACE | nomaster] [netns PID]" +//usage: " [master IFACE | nomaster] [netns PID] [type can bitrate NUM]" // * short help shows only "set" command, long help continues (with just one "\n") // * and shows all other commands: //usage:#define iplink_full_usage "\n" diff --git a/networking/libiproute/iplink.c b/networking/libiproute/iplink.c index 68d199044..c751087d8 100644 --- a/networking/libiproute/iplink.c +++ b/networking/libiproute/iplink.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "ip_common.h" /* #include "libbb.h" is inside */ #include "rt_names.h" @@ -241,6 +242,55 @@ static void die_must_be_on_off(const char *msg) bb_error_msg_and_die("argument of \"%s\" must be \"on\" or \"off\"", msg); } +/* Return value becomes exitcode. It's okay to not return at all */ +static int do_set_type(char **argv, char *dev) +{ + struct rtnl_handle rth; + struct { + struct nlmsghdr n; + struct ifinfomsg i; + char buf[1024]; + } req; + struct rtattr *linkinfo; + struct rtattr *data; + + memset(&req, 0, sizeof(req)); + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWLINK; + req.i.ifi_family = preferred_family; + + xrtnl_open(&rth); + req.i.ifi_index = xll_name_to_index(dev); + linkinfo = addattr_nest(&req.n, sizeof(req), IFLA_LINKINFO); + + if (!strcmp(*argv, "can")) { + struct can_bittiming bt = { 0 }; + + addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, *argv, + strlen(*argv)); + data = addattr_nest(&req.n, sizeof(req), IFLA_INFO_DATA); + NEXT_ARG(); + if (!strcmp(*argv, "bitrate")) { + NEXT_ARG(); + bt.bitrate = get_u32(*argv, "bitrate"); + addattr_l(&req.n, sizeof(req), IFLA_CAN_BITTIMING, &bt, + sizeof(bt)); + } else { + bb_error_msg_and_die("arg \"%s\" unkown", *argv); + } + addattr_nest_end(&req.n, data); + } else { + invarg_1_to_2(*argv, "type"); + } + addattr_nest_end(&req.n, linkinfo); + + if (rtnl_talk(&rth, &req.n, 0, 0, NULL, NULL, NULL) < 0) + xfunc_die(); + + return 0; +} + /* Return value becomes exitcode. It's okay to not return at all */ static int do_set(char **argv) { @@ -260,11 +310,11 @@ static int do_set(char **argv) static const char keywords[] ALIGN1 = "up\0""down\0""name\0""mtu\0""qlen\0""multicast\0" "arp\0""promisc\0""address\0""netns\0" - "master\0""nomaster\0" + "master\0""nomaster\0" "type\0" "dev\0" /* must be last */; enum { ARG_up = 0, ARG_down, ARG_name, ARG_mtu, ARG_qlen, ARG_multicast, ARG_arp, ARG_promisc, ARG_addr, ARG_netns, - ARG_master, ARG_nomaster, + ARG_master, ARG_nomaster, ARG_type, ARG_dev }; enum { PARM_on = 0, PARM_off }; smalluint key; @@ -304,6 +354,9 @@ static int do_set(char **argv) } else if (key == ARG_netns) { NEXT_ARG(); netns = get_unsigned(*argv, "netns"); + } else if (key == ARG_type) { + NEXT_ARG(); + return do_set_type(argv, dev); } else if (key >= ARG_dev) { /* ^^^^^^ ">=" here results in "dev IFACE" treated as default */ if (key == ARG_dev) { diff --git a/networking/libiproute/libnetlink.c b/networking/libiproute/libnetlink.c index 7e3473a1c..5b6273245 100644 --- a/networking/libiproute/libnetlink.c +++ b/networking/libiproute/libnetlink.c @@ -390,6 +390,20 @@ int FAST_FUNC addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, in return 0; } +struct rtattr * FAST_FUNC addattr_nest(struct nlmsghdr *n, int maxlen, int type) +{ + struct rtattr *nest = NLMSG_TAIL(n); + + addattr_l(n, maxlen, type, NULL, 0); + return nest; +} + +int FAST_FUNC addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) +{ + nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest; + return n->nlmsg_len; +} + int FAST_FUNC rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data) { int len = RTA_LENGTH(4); diff --git a/networking/libiproute/libnetlink.h b/networking/libiproute/libnetlink.h index 1b082e019..b7eefa1e5 100644 --- a/networking/libiproute/libnetlink.h +++ b/networking/libiproute/libnetlink.h @@ -54,11 +54,16 @@ static ALWAYS_INLINE void rtnl_send(struct rtnl_handle *rth, const void *buf, in extern int addattr32(struct nlmsghdr *n, int maxlen, int type, uint32_t data) FAST_FUNC; extern int addattr_l(struct nlmsghdr *n, int maxlen, int type, void *data, int alen) FAST_FUNC; +extern struct rtattr * addattr_nest(struct nlmsghdr *n, int maxlen, int type) FAST_FUNC; +extern int addattr_nest_end(struct nlmsghdr *n, struct rtattr *nest) FAST_FUNC; extern int rta_addattr32(struct rtattr *rta, int maxlen, int type, uint32_t data) FAST_FUNC; extern int rta_addattr_l(struct rtattr *rta, int maxlen, int type, void *data, int alen) FAST_FUNC; extern void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len) FAST_FUNC; +#define NLMSG_TAIL(nmsg) \ + ((struct rtattr *) (((void *) (nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len))) + POP_SAVED_FUNCTION_VISIBILITY #endif -- 2.34.1 From steffen at sdaoden.eu Thu Aug 11 14:18:42 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Thu, 11 Aug 2022 16:18:42 +0200 Subject: [PATCH] shell: fill in tremendous $(()) evaluator miss In-Reply-To: <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> References: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> Message-ID: <20220811141842.il4q6%steffen@sdaoden.eu> Sigh. I am deeply sorry, but i really have to come back. Now, this is ridiculous. I am sorry for that. ... |The three new tests | | s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" | s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" | s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" ... |Interestingly we now have a divergence to the bash $(()) |implementation, and i have opened a bash bug report because of it, Brain damage. |as i think we are correct: given this file | | # make this work with (ba)sh \ | command -v shopt && shopt -s expand_aliases;\ | alias p=printf;alias e=echo;alias s=export | s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" | s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" | |we get | | -<6><6><6> | -<10><10><5> | +<6><0><6> | +<1><1><5> | |compared to bash, and i think the latter two are correct. No, they are not. Bash is likely correct there according to standard precedence rules, as pointed out by Koichi Murase on bug-bash@, the assignment needs to be lost completely due to precedence rules, as they come in via ISO C: $ cat t.c;tcc -run t.c #include int main(void){ int i=5,j=10; printf("%d\n", (i=100?i:j)); printf("%d\n",i); return 0; } 5 5 Hm,hm. I think this needs special treatment. Unchanged busybox $(()) gets that wrong too, but a tad different. People. I am sorry for all that. I really thought i paved my way through the problems, _before_ i sent the first mail of this thread. I will now implement this precedence fix and let the thing rest for a while, maybe something else shows up to be fixed. I really cannot imagine, but now i am confused as well, so better it is to stand back and wait a while. I will send a third version, and a furtherly extended test, once that ship has sailed. Maybe then directly stripped down and better integrated for busybox (smallified). Thank you. And ciao. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Thu Aug 11 14:25:17 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Thu, 11 Aug 2022 16:25:17 +0200 Subject: [PATCH] shell: fill in tremendous $(()) evaluator miss In-Reply-To: <20220811141842.il4q6%steffen@sdaoden.eu> References: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> <20220811141842.il4q6%steffen@sdaoden.eu> Message-ID: <20220811142517.-X8Pr%steffen@sdaoden.eu> Steffen Nurpmeso wrote in <20220811141842.il4q6%steffen at sdaoden.eu>: ... P.S.: we are of course in the field of things which are beyond standard definitions. And there _are_ bugs in the bash evaluator, on the same precedence level as i have shown: $ bash -c ' I1=I2=10 I2=5 I3=I2+=1; echo "<$(( I1*=1?I1:I3 ))>";echo "<$I1><$I2><$I3>"' <100> <100><10> $ bash -c ' I1=I2=10 I2=5 I3=I2+=1; echo "<$(( I1=1?I1:I3 ))>";echo "<$I1><$I2><$I3>"' <10> <10><10> ... |Now, this is ridiculous. I am sorry for that. Sigh!! --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Thu Aug 11 16:09:17 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Thu, 11 Aug 2022 18:09:17 +0200 Subject: [PATCH] shell: fix $(()) precedence bug in "X=A?B:C" (it is _not_ "(X=A)?..)" In-Reply-To: <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> References: <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> Message-ID: <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> --- It was a one line fix. Five more tests +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" I hope i am out now. Sorry for all the noise. shell/shexp-arith.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h index 17582f7d49..f48ada1b46 100644 --- a/shell/shexp-arith.h +++ b/shell/shexp-arith.h @@ -584,8 +584,7 @@ junapre: op |= x; /* Resolve as much as possible */ - while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && - lprec != a_SHEXP_ARITH_PREC_COND){ + while(lprec > a_SHEXP_ARITH_PREC_COND){ if(!a_shexp__arith_op_apply(self)) goto jleave; lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; -- 2.37.1 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Thu Aug 18 23:10:06 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 19 Aug 2022 01:10:06 +0200 Subject: [PATCH] shell: fix $(()) precedence bug in "X=A?B:C" (it is _not_ "(X=A)?..)" In-Reply-To: <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> References: <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> Message-ID: <20220818231006.ZYhYJ%steffen@sdaoden.eu> I just fixed two other bugs regarding $: ternary resolution which affect strange cases regarding precedence in whiteouts $((0?I1=10:(1?I3:I2=12))) vs $((0?I1=10:(0?I3:I2=12))) as well as a missing variable-expansion prevention in a whiteout. (Let me allow to keep saying whiteout, please.) It bugs me. "Three line fix", so to say. Locally i added a couple of more tests, and i am now really out of ideas what other tests i could throw at the code. Sorry again for now waiting until i had an iteration. In the meantime i added a better PO-error tracking, too, via a third stack that eventually gets used. It also makes "error tracking" entirely optional, ending with a constant error message only without it. I was planning to send a v3 with a new configuration option that selects this when i stumbled over the in-whiteout errors. I will send the small bugfix patch onto v2 tomorrow, and then also a brand new one-commit v3 patch with the configuration option and my current code. It will also move most of the compat shims into shexp-arith.h, but is still also not a fully busybox integrated thing. I hope these threads then fall silent. Ciao. --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Fri Aug 19 17:00:50 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 19 Aug 2022 19:00:50 +0200 Subject: [PATCH] shell: $(()): hush.c,math.c: fix two usage bugs of new evaluator In-Reply-To: <20220818231006.ZYhYJ%steffen@sdaoden.eu> References: <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> Message-ID: <42ea65bd1983b9ec9d858cdebf0222dd1b2fe7d7.1660928198.git.steffen@sdaoden.eu> --- shell/hush.c | 4 +++- shell/math.c | 2 -- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/hush.c b/shell/hush.c index f59bb57b3b..15b36bbe41 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6843,8 +6843,10 @@ static NOINLINE int expand_one_var(o_string *output, int n, goto empty_result; } len = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { + free(errmsg); goto empty_result; + } debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); debug_printf_varexp("from val:'%s'\n", val); if (len < 0) { diff --git a/shell/math.c b/shell/math.c index 8388fdecad..03cddc34a2 100644 --- a/shell/math.c +++ b/shell/math.c @@ -254,12 +254,10 @@ static inline u32 a_idec_x(void *resp, char const *cbuf, char const **endptr_or_nil){ u32 rv; arith_t res; -#if ENABLE_FEATURE_SH_MATH_BASE char const *eptr; if(endptr_or_nil == NIL) endptr_or_nil = &eptr; -#endif errno = 0; res = strto_arith_t(cbuf, (char**)endptr_or_nil); -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Fri Aug 19 17:00:47 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 19 Aug 2022 19:00:47 +0200 Subject: [PATCH] shell: $(()): fix two more (?: whiteout related) errors In-Reply-To: <20220818231006.ZYhYJ%steffen@sdaoden.eu> References: <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> Message-ID: --- 20220818231006.ZYhYJ%steffen at sdaoden.eu: I just fixed two other bugs regarding $: ternary resolution which affect strange cases regarding precedence in whiteouts $((0?I1=10:(1?I3:I2=12))) vs $((0?I1=10:(0?I3:I2=12))) as well as a missing variable-expansion prevention in a whiteout. (Let me allow to keep saying whiteout, please.) It bugs me. "Three line fix", so to say. Locally i added a couple of more tests, and i am now really out of ideas what other tests i could throw at the code. Sorry again for now waiting until i had an iteration. [.]I will send the small bugfix patch onto v2 tomorrow,[.] It is a bit larger than it could be because it adds a dedicated error constant, as well as renaming another one. shell/math.c | 5 +++-- shell/shexp-arith.h | 53 ++++++++++++++++++++++++++++++--------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/shell/math.c b/shell/math.c index a20596089d..8388fdecad 100644 --- a/shell/math.c +++ b/shell/math.c @@ -214,7 +214,7 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) #define CONCAT(S1,S2) su__CONCAT_1(S1, S2) # define su__CONCAT_1(S1,S2) su__CONCAT_2(S1, S2) # define su__CONCAT_2(S1,S2) S1 ## S2 -#define DBG(X) +#define DBGX(X) #define FALLTHRU #define N_(X) X #define NIL NULL @@ -299,8 +299,9 @@ arith(arith_state_t *math_state, const char *expr) a_X(ASSIGN_NO_VAR, "assignment without variable"); a_X(DIV_BY_ZERO, "division by zero"); a_X(EXP_INVALID, "invalid exponent"); - a_X(NO_OPERAND, "syntax error, expected operand"); + a_X(NO_OP, "syntax error, expected operand"); a_X(COND_NO_COLON, "syntax error, incomplete ?: condition"); + a_X(COND_PREC_INVALID, "?: condition, invalid precedence (1:v2:v3=3)"); a_X(NAME_LOOP, "recursive variable name reference"); a_X(OP_INVALID, "unknown operator"); } diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h index f48ada1b46..fa44d4234d 100644 --- a/shell/shexp-arith.h +++ b/shell/shexp-arith.h @@ -47,8 +47,9 @@ enum a_shexp_arith_error{ a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR, /* Assignment without variable */ a_SHEXP_ARITH_ERR_DIV_BY_ZERO, a_SHEXP_ARITH_ERR_EXP_INVALID, /* Invalid exponent */ - a_SHEXP_ARITH_ERR_NO_OPERAND, /* Expected an argument here */ + a_SHEXP_ARITH_ERR_NO_OP, /* Expected an argument here */ a_SHEXP_ARITH_ERR_COND_NO_COLON, /* Incomplete ?: condition */ + a_SHEXP_ARITH_ERR_COND_PREC_INVALID, /* 1 ? VAR1 : VAR2 = 3 */ a_SHEXP_ARITH_ERR_NAME_LOOP, /* Variable self-reference loop */ a_SHEXP_ARITH_ERR_OP_INVALID /* Unknown operator */ }; @@ -269,10 +270,10 @@ a_shexp_arith_eval( #endif ASSERT_NYD_EXEC(resp != NIL, - self.sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND); - DBG( *resp = 0; ) + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); + DBGX( *resp = 0; ) ASSERT_NYD_EXEC(exp_len == 0 || exp_buf != NIL, - self.sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND); + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); a_SHEXP_ARITH_IFS( self.sac_ifs_ws = ok_vlook(ifs_ws); ) self.sac_stack = &sas_stack; @@ -425,6 +426,7 @@ a_shexp__arith_eval(struct a_shexp_arith_ctx *self, ++sasp->sas_nums_top; lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval VAR <%s>\n", sasp->sas_nums_top[-1].sav_var)); continue; @@ -533,7 +535,7 @@ junapre: prec < a_SHEXP_ARITH_PREC_UNARY) || prec >= a_SHEXP_ARITH_PREC_SKY){ if(lop != a_SHEXP_ARITH_OP_NUM){ - self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; goto jleave; } @@ -594,9 +596,11 @@ junapre: ASSERT(sasp->sas_nums_top > sasp->sas_nums); --sasp->sas_nums_top; - if((op & a_SHEXP_ARITH_OP_MASK) != a_SHEXP_ARITH_OP_ASSIGN && - sasp->sas_nums_top->sav_var != NIL){ - if(!a_shexp__arith_val_eval(self, sasp->sas_nums_top)) + if(sasp->sas_nums_top->sav_var != NIL){ + if(!(op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + (op & a_SHEXP_ARITH_OP_MASK) != + a_SHEXP_ARITH_OP_ASSIGN && + !a_shexp__arith_val_eval(self, sasp->sas_nums_top)) goto jleave; sasp->sas_nums_top->sav_var = NIL; } @@ -669,17 +673,29 @@ junapre: } /* Is this a right-associative operation? */ else{ - boole doit=FAL0; + boole doit; + doit = FAL0; if(lprec < prec){ doit = TRU1; a_SHEXP_ARITH_L((" + _arith_eval DELAY PRECEDENCE\n")); }else if(lprec == prec && prec == a_SHEXP_ARITH_PREC_ASSIGN){ doit = TRU1; a_SHEXP_ARITH_L((" + _arith_eval DELAY RIGHT ASSOC\n")); - }else if(lop == a_SHEXP_ARITH_OP_COND){ - doit = TRU1; - a_SHEXP_ARITH_L((" + _arith_eval DELAY CONDITION\n")); + }else if(lprec == a_SHEXP_ARITH_PREC_COND){ + if(lop == a_SHEXP_ARITH_OP_COND){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY CONDITION\n")); + } + /* Without massive rewrite this is the location to detect + * in-whiteout precedence bugs as in + * $((0?I1=10:(1?I3:I2=12))) + * which would be parsed like (1?I3:I2)=12 without error + * (different to 0?I3:I2=12) otherwise */ + else if(op != a_SHEXP_ARITH_OP_COMMA){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_PREC_INVALID; + goto jleave; + } } if(doit){ @@ -887,7 +903,7 @@ a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ /* At least one argument is always needed */ if((nums_top = sasp->sas_nums_top) == sasp->sas_nums){ - self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; goto jleave; } --nums_top; @@ -922,7 +938,9 @@ a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ } goto jquick; }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops); ASSERT(nums_top > sasp->sas_nums); + if(!ign){ /* Move the ternary value over to LHV where we find it as a result, * and ensure LHV's name is forgotten so not to evaluate it (for @@ -930,18 +948,17 @@ a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ * outer group, because it still exists on number stack) */ nums_top[-1].sav_val = nums_top[0].sav_val; nums_top[-1].sav_var = NIL; - }else{ + }else val = nums_top[-1].sav_val; - /*nums_top[0].sav_var = NIL;*/ - } + sasp->sas_nums_top = nums_top; - ASSERT(sasp->sas_ops_top > sasp->sas_ops); if((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK ) == a_SHEXP_ARITH_OP_COND_COLON){ --sasp->sas_ops_top; if(!a_shexp__arith_op_apply_colons(self)) goto jleave; + ASSERT(sasp->sas_nums_top > sasp->sas_nums); if(!ign) sasp->sas_nums_top[-1].sav_val = val; } @@ -950,7 +967,7 @@ a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ s64 rval; if(nums_top == sasp->sas_nums){ - self->sac_error = a_SHEXP_ARITH_ERR_NO_OPERAND; + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; goto jleave; } sasp->sas_nums_top = nums_top--; -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Fri Aug 19 17:58:59 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 19 Aug 2022 19:58:59 +0200 Subject: [PATCHv3] shell: exchange Dijkstra $(( )) evaluator.. Message-ID: <7fe77ec0ad639633963576f247564477b057414c.1660930743.git.steffen@sdaoden.eu> The former implementation was not correct regarding whiteouts in ?: conditional branches. The new one also parses a bit better, in effect on equal level than bash with its recursive descendent parser. --- v3 of the patch that includes all the fixes that were unfortunately necessary. Now hopefully a hundred percent correct. It adds a FEATURE_SH_MATH_ERROR_TRACK configuration option, and hides most compatibility shims behind a request macro. Size: Current busybox implementation (incorrect) 2938 0 0 2938 b7a math.o With/out ERROR_TRACK 4889 0 0 4889 1319 shell/math.o 5039 0 0 5039 13af shell/math.o It surely could be shrinked a bit by removing compatibility shims and fitting neatlessly in busybox. But it grows quite a bit... When i run the test (i will post the new positive one as a reply; the negative one exists for my mailer, but since sh(1) will just bail out for each and every test, it is a bit hard to do) there remains the very same difference to the very first version i posted, namely #?0|kent:tmp$ diff- tg.mx tg.busy --- tg.mx 2022-08-19 18:24:05.427687786 +0200 +++ tg.busy 2022-08-19 18:23:02.411022307 +0200 @@ -9,7 +9,7 @@ <1> <1> <1> -<9223372036854775807> +<0> <10> <9191919191919> <13> which comes due to e "<$((999999999999999999999999999999999999999999999))>" I have not touched the busybox way of parsing integers. When i compare the test run from tg.mx and bash i still see #?0|kent:tmp$ bash tarith-good.in > tg.bash tarith-good.in: line 284: 1>>>1: syntax error: operand expected (error token is ">1") tarith-good.in: line 285: 1 >>> 1 : syntax error: operand expected (error token is "> 1 ") tarith-good.in: line 288: 1111>>>2222: syntax error: operand expected (error token is ">2222") tarith-good.in: line 289: 2222>>>1111: syntax error: operand expected (error token is ">1111") tarith-good.in: line 293: -0x10 >>> -0x11 : syntax error: operand expected (error token is "> -0x11 ") tarith-good.in: line 312: 0xFFFFFFFFFFFFFFFF>>>11: syntax error: operand expected (error token is ">11") tarith-good.in: line 657: I**=1: syntax error: operand expected (error token is "=1") tarith-good.in: line 658: I**=2: syntax error: operand expected (error token is "=2") tarith-good.in: line 659: I**=1+1: syntax error: operand expected (error token is "=1+1") tarith-good.in: line 665: I>>>=1: syntax error: operand expected (error token is ">=1") tarith-good.in: line 888: +10++I: syntax error in expression (error token is "++I") because bash does not support unsigned right shift, exponent- assignment, and it cannot (read: does not want to!) parse "+10++I" (it can parse "+10++11"). If i leave out these missing tests the diff boils down to @@ -9,7 +10,7 @@ <1> <1> <1> -<9223372036854775807> +<802379605485813759> <10> <9191919191919> <13> aka the same thing as above. (bash simply walks on decoding the integer not looking out for overflow, says it's expr.c.) So given how bad the Dijkstra algorithm fits into anything other than unary and binary calculations, i think it still comes out as an acceptible piece of code, and i hope you like it. Sorry for all the noise in this thread. Ciao. shell/Config.src | 8 + shell/ash.c | 6 +- shell/hush.c | 23 +- shell/math.c | 710 +++--------------------- shell/math.h | 7 +- shell/shexp-arith.h | 1288 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 1406 insertions(+), 636 deletions(-) create mode 100644 shell/shexp-arith.h diff --git a/shell/Config.src b/shell/Config.src index 5efbf99959..32aaab58e8 100644 --- a/shell/Config.src +++ b/shell/Config.src @@ -108,6 +108,14 @@ config FEATURE_SH_MATH_BASE default y depends on FEATURE_SH_MATH +config FEATURE_SH_MATH_ERROR_TRACK + bool "Extend POSIX math support with error location tracking" + default y + depends on FEATURE_SH_MATH + help + Enable error location tracking in the shell's math support. + Without it only the type of error will be logged. + config FEATURE_SH_EXTRA_QUIET bool "Hide message on interactive shell startup" default y diff --git a/shell/ash.c b/shell/ash.c index 55c1034f55..4029a3c04f 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6029,8 +6029,12 @@ ash_arith(const char *s) INT_OFF; result = arith(&math_state, s); - if (math_state.errmsg) + if (math_state.errmsg) { ash_msg_and_raise_error(math_state.errmsg); +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(math_state.errmsg); +# endif + } INT_ON; return result; diff --git a/shell/hush.c b/shell/hush.c index 051b123e78..be01ed035c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6475,7 +6475,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, } #if ENABLE_FEATURE_SH_MATH -static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) +static arith_t expand_and_evaluate_arith(const char *arg, char **errmsg_p) { arith_state_t math_state; arith_t res; @@ -6489,8 +6489,13 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) free(exp_str); if (errmsg_p) *errmsg_p = math_state.errmsg; - if (math_state.errmsg) + if (math_state.errmsg) { msg_and_die_if_script(math_state.errmsg); +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + if (errmsg_p == NULL) + free(math_state.errmsg); +# endif + } return res; } #endif @@ -6814,11 +6819,15 @@ static NOINLINE int expand_one_var(o_string *output, int n, */ arith_t beg, len; unsigned vallen; - const char *errmsg; + char *errmsg; beg = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(errmsg); +# endif goto empty_result; + } debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); *p++ = SPECIAL_VAR_SYMBOL; exp_word = p; @@ -6838,8 +6847,12 @@ static NOINLINE int expand_one_var(o_string *output, int n, goto empty_result; } len = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(errmsg); +# endif goto empty_result; + } debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); debug_printf_varexp("from val:'%s'\n", val); if (len < 0) { diff --git a/shell/math.c b/shell/math.c index 76d22c9bd5..b9b3d7acb1 100644 --- a/shell/math.c +++ b/shell/math.c @@ -116,398 +116,6 @@ #include "libbb.h" #include "math.h" -typedef unsigned char operator; - -/* An operator's token id is a bit of a bitfield. The lower 5 bits are the - * precedence, and 3 high bits are an ID unique across operators of that - * precedence. The ID portion is so that multiple operators can have the - * same precedence, ensuring that the leftmost one is evaluated first. - * Consider * and / - */ -#define tok_decl(prec,id) (((id)<<5) | (prec)) -#define PREC(op) ((op) & 0x1F) - -#define TOK_LPAREN tok_decl(0,0) - -#define TOK_COMMA tok_decl(1,0) - -/* All assignments are right associative and have the same precedence, - * but there are 11 of them, which doesn't fit into 3 bits for unique id. - * Abusing another precedence level: - */ -#define TOK_ASSIGN tok_decl(2,0) -#define TOK_AND_ASSIGN tok_decl(2,1) -#define TOK_OR_ASSIGN tok_decl(2,2) -#define TOK_XOR_ASSIGN tok_decl(2,3) -#define TOK_PLUS_ASSIGN tok_decl(2,4) -#define TOK_MINUS_ASSIGN tok_decl(2,5) -#define TOK_LSHIFT_ASSIGN tok_decl(2,6) -#define TOK_RSHIFT_ASSIGN tok_decl(2,7) - -#define TOK_MUL_ASSIGN tok_decl(3,0) -#define TOK_DIV_ASSIGN tok_decl(3,1) -#define TOK_REM_ASSIGN tok_decl(3,2) - -#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) - -/* Ternary conditional operator is right associative too */ -#define TOK_CONDITIONAL tok_decl(4,0) -#define TOK_CONDITIONAL_SEP tok_decl(4,1) - -#define TOK_OR tok_decl(5,0) - -#define TOK_AND tok_decl(6,0) - -#define TOK_BOR tok_decl(7,0) - -#define TOK_BXOR tok_decl(8,0) - -#define TOK_BAND tok_decl(9,0) - -#define TOK_EQ tok_decl(10,0) -#define TOK_NE tok_decl(10,1) - -#define TOK_LT tok_decl(11,0) -#define TOK_GT tok_decl(11,1) -#define TOK_GE tok_decl(11,2) -#define TOK_LE tok_decl(11,3) - -#define TOK_LSHIFT tok_decl(12,0) -#define TOK_RSHIFT tok_decl(12,1) - -#define TOK_ADD tok_decl(13,0) -#define TOK_SUB tok_decl(13,1) - -#define TOK_MUL tok_decl(14,0) -#define TOK_DIV tok_decl(14,1) -#define TOK_REM tok_decl(14,2) - -/* Exponent is right associative */ -#define TOK_EXPONENT tok_decl(15,1) - -/* Unary operators */ -#define UNARYPREC 16 -#define TOK_BNOT tok_decl(UNARYPREC,0) -#define TOK_NOT tok_decl(UNARYPREC,1) - -#define TOK_UMINUS tok_decl(UNARYPREC+1,0) -#define TOK_UPLUS tok_decl(UNARYPREC+1,1) - -#define PREC_PRE (UNARYPREC+2) - -#define TOK_PRE_INC tok_decl(PREC_PRE, 0) -#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) - -#define PREC_POST (UNARYPREC+3) - -#define TOK_POST_INC tok_decl(PREC_POST, 0) -#define TOK_POST_DEC tok_decl(PREC_POST, 1) - -#define SPEC_PREC (UNARYPREC+4) - -#define TOK_NUM tok_decl(SPEC_PREC, 0) -#define TOK_RPAREN tok_decl(SPEC_PREC, 1) - -static int -is_assign_op(operator op) -{ - operator prec = PREC(op); - fix_assignment_prec(prec); - return prec == PREC(TOK_ASSIGN) - || prec == PREC_PRE - || prec == PREC_POST; -} - -static int -is_right_associative(operator prec) -{ - return prec == PREC(TOK_ASSIGN) - || prec == PREC(TOK_EXPONENT) - || prec == PREC(TOK_CONDITIONAL); -} - - -typedef struct { - arith_t val; - /* We acquire second_val only when "expr1 : expr2" part - * of ternary ?: op is evaluated. - * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). - * ':' produces a new value which has two parts, val and second_val; - * then '?' selects one of them based on its left side. - */ - arith_t second_val; - char second_val_present; - /* If NULL then it's just a number, else it's a named variable */ - char *var; -} var_or_num_t; - -typedef struct remembered_name { - struct remembered_name *next; - const char *var; -} remembered_name; - - -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr); - -static const char* -arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) -{ - if (t->var) { - const char *p = math_state->lookupvar(t->var); - if (p) { - remembered_name *cur; - remembered_name cur_save; - - /* did we already see this name? - * testcase: a=b; b=a; echo $((a)) - */ - for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { - if (strcmp(cur->var, t->var) == 0) { - /* Yes */ - return "expression recursion loop detected"; - } - } - - /* push current var name */ - cur = math_state->list_of_recursed_names; - cur_save.var = t->var; - cur_save.next = cur; - math_state->list_of_recursed_names = &cur_save; - - /* recursively evaluate p as expression */ - t->val = evaluate_string(math_state, p); - - /* pop current var name */ - math_state->list_of_recursed_names = cur; - - return math_state->errmsg; - } - /* treat undefined var as 0 */ - t->val = 0; - } - return 0; -} - -/* "Applying" a token means performing it on the top elements on the integer - * stack. For an unary operator it will only change the top element, but a - * binary operator will pop two arguments and push the result */ -static NOINLINE const char* -arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) -{ -#define NUMPTR (*numstackptr) - - var_or_num_t *top_of_stack; - arith_t rez; - const char *err; - - /* There is no operator that can work without arguments */ - if (NUMPTR == numstack) - goto err; - - top_of_stack = NUMPTR - 1; - - /* Resolve name to value, if needed */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - - rez = top_of_stack->val; - if (op == TOK_UMINUS) - rez = -rez; - else if (op == TOK_NOT) - rez = !rez; - else if (op == TOK_BNOT) - rez = ~rez; - else if (op == TOK_POST_INC || op == TOK_PRE_INC) - rez++; - else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) - rez--; - else if (op != TOK_UPLUS) { - /* Binary operators */ - arith_t right_side_val; - char bad_second_val; - - /* Binary operators need two arguments */ - if (top_of_stack == numstack) - goto err; - /* ...and they pop one */ - NUMPTR = top_of_stack; /* this decrements NUMPTR */ - - bad_second_val = top_of_stack->second_val_present; - if (op == TOK_CONDITIONAL) { /* ? operation */ - /* Make next if (...) protect against - * $((expr1 ? expr2)) - that is, missing ": expr" */ - bad_second_val = !bad_second_val; - } - if (bad_second_val) { - /* Protect against $((expr expr1 : expr2)) */ - return "malformed ?: operator"; - } - - top_of_stack--; /* now points to left side */ - - if (op != TOK_ASSIGN) { - /* Resolve left side value (unless the op is '=') */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - } - - right_side_val = rez; - rez = top_of_stack->val; - if (op == TOK_CONDITIONAL) /* ? operation */ - rez = (rez ? right_side_val : top_of_stack[1].second_val); - else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ - if (top_of_stack == numstack) { - /* Protect against $((expr : expr)) */ - return "malformed ?: operator"; - } - top_of_stack->second_val_present = op; - top_of_stack->second_val = right_side_val; - } - else if (op == TOK_BOR || op == TOK_OR_ASSIGN) - rez |= right_side_val; - else if (op == TOK_OR) - rez = right_side_val || rez; - else if (op == TOK_BAND || op == TOK_AND_ASSIGN) - rez &= right_side_val; - else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) - rez ^= right_side_val; - else if (op == TOK_AND) - rez = rez && right_side_val; - else if (op == TOK_EQ) - rez = (rez == right_side_val); - else if (op == TOK_NE) - rez = (rez != right_side_val); - else if (op == TOK_GE) - rez = (rez >= right_side_val); - else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) - rez >>= right_side_val; - else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) - rez <<= right_side_val; - else if (op == TOK_GT) - rez = (rez > right_side_val); - else if (op == TOK_LT) - rez = (rez < right_side_val); - else if (op == TOK_LE) - rez = (rez <= right_side_val); - else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) - rez *= right_side_val; - else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) - rez += right_side_val; - else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) - rez -= right_side_val; - else if (op == TOK_ASSIGN || op == TOK_COMMA) - rez = right_side_val; - else if (op == TOK_EXPONENT) { - arith_t c; - if (right_side_val < 0) - return "exponent less than 0"; - c = 1; - while (--right_side_val >= 0) - c *= rez; - rez = c; - } - else if (right_side_val == 0) - return "divide by zero"; - else if (op == TOK_DIV || op == TOK_DIV_ASSIGN - || op == TOK_REM || op == TOK_REM_ASSIGN) { - /* - * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' - * - * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1 - * and thus is not representable. - * Some CPUs segfault trying such op. - * Others overflow MAX_POSITIVE_INT+1 to - * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000). - * Make sure to at least not SEGV here: - */ - if (right_side_val == -1 - && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */ - ) { - right_side_val = 1; - } - if (op == TOK_DIV || op == TOK_DIV_ASSIGN) - rez /= right_side_val; - else { - rez %= right_side_val; - } - } - } - - if (is_assign_op(op)) { - char buf[sizeof(arith_t)*3 + 2]; - - if (top_of_stack->var == NULL) { - /* Hmm, 1=2 ? */ - goto err; - } - /* Save to shell variable */ - sprintf(buf, ARITH_FMT, rez); - math_state->setvar(top_of_stack->var, buf); - /* After saving, make previous value for v++ or v-- */ - if (op == TOK_POST_INC) - rez--; - if (op == TOK_POST_DEC) - rez++; - } - - top_of_stack->val = rez; - /* Erase var name, it is just a number now */ - top_of_stack->var = NULL; - return NULL; - err: - return "arithmetic syntax error"; -#undef NUMPTR -} - -/* longest must be first */ -static const char op_tokens[] ALIGN1 = { - '<','<','=',0, TOK_LSHIFT_ASSIGN, - '>','>','=',0, TOK_RSHIFT_ASSIGN, - '<','<', 0, TOK_LSHIFT, - '>','>', 0, TOK_RSHIFT, - '|','|', 0, TOK_OR, - '&','&', 0, TOK_AND, - '!','=', 0, TOK_NE, - '<','=', 0, TOK_LE, - '>','=', 0, TOK_GE, - '=','=', 0, TOK_EQ, - '|','=', 0, TOK_OR_ASSIGN, - '&','=', 0, TOK_AND_ASSIGN, - '*','=', 0, TOK_MUL_ASSIGN, - '/','=', 0, TOK_DIV_ASSIGN, - '%','=', 0, TOK_REM_ASSIGN, - '+','=', 0, TOK_PLUS_ASSIGN, - '-','=', 0, TOK_MINUS_ASSIGN, - '-','-', 0, TOK_POST_DEC, - '^','=', 0, TOK_XOR_ASSIGN, - '+','+', 0, TOK_POST_INC, - '*','*', 0, TOK_EXPONENT, - '!', 0, TOK_NOT, - '<', 0, TOK_LT, - '>', 0, TOK_GT, - '=', 0, TOK_ASSIGN, - '|', 0, TOK_BOR, - '&', 0, TOK_BAND, - '*', 0, TOK_MUL, - '/', 0, TOK_DIV, - '%', 0, TOK_REM, - '+', 0, TOK_ADD, - '-', 0, TOK_SUB, - '^', 0, TOK_BXOR, - /* uniq */ - '~', 0, TOK_BNOT, - ',', 0, TOK_COMMA, - '?', 0, TOK_CONDITIONAL, - ':', 0, TOK_CONDITIONAL_SEP, - ')', 0, TOK_RPAREN, - '(', 0, TOK_LPAREN, - 0 -}; -#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) - #if ENABLE_FEATURE_SH_MATH_BASE static arith_t strto_arith_t(const char *nptr, char **endptr) { @@ -577,250 +185,96 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) # endif #endif -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr) -{ - operator lasttok; - const char *errmsg; - const char *start_expr = expr = skip_whitespace(expr); - unsigned expr_len = strlen(expr) + 2; - /* Stack of integers */ - /* The proof that there can be no more than strlen(startbuf)/2+1 - * integers in any given correct or incorrect expression - * is left as an exercise to the reader. */ - var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); - var_or_num_t *numstackptr = numstack; - /* Stack of operator tokens */ - operator *const stack = alloca(expr_len * sizeof(stack[0])); - operator *stackptr = stack; - - /* Start with a left paren */ - *stackptr++ = lasttok = TOK_LPAREN; - errmsg = NULL; - - while (1) { - const char *p; - operator op; - operator prec; - - expr = skip_whitespace(expr); - if (*expr == '\0') { - if (expr == start_expr) { - /* Null expression */ - numstack->val = 0; - goto ret; - } - - /* This is only reached after all tokens have been extracted from the - * input stream. If there are still tokens on the operator stack, they - * are to be applied in order. At the end, there should be a final - * result on the integer stack */ - - if (expr != ptr_to_rparen + 1) { - /* If we haven't done so already, - * append a closing right paren - * and let the loop process it */ - expr = ptr_to_rparen; -//bb_error_msg("expr=')'"); - continue; - } - /* At this point, we're done with the expression */ - if (numstackptr != numstack + 1) { - /* ...but if there isn't, it's bad */ - goto err; - } - goto ret; - } - - p = endofname(expr); - if (p != expr) { - /* Name */ - size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ - numstackptr->var = alloca(var_name_size); - safe_strncpy(numstackptr->var, expr, var_name_size); -//bb_error_msg("var:'%s'", numstackptr->var); - expr = p; - num: - numstackptr->second_val_present = 0; - numstackptr++; - lasttok = TOK_NUM; - continue; - } - - if (isdigit(*expr)) { - /* Number */ - numstackptr->var = NULL; - errno = 0; - numstackptr->val = strto_arith_t(expr, (char**) &expr); -//bb_error_msg("val:%lld", numstackptr->val); - if (errno) - numstackptr->val = 0; /* bash compat */ - goto num; - } - - /* Should be an operator */ - - /* Special case: XYZ--, XYZ++, --XYZ, ++XYZ are recognized - * only if XYZ is a variable name, not a number or EXPR. IOW: - * "a+++v" is a++ + v. - * "(a)+++7" is ( a ) + + + 7. - * "7+++v" is 7 + ++v, not 7++ + v. - * "--7" is - - 7, not --7. - * "++++a" is + + ++a, not ++ ++a. - */ - if ((expr[0] == '+' || expr[0] == '-') - && (expr[1] == expr[0]) - ) { - if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ - char next = skip_whitespace(expr + 2)[0]; - if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ - //bb_error_msg("special %c%c", expr[0], expr[0]); - op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); - expr++; - goto tok_found1; - } - } - } - - p = op_tokens; - while (1) { - /* Compare expr to current op_tokens[] element */ - const char *e = expr; - while (1) { - if (*p == '\0') { - /* Match: operator is found */ - expr = e; - goto tok_found; - } - if (*p != *e) - break; - p++; - e++; - } - /* No match, go to next element of op_tokens[] */ - while (*p) - p++; - p += 2; /* skip NUL and TOK_foo bytes */ - if (*p == '\0') { - /* No next element, operator not found */ - //math_state->syntax_error_at = expr; - goto err; - } - } - tok_found: - op = p[1]; /* fetch TOK_foo value */ - tok_found1: - /* NB: expr now points past the operator */ - - /* post grammar: a++ reduce to num */ - if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) - lasttok = TOK_NUM; - - /* Plus and minus are binary (not unary) _only_ if the last - * token was a number, or a right paren (which pretends to be - * a number, since it evaluates to one). Think about it. - * It makes sense. */ - if (lasttok != TOK_NUM) { - switch (op) { - case TOK_ADD: - op = TOK_UPLUS; - break; - case TOK_SUB: - op = TOK_UMINUS; - break; - case TOK_POST_INC: - op = TOK_PRE_INC; - break; - case TOK_POST_DEC: - op = TOK_PRE_DEC; - break; - } - } - /* We don't want an unary operator to cause recursive descent on the - * stack, because there can be many in a row and it could cause an - * operator to be evaluated before its argument is pushed onto the - * integer stack. - * But for binary operators, "apply" everything on the operator - * stack until we find an operator with a lesser priority than the - * one we have just extracted. If op is right-associative, - * then stop "applying" on the equal priority too. - * Left paren is given the lowest priority so it will never be - * "applied" in this way. - */ - prec = PREC(op); -//bb_error_msg("prec:%02x", prec); - if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { - /* not left paren or unary */ - if (lasttok != TOK_NUM) { - /* binary op must be preceded by a num */ - goto err; - } - /* The algorithm employed here is simple: while we don't - * hit an open paren nor the bottom of the stack, pop - * tokens and apply them */ - while (stackptr != stack) { - operator prev_op = *--stackptr; - if (op == TOK_RPAREN) { -//bb_error_msg("op == TOK_RPAREN"); - if (prev_op == TOK_LPAREN) { -//bb_error_msg("prev_op == TOK_LPAREN"); -//bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); - if (numstackptr[-1].var) { - /* Expression is (var), lookup now */ - errmsg = arith_lookup_val(math_state, &numstackptr[-1]); - if (errmsg) - goto err_with_custom_msg; - /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ - numstackptr[-1].var = NULL; - } - /* Any operator directly after a - * close paren should consider itself binary */ - lasttok = TOK_NUM; - goto next; - } -//bb_error_msg("prev_op != TOK_LPAREN"); - } else { - operator prev_prec = PREC(prev_op); -//bb_error_msg("op != TOK_RPAREN"); - fix_assignment_prec(prec); - fix_assignment_prec(prev_prec); - if (prev_prec < prec - || (prev_prec == prec && is_right_associative(prec)) - ) { - stackptr++; - break; - } - } -//bb_error_msg("arith_apply(prev_op:%02x)", prev_op); - errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); - if (errmsg) - goto err_with_custom_msg; - } - if (op == TOK_RPAREN) - goto err; - } +#define a_SHEXP_ARITH_COMPAT_SHIMS +# define s64 arith_t +# if ENABLE_FEATURE_SH_MATH_64 +# define S64_MIN LLONG_MIN +# define u64 unsigned long long +# else +# define S64_MIN LONG_MIN +# define u64 unsigned long +# endif +# define savestr(X) xstrdup(X) +# define su_IDEC_STATE_EMASK (1u<<0) +# define su_IDEC_STATE_CONSUMED (1u<<1) +#define a_SHEXP_ARITH_COOKIE arith_state_t * +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK +# define a_SHEXP_ARITH_ERROR_TRACK +#endif - /* Push this operator to the stack and remember it */ -//bb_error_msg("push op:%02x", op); - *stackptr++ = lasttok = op; - next: ; - } /* while (1) */ +#define su_ienc_s64(X,Y,Z) (sprintf(X, ARITH_FMT, Y), X) +#define n_var_vlook(X,Y) (*self->sac_cookie->lookupvar)(X) +#define n_var_vset(X,Y,Z) (*self->sac_cookie->setvar)(X, (char*)(Y)) +#define su_idec_cp(A,B,C,D,E) a_idec_x(A, B, E) + +static inline uint32_t a_idec_x(void *resp, char const *cbuf, + char const **endptr_or_nil){ + uint32_t rv; + arith_t res; + char const *eptr; + + if(endptr_or_nil == NULL) + endptr_or_nil = &eptr; + + errno = 0; + res = strto_arith_t(cbuf, (char**)endptr_or_nil); + rv = 0; + if(errno == 0){ + if(**endptr_or_nil == '\0') + rv = su_IDEC_STATE_CONSUMED; + }else{ + rv = su_IDEC_STATE_EMASK; + res = 0; + } - err: - errmsg = "arithmetic syntax error"; - err_with_custom_msg: - numstack->val = -1; - ret: - math_state->errmsg = errmsg; - return numstack->val; + *(arith_t*)resp = res; + return rv; } +#include "shexp-arith.h" + arith_t FAST_FUNC arith(arith_state_t *math_state, const char *expr) { +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + char *err_rest; +#endif + char const *emsg; + s64 res; + math_state->errmsg = NULL; - math_state->list_of_recursed_names = NULL; - return evaluate_string(math_state, expr); + + switch(a_shexp_arith_eval(math_state, &res, expr, UZ_MAX +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + , &err_rest +#endif + )){ + default: + return res; +#undef a_X +#define a_X(X,N) case CONCAT(a_SHEXP_ARITH_ERR_,X): emsg = N_(N); break + a_X(NOMEM, "out of memory"); + a_X(SYNTAX, "syntax error"); + a_X(ASSIGN_NO_VAR, "assignment without variable"); + a_X(DIV_BY_ZERO, "division by zero"); + a_X(EXP_INVALID, "invalid exponent"); + a_X(NO_OP, "syntax error, expected operand"); + a_X(COND_NO_COLON, "syntax error, incomplete ?: condition"); + a_X(COND_PREC_INVALID, "?: condition, invalid precedence (1:v2:v3=3)"); + a_X(NAME_LOOP, "recursive variable name reference"); + a_X(OP_INVALID, "unknown operator"); + } +#undef a_X + + math_state->errmsg = +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + xasprintf("%s (rest: %s)", emsg, err_rest); +#else + emsg +#endif + ; + + return -1; } /* diff --git a/shell/math.h b/shell/math.h index 41ef6e8dfa..4bb8d5cdfa 100644 --- a/shell/math.h +++ b/shell/math.h @@ -76,11 +76,14 @@ typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *v //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); typedef struct arith_state_t { - const char *errmsg; + /* ENABLE_FEATURE_SH_MATH_ERROR_TRACK: must be free(3)d if !NULL */ +#if !ENABLE_FEATURE_SH_MATH_ERROR_TRACK + const +#endif + char *errmsg; arith_var_lookup_t lookupvar; arith_var_set_t setvar; // arith_var_endofname_t endofname; - void *list_of_recursed_names; } arith_state_t; arith_t FAST_FUNC arith(arith_state_t *state, const char *expr); diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h new file mode 100644 index 0000000000..bc2ca52784 --- /dev/null +++ b/shell/shexp-arith.h @@ -0,0 +1,1288 @@ +/*@ S-nail - a mail user agent derived from Berkeley Mail. + *@ Signed 64-bit sh(1)ell-style $(( ARITH ))metic expression evaluator. + *@ POW2 bases are parsed as unsigned, operation overflow -> limit constant(s), + *@ saturated mode is not supported, division by zero is handled via error. + *@ The expression length limit is ~100.000.000 on 32-bit, U32_MAX otherwise. + *@ After reading on Dijkstra's two stack algorithm, as well as bash:expr.c. + *@ Most heavily inspired by busybox -- conclusion: the Dijkstra algorithm + *@ scales very badly to ternary as are used to implement conditionals and + *@ their ignored sub-expressions. + *@ + *@ #define's: + *@ - a_SHEXP_ARITH_COMPAT_SHIMS: for inclusion in other code bases, setting + *@ this defines most necessary compat macros. + *@ We still need s64, u64, S64_MIN, savestr(CP) <> strdup(3) that does not + *@ return NIL (only with _ERROR_TRACK). Plus stdint.h, ctype.h, string.h. + *@ We need su_idec_cp(), su_ienc_s64(), n_var_vlook() and n_var_vset(). + *@ We need su_IDEC_STATE_EMASK (= 1) and su_IDEC_STATE_CONSUMED (= 2), e.g.: + *@ errno = 0; + *@ res = strto_arith_t(cbuf, (char**)endptr_or_nil); + *@ rv = 0; + *@ if(errno == 0){ + *@ if(**endptr_or_nil == '\0') + *@ rv = su_IDEC_STATE_CONSUMED; + *@ }else{ + *@ rv = su_IDEC_STATE_EMASK; + *@ res = 0; + *@ } + *@ *S(s64*,resp) = res; + *@ - a_SHEXP_ARITH_COOKIE: adds struct a_shexp_arith_ctx:sac_cookie, and + *@ a cookie arg to a_shexp_arith_eval(). + *@ - a_SHEXP_ARITH_ERROR_TRACK: add "char **error_track_or_nil" to + *@ a_shexp_arith_eval(), and according error stack handling, so that users + *@ can be given hint where an error occurred. ("Three stack algorithm.") + * + * Copyright (c) 2022 Steffen Nurpmeso . + * SPDX-License-Identifier: ISC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Tracking of error location in input */ +#ifdef a_SHEXP_ARITH_ERROR_TRACK +# undef a_SHEXP_ARITH_ERROR_TRACK +# define a_SHEXP_ARITH_ERROR_TRACK(X) X +#else +# define a_SHEXP_ARITH_ERROR_TRACK(X) +#endif + +/* IFS whitespace removal */ +#undef a_SHEXP_ARITH_IFS +#ifdef mx_SOURCE +# define a_SHEXP_ARITH_IFS(X) X +#else +# define a_SHEXP_ARITH_IFS(X) +#endif + +/* (Most necessary) Compat shims */ +#ifdef a_SHEXP_ARITH_COMPAT_SHIMS +# define boole bool +# define FAL0 false +# define TRU1 true +# define u8 uint8_t +# define u16 uint16_t +# define u32 uint32_t +# define U32_MAX UINT32_MAX +# define ul unsigned long +# define up uintptr_t +# define UZ_MAX SIZE_MAX +# define uz size_t +# define a_SHEXP_ISVARC(C) ((C) == '_' || isalnum(S(unsigned char,C))) +# define a_SHEXP_ISVARC_BAD1ST(C) su_cs_is_digit(C) +# define a_SHEXP_ISVARC_BADNST(C) FAL0 +# define ASSERT(X) +# define ASSERT_NYD_EXEC(X,Y) +# define BITENUM_IS(X,Y) X +# define CONCAT(S1,S2) su__CONCAT_1(S1, S2) +# define su__CONCAT_1(S1,S2) su__CONCAT_2(S1, S2) +# define su__CONCAT_2(S1,S2) S1 ## S2 +# define DBGX(X) +# define FALLTHRU +# define N_(X) X +# define NIL NULL +# define NYD_IN S(void,0) +# define NYD2_IN S(void,0) +# define NYD_OU S(void,0) +# define NYD2_OU S(void,0) +# define P2UZ(X) S(size_t,X) +# define S(X,Y) ((X)(Y)) +# define su_ALIGNOF(X) ((sizeof(X) + 15) & ~15) +# define su_COMMA , +# define su_cs_cmp(X,Y) strcmp(X, Y) +# define su_cs_is_digit(X) isdigit(S(unsigned char,X)) +# define su_cs_is_space(X) isspace(S(unsigned char,X)) +# define su_empty "" +# define su_IDEC_STATE_EBASE 0 /* (could cause $CC optimiz.) */ +# define su_IENC_BUFFER_SIZE 80u +# define su_LOFI_ALLOC(X) alloca(X) +# define su_LOFI_FREE(X) +# define su_mem_move(X,Y,Z) memmove(X, Y, Z) +# define STRUCT_ZERO(X,Y) memset(Y, 0, sizeof(X)) +# define UNLIKELY(X) X +# define UNUSED(X) S(void,X) +# if LONG_MAX - 1 > 0x7FFFFFFFl - 1 +# define su_64(X) X +# else +# define su_64(X) +# endif +#endif /* a_SHEXP_ARITH_COMPAT_SHIMS */ + +/* -- >8 -- 8< -- */ + +#if 1 +# define a_SHEXP_ARITH_DBG 0 +# define a_SHEXP_ARITH_L(X) +#else +# define a_SHEXP_ARITH_DBG 1 +# define a_SHEXP_ARITH_L(X) a_shexp__arith_log X +#endif + +/* We parse with base 0: set _RESCAN to allow "I=' -10';$((10#$I))" */ +#define a_SHEXP_ARITH_IDEC_MODE (su_IDEC_MODE_SIGNED_TYPE |\ + su_IDEC_MODE_POW2BASE_UNSIGNED | su_IDEC_MODE_LIMIT_NOERROR |\ + su_IDEC_MODE_BASE0_NUMBER_SIGN_RESCAN) + +enum a_shexp_arith_error{ + a_SHEXP_ARITH_ERR_NONE, + a_SHEXP_ARITH_ERR_NOMEM, /* Out of memory */ + a_SHEXP_ARITH_ERR_SYNTAX, /* General syntax error */ + a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR, /* Assignment without variable */ + a_SHEXP_ARITH_ERR_DIV_BY_ZERO, + a_SHEXP_ARITH_ERR_EXP_INVALID, /* Invalid exponent */ + a_SHEXP_ARITH_ERR_NO_OP, /* Expected an argument here */ + a_SHEXP_ARITH_ERR_COND_NO_COLON, /* Incomplete ?: condition */ + a_SHEXP_ARITH_ERR_COND_PREC_INVALID, /* 1 ? VAR1 : VAR2 = 3 */ + a_SHEXP_ARITH_ERR_NAME_LOOP, /* Variable self-reference loop */ + a_SHEXP_ARITH_ERR_OP_INVALID /* Unknown operator */ +}; + +/* Operators and precedences in increasing precedence order. + * (The operator stack as such is u16: [OP_FLAGS |] (OP<<8) | PREC.) */ +enum a_shexp_arith_ops{ +#undef a_X +#define a_X(N,P,O) \ + CONCAT(a_SHEXP_ARITH_PREC_,N) = CONCAT(P,u),\ + CONCAT(a_SHEXP_ARITH_OP_,N) =\ + (CONCAT(O,u) << 8) | CONCAT(a_SHEXP_ARITH_PREC_,N) + + a_X(PAREN_LEFT, 0, 0), + + a_X(COMMA, 1, 0), + + a_X(ASSIGN, 2, 0), + a_X(ASSIGN_BIT_OR, 2, 1), + a_X(ASSIGN_BIT_XOR, 2, 2), + a_X(ASSIGN_BIT_AND, 2, 3), + a_X(ASSIGN_SHIFT_LEFT, 2, 4), a_X(ASSIGN_SHIFT_RIGHT, 2, 5), + a_X(ASSIGN_SHIFT_RIGHTU, 2, 6), + a_X(ASSIGN_ADD, 2, 7), a_X(ASSIGN_SUB, 2, 8), + a_X(ASSIGN_MUL, 2, 9), a_X(ASSIGN_DIV, 2, 10), a_X(ASSIGN_MOD, 2, 11), + a_X(ASSIGN_EXP, 2, 12), + + a_X(COND, 3, 0), + a_X(COND_COLON, 3, 1), + + a_X(OR, 4, 0), + a_X(AND, 5, 0), + a_X(BIT_OR, 6, 0), + a_X(BIT_XOR, 7, 0), + a_X(BIT_AND, 8, 0), + a_X(EQ, 9, 0), a_X(NE, 9, 1), + a_X(LE, 10, 0), a_X(GE, 10, 1), a_X(LT, 10, 2), a_X(GT, 10, 3), + a_X(SHIFT_LEFT, 11, 0), a_X(SHIFT_RIGHT, 11, 1), a_X(SHIFT_RIGHTU, 11, 2), + a_X(ADD, 12, 0), a_X(SUB, 12, 1), + a_X(MUL, 13, 0), a_X(DIV, 13, 1), a_X(MOD, 13, 2), + a_X(EXP, 14, 0), + + /* Further operators are unary, pre- or postfix */ + a_SHEXP_ARITH_PREC_UNARY = 15, + a_SHEXP_ARITH_PREC_PREFIX = 16, + a_SHEXP_ARITH_PREC_POSTFIX = 18, + + a_X(UNARY_NOT, 15, 0), a_X(UNARY_BIT_NOT, 15, 1), + a_X(PREFIX_INC, 16, 0), a_X(PREFIX_DEC, 16, 1), + a_X(UNARY_PLUS, 17, 1), a_X(UNARY_MINUS, 17, 0), + a_X(POSTFIX_INC, 18, 0), a_X(POSTFIX_DEC, 18, 1), + + /* Beyond operator profanity; the first "is a number" */ + a_SHEXP_ARITH_PREC_SKY = 19, + a_X(NUM, 19, 0), a_X(PAREN_RIGHT, 19, 1), + +#undef a_X +}; + +enum arith_op_flags{ + /* Mask off operator and precision */ + a_SHEXP_ARITH_OP_MASK = 0x1FFF, + a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON = 1u<<13, + a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT = 1u<<14, + a_SHEXP_ARITH_OP_FLAG_WHITEOUT = 1u<<15, + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK = a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT | + a_SHEXP_ARITH_OP_FLAG_WHITEOUT, + a_SHEXP_ARITH_OP_FLAG_MASK = a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON | + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK +}; + +struct a_shexp_arith_name_stack{ + struct a_shexp_arith_name_stack *sans_last; + char const *sans_var; +}; + +struct a_shexp_arith_val{ + s64 sav_val; + char *sav_var; /* Named variable or NIL */ +}; + +struct a_shexp_arith_stack{ + struct a_shexp_arith_val *sas_nums; + struct a_shexp_arith_val *sas_nums_top; + u16 *sas_ops; + u16 *sas_ops_top; + a_SHEXP_ARITH_ERROR_TRACK( + char **sas_error_track; + char **sas_error_track_top; + ) +}; + +struct a_shexp_arith_ctx{ + enum a_shexp_arith_error sac_error; + boole sac_have_error_track; + u8 sac__pad[3]; + s64 sac_rv; + struct a_shexp_arith_stack *sac_stack; + struct a_shexp_arith_name_stack *sac_name_stack; + a_SHEXP_ARITH_ERROR_TRACK( char **sac_error_track_or_nil; ) + a_SHEXP_ARITH_IFS( char const *sac_ifs_ws; ) +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE sac_cookie; +#endif +}; + +/* Sort by ~expected usage -- however, longest first if ambiguous! + * Follow busybox, save space by compressing data in char[] not struct[]! + * (XXX Instead use 1-st byte jump table like for commands) */ +static char const a_shexp_arith_op_toks[] = { +#undef a_X +#define a_X(X) \ + S(char,(CONCAT(a_SHEXP_ARITH_OP_,X) & 0xFF00u) >> 8),\ + S(char,CONCAT(a_SHEXP_ARITH_PREC_,X)) + + '+','+','\0', a_X(POSTFIX_INC), + '+','=','\0', a_X(ASSIGN_ADD), + '+','\0', a_X(ADD), + '-','-','\0', a_X(POSTFIX_DEC), + '-','=','\0', a_X(ASSIGN_SUB), + '-','\0', a_X(SUB), + '*','*','=','\0', a_X(ASSIGN_EXP), + '*','*','\0', a_X(EXP), + '*','=','\0', a_X(ASSIGN_MUL), + '*','\0', a_X(MUL), + '/','=','\0', a_X(ASSIGN_DIV), + '/','\0', a_X(DIV), + '%','=','\0', a_X(ASSIGN_MOD), + '%','\0', a_X(MOD), + '|','|','\0', a_X(OR), + '|','=','\0', a_X(ASSIGN_BIT_OR), + '|','\0', a_X(BIT_OR), + '^','=','\0', a_X(ASSIGN_BIT_XOR), + '^','\0', a_X(BIT_XOR), + '&','&','\0', a_X(AND), + '&','=','\0', a_X(ASSIGN_BIT_AND), + '&','\0', a_X(BIT_AND), + '<','<','=',0, a_X(ASSIGN_SHIFT_LEFT), + '<','<','\0', a_X(SHIFT_LEFT), + '>','>','>','=',0, a_X(ASSIGN_SHIFT_RIGHTU), + '>','>','>','\0', a_X(SHIFT_RIGHTU), + '>','>','=',0, a_X(ASSIGN_SHIFT_RIGHT), + '>','>','\0', a_X(SHIFT_RIGHT), + + '~','\0', a_X(UNARY_BIT_NOT), + '!','=','\0', a_X(NE), + '!','\0', a_X(UNARY_NOT), + + ')','\0', a_X(PAREN_RIGHT), + '(','\0', a_X(PAREN_LEFT), + ',','\0', a_X(COMMA), + + '<','=','\0', a_X(LE), + '>','=','\0', a_X(GE), + '=','=','\0', a_X(EQ), + '<','\0', a_X(LT), + '>','\0', a_X(GT), + '=','\0', a_X(ASSIGN), + + '?','\0', a_X(COND), + ':','\0', a_X(COND_COLON), + + '\0' +#undef a_X +}; + +/* Our "public" entry point. exp_buf can be NIL if exp_len is 0, it need not + * be NUL terminated (stop for NUL or out of length). + * Upon error *error_track_or_nil is set to a "newly allocated" string that + * points to where parse stopped, or NIL upon initial setup failure. */ +static enum a_shexp_arith_error a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len + a_SHEXP_ARITH_ERROR_TRACK( su_COMMA char **error_track_or_nil )); + +static void a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len); + +/* Count non-WS as well as normalized WS ([:"space":]+ -> ' ') in exp_buf, + * return count. If store!=NIL, also copy normalization. + * An all-WS exp_buf returns 0 */ +static uz a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil); + +/* Resolve and evaluate the "self-contained string" savp->sav_var. + * Take care to avoid name lookup loops */ +static boole a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp); + +/* Work top of the stack, which may pop & push etc */ +static boole a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self); + +static boole a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self); + +#if a_SHEXP_ARITH_DBG +static void a_shexp__arith_log(char const *fmt, ...); +#endif + +static enum a_shexp_arith_error +a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len + a_SHEXP_ARITH_ERROR_TRACK( su_COMMA char **error_track_or_nil )){ + struct a_shexp_arith_stack sas_stack; + struct a_shexp_arith_ctx self; + NYD_IN; + + a_SHEXP_ARITH_L(("> arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + a_SHEXP_ARITH_ERROR_TRACK(DBGX( + if(error_track_or_nil != NIL) + *error_track_or_nil = NIL; + )); + + STRUCT_ZERO(struct a_shexp_arith_ctx, &self); +#ifdef a_SHEXP_ARITH_COOKIE + self.sac_cookie = cookie; +#endif + a_SHEXP_ARITH_ERROR_TRACK( + if((self.sac_error_track_or_nil = error_track_or_nil) != NIL) + self.sac_have_error_track = TRU1; + ) + + ASSERT_NYD_EXEC(resp != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); + DBGX( *resp = 0; ) + ASSERT_NYD_EXEC(exp_len == 0 || exp_buf != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); + + a_SHEXP_ARITH_IFS( self.sac_ifs_ws = ok_vlook(ifs_ws); ) + self.sac_stack = &sas_stack; + a_shexp__arith_eval(&self, exp_buf, exp_len); + *resp = self.sac_rv; + + a_SHEXP_ARITH_L(("< arith_eval %zu <%.*s> -> <%lld> ERR<%d>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf, self.sac_rv, self.sac_error)); + + NYD_OU; + return self.sac_error; +} + +static void +a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len){ + char *ep, *varp, *cp, c; + u16 lop; + struct a_shexp_arith_stack *sasp; + void *mem_p; + NYD2_IN; + + a_SHEXP_ARITH_L((" > _arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + mem_p = NIL; + sasp = self->sac_stack; + + /* Create a single continuous allocation for anything */ + /* C99 */{ + union {void *v; char *c;} p; + uz i, j, a; + + /* Done for empty expression */ + if((i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, NIL)) == 0) + goto jleave; + ++i; + + /* Overflow check: since arithmetic expressions are rarely long enough + * to come near this limit, xxx laxe & fuzzy, not exact; max U32_MAX! */ + if(su_64( i > U32_MAX || ) i >= UZ_MAX / 2 || + i >= ((UZ_MAX - (i a_SHEXP_ARITH_ERROR_TRACK( * 2))) / + ((su_ALIGNOF(*sasp->sas_nums) + sizeof(*sasp->sas_ops) * 2) + a_SHEXP_ARITH_ERROR_TRACK( + + sizeof(*sasp->sas_error_track) * 2 )) + )){ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + + ++i; + j = su_ALIGNOF(*sasp->sas_nums) * (i >> 1); + a = j + (sizeof(*sasp->sas_ops) * i) + + a_SHEXP_ARITH_ERROR_TRACK( (sizeof(*sasp->sas_error_track) * i) + ) + 1 + (i a_SHEXP_ARITH_ERROR_TRACK( * 2 )); + mem_p = p.v = su_LOFI_ALLOC(a); + if(p.v == NIL){ + /* (For MX LOFI has _MUSTFAIL set though) */ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + sasp->sas_nums = sasp->sas_nums_top = S(struct a_shexp_arith_val*,p.v); + p.c += j; + sasp->sas_ops = sasp->sas_ops_top = S(u16*,p.v); + p.c += sizeof(*sasp->sas_ops) * i; + a_SHEXP_ARITH_ERROR_TRACK( + sasp->sas_error_track_top = sasp->sas_error_track = S(char**,p.v); + p.c += sizeof(*sasp->sas_error_track) * i; + ) + + ep = ++p.c; /* ++ to copy varnames in !_ARITH_ERROR cases */ + i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, ep); + varp = &ep[ +#if 0 a_SHEXP_ARITH_ERROR_TRACK( + 1) + i + 1 +#else + -1 +#endif + ]; + + a_SHEXP_ARITH_L((" ! _arith_eval ALLOC <%lu> " + "nums=%p (%lu) ops=%p varp=%p %lu <%s>\n", + S(ul,a), sasp->sas_nums, S(ul,j / su_ALIGNOF(*sasp->sas_nums)), + sasp->sas_ops, varp, S(ul,i - 1), ep)); + } + + /* Start with a left paren */ + a_SHEXP_ARITH_ERROR_TRACK( *sasp->sas_error_track_top++ = ep; ) + *sasp->sas_ops_top++ = lop = a_SHEXP_ARITH_OP_PAREN_LEFT; + + for(;;) Jouter:{ + u16 op; + + a_SHEXP_ARITH_L((" = _arith_eval TICK LOP <0x%02X %u> " + "nums=%lu ops=%lu DATA %lu <%s>\n", + lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(*ep == '\0'){ + /* At the end of the expression pop anything left. + * Assume we have read PAREN_RIGHT */ + if(exp_buf != NIL){ + exp_buf = NIL; + op = a_SHEXP_ARITH_OP_PAREN_RIGHT; + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + goto jtok_go; + } + + /* After PAREN_RIGHT, we must be finished */ + if(sasp->sas_nums_top != &sasp->sas_nums[1]) + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } + + /* Skip (normalized) WS now */ + if(*ep == ' ') + ++ep; + ASSERT(!su_cs_is_space(*ep)); + + /* A number? */ + if(su_cs_is_digit(*ep)){ + BITENUM_IS(u32,su_idec_state) is; + + is = su_idec_cp(&sasp->sas_nums_top->sav_val, ep, 0, + a_SHEXP_ARITH_IDEC_MODE, S(char const**,&ep)); + if((is &= su_IDEC_STATE_EMASK) && is != su_IDEC_STATE_EBASE) + sasp->sas_nums_top->sav_val = 0; + sasp->sas_nums_top->sav_var = NIL; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval NUM <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + continue; + } + + /* Is it a variable name? */ + for(cp = ep; (c = *cp, a_SHEXP_ISVARC(c)); ++cp) + if(cp == ep && a_SHEXP_ISVARC_BAD1ST(c)) + break; + + if(cp != ep){ + for(;;){ + c = cp[-1]; + if(!a_SHEXP_ISVARC_BADNST(c)) + break; + if(--cp == ep){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* Copy over to pre-allocated var storage */ + /* C99 */{ + uz i; + + i = P2UZ(cp - ep); + /* (Need to move for !_ARITH_ERROR cases) */ + su_mem_move(sasp->sas_nums_top->sav_var = varp, ep, i); + varp += i; + *varp++ = '\0'; + } + ep = cp; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + + a_SHEXP_ARITH_L((" + _arith_eval VAR <%s>\n", + sasp->sas_nums_top[-1].sav_var)); + continue; + } + + /* An operator. + * We turn prefix operators to multiple unary plus/minus if + * not attached to a variable name (++10 -> + + 10). + * (We adjust postfix to prefix below) */ + if((ep[0] == '+' || ep[0] == '-') && (ep[1] == ep[0])){ + if(sasp->sas_nums_top == sasp->sas_nums || + sasp->sas_nums_top[-1].sav_var == NIL){ + if((c = ep[2]) == ' ') + c = ep[3]; + + if(c != '\0' && (!a_SHEXP_ISVARC(c) || a_SHEXP_ISVARC_BAD1ST(c))){ + op = (ep[0] == '+') ? a_SHEXP_ARITH_OP_ADD + : a_SHEXP_ARITH_OP_SUB; + ++ep; + a_SHEXP_ARITH_L((" + _arith_eval OP PREFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", ep[0], ep[0], ep[0])); + goto jtok_go; + } + } + } + + /* Operator search */ + /* C99 */{ + char const *tokp; + + /* 3=NUL+OP+PREC */ + for(tokp = a_shexp_arith_op_toks; *tokp != '\0'; tokp += 3){ + for(cp = ep;; ++tokp, ++cp){ + if(*tokp == '\0'){ + ep = cp; + op = (S(u16,tokp[1]) << 8) | S(u8,tokp[2]); + goto jtok_go; + }else if(*tokp != *cp) + break; + } + + while(*tokp != '\0') + ++tokp; + } + self->sac_error = a_SHEXP_ARITH_ERR_OP_INVALID; + goto jleave; + } + +jtok_go:/* C99 */{ + u8 prec; + + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP <0x%02X %u> LOP <0x%02X %u> " + "nums=%lu ops=%lu %lu <%s>\n", + op, prec, lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(op == a_SHEXP_ARITH_OP_UNARY_PLUS){ + a_SHEXP_ARITH_L((" + _arith_eval IGNORE UNARY PLUS\n")); + continue; + } + + /* Correct our understanding of what there is. + * Post grammar: VAR++ reduces to num */ + if((lop & 0xFF) == a_SHEXP_ARITH_PREC_POSTFIX){ + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval LOP POSTFIX REDUCED to NUM\n")); + } + /* Adjust some binary/postfix operators to make them flow */ + else if(lop != a_SHEXP_ARITH_OP_NUM){ + switch(op){ + case a_SHEXP_ARITH_OP_ADD: + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST: IGNORE UNARY PLUS\n")); + continue; + case a_SHEXP_ARITH_OP_SUB: + op = a_SHEXP_ARITH_OP_UNARY_MINUS; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_INC: + op = a_SHEXP_ARITH_OP_PREFIX_INC; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_DEC: + op = a_SHEXP_ARITH_OP_PREFIX_DEC; +junapre: + prec = a_SHEXP_ARITH_PREC_PREFIX; + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST TO UNARY/PREFIX\n")); + break; + } + } + /* Special: +10++VAR -> +10 + +VAR. (Since we do handle +10++11 + * correctly via "prefix split", we should also handle this) */ + else if(prec == a_SHEXP_ARITH_PREC_POSTFIX){ + ASSERT(lop == a_SHEXP_ARITH_OP_NUM); + if((c = ep[0]) == ' ') + c = ep[1]; + if(c != '\0' && (a_SHEXP_ISVARC(c) && !a_SHEXP_ISVARC_BAD1ST(c))){ + c = *--ep; + op = (c == '+') ? a_SHEXP_ARITH_OP_ADD : a_SHEXP_ARITH_OP_SUB; + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP POSTFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", c, c, c)); + } + } + + /* Check whether we can work it a bit */ + if((prec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + prec < a_SHEXP_ARITH_PREC_UNARY) || + prec >= a_SHEXP_ARITH_PREC_SKY){ + if(lop != a_SHEXP_ARITH_OP_NUM){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + + /* Pop as much as possible */ + while(sasp->sas_ops_top != sasp->sas_ops){ + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + + a_SHEXP_ARITH_L((" + _arith_eval TRY POP - OP " + "<0x%02X %u>, NEW LOP <0x%02X %u 0x%X> nums=%lu ops=%lu\n", + op, op & 0xFF, lop, lop & 0xFF, + (*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK), + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* Special-case parenthesis groups */ + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + if(lop == a_SHEXP_ARITH_OP_PAREN_LEFT){ + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + /* Resolve VAR to NUM */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + ASSERT(!(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK)); + if(!a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + } + sasp->sas_nums_top[-1].sav_var = NIL; + a_SHEXP_ARITH_L((" + _arith_eval OP () RESOLVED <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + lop = a_SHEXP_ARITH_OP_NUM; + goto Jouter; + } + }else{ + u8 lprec; + + lprec = lop & 0xFF; + + /* */ + if(op == a_SHEXP_ARITH_OP_COND){ + u16 x; + + x = *sasp->sas_ops_top; + x &= a_SHEXP_ARITH_OP_FLAG_MASK; + if(x & a_SHEXP_ARITH_OP_FLAG_WHITEOUT){ + x ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + x |= a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT; + } + op |= x; + + /* Resolve as much as possible */ + while(lprec > a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + --sasp->sas_nums_top; + + if(sasp->sas_nums_top->sav_var != NIL){ + if(!(op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + (op & a_SHEXP_ARITH_OP_MASK) != + a_SHEXP_ARITH_OP_ASSIGN && + !a_shexp__arith_val_eval(self, sasp->sas_nums_top)) + goto jleave; + sasp->sas_nums_top->sav_var = NIL; + } + + if((sasp->sas_nums_top)->sav_val == 0) + op |= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + op |= *sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK; + + /* Delay ternary */ + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + break; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + uz recur; + u16 *opsp, x; + boole delay; + + delay = TRU1; + + /* Find our counterpart ? so we can toggle whiteout */ + opsp = sasp->sas_ops_top; + for(recur = 1;; --opsp){ + if(opsp == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + x = *opsp & a_SHEXP_ARITH_OP_MASK; + if(x == a_SHEXP_ARITH_OP_COND_COLON) + ++recur; + else if(x == a_SHEXP_ARITH_OP_COND && --recur == 0){ + *opsp |= a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON; + break; + } + } + op |= *opsp & a_SHEXP_ARITH_OP_FLAG_MASK; + op ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + + /* Resolve innermost condition asap. + * In "1 ? 0 ? 5 : 6 : 3", resolve innermost upon :3 */ + while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + lprec != a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + /* If we now see a COLON, we have to resolve further! + * Code flow restrictions of the Dijkstra algorithm!, which + * fits ternary badly (the way we do): pop as pop can! */ + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + delay = FAL0; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + lop = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + } + + if(lop != a_SHEXP_ARITH_OP_COND){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + if(delay){ + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + } + a_SHEXP_ARITH_L((" + _arith_eval %sTERNARY ?:%s\n", + (delay ? "DELAY " : su_empty), + ((op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) + ? " WHITEOUT" : su_empty))); + break; + } + /* Is this a right-associative operation? */ + else{ + boole doit; + + doit = FAL0; + if(lprec < prec){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY PRECEDENCE\n")); + }else if(lprec == prec && prec == a_SHEXP_ARITH_PREC_ASSIGN){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY RIGHT ASSOC\n")); + }else if(lprec == a_SHEXP_ARITH_PREC_COND){ + if(lop == a_SHEXP_ARITH_OP_COND){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY CONDITION\n")); + } + /* Without massive rewrite this is the location to detect + * in-whiteout precedence bugs as in + * $((0?I1=10:(1?I3:I2=12))) + * which would be parsed like (1?I3:I2)=12 without error + * (different to 0?I3:I2=12) otherwise */ + else if(op != a_SHEXP_ARITH_OP_COMMA){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_PREC_INVALID; + goto jleave; + } + } + + if(doit){ + /* If we are about to delay and LHV is a VAR, expand that + * immediately to expand in correct order things like + * I1=I2=10 I2=3; echo $((I1,I2)) + * I1=I2=10 I2=3; echo $((I1+=I2)) */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + if(op != a_SHEXP_ARITH_OP_ASSIGN && + !(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + !a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + if(prec != a_SHEXP_ARITH_PREC_ASSIGN) + sasp->sas_nums_top[-1].sav_var = NIL; + } + + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + break; + } + } + } + + /* */ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops && + &sasp->sas_ops_top[-1] > sasp->sas_ops); + ASSERT((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND); + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + --sasp->sas_ops_top; + *sasp->sas_ops_top ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + } + } + + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* Push this operator to the stack and remember it */ + a_SHEXP_ARITH_ERROR_TRACK( *sasp->sas_error_track_top++ = ep; ) + if(sasp->sas_ops_top > sasp->sas_ops && + (op & 0xFF) != a_SHEXP_ARITH_PREC_COND) + op |= sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_FLAG_MASK; + *sasp->sas_ops_top++ = op; + lop = op & a_SHEXP_ARITH_OP_MASK; + a_SHEXP_ARITH_L((" + _arith_eval OP PUSH <0x%02X %u> nums=%lu ops=%lu\n", + op, (op & 0xFF), S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + } + } + + self->sac_rv = sasp->sas_nums->sav_val; + +jleave: +#if 0 a_SHEXP_ARITH_ERROR_TRACK( + 1 ) + if(self->sac_error != a_SHEXP_ARITH_ERR_NONE && mem_p != NIL && + self->sac_have_error_track){ + if(sasp->sas_error_track_top > sasp->sas_error_track) + --sasp->sas_error_track_top; + *self->sac_error_track_or_nil = savestr(*sasp->sas_error_track_top); + } +#endif + + if(mem_p != NIL) + su_LOFI_FREE(mem_p); + + a_SHEXP_ARITH_L((" < _arith_eval <%lld> ERR<%d>\n", + self->sac_rv, self->sac_error)); + + NYD2_OU; +} + +static uz +a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil){ + a_SHEXP_ARITH_IFS( char const *ifs_ws; ) + char c; + boole last_ws, ws; + uz rv; + NYD2_IN; + UNUSED(self); + + rv = 0; + a_SHEXP_ARITH_IFS( ifs_ws = self->sac_ifs_ws; ) + + for(;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + goto jleave; + if(!(su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + )) + break; + } + + for(last_ws = FAL0;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + break; + + ws = (su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + ); + if(ws){ + if(last_ws) + continue; + c = ' '; + } + last_ws = ws; + + ++rv; + if(store_or_nil != NIL) + *store_or_nil++ = c; + } + + if(last_ws){ + --rv; + if(store_or_nil != NIL) + --store_or_nil; + } + +jleave: + if(store_or_nil != NIL) + *store_or_nil = '\0'; + + NYD2_OU; + return rv; +} + +static boole +a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp){ + struct a_shexp_arith_name_stack sans_stack, *sansp; + struct a_shexp_arith_stack sas_stack, *sasp; + char const *cp; + NYD_IN; + ASSERT(savp->sav_var != NIL); + + a_SHEXP_ARITH_L(("> _arith_val_eval %p <%s>\n", savp, savp->sav_var)); + + savp->sav_val = 0; + + /* Also look in program environment XXX configurable? */ + cp = n_var_vlook(savp->sav_var, TRU1); + if(cp == NIL) + goto jleave; + + for(sansp = self->sac_name_stack; sansp != NIL; sansp = sansp->sans_last){ + if(!su_cs_cmp(sansp->sans_var, savp->sav_var)){ + self->sac_error = a_SHEXP_ARITH_ERR_NAME_LOOP; + goto jleave; + } + } + + /* cp must be a self-contained expression. + * However, in most cases it solely consists of an integer, shortcut that */ + if(su_idec_cp(&savp->sav_val, cp, 0, a_SHEXP_ARITH_IDEC_MODE, NIL + ) & su_IDEC_STATE_CONSUMED){ + a_SHEXP_ARITH_L((" + _arith_val_eval NUM DIRECT <%lld>\n", + savp->sav_val)); + }else{ + sasp = self->sac_stack; + self->sac_stack = &sas_stack; + + sans_stack.sans_last = sansp = self->sac_name_stack; + sans_stack.sans_var = savp->sav_var; + self->sac_name_stack = &sans_stack; + + a_shexp__arith_eval(self, cp, UZ_MAX); + savp->sav_val = self->sac_rv; + /* .sav_var may be needed further on for updating purposes */ + + self->sac_stack = sasp; + self->sac_name_stack = sansp; + } + + cp = NIL; +jleave: + a_SHEXP_ARITH_L(("< _arith_val_eval %p <%s> <%lld> -> OK <%d>\n", + savp, savp->sav_var, savp->sav_val, + (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE))); + + NYD_OU; + return (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE); +} + +static boole +a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ + struct a_shexp_arith_val *nums_top; + u8 prec; + u16 op; + struct a_shexp_arith_stack *sasp; + s64 val; + boole rv, ign; + NYD_IN; + + rv = FAL0; + val = 0; + sasp = self->sac_stack; + op = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + ign = ((*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) != 0); + + a_SHEXP_ARITH_L((" > _arith_op_apply %s<0x%02X %u> " + "nums_top=%p (%lu) ops_top=%p (%lu)\n", + (ign ? "WHITEOUT " : su_empty), op, (op & 0xFF), sasp->sas_nums_top, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + sasp->sas_ops_top, S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* At least one argument is always needed */ + if((nums_top = sasp->sas_nums_top) == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + --nums_top; + + /* Resolve name ([R]VAL) to value as necessary */ + if(!ign && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + val = nums_top->sav_val; + prec = op & 0xFF; + + /* Not a binary operator? */ + if(prec >= a_SHEXP_ARITH_PREC_UNARY && prec < a_SHEXP_ARITH_PREC_SKY){ + if(ign) + goto jquick; + + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_UNARY_NOT: val = !val; break; + case a_SHEXP_ARITH_OP_UNARY_BIT_NOT: val = ~val; break; + case a_SHEXP_ARITH_OP_UNARY_MINUS: val = -val; break; + case a_SHEXP_ARITH_OP_PREFIX_INC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_INC: ++val; break; + case a_SHEXP_ARITH_OP_PREFIX_DEC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_DEC: --val; break; + } + }else if(op == a_SHEXP_ARITH_OP_COND){ + if(!(*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON)){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_NO_COLON; + goto jleave; + } + goto jquick; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + ASSERT(nums_top > sasp->sas_nums); + + if(!ign){ + /* Move the ternary value over to LHV where we find it as a result, + * and ensure LHV's name is forgotten so not to evaluate it (for + * example in 0?I1:I2 I1 would be evaluated when resolving the virtual + * outer group, because it still exists on number stack) */ + nums_top[-1].sav_val = nums_top[0].sav_val; + nums_top[-1].sav_var = NIL; + }else + val = nums_top[-1].sav_val; + + sasp->sas_nums_top = nums_top; + + if((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND_COLON){ + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + --sasp->sas_ops_top; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + if(!ign) + sasp->sas_nums_top[-1].sav_val = val; + } + }else{ + /* Binaries need two numbers: one is popped, the other replaced */ + s64 rval; + + if(nums_top == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + sasp->sas_nums_top = nums_top--; + + if(ign) + goto jquick; + + /* Resolve LHV as necessary */ + if(op != a_SHEXP_ARITH_OP_ASSIGN && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + rval = val; + val = nums_top->sav_val; /* (may be bogus for assign, fixed soon) */ + + /* In precedence order (excluding assignments) */ + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_COMMA: FALLTHRU + + case a_SHEXP_ARITH_OP_ASSIGN: val = rval; break; + + case a_SHEXP_ARITH_OP_OR: val = (val != 0 || rval != 0); break; + case a_SHEXP_ARITH_OP_AND: val = (val != 0 && rval != 0); break; + + case a_SHEXP_ARITH_OP_BIT_OR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_OR: val |= rval; break; + case a_SHEXP_ARITH_OP_BIT_XOR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_XOR: val ^= rval; break; + case a_SHEXP_ARITH_OP_BIT_AND: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_AND: val &= rval; break; + + case a_SHEXP_ARITH_OP_EQ: val = (val == rval); break; + case a_SHEXP_ARITH_OP_NE: val = (val != rval); break; + + case a_SHEXP_ARITH_OP_LE: val = (val <= rval); break; + case a_SHEXP_ARITH_OP_GE: val = (val >= rval); break; + case a_SHEXP_ARITH_OP_LT: val = (val < rval); break; + case a_SHEXP_ARITH_OP_GT: val = (val > rval); break; + + case a_SHEXP_ARITH_OP_SHIFT_LEFT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_LEFT: val <<= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHT: val >>= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHTU: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHTU: + val = S(s64,S(u64,val) >> rval); + break; + + case a_SHEXP_ARITH_OP_ADD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_ADD: val += rval; break; + case a_SHEXP_ARITH_OP_SUB: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SUB: val -= rval; break; + + case a_SHEXP_ARITH_OP_MUL: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MUL: val *= rval; break; + /* For /,%, avoid lvh=S64_MIN, rhv=-1: + * CHANGES, bash 4.3 [ac50fbac377e32b98d2de396f016ea81e8ee9961]: + * Fixed a bug that caused floating-point exceptions and + * overflow errors for the / and % arithmetic operators when + * using INTMAX_MIN and -1. */ + case a_SHEXP_ARITH_OP_DIV: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_DIV: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val != S64_MIN || rval != -1) + val /= rval; + break; + case a_SHEXP_ARITH_OP_MOD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MOD: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val == S64_MIN && rval == -1) + val = 0; + else + val %= rval; + break; + + case a_SHEXP_ARITH_OP_EXP: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_EXP: + if(rval < 0){ + self->sac_error = a_SHEXP_ARITH_ERR_EXP_INVALID; + goto jleave; + }else{ + s64 i; + + for(i = 1; rval > 0; --rval) + i *= val; + val = i; + } + break; + } + } + + /* Assignment updates a variable, which must exist. + * For prefix and postfix operators, too: we already turned them into + * multiple unary plus/minus unless we had seen a variable name */ +jquick: + if(prec == a_SHEXP_ARITH_PREC_ASSIGN || prec == a_SHEXP_ARITH_PREC_PREFIX || + prec == a_SHEXP_ARITH_PREC_POSTFIX){ + char buf[su_IENC_BUFFER_SIZE], *bp; + + if(nums_top->sav_var == NIL){ + self->sac_error = a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR; + goto jleave; + } + + if(!ign){ + bp = su_ienc_s64(buf, val, 10); + n_var_vset(nums_top->sav_var, S(up,bp), FAL0); + } + + /* And restore the stack value again for postfix operators */ + if(op == a_SHEXP_ARITH_OP_POSTFIX_INC) + --val; + else if(op == a_SHEXP_ARITH_OP_POSTFIX_DEC) + ++val; + + if(!ign) + a_SHEXP_ARITH_L((" + _arith_op_apply VAR <%s> SET <%s> VAL <%lld>\n", + nums_top->sav_var, bp, val)); + } + + nums_top->sav_val = val; + nums_top->sav_var = NIL; + + rv = TRU1; +jleave: + a_SHEXP_ARITH_L((" < _arith_op_apply RV %d <0x%02X %u> RES<%lld> ERR<%d> " + "nums=%lu ops=%lu\n", + rv, op, op & 0xFF, val, self->sac_error, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + NYD_OU; + return rv; +} + +static boole +a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self){ + u16 lop, lprec; + boole next_stop; + NYD_IN; + + for(next_stop = FAL0;;){ + if(!a_shexp__arith_op_apply(self)){ + next_stop = FAL0; + break; + } + if(next_stop) + break; + a_SHEXP_ARITH_ERROR_TRACK( --self->sac_stack->sas_error_track_top; ) + lop = *--self->sac_stack->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + next_stop = (lprec == a_SHEXP_ARITH_PREC_PAREN_LEFT || + lop == a_SHEXP_ARITH_OP_COND); + } + + NYD_OU; + return next_stop; +} + +#if a_SHEXP_ARITH_DBG +static void +a_shexp__arith_log(char const *fmt, ...){ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +#endif + +#undef a_SHEXP_ARITH_ERROR_TRACK +#undef a_SHEXP_ARITH_IFS +#undef a_SHEXP_ARITH_DBG +#undef a_SHEXP_ARITH_L +#undef a_SHEXP_ARITH_IDEC_MODE + +/* s-it-mode */ -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From vda.linux at googlemail.com Mon Aug 22 12:07:08 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Mon, 22 Aug 2022 14:07:08 +0200 Subject: [PATCH] libbb: make '--help' handling more consistent In-Reply-To: <626e48b1.tK1V0rIBgUJEJdo0%rmy@pobox.com> References: <626e48b1.tK1V0rIBgUJEJdo0%rmy@pobox.com> Message-ID: Applied, thank you On Sun, May 1, 2022 at 10:46 AM Ron Yorston wrote: > > Running an applet with '--help' as its only argument is treated > as a special case. If additional arguments follow '--help' the > behaviour is inconsistent: > > - applets which call single_argv() print help and do nothing else; > > - applets which call getopt() report "unrecognized option '--help'" > and print help anyway; > > - expr says "expr: syntax error" and doesn't print help; > > - printenv silently ignores '--help', prints any other variables > and doesn't print help; > > - realpath says "--help: No such file or directory", prints the path > of any other files and doesn't print help. > > If the first argument is '--help' ignore any other arguments and print > help. This is more consistent and most likely what the user wanted. > > See also commit 6bdfbc4cb (libbb: fix '--help' handling in > FEATURE_SH_NOFORK=y). > > function old new delta > show_usage_if_dash_dash_help 75 69 -6 > ------------------------------------------------------------------------------ > (add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-6) Total: -6 bytes > > Signed-off-by: Ron Yorston > --- > libbb/appletlib.c | 5 ++--- > 1 file changed, 2 insertions(+), 3 deletions(-) > > diff --git a/libbb/appletlib.c b/libbb/appletlib.c > index 841b3b873..d56b5b409 100644 > --- a/libbb/appletlib.c > +++ b/libbb/appletlib.c > @@ -258,7 +258,6 @@ void lbb_prepare(const char *applet > /* Redundant for busybox (run_applet_and_exit covers that case) > * but needed for "individual applet" mode */ > if (argv[1] > - && !argv[2] > && strcmp(argv[1], "--help") == 0 > && !is_prefixed_with(applet, "busybox") > ) { > @@ -940,8 +939,8 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv) > && applet_no != APPLET_NO_echo > # endif > ) { > - if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) { > - /* Make "foo --help" exit with 0: */ > + if (argv[1] && strcmp(argv[1], "--help") == 0) { > + /* Make "foo --help [...]" exit with 0: */ > xfunc_error_retval = 0; > bb_show_usage(); > } > -- > 2.35.1 > > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From harald at gigawatt.nl Thu Aug 25 05:26:28 2022 From: harald at gigawatt.nl (Harald van Dijk) Date: Thu, 25 Aug 2022 06:26:28 +0100 Subject: [PATCH] libbb: make '--help' handling more consistent In-Reply-To: References: <626e48b1.tK1V0rIBgUJEJdo0%rmy@pobox.com> Message-ID: <001e1930-11fd-0ab9-c72a-a73b02496357@gigawatt.nl> This breaks the [ command. Previously, [ --help would show help, which is okay as an extension since it is not valid with the standard [ command. This change makes [ --help ] also show help, when it is required to just check that the string --help is not empty. On 22/08/2022 13:07, Denys Vlasenko wrote: > Applied, thank you > > On Sun, May 1, 2022 at 10:46 AM Ron Yorston wrote: >> >> Running an applet with '--help' as its only argument is treated >> as a special case. If additional arguments follow '--help' the >> behaviour is inconsistent: >> >> - applets which call single_argv() print help and do nothing else; >> >> - applets which call getopt() report "unrecognized option '--help'" >> and print help anyway; >> >> - expr says "expr: syntax error" and doesn't print help; >> >> - printenv silently ignores '--help', prints any other variables >> and doesn't print help; >> >> - realpath says "--help: No such file or directory", prints the path >> of any other files and doesn't print help. >> >> If the first argument is '--help' ignore any other arguments and print >> help. This is more consistent and most likely what the user wanted. >> >> See also commit 6bdfbc4cb (libbb: fix '--help' handling in >> FEATURE_SH_NOFORK=y). >> >> function old new delta >> show_usage_if_dash_dash_help 75 69 -6 >> ------------------------------------------------------------------------------ >> (add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-6) Total: -6 bytes >> >> Signed-off-by: Ron Yorston >> --- >> libbb/appletlib.c | 5 ++--- >> 1 file changed, 2 insertions(+), 3 deletions(-) >> >> diff --git a/libbb/appletlib.c b/libbb/appletlib.c >> index 841b3b873..d56b5b409 100644 >> --- a/libbb/appletlib.c >> +++ b/libbb/appletlib.c >> @@ -258,7 +258,6 @@ void lbb_prepare(const char *applet >> /* Redundant for busybox (run_applet_and_exit covers that case) >> * but needed for "individual applet" mode */ >> if (argv[1] >> - && !argv[2] >> && strcmp(argv[1], "--help") == 0 >> && !is_prefixed_with(applet, "busybox") >> ) { >> @@ -940,8 +939,8 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv) >> && applet_no != APPLET_NO_echo >> # endif >> ) { >> - if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) { >> - /* Make "foo --help" exit with 0: */ >> + if (argv[1] && strcmp(argv[1], "--help") == 0) { >> + /* Make "foo --help [...]" exit with 0: */ >> xfunc_error_retval = 0; >> bb_show_usage(); >> } >> -- >> 2.35.1 >> >> _______________________________________________ >> busybox mailing list >> busybox at busybox.net >> http://lists.busybox.net/mailman/listinfo/busybox > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox > From rmy at pobox.com Thu Aug 25 10:34:14 2022 From: rmy at pobox.com (Ron Yorston) Date: Thu, 25 Aug 2022 11:34:14 +0100 Subject: [PATCH] libbb: special treatment for aliases of test In-Reply-To: <001e1930-11fd-0ab9-c72a-a73b02496357@gigawatt.nl> References: <626e48b1.tK1V0rIBgUJEJdo0%rmy@pobox.com> <001e1930-11fd-0ab9-c72a-a73b02496357@gigawatt.nl> Message-ID: <63075026.Ik1rCtweWTlfqcLE%rmy@pobox.com> Commit 5a9d2b6e0 (libbb: make '--help' handling more consistent) broke the command "[ --help ]". This should check that the string is non-empty, not display help. function old new delta show_usage_if_dash_dash_help 69 74 +5 ------------------------------------------------------------------------------ (add/remove: 0/0 grow/shrink: 1/0 up/down: 5/0) Total: 5 bytes Signed-off-by: Ron Yorston Reported-by: Harald van Dijk --- applets/applet_tables.c | 4 ++++ libbb/appletlib.c | 8 +++++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/applets/applet_tables.c b/applets/applet_tables.c index 66ef7e4ac..923cb2a22 100644 --- a/applets/applet_tables.c +++ b/applets/applet_tables.c @@ -147,6 +147,10 @@ int main(int argc, char **argv) for (i = 0; i < NUM_APPLETS; i++) { if (str_isalnum_(applets[i].name)) printf("#define APPLET_NO_%s %d\n", applets[i].name, i); + else if (strcmp(applets[i].name, "[") == 0) + printf("#define APPLET_NO_test1 %d\n", i); + else if (strcmp(applets[i].name, "[[") == 0) + printf("#define APPLET_NO_test2 %d\n", i); } printf("\n"); diff --git a/libbb/appletlib.c b/libbb/appletlib.c index 9b9d7dbd6..a9901aa72 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c @@ -923,12 +923,18 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv) /* Special case. POSIX says "test --help" * should be no different from e.g. "test --foo". * Thus for "test", we skip --help check. - * "true", "false", "echo" are also special. + * "[", "[[", "true", "false", "echo" are also special. */ if (1 # if defined APPLET_NO_test && applet_no != APPLET_NO_test # endif +# if defined APPLET_NO_test1 + && applet_no != APPLET_NO_test1 +# endif +# if defined APPLET_NO_test2 + && applet_no != APPLET_NO_test2 +# endif # if defined APPLET_NO_true && applet_no != APPLET_NO_true # endif -- 2.37.2 From ludwig.nussel at suse.de Thu Aug 25 10:48:07 2022 From: ludwig.nussel at suse.de (Ludwig Nussel) Date: Thu, 25 Aug 2022 12:48:07 +0200 Subject: [PATCH] libbbb: mark stack in assembly files read-only Message-ID: <20220825104806.307-1-ludwig.nussel@suse.de> Signed-off-by: Ludwig Nussel --- libbb/hash_md5_sha256_x86-32_shaNI.S | 3 +++ libbb/hash_md5_sha256_x86-64_shaNI.S | 3 +++ libbb/hash_md5_sha_x86-32_shaNI.S | 3 +++ libbb/hash_md5_sha_x86-64.S | 3 +++ libbb/hash_md5_sha_x86-64.S.sh | 3 +++ libbb/hash_md5_sha_x86-64_shaNI.S | 3 +++ 6 files changed, 18 insertions(+) diff --git a/libbb/hash_md5_sha256_x86-32_shaNI.S b/libbb/hash_md5_sha256_x86-32_shaNI.S index 3905bad9a..5b79e847b 100644 --- a/libbb/hash_md5_sha256_x86-32_shaNI.S +++ b/libbb/hash_md5_sha256_x86-32_shaNI.S @@ -19,6 +19,9 @@ // We do not check SSSE3 in cpuid, // all SHA-capable CPUs support it as well. +#ifdef __linux__ + .section .note.GNU-stack,"", at progbits +#endif .section .text.sha256_process_block64_shaNI, "ax", @progbits .globl sha256_process_block64_shaNI .hidden sha256_process_block64_shaNI diff --git a/libbb/hash_md5_sha256_x86-64_shaNI.S b/libbb/hash_md5_sha256_x86-64_shaNI.S index 082ceafe4..f89abb91a 100644 --- a/libbb/hash_md5_sha256_x86-64_shaNI.S +++ b/libbb/hash_md5_sha256_x86-64_shaNI.S @@ -19,6 +19,9 @@ // We do not check SSSE3 in cpuid, // all SHA-capable CPUs support it as well. +#ifdef __linux__ + .section .note.GNU-stack,"", at progbits +#endif .section .text.sha256_process_block64_shaNI, "ax", @progbits .globl sha256_process_block64_shaNI .hidden sha256_process_block64_shaNI diff --git a/libbb/hash_md5_sha_x86-32_shaNI.S b/libbb/hash_md5_sha_x86-32_shaNI.S index 2366b046a..3ef2b8469 100644 --- a/libbb/hash_md5_sha_x86-32_shaNI.S +++ b/libbb/hash_md5_sha_x86-32_shaNI.S @@ -25,6 +25,9 @@ // We do not check SSSE3/SSE4.1 in cpuid, // all SHA-capable CPUs support them as well. +#ifdef __linux__ + .section .note.GNU-stack,"", at progbits +#endif .section .text.sha1_process_block64_shaNI, "ax", @progbits .globl sha1_process_block64_shaNI .hidden sha1_process_block64_shaNI diff --git a/libbb/hash_md5_sha_x86-64.S b/libbb/hash_md5_sha_x86-64.S index 1d55b91f8..2e8a0cfcd 100644 --- a/libbb/hash_md5_sha_x86-64.S +++ b/libbb/hash_md5_sha_x86-64.S @@ -1,6 +1,9 @@ ### Generated by hash_md5_sha_x86-64.S.sh ### #if CONFIG_SHA1_SMALL == 0 && defined(__GNUC__) && defined(__x86_64__) +#ifdef __linux__ + .section .note.GNU-stack,"", at progbits +#endif .section .text.sha1_process_block64, "ax", @progbits .globl sha1_process_block64 .hidden sha1_process_block64 diff --git a/libbb/hash_md5_sha_x86-64.S.sh b/libbb/hash_md5_sha_x86-64.S.sh index 40c979d35..2eab1c0ba 100755 --- a/libbb/hash_md5_sha_x86-64.S.sh +++ b/libbb/hash_md5_sha_x86-64.S.sh @@ -127,6 +127,9 @@ echo \ "### Generated by hash_md5_sha_x86-64.S.sh ### #if CONFIG_SHA1_SMALL == 0 && defined(__GNUC__) && defined(__x86_64__) +#ifdef __linux__ + .section .note.GNU-stack,\"\", at progbits +#endif .section .text.sha1_process_block64, \"ax\", @progbits .globl sha1_process_block64 .hidden sha1_process_block64 diff --git a/libbb/hash_md5_sha_x86-64_shaNI.S b/libbb/hash_md5_sha_x86-64_shaNI.S index 794e97040..97435e3c2 100644 --- a/libbb/hash_md5_sha_x86-64_shaNI.S +++ b/libbb/hash_md5_sha_x86-64_shaNI.S @@ -25,6 +25,9 @@ // We do not check SSSE3/SSE4.1 in cpuid, // all SHA-capable CPUs support them as well. +#ifdef __linux__ + .section .note.GNU-stack,"", at progbits +#endif .section .text.sha1_process_block64_shaNI, "ax", @progbits .globl sha1_process_block64_shaNI .hidden sha1_process_block64_shaNI -- 2.37.2 From aaro.koskinen at iki.fi Thu Aug 25 15:47:02 2022 From: aaro.koskinen at iki.fi (Aaro Koskinen) Date: Thu, 25 Aug 2022 18:47:02 +0300 Subject: [PATCH] devmem: add 128-bit width Message-ID: <20220825154702.GK55614@darkstar.musicnaut.iki.fi> From: Aaro Koskinen Add 128-bit width if the compiler provides the needed type. function old new delta devmem_main 412 517 +105 usage_messages 247 313 +66 .rodata 3553 3571 +18 ------------------------------------------------------------------------------ (add/remove: 2/0 grow/shrink: 3/0 up/down: 189/0) Total: 189 bytes Signed-off-by: Aaro Koskinen Signed-off-by: Aaro Koskinen --- miscutils/devmem.c | 113 +++++++++++++++++++++++++++------------------ 1 file changed, 69 insertions(+), 44 deletions(-) diff --git a/miscutils/devmem.c b/miscutils/devmem.c index f9f0276bc..a6991886a 100644 --- a/miscutils/devmem.c +++ b/miscutils/devmem.c @@ -15,12 +15,15 @@ //kbuild:lib-$(CONFIG_DEVMEM) += devmem.o //usage:#define devmem_trivial_usage -//usage: "ADDRESS [WIDTH [VALUE]]" +//usage: "ADDRESS [WIDTH [VALUE]...]" //usage:#define devmem_full_usage "\n\n" //usage: "Read/write from physical address\n" //usage: "\n ADDRESS Address to act upon" //usage: "\n WIDTH Width (8/16/...)" //usage: "\n VALUE Data to be written" +#ifdef __SIZEOF_INT128__ +//usage: "\n For 128-bit data, provide 64-bit values (LOW, or HI and LOW)" +#endif #include "libbb.h" @@ -28,7 +31,7 @@ int devmem_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int devmem_main(int argc UNUSED_PARAM, char **argv) { void *map_base, *virt_addr; - uint64_t read_result; + uint64_t read_result = read_result; /* for compiler */ uint64_t writeval = writeval; /* for compiler */ off_t target; unsigned page_size, mapped_size, offset_in_page; @@ -96,51 +99,73 @@ int devmem_main(int argc UNUSED_PARAM, char **argv) virt_addr = (char*)map_base + offset_in_page; if (!argv[3]) { - switch (width) { - case 8: - read_result = *(volatile uint8_t*)virt_addr; - break; - case 16: - read_result = *(volatile uint16_t*)virt_addr; - break; - case 32: - read_result = *(volatile uint32_t*)virt_addr; - break; - case 64: - read_result = *(volatile uint64_t*)virt_addr; - break; - default: - bb_simple_error_msg_and_die("bad width"); +#ifdef __SIZEOF_INT128__ + if (width == 128) { + unsigned __int128 rd = + *(volatile unsigned __int128 *)virt_addr; + printf("0x%016llX%016llX\n", (uint64_t)(rd >> 64), + (uint64_t)rd); + } else +#endif + { + switch (width) { + case 8: + read_result = *(volatile uint8_t*)virt_addr; + break; + case 16: + read_result = *(volatile uint16_t*)virt_addr; + break; + case 32: + read_result = *(volatile uint32_t*)virt_addr; + break; + case 64: + read_result = *(volatile uint64_t*)virt_addr; + break; + default: + bb_simple_error_msg_and_die("bad width"); + } +// printf("Value at address 0x%"OFF_FMT"X (%p): 0x%llX\n", +// target, virt_addr, +// (unsigned long long)read_result); + /* Zero-padded output shows the width of access just done */ + printf("0x%0*llX\n", (width >> 2), (unsigned long long)read_result); } -// printf("Value at address 0x%"OFF_FMT"X (%p): 0x%llX\n", -// target, virt_addr, -// (unsigned long long)read_result); - /* Zero-padded output shows the width of access just done */ - printf("0x%0*llX\n", (width >> 2), (unsigned long long)read_result); } else { - switch (width) { - case 8: - *(volatile uint8_t*)virt_addr = writeval; -// read_result = *(volatile uint8_t*)virt_addr; - break; - case 16: - *(volatile uint16_t*)virt_addr = writeval; -// read_result = *(volatile uint16_t*)virt_addr; - break; - case 32: - *(volatile uint32_t*)virt_addr = writeval; -// read_result = *(volatile uint32_t*)virt_addr; - break; - case 64: - *(volatile uint64_t*)virt_addr = writeval; -// read_result = *(volatile uint64_t*)virt_addr; - break; - default: - bb_simple_error_msg_and_die("bad width"); +#ifdef __SIZEOF_INT128__ + if (width == 128) { + unsigned __int128 wr; + + wr = writeval; + if (argv[4]) + wr = (wr << 64) | bb_strtoull(argv[4], NULL, 0); + *(volatile unsigned __int128 *)virt_addr = wr; + } else +#endif + { + switch (width) { + case 8: + *(volatile uint8_t*)virt_addr = writeval; +// read_result = *(volatile uint8_t*)virt_addr; + break; + case 16: + *(volatile uint16_t*)virt_addr = writeval; +// read_result = *(volatile uint16_t*)virt_addr; + break; + case 32: + *(volatile uint32_t*)virt_addr = writeval; +// read_result = *(volatile uint32_t*)virt_addr; + break; + case 64: + *(volatile uint64_t*)virt_addr = writeval; +// read_result = *(volatile uint64_t*)virt_addr; + break; + default: + bb_simple_error_msg_and_die("bad width"); + } +// printf("Written 0x%llX; readback 0x%llX\n", +// (unsigned long long)writeval, +// (unsigned long long)read_result); } -// printf("Written 0x%llX; readback 0x%llX\n", -// (unsigned long long)writeval, -// (unsigned long long)read_result); } if (ENABLE_FEATURE_CLEAN_UP) { -- 2.17.0 From benwolsieffer at gmail.com Thu Aug 25 20:29:30 2022 From: benwolsieffer at gmail.com (Ben Wolsieffer) Date: Thu, 25 Aug 2022 16:29:30 -0400 Subject: [PATCH] httpd: fix index.html with CGI and no basic auth Message-ID: <20220825202930.3815588-1-benwolsieffer@gmail.com> The URL gets truncated if ENABLE_FEATURE_HTTPD_BASIC_AUTH or ENABLE_FEATURE_HTTPD_CGI is set, but it is only restored if ENABLE_FEATURE_HTTPD_BASIC_AUTH is set. This breaks index.html redirects for configurations that have CGI enabled but basic auth disabled. Fixes: 91a58b207ea0 ("httpd: no need to strcpy() when we only need to copy one byte") Signed-off-by: Ben Wolsieffer --- networking/httpd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/httpd.c b/networking/httpd.c index ffc58e10b..67b4ecfe4 100644 --- a/networking/httpd.c +++ b/networking/httpd.c @@ -2614,7 +2614,7 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr) /* !CGI: it can be only GET or HEAD */ #endif -#if ENABLE_FEATURE_HTTPD_BASIC_AUTH +#if ENABLE_FEATURE_HTTPD_BASIC_AUTH || ENABLE_FEATURE_HTTPD_CGI /* Restore truncated .../index.html */ if (urlp[-1] == '/') urlp[0] = index_page[0]; -- 2.37.0 From vda.linux at googlemail.com Fri Aug 26 15:25:22 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Fri, 26 Aug 2022 17:25:22 +0200 Subject: [PATCH] devmem: add 128-bit width In-Reply-To: <20220825154702.GK55614@darkstar.musicnaut.iki.fi> References: <20220825154702.GK55614@darkstar.musicnaut.iki.fi> Message-ID: On Thu, Aug 25, 2022 at 5:55 PM Aaro Koskinen wrote: > > From: Aaro Koskinen > > Add 128-bit width if the compiler provides the needed type. > > function old new delta > devmem_main 412 517 +105 > usage_messages 247 313 +66 > .rodata 3553 3571 +18 > ------------------------------------------------------------------------------ > (add/remove: 2/0 grow/shrink: 3/0 up/down: 189/0) Total: 189 bytes > > Signed-off-by: Aaro Koskinen > Signed-off-by: Aaro Koskinen > --- > miscutils/devmem.c | 113 +++++++++++++++++++++++++++------------------ > 1 file changed, 69 insertions(+), 44 deletions(-) > > diff --git a/miscutils/devmem.c b/miscutils/devmem.c > index f9f0276bc..a6991886a 100644 > --- a/miscutils/devmem.c > +++ b/miscutils/devmem.c > @@ -15,12 +15,15 @@ > //kbuild:lib-$(CONFIG_DEVMEM) += devmem.o > > //usage:#define devmem_trivial_usage > -//usage: "ADDRESS [WIDTH [VALUE]]" > +//usage: "ADDRESS [WIDTH [VALUE]...]" > //usage:#define devmem_full_usage "\n\n" > //usage: "Read/write from physical address\n" > //usage: "\n ADDRESS Address to act upon" > //usage: "\n WIDTH Width (8/16/...)" > //usage: "\n VALUE Data to be written" > +#ifdef __SIZEOF_INT128__ > +//usage: "\n For 128-bit data, provide 64-bit values (LOW, or HI and LOW)" > +#endif Let's use strtoumax() to parse a long number, instead of making user jump through such hoops. Applied, thank you. Is there a need to make these 128 reads actually this wide as one atomic load? (On x86, SSE insn do that). From aaro.koskinen at iki.fi Fri Aug 26 16:03:06 2022 From: aaro.koskinen at iki.fi (Aaro Koskinen) Date: Fri, 26 Aug 2022 19:03:06 +0300 Subject: [PATCH] devmem: add 128-bit width In-Reply-To: References: <20220825154702.GK55614@darkstar.musicnaut.iki.fi> Message-ID: <20220826160306.GL55614@darkstar.musicnaut.iki.fi> Hi, On Fri, Aug 26, 2022 at 05:25:22PM +0200, Denys Vlasenko wrote: > Is there a need to make these 128 reads actually this wide as one atomic load? > (On x86, SSE insn do that). There are arm64 hardware where such register access is needed, and GCC handles that correctly. A. From steffen at sdaoden.eu Fri Aug 26 17:04:59 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 26 Aug 2022 19:04:59 +0200 Subject: [PATCH 1/3] shell: plug SH_MATH_ERROR_TRACK memory leak In-Reply-To: <7fe77ec0ad639633963576f247564477b057414c.1660930743.git.steffen@sdaoden.eu> References: <7fe77ec0ad639633963576f247564477b057414c.1660930743.git.steffen@sdaoden.eu> Message-ID: <35b945a55556e098ca7fbb0967562e4b53f70f43.1661533052.git.steffen@sdaoden.eu> --- Hello. I have to come back unfortunately with this memory plug fix in case SH_MATCH_ERROR_TRACK is enabled. The second patch sync shexp-arith.h with my local version, i made a review and that changed some comments, i did a logical code reorder, and one superfluous value toggle i removed, too. The second patch simply adds my mailer's $(( )) test script to the busybox test ones! So easy it could have been. Note i never ran hush_test/hush-arith, only ash_test/ash-arith. shell/math.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/math.c b/shell/math.c index b9b3d7acb1..8ba0d2f7fb 100644 --- a/shell/math.c +++ b/shell/math.c @@ -273,6 +273,9 @@ arith(arith_state_t *math_state, const char *expr) emsg #endif ; +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(err_rest); +#endif return -1; } -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Fri Aug 26 17:05:04 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 26 Aug 2022 19:05:04 +0200 Subject: [PATCH 2/3] shell: $(()): sync (comments, a bit reorder, one small fix) In-Reply-To: <35b945a55556e098ca7fbb0967562e4b53f70f43.1661533474.git.steffen@sdaoden.eu> References: <35b945a55556e098ca7fbb0967562e4b53f70f43.1661533474.git.steffen@sdaoden.eu> Message-ID: --- shell/shexp-arith.h | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h index bc2ca52784..71c848455d 100644 --- a/shell/shexp-arith.h +++ b/shell/shexp-arith.h @@ -482,7 +482,13 @@ a_shexp__arith_eval(struct a_shexp_arith_ctx *self, if(exp_buf != NIL){ exp_buf = NIL; op = a_SHEXP_ARITH_OP_PAREN_RIGHT; - ASSERT(sasp->sas_ops_top > sasp->sas_ops); + /* Could fail for "1)" (how could that enter at all?) + * ASSERT(sasp->sas_ops_top > sasp->sas_ops); + * Can only be a syntax error! */ + if(sasp->sas_ops_top == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } goto jtok_go; } @@ -522,6 +528,7 @@ a_shexp__arith_eval(struct a_shexp_arith_ctx *self, if(cp != ep){ for(;;){ c = cp[-1]; + /* (For example, hyphen-minus as a sh(1) extension!) */ if(!a_SHEXP_ISVARC_BADNST(c)) break; if(--cp == ep){ @@ -535,7 +542,7 @@ a_shexp__arith_eval(struct a_shexp_arith_ctx *self, uz i; i = P2UZ(cp - ep); - /* (Need to move for !_ARITH_ERROR cases) */ + /* (move not copy for !_ARITH_ERROR cases (says ISO C?)) */ su_mem_move(sasp->sas_nums_top->sav_var = varp, ep, i); varp += i; *varp++ = '\0'; @@ -552,7 +559,7 @@ a_shexp__arith_eval(struct a_shexp_arith_ctx *self, /* An operator. * We turn prefix operators to multiple unary plus/minus if - * not attached to a variable name (++10 -> + + 10). + * not pre- or post-attached to a variable name (++10 -> + + 10). * (We adjust postfix to prefix below) */ if((ep[0] == '+' || ep[0] == '-') && (ep[1] == ep[0])){ if(sasp->sas_nums_top == sasp->sas_nums || @@ -704,7 +711,7 @@ junapre: } op |= x; - /* Resolve as much as possible */ + /* Resolve as resolve can, need to assert our condition! */ while(lprec > a_SHEXP_ARITH_PREC_COND){ if(!a_shexp__arith_op_apply(self)) goto jleave; @@ -713,13 +720,12 @@ junapre: lprec = lop & 0xFF; } + /* Evaluate condition assertion */ ASSERT(sasp->sas_nums_top > sasp->sas_nums); --sasp->sas_nums_top; if(sasp->sas_nums_top->sav_var != NIL){ if(!(op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && - (op & a_SHEXP_ARITH_OP_MASK) != - a_SHEXP_ARITH_OP_ASSIGN && !a_shexp__arith_val_eval(self, sasp->sas_nums_top)) goto jleave; sasp->sas_nums_top->sav_var = NIL; @@ -729,7 +735,9 @@ junapre: op |= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; op |= *sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK; - /* Delay ternary */ + /* Delay ternary: this ? op will last until we can resolve + * the entire condition, it's number stack position is used + * as storage for the actual condition result */ a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) ++sasp->sas_ops_top; break; @@ -760,7 +768,7 @@ junapre: op ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; /* Resolve innermost condition asap. - * In "1 ? 0 ? 5 : 6 : 3", resolve innermost upon :3 */ + * In "1?0?5:6:3", resolve innermost upon :3 */ while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && lprec != a_SHEXP_ARITH_PREC_COND){ if(!a_shexp__arith_op_apply(self)) @@ -770,9 +778,9 @@ junapre: lprec = lop & 0xFF; } - /* If we now see a COLON, we have to resolve further! - * Code flow restrictions of the Dijkstra algorithm!, which - * fits ternary badly (the way we do): pop as pop can! */ + /* If at a COLON we have to resolve further, otherwise syntax + * error would happen for 1?2?3:6:7 (due to how Dijkstra's + * algorithm applies, and our squeezing of ?: constructs) */ if(lop == a_SHEXP_ARITH_OP_COND_COLON){ delay = FAL0; if(!a_shexp__arith_op_apply_colons(self)) @@ -856,14 +864,11 @@ junapre: ) == a_SHEXP_ARITH_OP_COND); a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) --sasp->sas_ops_top; - *sasp->sas_ops_top ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; } } - if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ - self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; - goto jleave; - } + /* Should have been catched in *ep==\0,exp_buf!=NIL case */ + ASSERT(op != a_SHEXP_ARITH_OP_PAREN_RIGHT); } /* Push this operator to the stack and remember it */ @@ -896,7 +901,6 @@ jleave: a_SHEXP_ARITH_L((" < _arith_eval <%lld> ERR<%d>\n", self->sac_rv, self->sac_error)); - NYD2_OU; } @@ -1081,8 +1085,8 @@ a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ * outer group, because it still exists on number stack) */ nums_top[-1].sav_val = nums_top[0].sav_val; nums_top[-1].sav_var = NIL; - }else - val = nums_top[-1].sav_val; + } + DBGX( else val = -1; ) sasp->sas_nums_top = nums_top; -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Fri Aug 26 17:05:05 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Fri, 26 Aug 2022 19:05:05 +0200 Subject: [PATCH 3/3] shell: $(()): add tests In-Reply-To: <35b945a55556e098ca7fbb0967562e4b53f70f43.1661533474.git.steffen@sdaoden.eu> References: <35b945a55556e098ca7fbb0967562e4b53f70f43.1661533474.git.steffen@sdaoden.eu> Message-ID: <14ce57055c5e370e1e5cdc9c52e0c6e0855197ee.1661533474.git.steffen@sdaoden.eu> --- shell/ash_test/ash-arith/bigbadbison.right | 880 ++++++++++++++++++ shell/ash_test/ash-arith/bigbadbison.tests | 914 +++++++++++++++++++ shell/hush_test/hush-arith/bigbadbison.right | 880 ++++++++++++++++++ shell/hush_test/hush-arith/bigbadbison.tests | 914 +++++++++++++++++++ 4 files changed, 3588 insertions(+) create mode 100644 shell/ash_test/ash-arith/bigbadbison.right create mode 100755 shell/ash_test/ash-arith/bigbadbison.tests create mode 100644 shell/hush_test/hush-arith/bigbadbison.right create mode 100755 shell/hush_test/hush-arith/bigbadbison.tests diff --git a/shell/ash_test/ash-arith/bigbadbison.right b/shell/ash_test/ash-arith/bigbadbison.right new file mode 100644 index 0000000000..a6446c81cd --- /dev/null +++ b/shell/ash_test/ash-arith/bigbadbison.right @@ -0,0 +1,880 @@ += BASE +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<0> +<10> +<9191919191919> +<13> +<11> +<1023> +<1295> +<1295> +<9322365> +<16242915> +<10> +<33> +<10> +<33> +<1> +<1> +<1> +<33> +<33> +<33> +<33> += UNA PLUS/MINUS +<0> +<0> +<1> +<1> +<4221> +<16929> +<16242915> +<16242915> +<1> +<1> +<1> +<0> +<0> +<-1> +<-1> +<-4221> +<-16929> +<-16242915> +<-16242915> +<-1> +<-1> +<-1> +<-1> +<1> +<-1> += UNA ! +<1> +<1> +<0> +<0> +<1> +<0> += UNA ~ +<-1> +<-1> +<-2> +<-2> +<-2276> +<0> +<0> +<-1> +<-1> +<-1> +<-1> += BIN + +<0> +<0> +<1> +<1> +<1> +<1> +<2> +<2> +<2> +<-2> +<3333> +<3333> +<33> +<-33> +<-33> +<-1> +<33> +<-33> +<-33> +<1> +<9223372036854775807> +<-9223372036854775807> +<9223372036854775806> +<-9223372036854775808> +<-2> +<0> +<9223372036854775797> +<-9223372036854775797> +<9223372036854775796> +<-9223372036854775798> +<-12> +<10> += BIN - +<0> +<0> +<-1> +<-1> +<1> +<1> +<0> +<0> +<0> +<0> +<-1111> +<1111> +<-1> +<1> +<1> +<129> +<1> +<-1> +<-1> +<129> +<-9223372036854775807> +<9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<-9223372036854775797> +<9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN * +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<2468642> +<2468642> +<272> +<272> +<272> +<-4160> +<272> +<272> +<272> +<-4160> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775797> +<9223372036854775797> +<11> +<-11> += BIN / +<0> +<1> +<1> +<0> +<2> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<-1> +<2> +<3> +<1> +<1> +<0> +<0> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<838488366986797800> +<-838488366986797800> +<-838488366986797800> +<838488366986797800> +<0> +<0> += BIN % +<0> +<0> +<0> +<1111> +<0> +<16> +<-16> +<-16> +<64> +<1> +<-1> +<-1> +<1> +<0> +<0> +<1> +<0> +<3> +<-1> +<0> +<0> +<0> +<0> +<0> +<0> +<-8> +<-8> +<7> +<7> +<-1> +<-1> += BIN << +<0> +<0> +<0> +<0> +<1> +<1> +<2> +<2> +<78179674781384704> +<18639486976> +<2097152> +<-2251799813685248> +<-2251799813685248> +<0> +<1114112> +<-4785074604081152> +<-4785074604081152> +<65> +<64> +<0> +<0> +<-9223372036854775808> +<-2> +<-9223372036854775808> +<-2> +<0> +<0> +<-9007199254740992> +<-2048> +<-9007199254740992> +<-2048> += BIN >> +<0> +<0> +<0> +<0> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<-1> +<-1> +<131071> +<0> +<0> +<-1> +<-1> +<65> +<64> +<-1> +<-4611686018427387904> +<0> +<4611686018427387903> +<-1> +<-1> +<-1024> +<-4503599627370496> +<1023> +<4503599627370495> +<-1> +<-1> +<9007199254740991> += BIN ** +<0> +<2> +<4> +<8> +<16> +<10000> +<10000000000> +<100005> +<10000000000> += LOG OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> += LOG AND +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> += BIN BIT_OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<3327> +<3327> +<17> +<-1> +<-1> +<-1> +<17> +<-1> +<-1> +<-63> +<1088> +<-1> +<-9223372036854775807> +<-1> +<9223372036854775807> +<-1> +<-1> +<-11> +<-9223372036854775797> +<-1> +<9223372036854775807> +<-1> +<-1> += BIN BIT_XOR +<0> +<0> +<1> +<1> +<1> +<1> +<0> +<0> +<3321> +<3321> +<1> +<31> +<31> +<-1> +<1> +<31> +<31> +<-127> +<1088> +<9223372036854775807> +<-9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<9223372036854775797> +<-9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN BIT_AND +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<6> +<6> +<16> +<-32> +<-32> +<0> +<16> +<-32> +<-32> +<64> +<0> +<-9223372036854775808> +<0> +<9223372036854775807> +<1> +<-1> +<1> +<-9223372036854775808> +<0> +<9223372036854775797> +<11> +<-11> +<11> += BIN EQ +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> += BIN NE +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> += BIN LE +<1> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<0> +<1> += BIN GE +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<1> +<0> += BIN LT +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> += BIN GT +<0> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<1> +<0> += PRECEDENCE I +<6> +<2> +<0> +<2> +<-1> +<1> +<1> +<2> +<8> +<7> +<12> +<5> +<10> +<1> +<72> +<48> +<288> +<1> +<3> +<1> +<4> +<76> +<1> +<1> +<1> +<1> +<2> +<2> += PARENS +<6> +<6> +<-4> +<-4> +<2> +<2> +<0> +<0> +<-3> +<-3> +<0> +<0> +<12> +<12> +<10> +<10> +<12> +<48> +<1> +<1> +<6> +<6> +<9> +<9> +<20> +<20> +<21> +<21> +<36864> +<36864> +<6> +<6> +<9> +<9> +<9> +<9> +<0> +<0> +<38> +<38> +<2> +<2> +<32> +<32> +<0> +<0> +<24> +<24> += ASSIGN I +<3><3> +<3><3> +<3><3> +<11><11> +<9><9> +<10><10> +<20><20> +<10><10> +<5><5> +<0><0> +<0><0> +<10><10> +<100><100> +<100><100> +<11><11> +<11><11> +<10><10> +<2><2> +<5><5> +<20><20> +<9223372036854775807><9223372036854775807> += ASSIGN II +<2><3> +<4><3> +<36><5><7> +<1501><100><15> +<3><3> +<10><1><2><3><10> += POSTFIX +<1><2> +<1><2><1> +<10><2><11> +<10><2><11> +<1><0> +<1><0><1> +<10><0><9> +<10><0><9> += PREFIX +<2><2> +<2><2><2> +<22><2><11> +<10><1><10> +<22><2><11> +<0><0> +<0><0><0> +<9><1><9> +<10><1><10> +<0><0><9> += VAR RECUR +<2><1 + 1> +<2><1 + 1> +<3><3> +<2><3> +<3><1 + 1> +<4><1 + 1 * 2> +<5><(1 + 1) * 2> +<3><3><3 / 2> +<2><2> +<2><2> +<3><3> +<4><4> +<5><5> +<5><5><3 / 2> += COMMA +<2> +<3> +<4> +<4> +<133><133> +<10><10> += COND +<3> +<3> +<2> +<3> +<2> +<2> +<111> +<5> +<7> +<5> +<5> +<7> +<7> +<7> +<7> +<5> +-- COND .2 +<-1> +<0> +<1> +<1> +<32> +<32> +<1> +<1> +<32> +-- COND .3 +<3> +<4> +<3> +<3> +<3> +<4> +<3> +<3> +<3> +<4> +<1> +<4> +<5> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<5> +<5> +<8> +<8> +<10> +<10> +<10> +<10> +<10> +-- COND .4 +<5> +<6> +<7> +<8> +<9> +<12> +<10> +<12> +<10> +-- COND .5 +<12> +<10> +<12> +<10> +-- COND .6 +<12> +<9> +<-2> +<-1> +<23> +<26> +<24> +<0> +<23> +<23> +<23> +-- COND .7 +<16><2><3><16><5> +<16><2><3><16><5> +<16><2><3><16><5> +<25><2><3><4><25><> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<16><2><3><16><5> +<9><2><9><4><5><> +<4><4><3><4><5><> +-- COND .8 +<10><2> +<20><0> +<10><10> +<20><0> +<10><10> +<20><0> +<20><20> +-- COND .9 +<2><+E+><1+1> +<2><1+1><+E+> +<2><+E+><2> +<1><1><+E+> +<4><+E+><4> +<4><4><+E+> +-- COND .10 +<-1><+E+><+E+><+E+><-1> +<2><1><2><+E+><+E+> +<3><0><+E+><3><+E+> += WILD I +<14> +<1> +<1> +<1> +<3> +<87> +<2097152> +<20><10> +<100><10> +<0><10> += WILD II +<36><11> +<33><11> +<36><12> +<39><12> +<39><12> +<-33><12> +<-27> +<20><10> +<21><11> +<20><10> +<21><11> +<21><11> +<20><10> +<20><10> +<21> +<21> +<21> += WILD RECUR +<20><20><10> +<10><10> +<11><11> +<21><11> +<10><10> +<1><1> +<6><6><6> +<10><10><5> +<12><12> +<12><12> +<10><11> +<10><11> +<10><10><5> +<6><6> +<10><10> +<10><0><10><20> +<10><6><10><20> +<10><10><10><20> +<50><50><10><20> +<50><50><10><20> +<500><500><10><20> diff --git a/shell/ash_test/ash-arith/bigbadbison.tests b/shell/ash_test/ash-arith/bigbadbison.tests new file mode 100755 index 0000000000..2b15fda7c9 --- /dev/null +++ b/shell/ash_test/ash-arith/bigbadbison.tests @@ -0,0 +1,914 @@ +# make this work with (ba)sh \ +command -v shopt && shopt -s expand_aliases;\ +alias p=printf;alias e=echo;alias s=export +s I=10 J=33 +e '= BASE' +e "<$(())>" +e "<$(( ))>" +e "<$((1))>" +e "<$((0))>" +e "<$((0x0))>" +e "<$((0X0))>" +e "<$((000))>" +e "<$((000000000000001))>" +e "<$((2#00000000000000000000000000000000000001))>" +e "<$((0X00000000000000000000000000000000000000000001))>" +e "<$((999999999999999999999999999999999999999999999))>" +e "<$(( 10 ))>" +e "<$((9191919191919))>" +e "<$((0xD))>" +e "<$((013))>" +e "<$((32#VV))>" +e "<$((36#ZZ))>" +e "<$((36#zz))>" +e "<$(( 64#zzZZ ))>" +e "<$((64#ZZzz))>" +e "<$((I))>" +e "<$((J))>" +e "<$(( I ))>" +e "<$(( J ))>" +e "<$(( (1) ))>" +e "<$((((1))))>" +e "<$(((((1)))))>" +e "<$(( (J) ))>" +e "<$((((J))))>" +e "<$(((((J)))))>" +e "<$(( ( ( ( J ) ) ) ))>" +e '= UNA PLUS/MINUS' +e "<$((+0))>" +e "<$(( + 0 ))>" +e "<$(( +1))>" +e "<$((+ 1 ))>" +e "<$(( + 4221 ))>" +e "<$(( +0x4221 ))>" +e "<$(( + 64#ZZzz ))>" +e "<$(( +64#ZZzz ))>" +e "<$((+ (1) ))>" +e "<$((+((1))))>" +e "<$((+(((1)))))>" +e "<$((-0))>" +e "<$(( - 0 ))>" +e "<$(( -1))>" +e "<$((- 1 ))>" +e "<$(( - 4221 ))>" +e "<$(( -0x4221 ))>" +e "<$(( - 64#ZZzz ))>" +e "<$(( -64#ZZzz ))>" +e "<$((- (1) ))>" +e "<$((-((1))))>" +e "<$((-(((1)))))>" +e "<$((+ -(1) ))>" +e "<$((+(-(-1))))>" +e "<$((+(-(-(-1)))))>" +e '= UNA !' +e "<$((!0))>" +e "<$((! 00000000))>" +e "<$((!1))>" +e "<$((! 0x00001))>" +e "<$((! - 0))>" +e "<$((!-1))>" +e '= UNA ~' +e "<$((~0))>" +e "<$((~ 00000000))>" +e "<$((~1))>" +e "<$((~ 0x00001))>" +e "<$((~ 64#zz))>" +e "<$((~-1))>" +e "<$((~ - 1))>" +e "<$((~-0))>" +e "<$((~ - 0))>" +e "<$((~(-0)))>" +e "<$((~((- 0))))>" +e '= BIN +' +e "<$((0+0))>" +e "<$(( 0 + 0 ))>" +e "<$((0+1))>" +e "<$(( 0 + 1 ))>" +e "<$((1+0))>" +e "<$(( 1 + 0 ))>" +e "<$((1+1))>" +e "<$(( 1 + 1 ))>" +e "<$(( (1 + 1) ))>" +e "<$(((((((-1)))) + (((-1))))))>" +e "<$((1111+2222))>" +e "<$((2222+1111))>" +e "<$(( +0x10 + +0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( +64#10 + -64#11 ))>" +e "<$(( +0x11 + +0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( +64#11 + -64#10 ))>" +e "<$((0x8000000000000000+-1))>" +e "<$((0x8000000000000000+1))>" +e "<$((0x7FFFFFFFFFFFFFFF+-1))>" +e "<$((0x7FFFFFFFFFFFFFFF+1))>" +e "<$((0xFFFFFFFFFFFFFFFF+-1))>" +e "<$((0xFFFFFFFFFFFFFFFF+1))>" +e "<$((0x8000000000000000+-11))>" +e "<$((0x8000000000000000+11))>" +e "<$((0x7FFFFFFFFFFFFFFF+-11))>" +e "<$((0x7FFFFFFFFFFFFFFF+11))>" +e "<$((0xFFFFFFFFFFFFFFFF+-11))>" +e "<$((0xFFFFFFFFFFFFFFFF+11))>" +e '= BIN -' +e "<$((0-0))>" +e "<$(( 0 - 0 ))>" +e "<$((0-1))>" +e "<$(( 0 - 1 ))>" +e "<$((1-0))>" +e "<$(( 1 - 0 ))>" +e "<$((1-1))>" +e "<$(( 1 - 1 ))>" +e "<$(( (1 - 1) ))>" +e "<$(((((((+1)))) - (((+1))))))>" +e "<$((1111-2222))>" +e "<$((2222-1111))>" +e "<$(( +0x10 - +0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( +64#10 - -64#11 ))>" +e "<$(( +0x11 - +0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( +64#11 - -64#10 ))>" +e "<$((0x8000000000000000--1))>" +e "<$((0x8000000000000000-1))>" +e "<$((0x7FFFFFFFFFFFFFFF--1))>" +e "<$((0x7FFFFFFFFFFFFFFF-1))>" +e "<$((0xFFFFFFFFFFFFFFFF--1))>" +e "<$((0xFFFFFFFFFFFFFFFF-1))>" +e "<$((0x8000000000000000--11))>" +e "<$((0x8000000000000000-11))>" +e "<$((0x7FFFFFFFFFFFFFFF--11))>" +e "<$((0x7FFFFFFFFFFFFFFF-11))>" +e "<$((0xFFFFFFFFFFFFFFFF--11))>" +e "<$((0xFFFFFFFFFFFFFFFF-11))>" +e '= BIN *' +e "<$((0*0))>" +e "<$(( 0 * 0 ))>" +e "<$((0*1))>" +e "<$(( 0 * 1 ))>" +e "<$((1*0))>" +e "<$(( 1 * 0 ))>" +e "<$((1*1))>" +e "<$(( 1 * 1 ))>" +e "<$((1111*2222))>" +e "<$((2222*1111))>" +e "<$(( +0x10 * +0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( +64#10 * -64#11 ))>" +e "<$(( +0x11 * +0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( +64#11 * -64#10 ))>" +e "<$((0x8000000000000000*-1))>" +e "<$((0x8000000000000000*1))>" +e "<$((0x7FFFFFFFFFFFFFFF*-1))>" +e "<$((0x7FFFFFFFFFFFFFFF*1))>" +e "<$((0xFFFFFFFFFFFFFFFF*-1))>" +e "<$((0xFFFFFFFFFFFFFFFF*1))>" +e "<$((0x8000000000000000*-11))>" +e "<$((0x8000000000000000*11))>" +e "<$((0x7FFFFFFFFFFFFFFF*-11))>" +e "<$((0x7FFFFFFFFFFFFFFF*11))>" +e "<$((0xFFFFFFFFFFFFFFFF*-11))>" +e "<$((0xFFFFFFFFFFFFFFFF*11))>" +e '= BIN /' +e "<$(( 0 / 1 ))>" +e "<$((1/1))>" +e "<$(( 1 / 1 ))>" +e "<$((1111/2222))>" +e "<$((2222/1111))>" +e "<$(( +0x10 / +0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( +64#10 / -64#11 ))>" +e "<$(( +0x11 / +0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( +64#11 / -64#10 ))>" +e "<$((2/1))>" +e "<$((3/1))>" +e "<$((3/2))>" +e "<$((3/3))>" +e "<$((3/4))>" +e "<$((-1/4))>" +e "<$((0x8000000000000000/-1))>" +e "<$((0x8000000000000000/1))>" +e "<$((0x7FFFFFFFFFFFFFFF/-1))>" +e "<$((0x7FFFFFFFFFFFFFFF/1))>" +e "<$((0xFFFFFFFFFFFFFFFF/-1))>" +e "<$((0xFFFFFFFFFFFFFFFF/1))>" +e "<$((0x8000000000000000/-11))>" +e "<$((0x8000000000000000/11))>" +e "<$((0x7FFFFFFFFFFFFFFF/-11))>" +e "<$((0x7FFFFFFFFFFFFFFF/11))>" +e "<$((0xFFFFFFFFFFFFFFFF/-11))>" +e "<$((0xFFFFFFFFFFFFFFFF/11))>" +e '= BIN %' +e "<$(( 0 % 1 ))>" +e "<$((1%1))>" +e "<$(( 1 % 1 ))>" +e "<$((1111%2222))>" +e "<$((2222%1111))>" +e "<$(( +0x10 % +0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( +64#10 % -64#11 ))>" +e "<$(( +0x11 % +0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( +64#11 % -64#10 ))>" +e "<$((2%1))>" +e "<$((3%1))>" +e "<$((3%2))>" +e "<$((3%3))>" +e "<$((3%4))>" +e "<$((-1%4))>" +e "<$((0x8000000000000000%-1))>" +e "<$((0x8000000000000000%1))>" +e "<$((0x7FFFFFFFFFFFFFFF%-1))>" +e "<$((0x7FFFFFFFFFFFFFFF%1))>" +e "<$((0xFFFFFFFFFFFFFFFF%-1))>" +e "<$((0xFFFFFFFFFFFFFFFF%1))>" +e "<$((0x8000000000000000%-11))>" +e "<$((0x8000000000000000%11))>" +e "<$((0x7FFFFFFFFFFFFFFF%-11))>" +e "<$((0x7FFFFFFFFFFFFFFF%11))>" +e "<$((0xFFFFFFFFFFFFFFFF%-11))>" +e "<$((0xFFFFFFFFFFFFFFFF%11))>" +e '= BIN <<' +e "<$((0<<0))>" +e "<$(( 0 << 0 ))>" +e "<$((0<<1))>" +e "<$(( 0 << 1 ))>" +e "<$((1<<0))>" +e "<$(( 1 << 0 ))>" +e "<$((1<<1))>" +e "<$(( 1 << 1 ))>" +e "<$((1111<<2222))>" +e "<$((2222<<1111))>" +e "<$(( +0x10 << +0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( +64#10 << -64#11 ))>" +e "<$(( +0x11 << +0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( +64#11 << -64#10 ))>" +e "<$(( +64 << +1024 ))>" +e "<$((0x8000000000000000<<-1))>" +e "<$((0x8000000000000000<<1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<1))>" +e "<$((0x8000000000000000<<-11))>" +e "<$((0x8000000000000000<<11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<11))>" +e '= BIN >>' +e "<$((0>>0))>" +e "<$(( 0 >> 0 ))>" +e "<$((0>>1))>" +e "<$(( 0 >> 1 ))>" +e "<$((1>>0))>" +e "<$(( 1 >> 0 ))>" +e "<$((1>>1))>" +e "<$(( 1 >> 1 ))>" +e "<$((1>>>1))>" +e "<$(( 1 >>> 1 ))>" +e "<$((1111>>2222))>" +e "<$((2222>>1111))>" +e "<$((1111>>>2222))>" +e "<$((2222>>>1111))>" +e "<$(( +0x10 >> +0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >>> -0x11 ))>" +e "<$(( +64#10 >> -64#11 ))>" +e "<$(( +0x11 >> +0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( +64#11 >> -64#10 ))>" +e "<$(( +64 >> +1024 ))>" +e "<$((0x8000000000000000>>-1))>" +e "<$((0x8000000000000000>>1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>1))>" +e "<$((0x8000000000000000>>-11))>" +e "<$((0x8000000000000000>>11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" +e '= BIN **' +e "<$((0**1))>" +e "<$((2**1))>" +e "<$((2**2))>" +e "<$((2**3))>" +e "<$((2**4))>" +e "<$((10**4))>" +e "<$((10**10))>" +e "<$((10**5+5))>" +e "<$((10**(5+5)))>" +e '= LOG OR' +e "<$((0||0))>" +e "<$(( 000 || 0X0 ))>" +e "<$((01 || 64#1))>" +e "<$((01 || 64#1))>" +e "<$((0x1234 || 4660))>" +e "<$((0x1234 || 011064))>" +s I=33 J=33;e "<$((I||J))>" +s I=33 J=33;e "<$(( I || J ))>" +e "<$((0||1))>" +e "<$((0||0000000000000000000000001))>" +e "<$((1||2))>" +e "<$((0x1234 || 04660))>" +e "<$((0x1234 || 0x11064))>" +s I=10 J=33;e "<$((I||J))>" +s I=-10 J=-33;e "<$((I||J))>" +s I=-33 J=-33;e "<$((I||J))>" +s I=0 J=-33;e "<$((I||J))>" +s I=33 J=0;e "<$((I||J))>" +e '= LOG AND' +e "<$((0&&0))>" +e "<$(( 000 && 0X0 ))>" +e "<$((01 && 64#1))>" +e "<$((01 && 64#1))>" +e "<$((0x1234 && 4660))>" +e "<$((0x1234 && 011064))>" +s I=33 J=33;e "<$((I&&J))>" +s I=33 J=33;e "<$(( I && J ))>" +e "<$((0&&1))>" +e "<$((0&&0000000000000000000000001))>" +e "<$((1&&2))>" +e "<$((0x1234 && 04660))>" +e "<$((0x1234 && 0x11064))>" +s I=10 J=33;e "<$((I&&J))>" +s I=-10 J=-33;e "<$((I&&J))>" +s I=-33 J=-33;e "<$((I&&J))>" +s I=0 J=-33;e "<$((I&&J))>" +s I=33 J=0;e "<$((I&&J))>" +e '= BIN BIT_OR' +e "<$((0|0))>" +e "<$(( 0 | 0 ))>" +e "<$((0|1))>" +e "<$(( 0 | 1 ))>" +e "<$((1|0))>" +e "<$(( 1 | 0 ))>" +e "<$((1|1))>" +e "<$(( 1 | 1 ))>" +e "<$((1111|2222))>" +e "<$((2222|1111))>" +e "<$(( +0x10 | +0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( +64#10 | -64#11 ))>" +e "<$(( +0x11 | +0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( +64#11 | -64#10 ))>" +e "<$(( +64 | +1024 ))>" +e "<$((0x8000000000000000|-1))>" +e "<$((0x8000000000000000|1))>" +e "<$((0x7FFFFFFFFFFFFFFF|-1))>" +e "<$((0x7FFFFFFFFFFFFFFF|1))>" +e "<$((0xFFFFFFFFFFFFFFFF|-1))>" +e "<$((0xFFFFFFFFFFFFFFFF|1))>" +e "<$((0x8000000000000000|-11))>" +e "<$((0x8000000000000000|11))>" +e "<$((0x7FFFFFFFFFFFFFFF|-11))>" +e "<$((0x7FFFFFFFFFFFFFFF|11))>" +e "<$((0xFFFFFFFFFFFFFFFF|-11))>" +e "<$((0xFFFFFFFFFFFFFFFF|11))>" +e '= BIN BIT_XOR' +e "<$((0^0))>" +e "<$(( 0 ^ 0 ))>" +e "<$((0^1))>" +e "<$(( 0 ^ 1 ))>" +e "<$((1^0))>" +e "<$(( 1 ^ 0 ))>" +e "<$((1^1))>" +e "<$(( 1 ^ 1 ))>" +e "<$((1111^2222))>" +e "<$((2222^1111))>" +e "<$(( +0x10 ^ +0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( +64#10 ^ -64#11 ))>" +e "<$(( +0x11 ^ +0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( +64#11 ^ -64#10 ))>" +e "<$(( +64 ^ +1024 ))>" +e "<$((0x8000000000000000^-1))>" +e "<$((0x8000000000000000^1))>" +e "<$((0x7FFFFFFFFFFFFFFF^-1))>" +e "<$((0x7FFFFFFFFFFFFFFF^1))>" +e "<$((0xFFFFFFFFFFFFFFFF^-1))>" +e "<$((0xFFFFFFFFFFFFFFFF^1))>" +e "<$((0x8000000000000000^-11))>" +e "<$((0x8000000000000000^11))>" +e "<$((0x7FFFFFFFFFFFFFFF^-11))>" +e "<$((0x7FFFFFFFFFFFFFFF^11))>" +e "<$((0xFFFFFFFFFFFFFFFF^-11))>" +e "<$((0xFFFFFFFFFFFFFFFF^11))>" +e '= BIN BIT_AND' +e "<$((0&0))>" +e "<$(( 0 & 0 ))>" +e "<$((0&1))>" +e "<$(( 0 & 1 ))>" +e "<$((1&0))>" +e "<$(( 1 & 0 ))>" +e "<$((1&1))>" +e "<$(( 1 & 1 ))>" +e "<$((1111&2222))>" +e "<$((2222&1111))>" +e "<$(( +0x10 & +0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( +64#10 & -64#11 ))>" +e "<$(( +0x11 & +0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( +64#11 & -64#10 ))>" +e "<$(( +64 & +1024 ))>" +e "<$((0x8000000000000000&-1))>" +e "<$((0x8000000000000000&1))>" +e "<$((0x7FFFFFFFFFFFFFFF&-1))>" +e "<$((0x7FFFFFFFFFFFFFFF&1))>" +e "<$((0xFFFFFFFFFFFFFFFF&-1))>" +e "<$((0xFFFFFFFFFFFFFFFF&1))>" +e "<$((0x8000000000000000&-11))>" +e "<$((0x8000000000000000&11))>" +e "<$((0x7FFFFFFFFFFFFFFF&-11))>" +e "<$((0x7FFFFFFFFFFFFFFF&11))>" +e "<$((0xFFFFFFFFFFFFFFFF&-11))>" +e "<$((0xFFFFFFFFFFFFFFFF&11))>" +e '= BIN EQ' +e "<$((0==0))>" +e "<$(( 000 == 0X0 ))>" +e "<$((01 == 64#1))>" +e "<$((01 == 64#1))>" +e "<$((0x1234 == 4660))>" +e "<$((0x1234 == 011064))>" +s I=33 J=33;e "<$((I==J))>" +s I=33 J=33;e "<$(( I == J ))>" +e "<$((0==1))>" +e "<$((0==0000000000000000000000001))>" +e "<$((1==2))>" +e "<$((0x1234 == 04660))>" +e "<$((0x1234 == 0x11064))>" +s I=10 J=33;e "<$((I==J))>" +s I=-10 J=-33;e "<$((I==J))>" +s I=-33 J=-33;e "<$((I==J))>" +e '= BIN NE' +e "<$((0!=0))>" +e "<$(( 000 != 0X0 ))>" +e "<$((01 != 64#1))>" +e "<$((01 != 64#1))>" +e "<$((0x1234 != 4660))>" +e "<$((0x1234 != 011064))>" +s I=33 J=33;e "<$((I!=J))>" +s I=33 J=33;e "<$(( I != J ))>" +e "<$((0!=1))>" +e "<$((0!=0000000000000000000000001))>" +e "<$((1!=2))>" +e "<$((0x1234 != 04660))>" +e "<$((0x1234 != 0x11064))>" +s I=10 J=33;e "<$((I!=J))>" +s I=-10 J=-33;e "<$((I!=J))>" +s I=-33 J=-33;e "<$((I!=J))>" +e '= BIN LE' +e "<$((0<=0))>" +e "<$(( 000 <= 0X0 ))>" +e "<$((01 <= 64#1))>" +e "<$((01 <= 64#2))>" +e "<$((02 <= 64#1))>" +e "<$((0x1234 <= 4660))>" +e "<$((0x1234 <= 011064))>" +e "<$((0x1233 <= 011064))>" +e "<$((0x1235 <= 011064))>" +s I=33 J=33;e "<$((I<=J))>" +s I=33 J=33;e "<$((I<=J))>" +s I=32 J=33;e "<$((I<=J))>" +s I=34 J=33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-32 J=-33;e "<$((I<=J))>" +s I=-34 J=-33;e "<$((I<=J))>" +e '= BIN GE' +e "<$((0>=0))>" +e "<$(( 000 >= 0X0 ))>" +e "<$((01 >= 64#1))>" +e "<$((01 >= 64#2))>" +e "<$((02 >= 64#1))>" +e "<$((0x1234 >= 4660))>" +e "<$((0x1234 >= 011064))>" +e "<$((0x1233 >= 011064))>" +e "<$((0x1235 >= 011064))>" +s I=33 J=33;e "<$((I>=J))>" +s I=33 J=33;e "<$((I>=J))>" +s I=32 J=33;e "<$((I>=J))>" +s I=34 J=33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-32 J=-33;e "<$((I>=J))>" +s I=-34 J=-33;e "<$((I>=J))>" +e '= BIN LT' +e "<$((0<0))>" +e "<$(( 000 < 0X0 ))>" +e "<$((01 < 64#1))>" +e "<$((01 < 64#2))>" +e "<$((02 < 64#1))>" +e "<$((0x1234 < 4660))>" +e "<$((0x1234 < 011064))>" +e "<$((0x1233 < 011064))>" +e "<$((0x1235 < 011064))>" +s I=33 J=33;e "<$((I" +s I=33 J=33;e "<$((I" +s I=32 J=33;e "<$((I" +s I=34 J=33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-32 J=-33;e "<$((I" +s I=-34 J=-33;e "<$((I" +e '= BIN GT' +e "<$((0>0))>" +e "<$(( 000 > 0X0 ))>" +e "<$((01 > 64#1))>" +e "<$((01 > 64#2))>" +e "<$((02 > 64#1))>" +e "<$((0x1234 > 4660))>" +e "<$((0x1234 > 011064))>" +e "<$((0x1233 > 011064))>" +e "<$((0x1235 > 011064))>" +s I=33 J=33;e "<$((I>J))>" +s I=33 J=33;e "<$((I>J))>" +s I=32 J=33;e "<$((I>J))>" +s I=34 J=33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-32 J=-33;e "<$((I>J))>" +s I=-34 J=-33;e "<$((I>J))>" +# +# COMMA below +e '= PRECEDENCE I' +e "<$(( 1 + 2 + 3 ))>" +e "<$(( 1 - 2 + 3 ))>" +e "<$(( 3 - 2 - 1 ))>" +e "<$(( 3 - 2 + 1 ))>" +e "<$(( - 2 + 1 ))>" +e "<$(( 2 + -1 ))>" +e "<$(( ! 2 + 1 ))>" +e "<$(( 2 + !1 ))>" +e "<$(( 3 * 2 + 2 ))>" +e "<$(( 3 + 2 * 2 ))>" +e "<$(( 3 * 2 * 2 ))>" +e "<$(( 9 / 3 + 2 ))>" +e "<$(( 9 + 3 / 2 ))>" +e "<$(( 9 / 3 / 2 ))>" +e "<$(( 9 << 1 + 2 ))>" +e "<$(( 9 + 3 << 2 ))>" +e "<$(( 9 << 3 << 2 ))>" +e "<$(( 9 >> 1 + 2 ))>" +e "<$(( 9 + 3 >> 2 ))>" +e "<$(( 19 >> 3 >> 1 ))>" +e "<$(( 19 >> 3 << 1 ))>" +e "<$(( 19 << 3 >> 1 ))>" +e "<$(( 2 + 3 < 3 * 2 ))>" +e "<$(( 2 << 3 >= 3 << 2 ))>" +e "<$(( 0xfD & 0xF == 0xF ))>" +e "<$((0xfD&0xF==0xF))>" +e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" +e "<$((3*7,2<<8,9-7))>" +e '= PARENS' +e "<$(((1 + 2) + 3))>" +e "<$(((1+2)+3))>" +e "<$((1 - (2 + 3)))>" +e "<$((1-(2+3)))>" +e "<$((3 - (2 - 1)))>" +e "<$((3-(2-1)))>" +e "<$((3 - ( 2 + 1 )))>" +e "<$((3-(2+1)))>" +e "<$((- (2 + 1)))>" +e "<$((-(2+1)))>" +e "<$((! (2 + 1)))>" +e "<$((!(2+1)))>" +e "<$((3 * (2 + 2)))>" +e "<$((3*(2+2)))>" +e "<$(((3 + 2) * 2))>" +e "<$(((3+2)*2))>" +e "<$((3 * (2 * 2)))>" +e "<$((3*(2*8)))>" +e "<$((9 / (3 + 2)))>" +e "<$((9/(3+2)))>" +e "<$((( 9 + 3 ) / 2))>" +e "<$(((9+3)/2))>" +e "<$((9 / ( 3 / 2 )))>" +e "<$((9/(3/2)))>" +e "<$((( 9 << 1 ) + 2))>" +e "<$(((9<<1)+2))>" +e "<$((9 + (3 << 2)))>" +e "<$((9+(3<<2)))>" +e "<$((9 << (3 << 2)))>" +e "<$((9<<(3<<2)))>" +e "<$(((9 >> 1) + 2))>" +e "<$(((9>>1)+2))>" +e "<$((9 + (3 >> 2)))>" +e "<$((9+(3>>2)))>" +e "<$((19 >> (3 >> 1)))>" +e "<$((19>>(3>>1)))>" +e "<$((19 >> (3 << 1)))>" +e "<$((19>>(3<<1)))>" +e "<$((19 << (3 >> 1)))>" +e "<$((19<<(3>>1)))>" +e "<$((2 + (3 < 3) * 2))>" +e "<$((2+(3<3)*2))>" +e "<$((2 << ((3 >= 3) << 2)))>" +e "<$((2<<((3>=3)<<2)))>" +e "<$(((0xfD & 0xF) == 0xF))>" +e "<$(((0xfD&0xF)==0xF))>" +e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" +e "<$((3*(7,2)<<(8,9-7)))>" +# +# COND BELOW +e '= ASSIGN I' +unset I;p "<$(( I = 3 ))>";e "<$I>" +unset I;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I+=1))>";e "<$I>" +s I=10;p "<$((I-=1))>";e "<$I>" +s I=10;p "<$((I*=1))>";e "<$I>" +s I=10;p "<$((I*=2))>";e "<$I>" +s I=10;p "<$((I/=1))>";e "<$I>" +s I=10;p "<$((I/=2))>";e "<$I>" +s I=10;p "<$((I%=1))>";e "<$I>" +s I=10;p "<$((I%=2))>";e "<$I>" +s I=10;p "<$((I**=1))>";e "<$I>" +s I=10;p "<$((I**=2))>";e "<$I>" +s I=10;p "<$((I**=1+1))>";e "<$I>" +s I=10;p "<$((I|=1))>";e "<$I>" +s I=10;p "<$((I^=1))>";e "<$I>";p "<$((I^=1))>";e "<$I>" +s I=10;p "<$((I&=2))>";e "<$I>" +s I=10;p "<$((I>>=1))>";e "<$I>" +s I=10;p "<$((I<<=1))>";e "<$I>" +s I=-1;p "<$((I>>>=1))>";e "<$I>" +e '= ASSIGN II' +s I=2;p "<$(((I+=1)-1))>";e "<$I>" +s I=4;p "<$(((I-=1)+1))>";e "<$I>" +s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" +s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" +s I=10;p "<$((I=2,I|=1))>";e "<$I>" +s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" +e '= POSTFIX' +s I=1;p "<$((I++))>";e "<$I>" +s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" +s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" +s I=1;p "<$((I--))>";e "<$I>" +s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" +s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" +e '= PREFIX' +s I=1;p "<$((++I))>";e "<$I>" +s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" +s I=1;p "<$((--I))>";e "<$I>" +s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" +s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" +s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" +e '= VAR RECUR' +s I='1 + 1';p "<$((I))>";e "<$I>" +s I='1 + 1';p "<$((+I))>";e "<$I>" +s I='1 + 1';p "<$((++I))>";e "<$I>" +s I='1 + 1';p "<$((I++))>";e "<$I>" +s I='1 + 1';p "<$((1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" +s I='1 + 1';p "<$((I=I))>";e "<$I>" +s I='1 + 1';p "<$((I=+I))>";e "<$I>" +s I='1 + 1';p "<$((I=1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" +e '= COMMA' +e "<$(( 1 , 2 ))>" +e "<$(( 1 , 2 , 3 ))>" +e "<$(( 1 , 2 , 3 , 4 ))>" +e "<$((1,2,3,4))>" +s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" +s I1=I2=10 I2=3;p "<$((I1,I2))>";e "<$I1><$I2>" +e '= COND' +e "<$(( +0 ? 2 : 3 ))>" +e "<$((-0?2:3))>" +e "<$(( +1 ? 2 : 3 ))>" +e "<$(( 1-1 ? 2 : 3 ))>" +e "<$(( 1-0 ? 2 : 3 ))>" +e "<$((-1?2:3))>" +e "<$(( 0x1234 ? 111 : 222 ))>" +e "<$((1**2 ? 5 : 7))>" +e "<$((0**2 ? 5 : 7))>" +e "<$((0**2>=0?5:7))>" +e "<$((-1<=0**2?5:7))>" +e "<$((1<=0**2?5:7))>" +e "<$((1>2||1*0?5:7))>" +e "<$((1>2&&1*0?5:7))>" +e "<$((1<2&&1*0?5:7))>" +e "<$((1<2&&1*0+1?5:7))>" +e '-- COND .2' +e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" +e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" +e "<$((2<1?-1:2>1?1:0))>" +e "<$((4<5 ? 1 : 32))>" +e "<$((4>5 ? 1 : 32))>" +e "<$((4>(2+3) ? 1 : 32))>" +e "<$((4<(2+3) ? 1 : 32))>" +e "<$(((2+2)<(2+3) ? 1 : 32))>" +e "<$(((2+2)>(2+3) ? 1 : 32))>" +## grouping protects precedence in : parts (syntax error tests below) +e '-- COND .3' +e "<$((1-1 < 1 ? 2,4 : 1,3))>" +e "<$((0<1?2,4:(1,3)))>" +e "<$((0,1,2,0?2,4:1,3))>" +e "<$((0,1,2,1?2,4:1,3))>" +e "<$((0,1,2,0?2,4:(1,3)))>" +e "<$((0,1,2,1?2,4:(1,3)))>" +e "<$((0,1,2,0?(2,4):1,3))>" +e "<$((0,1,2,1?(2,4):1,3))>" +e "<$((0,1,2,0?(2,4):(1,3)))>" +e "<$((0,1,2,1?(2,4):(1,3)))>" +e "<$((0?2:((0,3)?1:4)))>" +e "<$((1?2:3,0?1:4))>" +e "<$((1?2:3,0?1:4?5:6))>" +e "<$((1?2:(3,0)?1:4?5:6))>" +e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" +e '-- COND .4' +e "<$((1?2?3?4?5:6:7:8:9))>" +e "<$((1?2?3?0?5:6:7:8:9))>" +e "<$((1?2?0?0?5:6:7:8:9))>" +e "<$((1?0?0?0?5:6:7:8:9))>" +e "<$((0?0?0?0?5:6:7:8:9))>" +e "<$((0?3+4?10:11:5+6?12:13))>" +e "<$((1?3+4?10:11:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" +e '-- COND .5' +e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e '-- COND .6' +e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" +e '-- COND .7' +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$((((I1";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1";\ + e "<$I1><$I2><$I3><$I4><$I5>" +# only first +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(((I1I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +# last not etc. +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3";\ + e "<$I1><$I2><$I3><$I4><$I5>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1>I2)?(I2";\ + e "<$I1><$I2><$I3><$I4><$I5>" +e '-- COND .8' +s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" +s I=0;p "<$((1?20:(I+=2)))>";e "<$I>" +s I=0;p "<$((1?I+=10:(I+=2)))>";e "<$I>" +s I=0;p "<$((0?I+=2:20))>";e "<$I>" +s I=0;p "<$((0?I+=2:(I+=10)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(20)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(I+=20)))>";e "<$I>" +e '-- COND .9' +s I1=+E+ I2=1+1;p "<$((0?I1:I2))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1:I2))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +e '-- COND .10' +s I1=+E+ I2=+E+ I3=+E+ I4=-1;p "<$((0?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=1 I2=2 I3=+E+ I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=0 I2=+E+ I3=3 I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +e '= WILD I' +e "<$(( 3 + ( 11 ) ))>" +e "<$((1 + (2 - 2)))>" +e "<$((1 + (2 - 2)))>" +e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" +e "<$(( 3+((2 * 2))/6 ))>" +e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" +e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" +s I1=I2=10 I2=3;p "<$((I1 + I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 * I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 % I2))>";e "<$I1><$I2>" +e '= WILD II' +s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" +s I=10;p "<$((3+(3*(I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" +e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" +s I=10;p "<$(( +10 + + +I ))>";e "<$I>" +s I=10;p "<$(( +10 + ++I ))>";e "<$I>" +s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" +s I=10;p "<$(( +10 +++ I ))>";e "<$I>" +s I=10;p "<$(( +10+++I ))>";e "<$I>" +s I=10;p "<$((+10++I))>";e "<$I>" +s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" +e "<$(( +10 + + + ++++ +11 ))>" +e "<$(( +10 + + + ++++ ++11 ))>" +e "<$((+10++++++++11))>" +e '= WILD RECUR' # (some yet) +s I1=I2=10 I2=5;p "<$((I1+=I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=10)+1' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=(I2=10)+1)' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=+E+ I2=5;p "<$((I1=10))>";e "<$I1><$I2>" +s I1=I2=+E+ I2=5;p "<$((0?I1:++I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5;p "<$((I2,(1?I1:++I2)))>";e "<$I1><$I2>" +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" diff --git a/shell/hush_test/hush-arith/bigbadbison.right b/shell/hush_test/hush-arith/bigbadbison.right new file mode 100644 index 0000000000..a6446c81cd --- /dev/null +++ b/shell/hush_test/hush-arith/bigbadbison.right @@ -0,0 +1,880 @@ += BASE +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<0> +<10> +<9191919191919> +<13> +<11> +<1023> +<1295> +<1295> +<9322365> +<16242915> +<10> +<33> +<10> +<33> +<1> +<1> +<1> +<33> +<33> +<33> +<33> += UNA PLUS/MINUS +<0> +<0> +<1> +<1> +<4221> +<16929> +<16242915> +<16242915> +<1> +<1> +<1> +<0> +<0> +<-1> +<-1> +<-4221> +<-16929> +<-16242915> +<-16242915> +<-1> +<-1> +<-1> +<-1> +<1> +<-1> += UNA ! +<1> +<1> +<0> +<0> +<1> +<0> += UNA ~ +<-1> +<-1> +<-2> +<-2> +<-2276> +<0> +<0> +<-1> +<-1> +<-1> +<-1> += BIN + +<0> +<0> +<1> +<1> +<1> +<1> +<2> +<2> +<2> +<-2> +<3333> +<3333> +<33> +<-33> +<-33> +<-1> +<33> +<-33> +<-33> +<1> +<9223372036854775807> +<-9223372036854775807> +<9223372036854775806> +<-9223372036854775808> +<-2> +<0> +<9223372036854775797> +<-9223372036854775797> +<9223372036854775796> +<-9223372036854775798> +<-12> +<10> += BIN - +<0> +<0> +<-1> +<-1> +<1> +<1> +<0> +<0> +<0> +<0> +<-1111> +<1111> +<-1> +<1> +<1> +<129> +<1> +<-1> +<-1> +<129> +<-9223372036854775807> +<9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<-9223372036854775797> +<9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN * +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<2468642> +<2468642> +<272> +<272> +<272> +<-4160> +<272> +<272> +<272> +<-4160> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775797> +<9223372036854775797> +<11> +<-11> += BIN / +<0> +<1> +<1> +<0> +<2> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<-1> +<2> +<3> +<1> +<1> +<0> +<0> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<838488366986797800> +<-838488366986797800> +<-838488366986797800> +<838488366986797800> +<0> +<0> += BIN % +<0> +<0> +<0> +<1111> +<0> +<16> +<-16> +<-16> +<64> +<1> +<-1> +<-1> +<1> +<0> +<0> +<1> +<0> +<3> +<-1> +<0> +<0> +<0> +<0> +<0> +<0> +<-8> +<-8> +<7> +<7> +<-1> +<-1> += BIN << +<0> +<0> +<0> +<0> +<1> +<1> +<2> +<2> +<78179674781384704> +<18639486976> +<2097152> +<-2251799813685248> +<-2251799813685248> +<0> +<1114112> +<-4785074604081152> +<-4785074604081152> +<65> +<64> +<0> +<0> +<-9223372036854775808> +<-2> +<-9223372036854775808> +<-2> +<0> +<0> +<-9007199254740992> +<-2048> +<-9007199254740992> +<-2048> += BIN >> +<0> +<0> +<0> +<0> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<-1> +<-1> +<131071> +<0> +<0> +<-1> +<-1> +<65> +<64> +<-1> +<-4611686018427387904> +<0> +<4611686018427387903> +<-1> +<-1> +<-1024> +<-4503599627370496> +<1023> +<4503599627370495> +<-1> +<-1> +<9007199254740991> += BIN ** +<0> +<2> +<4> +<8> +<16> +<10000> +<10000000000> +<100005> +<10000000000> += LOG OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> += LOG AND +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> += BIN BIT_OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<3327> +<3327> +<17> +<-1> +<-1> +<-1> +<17> +<-1> +<-1> +<-63> +<1088> +<-1> +<-9223372036854775807> +<-1> +<9223372036854775807> +<-1> +<-1> +<-11> +<-9223372036854775797> +<-1> +<9223372036854775807> +<-1> +<-1> += BIN BIT_XOR +<0> +<0> +<1> +<1> +<1> +<1> +<0> +<0> +<3321> +<3321> +<1> +<31> +<31> +<-1> +<1> +<31> +<31> +<-127> +<1088> +<9223372036854775807> +<-9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<9223372036854775797> +<-9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN BIT_AND +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<6> +<6> +<16> +<-32> +<-32> +<0> +<16> +<-32> +<-32> +<64> +<0> +<-9223372036854775808> +<0> +<9223372036854775807> +<1> +<-1> +<1> +<-9223372036854775808> +<0> +<9223372036854775797> +<11> +<-11> +<11> += BIN EQ +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> += BIN NE +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> += BIN LE +<1> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<0> +<1> += BIN GE +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<1> +<0> += BIN LT +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> += BIN GT +<0> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<1> +<0> += PRECEDENCE I +<6> +<2> +<0> +<2> +<-1> +<1> +<1> +<2> +<8> +<7> +<12> +<5> +<10> +<1> +<72> +<48> +<288> +<1> +<3> +<1> +<4> +<76> +<1> +<1> +<1> +<1> +<2> +<2> += PARENS +<6> +<6> +<-4> +<-4> +<2> +<2> +<0> +<0> +<-3> +<-3> +<0> +<0> +<12> +<12> +<10> +<10> +<12> +<48> +<1> +<1> +<6> +<6> +<9> +<9> +<20> +<20> +<21> +<21> +<36864> +<36864> +<6> +<6> +<9> +<9> +<9> +<9> +<0> +<0> +<38> +<38> +<2> +<2> +<32> +<32> +<0> +<0> +<24> +<24> += ASSIGN I +<3><3> +<3><3> +<3><3> +<11><11> +<9><9> +<10><10> +<20><20> +<10><10> +<5><5> +<0><0> +<0><0> +<10><10> +<100><100> +<100><100> +<11><11> +<11><11> +<10><10> +<2><2> +<5><5> +<20><20> +<9223372036854775807><9223372036854775807> += ASSIGN II +<2><3> +<4><3> +<36><5><7> +<1501><100><15> +<3><3> +<10><1><2><3><10> += POSTFIX +<1><2> +<1><2><1> +<10><2><11> +<10><2><11> +<1><0> +<1><0><1> +<10><0><9> +<10><0><9> += PREFIX +<2><2> +<2><2><2> +<22><2><11> +<10><1><10> +<22><2><11> +<0><0> +<0><0><0> +<9><1><9> +<10><1><10> +<0><0><9> += VAR RECUR +<2><1 + 1> +<2><1 + 1> +<3><3> +<2><3> +<3><1 + 1> +<4><1 + 1 * 2> +<5><(1 + 1) * 2> +<3><3><3 / 2> +<2><2> +<2><2> +<3><3> +<4><4> +<5><5> +<5><5><3 / 2> += COMMA +<2> +<3> +<4> +<4> +<133><133> +<10><10> += COND +<3> +<3> +<2> +<3> +<2> +<2> +<111> +<5> +<7> +<5> +<5> +<7> +<7> +<7> +<7> +<5> +-- COND .2 +<-1> +<0> +<1> +<1> +<32> +<32> +<1> +<1> +<32> +-- COND .3 +<3> +<4> +<3> +<3> +<3> +<4> +<3> +<3> +<3> +<4> +<1> +<4> +<5> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<5> +<5> +<8> +<8> +<10> +<10> +<10> +<10> +<10> +-- COND .4 +<5> +<6> +<7> +<8> +<9> +<12> +<10> +<12> +<10> +-- COND .5 +<12> +<10> +<12> +<10> +-- COND .6 +<12> +<9> +<-2> +<-1> +<23> +<26> +<24> +<0> +<23> +<23> +<23> +-- COND .7 +<16><2><3><16><5> +<16><2><3><16><5> +<16><2><3><16><5> +<25><2><3><4><25><> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<16><2><3><16><5> +<9><2><9><4><5><> +<4><4><3><4><5><> +-- COND .8 +<10><2> +<20><0> +<10><10> +<20><0> +<10><10> +<20><0> +<20><20> +-- COND .9 +<2><+E+><1+1> +<2><1+1><+E+> +<2><+E+><2> +<1><1><+E+> +<4><+E+><4> +<4><4><+E+> +-- COND .10 +<-1><+E+><+E+><+E+><-1> +<2><1><2><+E+><+E+> +<3><0><+E+><3><+E+> += WILD I +<14> +<1> +<1> +<1> +<3> +<87> +<2097152> +<20><10> +<100><10> +<0><10> += WILD II +<36><11> +<33><11> +<36><12> +<39><12> +<39><12> +<-33><12> +<-27> +<20><10> +<21><11> +<20><10> +<21><11> +<21><11> +<20><10> +<20><10> +<21> +<21> +<21> += WILD RECUR +<20><20><10> +<10><10> +<11><11> +<21><11> +<10><10> +<1><1> +<6><6><6> +<10><10><5> +<12><12> +<12><12> +<10><11> +<10><11> +<10><10><5> +<6><6> +<10><10> +<10><0><10><20> +<10><6><10><20> +<10><10><10><20> +<50><50><10><20> +<50><50><10><20> +<500><500><10><20> diff --git a/shell/hush_test/hush-arith/bigbadbison.tests b/shell/hush_test/hush-arith/bigbadbison.tests new file mode 100755 index 0000000000..2b15fda7c9 --- /dev/null +++ b/shell/hush_test/hush-arith/bigbadbison.tests @@ -0,0 +1,914 @@ +# make this work with (ba)sh \ +command -v shopt && shopt -s expand_aliases;\ +alias p=printf;alias e=echo;alias s=export +s I=10 J=33 +e '= BASE' +e "<$(())>" +e "<$(( ))>" +e "<$((1))>" +e "<$((0))>" +e "<$((0x0))>" +e "<$((0X0))>" +e "<$((000))>" +e "<$((000000000000001))>" +e "<$((2#00000000000000000000000000000000000001))>" +e "<$((0X00000000000000000000000000000000000000000001))>" +e "<$((999999999999999999999999999999999999999999999))>" +e "<$(( 10 ))>" +e "<$((9191919191919))>" +e "<$((0xD))>" +e "<$((013))>" +e "<$((32#VV))>" +e "<$((36#ZZ))>" +e "<$((36#zz))>" +e "<$(( 64#zzZZ ))>" +e "<$((64#ZZzz))>" +e "<$((I))>" +e "<$((J))>" +e "<$(( I ))>" +e "<$(( J ))>" +e "<$(( (1) ))>" +e "<$((((1))))>" +e "<$(((((1)))))>" +e "<$(( (J) ))>" +e "<$((((J))))>" +e "<$(((((J)))))>" +e "<$(( ( ( ( J ) ) ) ))>" +e '= UNA PLUS/MINUS' +e "<$((+0))>" +e "<$(( + 0 ))>" +e "<$(( +1))>" +e "<$((+ 1 ))>" +e "<$(( + 4221 ))>" +e "<$(( +0x4221 ))>" +e "<$(( + 64#ZZzz ))>" +e "<$(( +64#ZZzz ))>" +e "<$((+ (1) ))>" +e "<$((+((1))))>" +e "<$((+(((1)))))>" +e "<$((-0))>" +e "<$(( - 0 ))>" +e "<$(( -1))>" +e "<$((- 1 ))>" +e "<$(( - 4221 ))>" +e "<$(( -0x4221 ))>" +e "<$(( - 64#ZZzz ))>" +e "<$(( -64#ZZzz ))>" +e "<$((- (1) ))>" +e "<$((-((1))))>" +e "<$((-(((1)))))>" +e "<$((+ -(1) ))>" +e "<$((+(-(-1))))>" +e "<$((+(-(-(-1)))))>" +e '= UNA !' +e "<$((!0))>" +e "<$((! 00000000))>" +e "<$((!1))>" +e "<$((! 0x00001))>" +e "<$((! - 0))>" +e "<$((!-1))>" +e '= UNA ~' +e "<$((~0))>" +e "<$((~ 00000000))>" +e "<$((~1))>" +e "<$((~ 0x00001))>" +e "<$((~ 64#zz))>" +e "<$((~-1))>" +e "<$((~ - 1))>" +e "<$((~-0))>" +e "<$((~ - 0))>" +e "<$((~(-0)))>" +e "<$((~((- 0))))>" +e '= BIN +' +e "<$((0+0))>" +e "<$(( 0 + 0 ))>" +e "<$((0+1))>" +e "<$(( 0 + 1 ))>" +e "<$((1+0))>" +e "<$(( 1 + 0 ))>" +e "<$((1+1))>" +e "<$(( 1 + 1 ))>" +e "<$(( (1 + 1) ))>" +e "<$(((((((-1)))) + (((-1))))))>" +e "<$((1111+2222))>" +e "<$((2222+1111))>" +e "<$(( +0x10 + +0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( +64#10 + -64#11 ))>" +e "<$(( +0x11 + +0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( +64#11 + -64#10 ))>" +e "<$((0x8000000000000000+-1))>" +e "<$((0x8000000000000000+1))>" +e "<$((0x7FFFFFFFFFFFFFFF+-1))>" +e "<$((0x7FFFFFFFFFFFFFFF+1))>" +e "<$((0xFFFFFFFFFFFFFFFF+-1))>" +e "<$((0xFFFFFFFFFFFFFFFF+1))>" +e "<$((0x8000000000000000+-11))>" +e "<$((0x8000000000000000+11))>" +e "<$((0x7FFFFFFFFFFFFFFF+-11))>" +e "<$((0x7FFFFFFFFFFFFFFF+11))>" +e "<$((0xFFFFFFFFFFFFFFFF+-11))>" +e "<$((0xFFFFFFFFFFFFFFFF+11))>" +e '= BIN -' +e "<$((0-0))>" +e "<$(( 0 - 0 ))>" +e "<$((0-1))>" +e "<$(( 0 - 1 ))>" +e "<$((1-0))>" +e "<$(( 1 - 0 ))>" +e "<$((1-1))>" +e "<$(( 1 - 1 ))>" +e "<$(( (1 - 1) ))>" +e "<$(((((((+1)))) - (((+1))))))>" +e "<$((1111-2222))>" +e "<$((2222-1111))>" +e "<$(( +0x10 - +0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( +64#10 - -64#11 ))>" +e "<$(( +0x11 - +0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( +64#11 - -64#10 ))>" +e "<$((0x8000000000000000--1))>" +e "<$((0x8000000000000000-1))>" +e "<$((0x7FFFFFFFFFFFFFFF--1))>" +e "<$((0x7FFFFFFFFFFFFFFF-1))>" +e "<$((0xFFFFFFFFFFFFFFFF--1))>" +e "<$((0xFFFFFFFFFFFFFFFF-1))>" +e "<$((0x8000000000000000--11))>" +e "<$((0x8000000000000000-11))>" +e "<$((0x7FFFFFFFFFFFFFFF--11))>" +e "<$((0x7FFFFFFFFFFFFFFF-11))>" +e "<$((0xFFFFFFFFFFFFFFFF--11))>" +e "<$((0xFFFFFFFFFFFFFFFF-11))>" +e '= BIN *' +e "<$((0*0))>" +e "<$(( 0 * 0 ))>" +e "<$((0*1))>" +e "<$(( 0 * 1 ))>" +e "<$((1*0))>" +e "<$(( 1 * 0 ))>" +e "<$((1*1))>" +e "<$(( 1 * 1 ))>" +e "<$((1111*2222))>" +e "<$((2222*1111))>" +e "<$(( +0x10 * +0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( +64#10 * -64#11 ))>" +e "<$(( +0x11 * +0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( +64#11 * -64#10 ))>" +e "<$((0x8000000000000000*-1))>" +e "<$((0x8000000000000000*1))>" +e "<$((0x7FFFFFFFFFFFFFFF*-1))>" +e "<$((0x7FFFFFFFFFFFFFFF*1))>" +e "<$((0xFFFFFFFFFFFFFFFF*-1))>" +e "<$((0xFFFFFFFFFFFFFFFF*1))>" +e "<$((0x8000000000000000*-11))>" +e "<$((0x8000000000000000*11))>" +e "<$((0x7FFFFFFFFFFFFFFF*-11))>" +e "<$((0x7FFFFFFFFFFFFFFF*11))>" +e "<$((0xFFFFFFFFFFFFFFFF*-11))>" +e "<$((0xFFFFFFFFFFFFFFFF*11))>" +e '= BIN /' +e "<$(( 0 / 1 ))>" +e "<$((1/1))>" +e "<$(( 1 / 1 ))>" +e "<$((1111/2222))>" +e "<$((2222/1111))>" +e "<$(( +0x10 / +0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( +64#10 / -64#11 ))>" +e "<$(( +0x11 / +0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( +64#11 / -64#10 ))>" +e "<$((2/1))>" +e "<$((3/1))>" +e "<$((3/2))>" +e "<$((3/3))>" +e "<$((3/4))>" +e "<$((-1/4))>" +e "<$((0x8000000000000000/-1))>" +e "<$((0x8000000000000000/1))>" +e "<$((0x7FFFFFFFFFFFFFFF/-1))>" +e "<$((0x7FFFFFFFFFFFFFFF/1))>" +e "<$((0xFFFFFFFFFFFFFFFF/-1))>" +e "<$((0xFFFFFFFFFFFFFFFF/1))>" +e "<$((0x8000000000000000/-11))>" +e "<$((0x8000000000000000/11))>" +e "<$((0x7FFFFFFFFFFFFFFF/-11))>" +e "<$((0x7FFFFFFFFFFFFFFF/11))>" +e "<$((0xFFFFFFFFFFFFFFFF/-11))>" +e "<$((0xFFFFFFFFFFFFFFFF/11))>" +e '= BIN %' +e "<$(( 0 % 1 ))>" +e "<$((1%1))>" +e "<$(( 1 % 1 ))>" +e "<$((1111%2222))>" +e "<$((2222%1111))>" +e "<$(( +0x10 % +0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( +64#10 % -64#11 ))>" +e "<$(( +0x11 % +0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( +64#11 % -64#10 ))>" +e "<$((2%1))>" +e "<$((3%1))>" +e "<$((3%2))>" +e "<$((3%3))>" +e "<$((3%4))>" +e "<$((-1%4))>" +e "<$((0x8000000000000000%-1))>" +e "<$((0x8000000000000000%1))>" +e "<$((0x7FFFFFFFFFFFFFFF%-1))>" +e "<$((0x7FFFFFFFFFFFFFFF%1))>" +e "<$((0xFFFFFFFFFFFFFFFF%-1))>" +e "<$((0xFFFFFFFFFFFFFFFF%1))>" +e "<$((0x8000000000000000%-11))>" +e "<$((0x8000000000000000%11))>" +e "<$((0x7FFFFFFFFFFFFFFF%-11))>" +e "<$((0x7FFFFFFFFFFFFFFF%11))>" +e "<$((0xFFFFFFFFFFFFFFFF%-11))>" +e "<$((0xFFFFFFFFFFFFFFFF%11))>" +e '= BIN <<' +e "<$((0<<0))>" +e "<$(( 0 << 0 ))>" +e "<$((0<<1))>" +e "<$(( 0 << 1 ))>" +e "<$((1<<0))>" +e "<$(( 1 << 0 ))>" +e "<$((1<<1))>" +e "<$(( 1 << 1 ))>" +e "<$((1111<<2222))>" +e "<$((2222<<1111))>" +e "<$(( +0x10 << +0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( +64#10 << -64#11 ))>" +e "<$(( +0x11 << +0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( +64#11 << -64#10 ))>" +e "<$(( +64 << +1024 ))>" +e "<$((0x8000000000000000<<-1))>" +e "<$((0x8000000000000000<<1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<1))>" +e "<$((0x8000000000000000<<-11))>" +e "<$((0x8000000000000000<<11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<11))>" +e '= BIN >>' +e "<$((0>>0))>" +e "<$(( 0 >> 0 ))>" +e "<$((0>>1))>" +e "<$(( 0 >> 1 ))>" +e "<$((1>>0))>" +e "<$(( 1 >> 0 ))>" +e "<$((1>>1))>" +e "<$(( 1 >> 1 ))>" +e "<$((1>>>1))>" +e "<$(( 1 >>> 1 ))>" +e "<$((1111>>2222))>" +e "<$((2222>>1111))>" +e "<$((1111>>>2222))>" +e "<$((2222>>>1111))>" +e "<$(( +0x10 >> +0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >>> -0x11 ))>" +e "<$(( +64#10 >> -64#11 ))>" +e "<$(( +0x11 >> +0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( +64#11 >> -64#10 ))>" +e "<$(( +64 >> +1024 ))>" +e "<$((0x8000000000000000>>-1))>" +e "<$((0x8000000000000000>>1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>1))>" +e "<$((0x8000000000000000>>-11))>" +e "<$((0x8000000000000000>>11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" +e '= BIN **' +e "<$((0**1))>" +e "<$((2**1))>" +e "<$((2**2))>" +e "<$((2**3))>" +e "<$((2**4))>" +e "<$((10**4))>" +e "<$((10**10))>" +e "<$((10**5+5))>" +e "<$((10**(5+5)))>" +e '= LOG OR' +e "<$((0||0))>" +e "<$(( 000 || 0X0 ))>" +e "<$((01 || 64#1))>" +e "<$((01 || 64#1))>" +e "<$((0x1234 || 4660))>" +e "<$((0x1234 || 011064))>" +s I=33 J=33;e "<$((I||J))>" +s I=33 J=33;e "<$(( I || J ))>" +e "<$((0||1))>" +e "<$((0||0000000000000000000000001))>" +e "<$((1||2))>" +e "<$((0x1234 || 04660))>" +e "<$((0x1234 || 0x11064))>" +s I=10 J=33;e "<$((I||J))>" +s I=-10 J=-33;e "<$((I||J))>" +s I=-33 J=-33;e "<$((I||J))>" +s I=0 J=-33;e "<$((I||J))>" +s I=33 J=0;e "<$((I||J))>" +e '= LOG AND' +e "<$((0&&0))>" +e "<$(( 000 && 0X0 ))>" +e "<$((01 && 64#1))>" +e "<$((01 && 64#1))>" +e "<$((0x1234 && 4660))>" +e "<$((0x1234 && 011064))>" +s I=33 J=33;e "<$((I&&J))>" +s I=33 J=33;e "<$(( I && J ))>" +e "<$((0&&1))>" +e "<$((0&&0000000000000000000000001))>" +e "<$((1&&2))>" +e "<$((0x1234 && 04660))>" +e "<$((0x1234 && 0x11064))>" +s I=10 J=33;e "<$((I&&J))>" +s I=-10 J=-33;e "<$((I&&J))>" +s I=-33 J=-33;e "<$((I&&J))>" +s I=0 J=-33;e "<$((I&&J))>" +s I=33 J=0;e "<$((I&&J))>" +e '= BIN BIT_OR' +e "<$((0|0))>" +e "<$(( 0 | 0 ))>" +e "<$((0|1))>" +e "<$(( 0 | 1 ))>" +e "<$((1|0))>" +e "<$(( 1 | 0 ))>" +e "<$((1|1))>" +e "<$(( 1 | 1 ))>" +e "<$((1111|2222))>" +e "<$((2222|1111))>" +e "<$(( +0x10 | +0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( +64#10 | -64#11 ))>" +e "<$(( +0x11 | +0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( +64#11 | -64#10 ))>" +e "<$(( +64 | +1024 ))>" +e "<$((0x8000000000000000|-1))>" +e "<$((0x8000000000000000|1))>" +e "<$((0x7FFFFFFFFFFFFFFF|-1))>" +e "<$((0x7FFFFFFFFFFFFFFF|1))>" +e "<$((0xFFFFFFFFFFFFFFFF|-1))>" +e "<$((0xFFFFFFFFFFFFFFFF|1))>" +e "<$((0x8000000000000000|-11))>" +e "<$((0x8000000000000000|11))>" +e "<$((0x7FFFFFFFFFFFFFFF|-11))>" +e "<$((0x7FFFFFFFFFFFFFFF|11))>" +e "<$((0xFFFFFFFFFFFFFFFF|-11))>" +e "<$((0xFFFFFFFFFFFFFFFF|11))>" +e '= BIN BIT_XOR' +e "<$((0^0))>" +e "<$(( 0 ^ 0 ))>" +e "<$((0^1))>" +e "<$(( 0 ^ 1 ))>" +e "<$((1^0))>" +e "<$(( 1 ^ 0 ))>" +e "<$((1^1))>" +e "<$(( 1 ^ 1 ))>" +e "<$((1111^2222))>" +e "<$((2222^1111))>" +e "<$(( +0x10 ^ +0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( +64#10 ^ -64#11 ))>" +e "<$(( +0x11 ^ +0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( +64#11 ^ -64#10 ))>" +e "<$(( +64 ^ +1024 ))>" +e "<$((0x8000000000000000^-1))>" +e "<$((0x8000000000000000^1))>" +e "<$((0x7FFFFFFFFFFFFFFF^-1))>" +e "<$((0x7FFFFFFFFFFFFFFF^1))>" +e "<$((0xFFFFFFFFFFFFFFFF^-1))>" +e "<$((0xFFFFFFFFFFFFFFFF^1))>" +e "<$((0x8000000000000000^-11))>" +e "<$((0x8000000000000000^11))>" +e "<$((0x7FFFFFFFFFFFFFFF^-11))>" +e "<$((0x7FFFFFFFFFFFFFFF^11))>" +e "<$((0xFFFFFFFFFFFFFFFF^-11))>" +e "<$((0xFFFFFFFFFFFFFFFF^11))>" +e '= BIN BIT_AND' +e "<$((0&0))>" +e "<$(( 0 & 0 ))>" +e "<$((0&1))>" +e "<$(( 0 & 1 ))>" +e "<$((1&0))>" +e "<$(( 1 & 0 ))>" +e "<$((1&1))>" +e "<$(( 1 & 1 ))>" +e "<$((1111&2222))>" +e "<$((2222&1111))>" +e "<$(( +0x10 & +0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( +64#10 & -64#11 ))>" +e "<$(( +0x11 & +0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( +64#11 & -64#10 ))>" +e "<$(( +64 & +1024 ))>" +e "<$((0x8000000000000000&-1))>" +e "<$((0x8000000000000000&1))>" +e "<$((0x7FFFFFFFFFFFFFFF&-1))>" +e "<$((0x7FFFFFFFFFFFFFFF&1))>" +e "<$((0xFFFFFFFFFFFFFFFF&-1))>" +e "<$((0xFFFFFFFFFFFFFFFF&1))>" +e "<$((0x8000000000000000&-11))>" +e "<$((0x8000000000000000&11))>" +e "<$((0x7FFFFFFFFFFFFFFF&-11))>" +e "<$((0x7FFFFFFFFFFFFFFF&11))>" +e "<$((0xFFFFFFFFFFFFFFFF&-11))>" +e "<$((0xFFFFFFFFFFFFFFFF&11))>" +e '= BIN EQ' +e "<$((0==0))>" +e "<$(( 000 == 0X0 ))>" +e "<$((01 == 64#1))>" +e "<$((01 == 64#1))>" +e "<$((0x1234 == 4660))>" +e "<$((0x1234 == 011064))>" +s I=33 J=33;e "<$((I==J))>" +s I=33 J=33;e "<$(( I == J ))>" +e "<$((0==1))>" +e "<$((0==0000000000000000000000001))>" +e "<$((1==2))>" +e "<$((0x1234 == 04660))>" +e "<$((0x1234 == 0x11064))>" +s I=10 J=33;e "<$((I==J))>" +s I=-10 J=-33;e "<$((I==J))>" +s I=-33 J=-33;e "<$((I==J))>" +e '= BIN NE' +e "<$((0!=0))>" +e "<$(( 000 != 0X0 ))>" +e "<$((01 != 64#1))>" +e "<$((01 != 64#1))>" +e "<$((0x1234 != 4660))>" +e "<$((0x1234 != 011064))>" +s I=33 J=33;e "<$((I!=J))>" +s I=33 J=33;e "<$(( I != J ))>" +e "<$((0!=1))>" +e "<$((0!=0000000000000000000000001))>" +e "<$((1!=2))>" +e "<$((0x1234 != 04660))>" +e "<$((0x1234 != 0x11064))>" +s I=10 J=33;e "<$((I!=J))>" +s I=-10 J=-33;e "<$((I!=J))>" +s I=-33 J=-33;e "<$((I!=J))>" +e '= BIN LE' +e "<$((0<=0))>" +e "<$(( 000 <= 0X0 ))>" +e "<$((01 <= 64#1))>" +e "<$((01 <= 64#2))>" +e "<$((02 <= 64#1))>" +e "<$((0x1234 <= 4660))>" +e "<$((0x1234 <= 011064))>" +e "<$((0x1233 <= 011064))>" +e "<$((0x1235 <= 011064))>" +s I=33 J=33;e "<$((I<=J))>" +s I=33 J=33;e "<$((I<=J))>" +s I=32 J=33;e "<$((I<=J))>" +s I=34 J=33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-32 J=-33;e "<$((I<=J))>" +s I=-34 J=-33;e "<$((I<=J))>" +e '= BIN GE' +e "<$((0>=0))>" +e "<$(( 000 >= 0X0 ))>" +e "<$((01 >= 64#1))>" +e "<$((01 >= 64#2))>" +e "<$((02 >= 64#1))>" +e "<$((0x1234 >= 4660))>" +e "<$((0x1234 >= 011064))>" +e "<$((0x1233 >= 011064))>" +e "<$((0x1235 >= 011064))>" +s I=33 J=33;e "<$((I>=J))>" +s I=33 J=33;e "<$((I>=J))>" +s I=32 J=33;e "<$((I>=J))>" +s I=34 J=33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-32 J=-33;e "<$((I>=J))>" +s I=-34 J=-33;e "<$((I>=J))>" +e '= BIN LT' +e "<$((0<0))>" +e "<$(( 000 < 0X0 ))>" +e "<$((01 < 64#1))>" +e "<$((01 < 64#2))>" +e "<$((02 < 64#1))>" +e "<$((0x1234 < 4660))>" +e "<$((0x1234 < 011064))>" +e "<$((0x1233 < 011064))>" +e "<$((0x1235 < 011064))>" +s I=33 J=33;e "<$((I" +s I=33 J=33;e "<$((I" +s I=32 J=33;e "<$((I" +s I=34 J=33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-32 J=-33;e "<$((I" +s I=-34 J=-33;e "<$((I" +e '= BIN GT' +e "<$((0>0))>" +e "<$(( 000 > 0X0 ))>" +e "<$((01 > 64#1))>" +e "<$((01 > 64#2))>" +e "<$((02 > 64#1))>" +e "<$((0x1234 > 4660))>" +e "<$((0x1234 > 011064))>" +e "<$((0x1233 > 011064))>" +e "<$((0x1235 > 011064))>" +s I=33 J=33;e "<$((I>J))>" +s I=33 J=33;e "<$((I>J))>" +s I=32 J=33;e "<$((I>J))>" +s I=34 J=33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-32 J=-33;e "<$((I>J))>" +s I=-34 J=-33;e "<$((I>J))>" +# +# COMMA below +e '= PRECEDENCE I' +e "<$(( 1 + 2 + 3 ))>" +e "<$(( 1 - 2 + 3 ))>" +e "<$(( 3 - 2 - 1 ))>" +e "<$(( 3 - 2 + 1 ))>" +e "<$(( - 2 + 1 ))>" +e "<$(( 2 + -1 ))>" +e "<$(( ! 2 + 1 ))>" +e "<$(( 2 + !1 ))>" +e "<$(( 3 * 2 + 2 ))>" +e "<$(( 3 + 2 * 2 ))>" +e "<$(( 3 * 2 * 2 ))>" +e "<$(( 9 / 3 + 2 ))>" +e "<$(( 9 + 3 / 2 ))>" +e "<$(( 9 / 3 / 2 ))>" +e "<$(( 9 << 1 + 2 ))>" +e "<$(( 9 + 3 << 2 ))>" +e "<$(( 9 << 3 << 2 ))>" +e "<$(( 9 >> 1 + 2 ))>" +e "<$(( 9 + 3 >> 2 ))>" +e "<$(( 19 >> 3 >> 1 ))>" +e "<$(( 19 >> 3 << 1 ))>" +e "<$(( 19 << 3 >> 1 ))>" +e "<$(( 2 + 3 < 3 * 2 ))>" +e "<$(( 2 << 3 >= 3 << 2 ))>" +e "<$(( 0xfD & 0xF == 0xF ))>" +e "<$((0xfD&0xF==0xF))>" +e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" +e "<$((3*7,2<<8,9-7))>" +e '= PARENS' +e "<$(((1 + 2) + 3))>" +e "<$(((1+2)+3))>" +e "<$((1 - (2 + 3)))>" +e "<$((1-(2+3)))>" +e "<$((3 - (2 - 1)))>" +e "<$((3-(2-1)))>" +e "<$((3 - ( 2 + 1 )))>" +e "<$((3-(2+1)))>" +e "<$((- (2 + 1)))>" +e "<$((-(2+1)))>" +e "<$((! (2 + 1)))>" +e "<$((!(2+1)))>" +e "<$((3 * (2 + 2)))>" +e "<$((3*(2+2)))>" +e "<$(((3 + 2) * 2))>" +e "<$(((3+2)*2))>" +e "<$((3 * (2 * 2)))>" +e "<$((3*(2*8)))>" +e "<$((9 / (3 + 2)))>" +e "<$((9/(3+2)))>" +e "<$((( 9 + 3 ) / 2))>" +e "<$(((9+3)/2))>" +e "<$((9 / ( 3 / 2 )))>" +e "<$((9/(3/2)))>" +e "<$((( 9 << 1 ) + 2))>" +e "<$(((9<<1)+2))>" +e "<$((9 + (3 << 2)))>" +e "<$((9+(3<<2)))>" +e "<$((9 << (3 << 2)))>" +e "<$((9<<(3<<2)))>" +e "<$(((9 >> 1) + 2))>" +e "<$(((9>>1)+2))>" +e "<$((9 + (3 >> 2)))>" +e "<$((9+(3>>2)))>" +e "<$((19 >> (3 >> 1)))>" +e "<$((19>>(3>>1)))>" +e "<$((19 >> (3 << 1)))>" +e "<$((19>>(3<<1)))>" +e "<$((19 << (3 >> 1)))>" +e "<$((19<<(3>>1)))>" +e "<$((2 + (3 < 3) * 2))>" +e "<$((2+(3<3)*2))>" +e "<$((2 << ((3 >= 3) << 2)))>" +e "<$((2<<((3>=3)<<2)))>" +e "<$(((0xfD & 0xF) == 0xF))>" +e "<$(((0xfD&0xF)==0xF))>" +e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" +e "<$((3*(7,2)<<(8,9-7)))>" +# +# COND BELOW +e '= ASSIGN I' +unset I;p "<$(( I = 3 ))>";e "<$I>" +unset I;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I+=1))>";e "<$I>" +s I=10;p "<$((I-=1))>";e "<$I>" +s I=10;p "<$((I*=1))>";e "<$I>" +s I=10;p "<$((I*=2))>";e "<$I>" +s I=10;p "<$((I/=1))>";e "<$I>" +s I=10;p "<$((I/=2))>";e "<$I>" +s I=10;p "<$((I%=1))>";e "<$I>" +s I=10;p "<$((I%=2))>";e "<$I>" +s I=10;p "<$((I**=1))>";e "<$I>" +s I=10;p "<$((I**=2))>";e "<$I>" +s I=10;p "<$((I**=1+1))>";e "<$I>" +s I=10;p "<$((I|=1))>";e "<$I>" +s I=10;p "<$((I^=1))>";e "<$I>";p "<$((I^=1))>";e "<$I>" +s I=10;p "<$((I&=2))>";e "<$I>" +s I=10;p "<$((I>>=1))>";e "<$I>" +s I=10;p "<$((I<<=1))>";e "<$I>" +s I=-1;p "<$((I>>>=1))>";e "<$I>" +e '= ASSIGN II' +s I=2;p "<$(((I+=1)-1))>";e "<$I>" +s I=4;p "<$(((I-=1)+1))>";e "<$I>" +s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" +s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" +s I=10;p "<$((I=2,I|=1))>";e "<$I>" +s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" +e '= POSTFIX' +s I=1;p "<$((I++))>";e "<$I>" +s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" +s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" +s I=1;p "<$((I--))>";e "<$I>" +s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" +s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" +e '= PREFIX' +s I=1;p "<$((++I))>";e "<$I>" +s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" +s I=1;p "<$((--I))>";e "<$I>" +s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" +s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" +s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" +e '= VAR RECUR' +s I='1 + 1';p "<$((I))>";e "<$I>" +s I='1 + 1';p "<$((+I))>";e "<$I>" +s I='1 + 1';p "<$((++I))>";e "<$I>" +s I='1 + 1';p "<$((I++))>";e "<$I>" +s I='1 + 1';p "<$((1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" +s I='1 + 1';p "<$((I=I))>";e "<$I>" +s I='1 + 1';p "<$((I=+I))>";e "<$I>" +s I='1 + 1';p "<$((I=1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" +e '= COMMA' +e "<$(( 1 , 2 ))>" +e "<$(( 1 , 2 , 3 ))>" +e "<$(( 1 , 2 , 3 , 4 ))>" +e "<$((1,2,3,4))>" +s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" +s I1=I2=10 I2=3;p "<$((I1,I2))>";e "<$I1><$I2>" +e '= COND' +e "<$(( +0 ? 2 : 3 ))>" +e "<$((-0?2:3))>" +e "<$(( +1 ? 2 : 3 ))>" +e "<$(( 1-1 ? 2 : 3 ))>" +e "<$(( 1-0 ? 2 : 3 ))>" +e "<$((-1?2:3))>" +e "<$(( 0x1234 ? 111 : 222 ))>" +e "<$((1**2 ? 5 : 7))>" +e "<$((0**2 ? 5 : 7))>" +e "<$((0**2>=0?5:7))>" +e "<$((-1<=0**2?5:7))>" +e "<$((1<=0**2?5:7))>" +e "<$((1>2||1*0?5:7))>" +e "<$((1>2&&1*0?5:7))>" +e "<$((1<2&&1*0?5:7))>" +e "<$((1<2&&1*0+1?5:7))>" +e '-- COND .2' +e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" +e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" +e "<$((2<1?-1:2>1?1:0))>" +e "<$((4<5 ? 1 : 32))>" +e "<$((4>5 ? 1 : 32))>" +e "<$((4>(2+3) ? 1 : 32))>" +e "<$((4<(2+3) ? 1 : 32))>" +e "<$(((2+2)<(2+3) ? 1 : 32))>" +e "<$(((2+2)>(2+3) ? 1 : 32))>" +## grouping protects precedence in : parts (syntax error tests below) +e '-- COND .3' +e "<$((1-1 < 1 ? 2,4 : 1,3))>" +e "<$((0<1?2,4:(1,3)))>" +e "<$((0,1,2,0?2,4:1,3))>" +e "<$((0,1,2,1?2,4:1,3))>" +e "<$((0,1,2,0?2,4:(1,3)))>" +e "<$((0,1,2,1?2,4:(1,3)))>" +e "<$((0,1,2,0?(2,4):1,3))>" +e "<$((0,1,2,1?(2,4):1,3))>" +e "<$((0,1,2,0?(2,4):(1,3)))>" +e "<$((0,1,2,1?(2,4):(1,3)))>" +e "<$((0?2:((0,3)?1:4)))>" +e "<$((1?2:3,0?1:4))>" +e "<$((1?2:3,0?1:4?5:6))>" +e "<$((1?2:(3,0)?1:4?5:6))>" +e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" +e '-- COND .4' +e "<$((1?2?3?4?5:6:7:8:9))>" +e "<$((1?2?3?0?5:6:7:8:9))>" +e "<$((1?2?0?0?5:6:7:8:9))>" +e "<$((1?0?0?0?5:6:7:8:9))>" +e "<$((0?0?0?0?5:6:7:8:9))>" +e "<$((0?3+4?10:11:5+6?12:13))>" +e "<$((1?3+4?10:11:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" +e '-- COND .5' +e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e '-- COND .6' +e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" +e '-- COND .7' +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$((((I1";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1";\ + e "<$I1><$I2><$I3><$I4><$I5>" +# only first +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(((I1I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +# last not etc. +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3";\ + e "<$I1><$I2><$I3><$I4><$I5>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1>I2)?(I2";\ + e "<$I1><$I2><$I3><$I4><$I5>" +e '-- COND .8' +s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" +s I=0;p "<$((1?20:(I+=2)))>";e "<$I>" +s I=0;p "<$((1?I+=10:(I+=2)))>";e "<$I>" +s I=0;p "<$((0?I+=2:20))>";e "<$I>" +s I=0;p "<$((0?I+=2:(I+=10)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(20)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(I+=20)))>";e "<$I>" +e '-- COND .9' +s I1=+E+ I2=1+1;p "<$((0?I1:I2))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1:I2))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +e '-- COND .10' +s I1=+E+ I2=+E+ I3=+E+ I4=-1;p "<$((0?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=1 I2=2 I3=+E+ I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=0 I2=+E+ I3=3 I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +e '= WILD I' +e "<$(( 3 + ( 11 ) ))>" +e "<$((1 + (2 - 2)))>" +e "<$((1 + (2 - 2)))>" +e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" +e "<$(( 3+((2 * 2))/6 ))>" +e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" +e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" +s I1=I2=10 I2=3;p "<$((I1 + I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 * I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 % I2))>";e "<$I1><$I2>" +e '= WILD II' +s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" +s I=10;p "<$((3+(3*(I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" +e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" +s I=10;p "<$(( +10 + + +I ))>";e "<$I>" +s I=10;p "<$(( +10 + ++I ))>";e "<$I>" +s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" +s I=10;p "<$(( +10 +++ I ))>";e "<$I>" +s I=10;p "<$(( +10+++I ))>";e "<$I>" +s I=10;p "<$((+10++I))>";e "<$I>" +s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" +e "<$(( +10 + + + ++++ +11 ))>" +e "<$(( +10 + + + ++++ ++11 ))>" +e "<$((+10++++++++11))>" +e '= WILD RECUR' # (some yet) +s I1=I2=10 I2=5;p "<$((I1+=I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=10)+1' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=(I2=10)+1)' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=+E+ I2=5;p "<$((I1=10))>";e "<$I1><$I2>" +s I1=I2=+E+ I2=5;p "<$((0?I1:++I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5;p "<$((I2,(1?I1:++I2)))>";e "<$I1><$I2>" +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From shawnlandden at tutanota.com Sat Aug 27 04:25:24 2022 From: shawnlandden at tutanota.com (shawnlandden at tutanota.com) Date: Sat, 27 Aug 2022 06:25:24 +0200 (CEST) Subject: Subject: [PATCH] ash: make 'sleep' a builtin Message-ID: This reduces the number of processes spawned when in an indefinate do-nothing loop in a shell script. Signed-off-by: Shawn Landden --- shell/ash.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/shell/ash.c b/shell/ash.c index 55c1034f5..eba7759e7 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -134,6 +134,11 @@ //config: default y //config: depends on SHELL_ASH //config: +//config:config ASH_SLEEP +//config: bool "sleep builtin" +//config: default y +//config: depends on SHELL_ASH +//config: //config:config ASH_HELP //config: bool "help builtin" //config: default y @@ -10155,6 +10160,9 @@ static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, #if ENABLE_ASH_TEST || BASH_TEST2 static int FAST_FUNC testcmd(int argc, char **argv)?? { return test_main(argc, argv); } #endif +#if ENABLE_ASH_SLEEP +static int FAST_FUNC sleepcmd(int argc, char **argv)?? { return sleep_main(argc, argv); } +#endif /* Keep these in proper order since it is searched via bsearch() */ static const struct builtincmd builtintab[] = { @@ -10217,6 +10225,9 @@ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG????? "return"? , returncmd? }, { BUILTIN_SPEC_REG????? "set"???? , setcmd???? }, { BUILTIN_SPEC_REG????? "shift"?? , shiftcmd?? }, +#if ENABLE_ASH_SLEEP + { BUILTIN_REGULAR?????? "sleep"?? , sleepcmd??? }, +#endif #if BASH_SOURCE { BUILTIN_SPEC_REG????? "source"? , dotcmd???? }, #endif -- 2.36.1 -------------- next part -------------- An HTML attachment was scrubbed... URL: From shawnlandden at tutanota.com Sat Aug 27 04:19:30 2022 From: shawnlandden at tutanota.com (shawnlandden at tutanota.com) Date: Sat, 27 Aug 2022 06:19:30 +0200 (CEST) Subject: Subject: [PATCH] ash: make 'sleep' a builtin Message-ID: This reduces the number of processes spawned when in an indefinate do-nothing loop in a shell script. Signed-off-by: Shawn Landden --- shell/ash.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/shell/ash.c b/shell/ash.c index 55c1034f5..eba7759e7 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -134,6 +134,11 @@ //config: default y //config: depends on SHELL_ASH //config: +//config:config ASH_SLEEP +//config: bool "sleep builtin" +//config: default y +//config: depends on SHELL_ASH +//config: //config:config ASH_HELP //config: bool "help builtin" //config: default y @@ -10155,6 +10160,9 @@ static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, #if ENABLE_ASH_TEST || BASH_TEST2 static int FAST_FUNC testcmd(int argc, char **argv)?? { return test_main(argc, argv); } #endif +#if ENABLE_ASH_SLEEP +static int FAST_FUNC sleepcmd(int argc, char **argv)?? { return sleep_main(argc, argv); } +#endif /* Keep these in proper order since it is searched via bsearch() */ static const struct builtincmd builtintab[] = { @@ -10217,6 +10225,9 @@ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG????? "return"? , returncmd? }, { BUILTIN_SPEC_REG????? "set"???? , setcmd???? }, { BUILTIN_SPEC_REG????? "shift"?? , shiftcmd?? }, +#if ENABLE_ASH_SLEEP + { BUILTIN_REGULAR?????? "sleep"?? , sleepcmd??? }, +#endif #if BASH_SOURCE { BUILTIN_SPEC_REG????? "source"? , dotcmd???? }, #endif -- 2.36.1 -------------- next part -------------- An HTML attachment was scrubbed... URL: From dario.binacchi at amarulasolutions.com Sat Aug 27 16:06:26 2022 From: dario.binacchi at amarulasolutions.com (Dario Binacchi) Date: Sat, 27 Aug 2022 18:06:26 +0200 Subject: [RFC PATCH 1/2] fbset: abort on not handled options Message-ID: <20220827160627.682125-1-dario.binacchi@amarulasolutions.com> Not all options are actually implemented. In this case, return a message and an error code to make it clear that the requested command has not been executed. Signed-off-by: Dario Binacchi --- util-linux/fbset.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util-linux/fbset.c b/util-linux/fbset.c index 41cc29f37b78..0eaa7c0a6427 100644 --- a/util-linux/fbset.c +++ b/util-linux/fbset.c @@ -519,6 +519,9 @@ int fbset_main(int argc, char **argv) var_set.bits_per_pixel = xatou32(argv[1]); break; #endif + default: + bb_perror_msg_and_die("option '%s' not handled", + g_cmdoptions[i].name); } switch (g_cmdoptions[i].code) { case CMD_FB: -- 2.32.0 From dario.binacchi at amarulasolutions.com Sat Aug 27 16:06:27 2022 From: dario.binacchi at amarulasolutions.com (Dario Binacchi) Date: Sat, 27 Aug 2022 18:06:27 +0200 Subject: [RFC PATCH 2/2] fbset: support setting pixel clock rate In-Reply-To: <20220827160627.682125-1-dario.binacchi@amarulasolutions.com> References: <20220827160627.682125-1-dario.binacchi@amarulasolutions.com> Message-ID: <20220827160627.682125-2-dario.binacchi@amarulasolutions.com> Only in case the FEATURE_FBSET_FANCY configuration is enabled. Signed-off-by: Dario Binacchi --- util-linux/fbset.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/util-linux/fbset.c b/util-linux/fbset.c index 0eaa7c0a6427..768ab80eb82d 100644 --- a/util-linux/fbset.c +++ b/util-linux/fbset.c @@ -518,6 +518,9 @@ int fbset_main(int argc, char **argv) case CMD_DEPTH: var_set.bits_per_pixel = xatou32(argv[1]); break; + case CMD_PIXCLOCK: + var_set.pixclock = xatou32(argv[1]); + break; #endif default: bb_perror_msg_and_die("option '%s' not handled", -- 2.32.0 From vda.linux at googlemail.com Sat Aug 27 17:36:42 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Sat, 27 Aug 2022 19:36:42 +0200 Subject: Subject: [PATCH] ash: make 'sleep' a builtin In-Reply-To: References: Message-ID: You forgot this: //kbuild:lib-$(CONFIG_SLEEP) += sleep.o +//kbuild:lib-$(CONFIG_ASH_SLEEP) += sleep.o /* BB_AUDIT SUSv3 compliant */ /* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */ diff --git a/include/libbb.h b/include/libbb.h index abbc9ac59..129858e27 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1511,6 +1511,7 @@ int hush_main(int argc, char** argv) IF_SHELL_HUSH(MAIN_EXTERNALLY_VISIBLE); /* If shell needs them, they exist even if not enabled as applets */ int echo_main(int argc, char** argv) IF_ECHO(MAIN_EXTERNALLY_VISIBLE); int printf_main(int argc, char **argv) IF_PRINTF(MAIN_EXTERNALLY_VISIBLE); +int sleep_main(int argc, char **argv) IF_SLEEP(MAIN_EXTERNALLY_VISIBLE); int test_main(int argc, char **argv) Also, your patch doesn't apply, whitespace damaged by mail agent. Please resend as attachment. On Sat, Aug 27, 2022 at 6:25 AM wrote: > > This reduces the number of processes spawned when in an indefinate > do-nothing loop in a shell script. > > Signed-off-by: Shawn Landden > --- > shell/ash.c | 11 +++++++++++ > 1 file changed, 11 insertions(+) > > diff --git a/shell/ash.c b/shell/ash.c > index 55c1034f5..eba7759e7 100644 > --- a/shell/ash.c > +++ b/shell/ash.c > @@ -134,6 +134,11 @@ > //config: default y > //config: depends on SHELL_ASH > //config: > +//config:config ASH_SLEEP > +//config: bool "sleep builtin" > +//config: default y > +//config: depends on SHELL_ASH > +//config: > //config:config ASH_HELP > //config: bool "help builtin" > //config: default y > @@ -10155,6 +10160,9 @@ static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, > #if ENABLE_ASH_TEST || BASH_TEST2 > static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } > #endif > +#if ENABLE_ASH_SLEEP > +static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); } > +#endif > > /* Keep these in proper order since it is searched via bsearch() */ > static const struct builtincmd builtintab[] = { > @@ -10217,6 +10225,9 @@ static const struct builtincmd builtintab[] = { > { BUILTIN_SPEC_REG "return" , returncmd }, > { BUILTIN_SPEC_REG "set" , setcmd }, > { BUILTIN_SPEC_REG "shift" , shiftcmd }, > +#if ENABLE_ASH_SLEEP > + { BUILTIN_REGULAR "sleep" , sleepcmd }, > +#endif > #if BASH_SOURCE > { BUILTIN_SPEC_REG "source" , dotcmd }, > #endif > -- > 2.36.1 > > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From shawnlandden at tutanota.com Tue Aug 30 01:25:21 2022 From: shawnlandden at tutanota.com (shawnlandden at tutanota.com) Date: Tue, 30 Aug 2022 03:25:21 +0200 (CEST) Subject: Subject: [PATCH] ash: make 'sleep' a builtin In-Reply-To: References: Message-ID: 27 ago 2022, 10:36 por vda.linux at googlemail.com: > You forgot this: > > //kbuild:lib-$(CONFIG_SLEEP) += sleep.o > +//kbuild:lib-$(CONFIG_ASH_SLEEP) += sleep.o > fixed > > /* BB_AUDIT SUSv3 compliant */ > /* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */ > diff --git a/include/libbb.h b/include/libbb.h > index abbc9ac59..129858e27 100644 > --- a/include/libbb.h > +++ b/include/libbb.h > @@ -1511,6 +1511,7 @@ int hush_main(int argc, char** argv) > IF_SHELL_HUSH(MAIN_EXTERNALLY_VISIBLE); > /* If shell needs them, they exist even if not enabled as applets */ > int echo_main(int argc, char** argv) IF_ECHO(MAIN_EXTERNALLY_VISIBLE); > int printf_main(int argc, char **argv) IF_PRINTF(MAIN_EXTERNALLY_VISIBLE); > +int sleep_main(int argc, char **argv) IF_SLEEP(MAIN_EXTERNALLY_VISIBLE); > int test_main(int argc, char **argv) > > > Also, your patch doesn't apply, whitespace damaged by mail agent. > Please resend as attachment. > Hopefully this works. > > On Sat, Aug 27, 2022 at 6:25 AM wrote: > >> >> This reduces the number of processes spawned when in an indefinate >> do-nothing loop in a shell script. >> >> Signed-off-by: Shawn Landden >> --- >> shell/ash.c | 11 +++++++++++ >> 1 file changed, 11 insertions(+) >> >> diff --git a/shell/ash.c b/shell/ash.c >> index 55c1034f5..eba7759e7 100644 >> --- a/shell/ash.c >> +++ b/shell/ash.c >> @@ -134,6 +134,11 @@ >> //config: default y >> //config: depends on SHELL_ASH >> //config: >> +//config:config ASH_SLEEP >> +//config: bool "sleep builtin" >> +//config: default y >> +//config: depends on SHELL_ASH >> +//config: >> //config:config ASH_HELP >> //config: bool "help builtin" >> //config: default y >> @@ -10155,6 +10160,9 @@ static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, >> #if ENABLE_ASH_TEST || BASH_TEST2 >> static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } >> #endif >> +#if ENABLE_ASH_SLEEP >> +static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); } >> +#endif >> >> /* Keep these in proper order since it is searched via bsearch() */ >> static const struct builtincmd builtintab[] = { >> @@ -10217,6 +10225,9 @@ static const struct builtincmd builtintab[] = { >> { BUILTIN_SPEC_REG "return" , returncmd }, >> { BUILTIN_SPEC_REG "set" , setcmd }, >> { BUILTIN_SPEC_REG "shift" , shiftcmd }, >> +#if ENABLE_ASH_SLEEP >> + { BUILTIN_REGULAR "sleep" , sleepcmd }, >> +#endif >> #if BASH_SOURCE >> { BUILTIN_SPEC_REG "source" , dotcmd }, >> #endif >> -- >> 2.36.1 >> >> _______________________________________________ >> busybox mailing list >> busybox at busybox.net >> http://lists.busybox.net/mailman/listinfo/busybox >> -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: 0001-ash-make-sleep-a-builtin.patch Type: text/x-patch Size: 2150 bytes Desc: not available URL: From David.Laight at ACULAB.COM Tue Aug 30 08:10:54 2022 From: David.Laight at ACULAB.COM (David Laight) Date: Tue, 30 Aug 2022 08:10:54 +0000 Subject: Subject: [PATCH] ash: make 'sleep' a builtin In-Reply-To: References: Message-ID: <698eac53516b4f28907e33eb6bb6b8c8@AcuMS.aculab.com> One question is why? Shell builtins (mostly) exist to speed things up. But sleep doesn?t need speeding up. David From: busybox On Behalf Of shawnlandden at tutanota.com Sent: 30 August 2022 02:25 To: Denys Vlasenko Cc: Busybox Subject: Re: Subject: [PATCH] ash: make 'sleep' a builtin 27 ago 2022, 10:36 por vda.linux at googlemail.com: You forgot this: //kbuild:lib-$(CONFIG_SLEEP) += sleep.o +//kbuild:lib-$(CONFIG_ASH_SLEEP) += sleep.o fixed /* BB_AUDIT SUSv3 compliant */ /* BB_AUDIT GNU issues -- fancy version matches except args must be ints. */ diff --git a/include/libbb.h b/include/libbb.h index abbc9ac59..129858e27 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -1511,6 +1511,7 @@ int hush_main(int argc, char** argv) IF_SHELL_HUSH(MAIN_EXTERNALLY_VISIBLE); /* If shell needs them, they exist even if not enabled as applets */ int echo_main(int argc, char** argv) IF_ECHO(MAIN_EXTERNALLY_VISIBLE); int printf_main(int argc, char **argv) IF_PRINTF(MAIN_EXTERNALLY_VISIBLE); +int sleep_main(int argc, char **argv) IF_SLEEP(MAIN_EXTERNALLY_VISIBLE); int test_main(int argc, char **argv) Also, your patch doesn't apply, whitespace damaged by mail agent. Please resend as attachment. Hopefully this works. On Sat, Aug 27, 2022 at 6:25 AM > wrote: This reduces the number of processes spawned when in an indefinate do-nothing loop in a shell script. Signed-off-by: Shawn Landden > --- shell/ash.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/shell/ash.c b/shell/ash.c index 55c1034f5..eba7759e7 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -134,6 +134,11 @@ //config: default y //config: depends on SHELL_ASH //config: +//config:config ASH_SLEEP +//config: bool "sleep builtin" +//config: default y +//config: depends on SHELL_ASH +//config: //config:config ASH_HELP //config: bool "help builtin" //config: default y @@ -10155,6 +10160,9 @@ static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, #if ENABLE_ASH_TEST || BASH_TEST2 static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } #endif +#if ENABLE_ASH_SLEEP +static int FAST_FUNC sleepcmd(int argc, char **argv) { return sleep_main(argc, argv); } +#endif /* Keep these in proper order since it is searched via bsearch() */ static const struct builtincmd builtintab[] = { @@ -10217,6 +10225,9 @@ static const struct builtincmd builtintab[] = { { BUILTIN_SPEC_REG "return" , returncmd }, { BUILTIN_SPEC_REG "set" , setcmd }, { BUILTIN_SPEC_REG "shift" , shiftcmd }, +#if ENABLE_ASH_SLEEP + { BUILTIN_REGULAR "sleep" , sleepcmd }, +#endif #if BASH_SOURCE { BUILTIN_SPEC_REG "source" , dotcmd }, #endif -- 2.36.1 _______________________________________________ busybox mailing list busybox at busybox.net http://lists.busybox.net/mailman/listinfo/busybox - Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK Registration No: 1397386 (Wales) -------------- next part -------------- An HTML attachment was scrubbed... URL: From vda.linux at googlemail.com Tue Aug 30 15:13:17 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 30 Aug 2022 17:13:17 +0200 Subject: [PATCH] libbb: make '--help' handling more consistent In-Reply-To: <001e1930-11fd-0ab9-c72a-a73b02496357@gigawatt.nl> References: <626e48b1.tK1V0rIBgUJEJdo0%rmy@pobox.com> <001e1930-11fd-0ab9-c72a-a73b02496357@gigawatt.nl> Message-ID: Fixed in git On Thu, Aug 25, 2022 at 7:26 AM Harald van Dijk wrote: > > This breaks the [ command. Previously, [ --help would show help, which > is okay as an extension since it is not valid with the standard [ > command. This change makes [ --help ] also show help, when it is > required to just check that the string --help is not empty. > > On 22/08/2022 13:07, Denys Vlasenko wrote: > > Applied, thank you > > > > On Sun, May 1, 2022 at 10:46 AM Ron Yorston wrote: > >> > >> Running an applet with '--help' as its only argument is treated > >> as a special case. If additional arguments follow '--help' the > >> behaviour is inconsistent: > >> > >> - applets which call single_argv() print help and do nothing else; > >> > >> - applets which call getopt() report "unrecognized option '--help'" > >> and print help anyway; > >> > >> - expr says "expr: syntax error" and doesn't print help; > >> > >> - printenv silently ignores '--help', prints any other variables > >> and doesn't print help; > >> > >> - realpath says "--help: No such file or directory", prints the path > >> of any other files and doesn't print help. > >> > >> If the first argument is '--help' ignore any other arguments and print > >> help. This is more consistent and most likely what the user wanted. > >> > >> See also commit 6bdfbc4cb (libbb: fix '--help' handling in > >> FEATURE_SH_NOFORK=y). > >> > >> function old new delta > >> show_usage_if_dash_dash_help 75 69 -6 > >> ------------------------------------------------------------------------------ > >> (add/remove: 0/0 grow/shrink: 0/1 up/down: 0/-6) Total: -6 bytes > >> > >> Signed-off-by: Ron Yorston > >> --- > >> libbb/appletlib.c | 5 ++--- > >> 1 file changed, 2 insertions(+), 3 deletions(-) > >> > >> diff --git a/libbb/appletlib.c b/libbb/appletlib.c > >> index 841b3b873..d56b5b409 100644 > >> --- a/libbb/appletlib.c > >> +++ b/libbb/appletlib.c > >> @@ -258,7 +258,6 @@ void lbb_prepare(const char *applet > >> /* Redundant for busybox (run_applet_and_exit covers that case) > >> * but needed for "individual applet" mode */ > >> if (argv[1] > >> - && !argv[2] > >> && strcmp(argv[1], "--help") == 0 > >> && !is_prefixed_with(applet, "busybox") > >> ) { > >> @@ -940,8 +939,8 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv) > >> && applet_no != APPLET_NO_echo > >> # endif > >> ) { > >> - if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) { > >> - /* Make "foo --help" exit with 0: */ > >> + if (argv[1] && strcmp(argv[1], "--help") == 0) { > >> + /* Make "foo --help [...]" exit with 0: */ > >> xfunc_error_retval = 0; > >> bb_show_usage(); > >> } > >> -- > >> 2.35.1 > >> > >> _______________________________________________ > >> busybox mailing list > >> busybox at busybox.net > >> http://lists.busybox.net/mailman/listinfo/busybox > > _______________________________________________ > > busybox mailing list > > busybox at busybox.net > > http://lists.busybox.net/mailman/listinfo/busybox > > From vda.linux at googlemail.com Tue Aug 30 15:14:20 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 30 Aug 2022 17:14:20 +0200 Subject: Subject: [PATCH] ash: make 'sleep' a builtin In-Reply-To: <698eac53516b4f28907e33eb6bb6b8c8@AcuMS.aculab.com> References: <698eac53516b4f28907e33eb6bb6b8c8@AcuMS.aculab.com> Message-ID: On Tue, Aug 30, 2022 at 10:10 AM David Laight wrote: > > One question is why? > > Shell builtins (mostly) exist to speed things up. > > But sleep doesn?t need speeding up. Avoiding having another, possibly long-living process From vda.linux at googlemail.com Tue Aug 30 15:15:23 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 30 Aug 2022 17:15:23 +0200 Subject: [PATCH] shell: fix $(()) precedence bug in "X=A?B:C" (it is _not_ "(X=A)?..)" In-Reply-To: <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> References: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> Message-ID: Your patches seem to be against dash, not busybox git tree? On Thu, Aug 11, 2022 at 6:16 PM Steffen Nurpmeso wrote: > > > --- > It was a one line fix. Five more tests > > +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" > +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" > +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" > +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" > +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" > +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" > > I hope i am out now. Sorry for all the noise. > > shell/shexp-arith.h | 3 +-- > 1 file changed, 1 insertion(+), 2 deletions(-) > > diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h > index 17582f7d49..f48ada1b46 100644 > --- a/shell/shexp-arith.h > +++ b/shell/shexp-arith.h > @@ -584,8 +584,7 @@ junapre: > op |= x; > > /* Resolve as much as possible */ > - while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && > - lprec != a_SHEXP_ARITH_PREC_COND){ > + while(lprec > a_SHEXP_ARITH_PREC_COND){ > if(!a_shexp__arith_op_apply(self)) > goto jleave; > lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; > -- > 2.37.1 > > > --steffen > | > |Der Kragenbaer, The moon bear, > |der holt sich munter he cheerfully and one by one > |einen nach dem anderen runter wa.ks himself off > |(By Robert Gernhardt) > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From vda.linux at googlemail.com Tue Aug 30 15:23:12 2022 From: vda.linux at googlemail.com (Denys Vlasenko) Date: Tue, 30 Aug 2022 17:23:12 +0200 Subject: [PATCH] libbbb: mark stack in assembly files read-only In-Reply-To: <20220825104806.307-1-ludwig.nussel@suse.de> References: <20220825104806.307-1-ludwig.nussel@suse.de> Message-ID: Applied, thanks On Thu, Aug 25, 2022 at 12:48 PM Ludwig Nussel wrote: > > Signed-off-by: Ludwig Nussel > --- > libbb/hash_md5_sha256_x86-32_shaNI.S | 3 +++ > libbb/hash_md5_sha256_x86-64_shaNI.S | 3 +++ > libbb/hash_md5_sha_x86-32_shaNI.S | 3 +++ > libbb/hash_md5_sha_x86-64.S | 3 +++ > libbb/hash_md5_sha_x86-64.S.sh | 3 +++ > libbb/hash_md5_sha_x86-64_shaNI.S | 3 +++ > 6 files changed, 18 insertions(+) > > diff --git a/libbb/hash_md5_sha256_x86-32_shaNI.S b/libbb/hash_md5_sha256_x86-32_shaNI.S > index 3905bad9a..5b79e847b 100644 > --- a/libbb/hash_md5_sha256_x86-32_shaNI.S > +++ b/libbb/hash_md5_sha256_x86-32_shaNI.S > @@ -19,6 +19,9 @@ > // We do not check SSSE3 in cpuid, > // all SHA-capable CPUs support it as well. > > +#ifdef __linux__ > + .section .note.GNU-stack,"", at progbits > +#endif > .section .text.sha256_process_block64_shaNI, "ax", @progbits > .globl sha256_process_block64_shaNI > .hidden sha256_process_block64_shaNI > diff --git a/libbb/hash_md5_sha256_x86-64_shaNI.S b/libbb/hash_md5_sha256_x86-64_shaNI.S > index 082ceafe4..f89abb91a 100644 > --- a/libbb/hash_md5_sha256_x86-64_shaNI.S > +++ b/libbb/hash_md5_sha256_x86-64_shaNI.S > @@ -19,6 +19,9 @@ > // We do not check SSSE3 in cpuid, > // all SHA-capable CPUs support it as well. > > +#ifdef __linux__ > + .section .note.GNU-stack,"", at progbits > +#endif > .section .text.sha256_process_block64_shaNI, "ax", @progbits > .globl sha256_process_block64_shaNI > .hidden sha256_process_block64_shaNI > diff --git a/libbb/hash_md5_sha_x86-32_shaNI.S b/libbb/hash_md5_sha_x86-32_shaNI.S > index 2366b046a..3ef2b8469 100644 > --- a/libbb/hash_md5_sha_x86-32_shaNI.S > +++ b/libbb/hash_md5_sha_x86-32_shaNI.S > @@ -25,6 +25,9 @@ > // We do not check SSSE3/SSE4.1 in cpuid, > // all SHA-capable CPUs support them as well. > > +#ifdef __linux__ > + .section .note.GNU-stack,"", at progbits > +#endif > .section .text.sha1_process_block64_shaNI, "ax", @progbits > .globl sha1_process_block64_shaNI > .hidden sha1_process_block64_shaNI > diff --git a/libbb/hash_md5_sha_x86-64.S b/libbb/hash_md5_sha_x86-64.S > index 1d55b91f8..2e8a0cfcd 100644 > --- a/libbb/hash_md5_sha_x86-64.S > +++ b/libbb/hash_md5_sha_x86-64.S > @@ -1,6 +1,9 @@ > ### Generated by hash_md5_sha_x86-64.S.sh ### > > #if CONFIG_SHA1_SMALL == 0 && defined(__GNUC__) && defined(__x86_64__) > +#ifdef __linux__ > + .section .note.GNU-stack,"", at progbits > +#endif > .section .text.sha1_process_block64, "ax", @progbits > .globl sha1_process_block64 > .hidden sha1_process_block64 > diff --git a/libbb/hash_md5_sha_x86-64.S.sh b/libbb/hash_md5_sha_x86-64.S.sh > index 40c979d35..2eab1c0ba 100755 > --- a/libbb/hash_md5_sha_x86-64.S.sh > +++ b/libbb/hash_md5_sha_x86-64.S.sh > @@ -127,6 +127,9 @@ echo \ > "### Generated by hash_md5_sha_x86-64.S.sh ### > > #if CONFIG_SHA1_SMALL == 0 && defined(__GNUC__) && defined(__x86_64__) > +#ifdef __linux__ > + .section .note.GNU-stack,\"\", at progbits > +#endif > .section .text.sha1_process_block64, \"ax\", @progbits > .globl sha1_process_block64 > .hidden sha1_process_block64 > diff --git a/libbb/hash_md5_sha_x86-64_shaNI.S b/libbb/hash_md5_sha_x86-64_shaNI.S > index 794e97040..97435e3c2 100644 > --- a/libbb/hash_md5_sha_x86-64_shaNI.S > +++ b/libbb/hash_md5_sha_x86-64_shaNI.S > @@ -25,6 +25,9 @@ > // We do not check SSSE3/SSE4.1 in cpuid, > // all SHA-capable CPUs support them as well. > > +#ifdef __linux__ > + .section .note.GNU-stack,"", at progbits > +#endif > .section .text.sha1_process_block64_shaNI, "ax", @progbits > .globl sha1_process_block64_shaNI > .hidden sha1_process_block64_shaNI > -- > 2.37.2 > > _______________________________________________ > busybox mailing list > busybox at busybox.net > http://lists.busybox.net/mailman/listinfo/busybox From shawnlandden at tutanota.com Tue Aug 30 16:59:58 2022 From: shawnlandden at tutanota.com (shawnlandden at tutanota.com) Date: Tue, 30 Aug 2022 18:59:58 +0200 (CEST) Subject: Subject: [PATCH] ash: make 'sleep' a builtin In-Reply-To: References: <698eac53516b4f28907e33eb6bb6b8c8@AcuMS.aculab.com> Message-ID: 30 ago 2022, 08:14 por vda.linux at googlemail.com: > On Tue, Aug 30, 2022 at 10:10 AM David Laight wrote: > >> >> One question is why? >> >> Shell builtins (mostly) exist to speed things up. >> >> But sleep doesn?t need speeding up. >> > > Avoiding having another, possibly long-living process > Linux allocates a huge amount of stack space to copy `env` for every process. https://refspecs.linuxfoundation.org/LSB_1.3.0/IA64/spec/auxiliaryvector.html -------------- next part -------------- An HTML attachment was scrubbed... URL: From bluca at debian.org Tue Aug 30 21:35:43 2022 From: bluca at debian.org (Luca Boccassi) Date: Tue, 30 Aug 2022 22:35:43 +0100 Subject: [PATCH] udhcpc: add support for sending DHCPINFORM requests In-Reply-To: Message-ID: <6dba2a568f75d69144159428fcb273a90ed8c0cf.camel@debian.org> > Apologies for the delay. > > On Wed, Apr 20, 2022 at 8:35 PM Sinan Kaya > wrote: > > From b906997217b363c459fdbd2824bfe6c5ac69607e Mon Sep 17 00:00:00 > 2001 > > From: Sinan Kaya > > Date: Tue, 19 Apr 2022 13:47:19 +0000 > > Subject: [PATCH] udhcpc: add support for sending DHCPINFORM > requests > > > > It is useful for applications to be able to query DHCP options > > without renewing IP address. > > I would like a more extended information. > > Imagine someone who never needed to do this before. > Explain to them what would happen when udhcpc > is run with -I option. How would DHCP handshake change? > > Give a real-world example > (say, one based on your situation which prompted you > to make this patch). > > Give an example where -e IP is useful. > > Maybe give references to RFCs which document > DHCPINFORM usage. Added in v2. > > /* Broadcast a DHCP request message */ > > /* RFC 2131 3.1 paragraph 3: > > - * "The client _broadcasts_ a DHCPREQUEST message..." > > + * "The client _broadcasts_ a DHCPREQUEST/INFORM message..." > > Incorrect. That's not what RFC 2131 3.1 paragraph 3 says. Rearranged the comment in v2. > > */ > > /* NOINLINE: limit stack usage in caller */ > > -static NOINLINE int send_select(uint32_t server, uint32_t > requested) > > +static NOINLINE int send_select(uint32_t server, uint32_t > requested, > > int inform) > > { > > struct dhcp_packet packet; > > struct in_addr temp_addr; > > char server_str[sizeof("255.255.255.255")]; > > + const char *direction; > > > > /* > > * RFC 2131 4.3.2 DHCPREQUEST message > > @@ -766,11 +771,12 @@ static NOINLINE int send_select(uint32_t > server, > > uint32_t requested) > > /* Fill in: op, htype, hlen, cookie, chaddr fields, > > * xid field, message type option: > > */ > > - init_packet(&packet, DHCPREQUEST); > > + init_packet(&packet, inform ? DHCPINFORM: DHCPREQUEST); > > > > udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, > requested); > > > > - udhcp_add_simple_option(&packet, DHCP_SERVER_ID, server); > > + if (server) > > + udhcp_add_simple_option(&packet, DHCP_SERVER_ID, > server); > > Do you mean, if server is not specified, SERVER_ID may be omitted? > Should be omitted? > Should this change be made regardless of DHCPINFORM support? Removed in v2. > > > > /* Add options: maxsize, > > * "param req" option according to -O, options specified > with -x > > @@ -780,11 +786,19 @@ static NOINLINE int send_select(uint32_t > server, > > uint32_t requested) > > temp_addr.s_addr = server; > > strcpy(server_str, inet_ntoa(temp_addr)); > > temp_addr.s_addr = requested; > > - bb_info_msg("broadcasting select for %s, server %s", > > - inet_ntoa(temp_addr), > > - server_str > > + if (server) > > + direction = "unicasting"; > > + else > > + direction = "broadcasting"; > > + > > + bb_info_msg("%s select for %s, server %s", > > + direction, > > + inet_ntoa(temp_addr), > > + server_str > > ); > > RFC 2131 3.1 paragraph 3 says: > "The client broadcasts a DHCPREQUEST message". > Always. Even if server-id is known. For DHCPREQUEST yes, for DHCPINFORM no: RFC 2131 paragraph 4.4.3: The client then unicasts the DHCPINFORM to the DHCP server if it knows the server's address, otherwise it broadcasts the message to the limited (all 1s) broadcast address. Rearranged in v2 so that the server/unicast mode is only used for DHCPINFORM. > > - return raw_bcast_from_client_data_ifindex(&packet, > INADDR_ANY); > > + > > + // return raw_bcast_from_client_data_ifindex(&packet, > INADDR_ANY); > > + return bcast_or_ucast(&packet, requested, server); > > } > > > > /* Unicast or broadcast a DHCP renew message */ > > @@ -1161,9 +1175,9 @@ static void client_background(void) > > //usage:# define IF_UDHCP_VERBOSE(...) > > //usage:#endif > > //usage:#define udhcpc_trivial_usage > > -//usage: > > "[-fbq"IF_UDHCP_VERBOSE("v")"RB]"IF_FEATURE_UDHCPC_ARPING(" > > [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC|-n]\n" > > +//usage: > > "[-fbq"IF_UDHCP_VERBOSE("v")"RBI]"IF_FEATURE_UDHCPC_ARPING(" > > [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC|-n]\n" > > //usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P > PORT]")" [-s > > PROG] [-p PIDFILE]\n" > > -//usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x > OPT:VAL]... > > [-O OPT]..." > > +//usage: " [-oC] [-r IP] [-e IP] [-V VENDOR] [-F NAME] > [-x > > OPT:VAL]... [-O OPT]..." > > //usage:#define udhcpc_full_usage "\n" > > //usage: "\n -i IFACE Interface to use (default > > "CONFIG_UDHCPC_DEFAULT_INTERFACE")" > > //usage: IF_FEATURE_UDHCP_PORT( > > @@ -1172,6 +1186,7 @@ static void client_background(void) > > //usage: "\n -s PROG Run PROG at DHCP events > (default > > "CONFIG_UDHCPC_DEFAULT_SCRIPT")" > > //usage: "\n -p FILE Create pidfile" > > //usage: "\n -B Request broadcast replies" > > +//usage: "\n -I Request using inform" > > This barely explains anything. Updated in v2. > > +//usage: "\n -e IP Request this server IP > address" > > This is not correct. We do not "request server IPs". > We send requests to servers (sometimes by unicasting to their IPs, > but usually via broadcast). Updated in v2. > > + > > + if (opt & OPT_I) > > + use_inform = 1; > > Why do you need to have use_inform variable when (opt & OPT_I) > works just as well? Removed in v2. -- Kind regards, Luca Boccassi -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 833 bytes Desc: This is a digitally signed message part URL: From bluca at debian.org Tue Aug 30 21:41:51 2022 From: bluca at debian.org (bluca at debian.org) Date: Tue, 30 Aug 2022 22:41:51 +0100 Subject: [PATCH v2] udhcpc: add support for sending DHCPINFORM packets In-Reply-To: <20220830213443.1922500-1-luca.boccassi@gmail.com> References: <20220830213443.1922500-1-luca.boccassi@gmail.com> Message-ID: <20220830214151.1923924-1-bluca@debian.org> From: Luca Boccassi It is useful for applications to be able to query DHCP options without renewing IP address. Instead of a full DHCP handshake, using -I will cause a single DHCPINFORM packet to be sent, and the server response (including DHCP options received) to be printed and terminate. No configuration will be changed. This is useful for clients that want to query additional information from a server, that might not be normally processed, like custom server options. Also useful for checking specific options via -O. As per RFC 2131, allow targeting the already-known DHCP server via unicast instead of broadcast, via new -e option. Tested by running isc-dhcp-server with the following configuration: option domain-name "example.org"; option domain-name-servers 1.1.1.1, 8.8.8.8; subnet 192.168.11.0 netmask 255.255.255.0 { range 192.168.11.1 192.168.11.100; authoritative; option default-url "default.url"; } $ busybox udhcpc -I -i host0 -O 114 -r 192.168.11.1 udhcpc: started, v1.36.0.git udhcpc: broadcasting inform for 192.168.11.1, server 0.0.0.0 udhcpc: lease of 0.0.0.0 obtained from 0.0.0.0, lease time 3600 (default) udhcpc: option: opt53=0x05 udhcpc: option: serverid=192.168.11.101 udhcpc: option: subnet=255.255.255.0 udhcpc: option: dns=1.1.1.1 8.8.8.8 udhcpc: option: domain=example.org udhcpc: option: opt114=0x64656661756c742e75726c $ busybox udhcpc -e 192.168.11.101 -I -i host0 -O 114 -r 192.168.11.1 udhcpc: started, v1.36.0.git udhcpc: unicasting inform for 192.168.11.1, server 192.168.11.101 udhcpc: lease of 0.0.0.0 obtained from 192.168.11.101, lease time 3600 (default) udhcpc: option: opt53=0x05 udhcpc: option: serverid=192.168.11.101 udhcpc: option: subnet=255.255.255.0 udhcpc: option: dns=1.1.1.1 8.8.8.8 udhcpc: option: domain=example.org udhcpc: option: opt114=0x64656661756c742e75726c Co-authored-by: Sinan Kaya Signed-off-by: Luca Boccassi --- v2: updated commit message and comments applied all review comments print received DHCP options and exit networking/udhcp/dhcpc.c | 116 ++++++++++++++++++++++++++++++++++----- 1 file changed, 103 insertions(+), 13 deletions(-) diff --git a/networking/udhcp/dhcpc.c b/networking/udhcp/dhcpc.c index c757fb37c..5762a69ca 100644 --- a/networking/udhcp/dhcpc.c +++ b/networking/udhcp/dhcpc.c @@ -75,6 +75,8 @@ static const char udhcpc_longopts[] ALIGN1 = "background\0" No_argument "b" ) "broadcast\0" No_argument "B" + "inform\0" No_argument "I" + "server\0" Required_argument "e" IF_FEATURE_UDHCPC_ARPING("arping\0" Optional_argument "a") IF_FEATURE_UDHCP_PORT("client-port\0" Required_argument "P") ; @@ -100,8 +102,10 @@ enum { OPT_x = 1 << 16, OPT_f = 1 << 17, OPT_B = 1 << 18, + OPT_I = 1 << 19, + OPT_e = 1 << 20, /* The rest has variable bit positions, need to be clever */ - OPTBIT_B = 18, + OPTBIT_B = 20, USE_FOR_MMU( OPTBIT_b,) IF_FEATURE_UDHCPC_ARPING(OPTBIT_a,) IF_FEATURE_UDHCP_PORT( OPTBIT_P,) @@ -740,16 +744,24 @@ static NOINLINE int send_discover(uint32_t requested) return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY); } -/* Broadcast a DHCP request message */ +/* Broadcast a DHCP request message or broadcast/unicast a DHCP inform message */ /* RFC 2131 3.1 paragraph 3: * "The client _broadcasts_ a DHCPREQUEST message..." + * + * RFC 2131 4.4.3 Initialization with an externally assigned network address + * + * The client then unicasts the DHCPINFORM to the DHCP server if it + * knows the server's address, otherwise it broadcasts the message to + * the limited (all 1s) broadcast address. */ /* NOINLINE: limit stack usage in caller */ -static NOINLINE int send_select(uint32_t server, uint32_t requested) +static NOINLINE int send_select(uint32_t server, uint32_t requested, int inform) { struct dhcp_packet packet; struct in_addr temp_addr; char server_str[sizeof("255.255.255.255")]; + const char *direction = (inform && server) ? "unicasting" : "broadcasting"; + const char *type = inform ? "inform" : "select"; /* * RFC 2131 4.3.2 DHCPREQUEST message @@ -766,7 +778,19 @@ static NOINLINE int send_select(uint32_t server, uint32_t requested) /* Fill in: op, htype, hlen, cookie, chaddr fields, * xid field, message type option: */ - init_packet(&packet, DHCPREQUEST); + init_packet(&packet, inform ? DHCPINFORM: DHCPREQUEST); + +/* + * RFC 2131 4.4.3 Initialization with an externally assigned network address + * + * The client sends a DHCPINFORM message. The client may request + * specific configuration parameters by including the 'parameter request + * list' option. The client generates and records a random transaction + * identifier and inserts that identifier into the 'xid' field. The + * client places its own network address in the 'ciaddr' field. + */ + if (inform) + packet.ciaddr = requested; udhcp_add_simple_option(&packet, DHCP_REQUESTED_IP, requested); @@ -780,10 +804,25 @@ static NOINLINE int send_select(uint32_t server, uint32_t requested) temp_addr.s_addr = server; strcpy(server_str, inet_ntoa(temp_addr)); temp_addr.s_addr = requested; - bb_info_msg("broadcasting select for %s, server %s", + bb_info_msg("%s %s for %s, server %s", + direction, + type, inet_ntoa(temp_addr), server_str ); + +/* + * RFC 2131 4.4.3 Initialization with an externally assigned network address + * + * The client then unicasts the DHCPINFORM to the DHCP server if it + * knows the server's address, otherwise it broadcasts the message to + * the limited (all 1s) broadcast address. DHCPINFORM messages MUST be + * directed to the 'DHCP server' UDP port. + */ + + if (inform && server) + return bcast_or_ucast(&packet, requested, server); + return raw_bcast_from_client_data_ifindex(&packet, INADDR_ANY); } @@ -1161,9 +1200,9 @@ static void client_background(void) //usage:# define IF_UDHCP_VERBOSE(...) //usage:#endif //usage:#define udhcpc_trivial_usage -//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RB]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC|-n]\n" +//usage: "[-fbq"IF_UDHCP_VERBOSE("v")"RBI]"IF_FEATURE_UDHCPC_ARPING(" [-a[MSEC]]")" [-t N] [-T SEC] [-A SEC|-n]\n" //usage: " [-i IFACE]"IF_FEATURE_UDHCP_PORT(" [-P PORT]")" [-s PROG] [-p PIDFILE]\n" -//usage: " [-oC] [-r IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..." +//usage: " [-oC] [-r IP] [-e SERVER_IP] [-V VENDOR] [-F NAME] [-x OPT:VAL]... [-O OPT]..." //usage:#define udhcpc_full_usage "\n" //usage: "\n -i IFACE Interface to use (default "CONFIG_UDHCPC_DEFAULT_INTERFACE")" //usage: IF_FEATURE_UDHCP_PORT( @@ -1172,6 +1211,7 @@ static void client_background(void) //usage: "\n -s PROG Run PROG at DHCP events (default "CONFIG_UDHCPC_DEFAULT_SCRIPT")" //usage: "\n -p FILE Create pidfile" //usage: "\n -B Request broadcast replies" +//usage: "\n -I Send DHCPINFORM, print received options and exit, instead of full DHCP handshake" //usage: "\n -t N Send up to N discover packets (default 3)" //usage: "\n -T SEC Pause between packets (default 3)" //usage: "\n -A SEC Wait if lease is not obtained (default 20)" @@ -1187,6 +1227,7 @@ static void client_background(void) //usage: "\n -a[MSEC] Validate offered address with ARP ping" //usage: ) //usage: "\n -r IP Request this IP address" +//usage: "\n -e IP Send to this server IP address when sending DHCPINFORM packets" //usage: "\n -o Don't request any options (unless -O is given)" //usage: "\n -O OPT Request option OPT from server (cumulative)" //usage: "\n -x OPT:VAL Include option OPT in sent packets (cumulative)" @@ -1209,7 +1250,7 @@ int udhcpc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; int udhcpc_main(int argc UNUSED_PARAM, char **argv) { uint8_t *message; - const char *str_V, *str_F, *str_r; + const char *str_V, *str_F, *str_r, *str_e; IF_FEATURE_UDHCPC_ARPING(const char *str_a = "2000";) IF_FEATURE_UDHCP_PORT(char *str_P;) uint8_t *clientid_mac_ptr; @@ -1218,7 +1259,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) int tryagain_timeout = 20; int discover_timeout = 3; int discover_retries = 3; - uint32_t server_id = server_id; /* for compiler */ + uint32_t server_id = 0; uint32_t requested_ip = 0; int packet_num; int timeout; /* must be signed */ @@ -1244,7 +1285,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* Parse command line */ opt = getopt32long(argv, "^" /* O,x: list; -T,-t,-A take numeric param */ - "CV:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fB" + "CV:F:i:np:qRr:s:T:+t:+SA:+O:*ox:*fBIe:" USE_FOR_MMU("b") IF_FEATURE_UDHCPC_ARPING("a::") IF_FEATURE_UDHCP_PORT("P:") @@ -1258,6 +1299,7 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) , &discover_timeout, &discover_retries, &tryagain_timeout /* T,t,A */ , &list_O , &list_x + , &str_e /* e */ IF_FEATURE_UDHCPC_ARPING(, &str_a) IF_FEATURE_UDHCP_PORT(, &str_P) IF_UDHCP_VERBOSE(, &dhcp_verbose) @@ -1283,6 +1325,10 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /*p[OPT_DATA + 2] = 0; */ memcpy(p + OPT_DATA + 3, str_F, len); /* do not store NUL byte */ } + /* Allow DHCPINFORM to target a particular server as per RFC 2131 4.4.3 */ + if (opt & OPT_e && opt & OPT_I) + if (!inet_aton(str_e, (void*)&server_id)) + bb_show_usage(); if (opt & OPT_r) if (!inet_aton(str_r, (void*)&requested_ip)) bb_show_usage(); @@ -1368,7 +1414,17 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) /* We want random_xid to be random... */ srand(monotonic_us()); - client_data.state = INIT_SELECTING; + if (opt & OPT_I) { + /* As per RFC 2131 4.4.3, a DHCPINFORM packet must set 'ciaddr' */ + if (!(opt & OPT_r)) + bb_error_msg_and_die("-I requires -r"); + client_data.state = REQUESTING; + change_listen_mode(LISTEN_RAW); + client_data.xid = random_xid(); + } else { + client_data.state = INIT_SELECTING; + } + d4_run_script_deconfig(); packet_num = 0; timeout = 0; @@ -1481,8 +1537,8 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) continue; case REQUESTING: if (packet_num < 3) { - /* send broadcast select packet */ - send_select(server_id, requested_ip); + /* send broadcast/unicast select packet */ + send_select(server_id, requested_ip, opt & OPT_I); timeout = discover_timeout; packet_num++; continue; @@ -1736,6 +1792,40 @@ int udhcpc_main(int argc UNUSED_PARAM, char **argv) inet_ntoa(temp_addr), server_str, (unsigned)lease_remaining, temp ? "" : " (default)" ); + + /* If INFORM was selected, print the returned DHCP options and exit */ + if (opt & OPT_I) { + uint8_t *optptr; + struct dhcp_scan_state scan_state; + char *new_opt; + + init_scan_state(&packet, &scan_state); + + while ((optptr = udhcp_scan_options(&packet, &scan_state)) != NULL) { + const struct dhcp_optflag *dh; + const char *opt_name; + struct dhcp_optitem *opt_item; + uint8_t code = optptr[OPT_CODE]; + uint8_t len = optptr[OPT_LEN]; + uint8_t *data = optptr + OPT_DATA; + + opt_item = concat_option(data, len, code); + opt_name = get_optname(code, &dh); + if (opt_name) + new_opt = xmalloc_optname_optval(opt_item, dh, opt_name); + else { + unsigned ofs; + new_opt = xmalloc(sizeof("optNNN=0x") + 1 + opt_item->len*2); + ofs = sprintf(new_opt, "opt%u=0x", opt_item->code); + bin2hex(new_opt + ofs, (char *)opt_item->data, opt_item->len)[0] = '\0'; + } + bb_info_msg("option: %s", new_opt); + free(new_opt); + } + + return 0; + } + /* paranoia: must not be too small and not prone to overflows */ /* NB: 60s leases _are_ used in real world * (temporary IPs while ISP modem initializes) -- 2.34.1 From steffen at sdaoden.eu Tue Aug 30 23:35:14 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Wed, 31 Aug 2022 01:35:14 +0200 Subject: [PATCH] shell: fix $(()) precedence bug in "X=A?B:C" (it is _not_ "(X=A)?..)" In-Reply-To: References: <2adea39306d7d9b64518729b2c1285b5d3efb2b5.1660144825.git.steffen@sdaoden.eu> <75a1539b2f77f6b890e0cc6736796f2236e7529a.1660176459.git.steffen@sdaoden.eu> <97be4848d3aa47d71051d42f8707330db067583f.1660234044.git.steffen@sdaoden.eu> Message-ID: <20220830233514.osBE3%steffen@sdaoden.eu> Denys Vlasenko wrote in : |Your patches seem to be against dash, not busybox git tree? No? I think dash uses a yacc thing..? (That only supports the most basic things, as per POSIX, which is two steps back for busybox. But smaller i presume.) And sorry for lots of noise over weeks! I have * 863f859305 (HEAD -> refs/heads/sharith-v3) shell: $(()): add tests * 5a27561d08 shell: $(()): sync (comments, a bit reorder, one small fix) * 038a85a843 shell: plug SH_MATH_ERROR_TRACK memory leak * 294a4dca78 shell: exchange Dijkstra $(( )) evaluator.. * 526625bc83 (refs/remotes/origin/master, refs/remotes/origin/HEAD, refs/heads/master) libbb: mark stack in assembly files read-only 'Rebased just fine. But you can have a big all-in-one v4 if you want? (I do not expect anything more to happen anytime soon.) --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt) From steffen at sdaoden.eu Tue Aug 30 23:43:26 2022 From: steffen at sdaoden.eu (Steffen Nurpmeso) Date: Wed, 31 Aug 2022 01:43:26 +0200 Subject: [PATCH v4] shell: exchange Dijkstra $(( )) evaluator.. Message-ID: <09f36b9e8c5793662b0f788b3396d02e7f1cb9ed.1661902741.git.steffen@sdaoden.eu> The former implementation was not correct regarding whiteouts in ?: conditional branches. The new one also parses a bit better, in effect on equal level than bash with its recursive descendent parser. --- All-in-one v4. Compares to v3 like - * the entire condition, it's number stack position is used + * the entire condition, its number stack position is used I hope it is ok like this. Ciao! shell/Config.src | 8 + shell/ash.c | 6 +- shell/ash_test/ash-arith/bigbadbison.right | 880 ++++++++++++ shell/ash_test/ash-arith/bigbadbison.tests | 914 +++++++++++++ shell/hush.c | 23 +- shell/hush_test/hush-arith/bigbadbison.right | 880 ++++++++++++ shell/hush_test/hush-arith/bigbadbison.tests | 914 +++++++++++++ shell/math.c | 713 ++-------- shell/math.h | 7 +- shell/shexp-arith.h | 1292 ++++++++++++++++++ 10 files changed, 5001 insertions(+), 636 deletions(-) create mode 100644 shell/ash_test/ash-arith/bigbadbison.right create mode 100755 shell/ash_test/ash-arith/bigbadbison.tests create mode 100644 shell/hush_test/hush-arith/bigbadbison.right create mode 100755 shell/hush_test/hush-arith/bigbadbison.tests create mode 100644 shell/shexp-arith.h diff --git a/shell/Config.src b/shell/Config.src index 5efbf99959..32aaab58e8 100644 --- a/shell/Config.src +++ b/shell/Config.src @@ -108,6 +108,14 @@ config FEATURE_SH_MATH_BASE default y depends on FEATURE_SH_MATH +config FEATURE_SH_MATH_ERROR_TRACK + bool "Extend POSIX math support with error location tracking" + default y + depends on FEATURE_SH_MATH + help + Enable error location tracking in the shell's math support. + Without it only the type of error will be logged. + config FEATURE_SH_EXTRA_QUIET bool "Hide message on interactive shell startup" default y diff --git a/shell/ash.c b/shell/ash.c index 326f8b2a98..af3db6162e 100644 --- a/shell/ash.c +++ b/shell/ash.c @@ -6034,8 +6034,12 @@ ash_arith(const char *s) INT_OFF; result = arith(&math_state, s); - if (math_state.errmsg) + if (math_state.errmsg) { ash_msg_and_raise_error(math_state.errmsg); +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(math_state.errmsg); +# endif + } INT_ON; return result; diff --git a/shell/ash_test/ash-arith/bigbadbison.right b/shell/ash_test/ash-arith/bigbadbison.right new file mode 100644 index 0000000000..a6446c81cd --- /dev/null +++ b/shell/ash_test/ash-arith/bigbadbison.right @@ -0,0 +1,880 @@ += BASE +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<0> +<10> +<9191919191919> +<13> +<11> +<1023> +<1295> +<1295> +<9322365> +<16242915> +<10> +<33> +<10> +<33> +<1> +<1> +<1> +<33> +<33> +<33> +<33> += UNA PLUS/MINUS +<0> +<0> +<1> +<1> +<4221> +<16929> +<16242915> +<16242915> +<1> +<1> +<1> +<0> +<0> +<-1> +<-1> +<-4221> +<-16929> +<-16242915> +<-16242915> +<-1> +<-1> +<-1> +<-1> +<1> +<-1> += UNA ! +<1> +<1> +<0> +<0> +<1> +<0> += UNA ~ +<-1> +<-1> +<-2> +<-2> +<-2276> +<0> +<0> +<-1> +<-1> +<-1> +<-1> += BIN + +<0> +<0> +<1> +<1> +<1> +<1> +<2> +<2> +<2> +<-2> +<3333> +<3333> +<33> +<-33> +<-33> +<-1> +<33> +<-33> +<-33> +<1> +<9223372036854775807> +<-9223372036854775807> +<9223372036854775806> +<-9223372036854775808> +<-2> +<0> +<9223372036854775797> +<-9223372036854775797> +<9223372036854775796> +<-9223372036854775798> +<-12> +<10> += BIN - +<0> +<0> +<-1> +<-1> +<1> +<1> +<0> +<0> +<0> +<0> +<-1111> +<1111> +<-1> +<1> +<1> +<129> +<1> +<-1> +<-1> +<129> +<-9223372036854775807> +<9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<-9223372036854775797> +<9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN * +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<2468642> +<2468642> +<272> +<272> +<272> +<-4160> +<272> +<272> +<272> +<-4160> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775797> +<9223372036854775797> +<11> +<-11> += BIN / +<0> +<1> +<1> +<0> +<2> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<-1> +<2> +<3> +<1> +<1> +<0> +<0> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<838488366986797800> +<-838488366986797800> +<-838488366986797800> +<838488366986797800> +<0> +<0> += BIN % +<0> +<0> +<0> +<1111> +<0> +<16> +<-16> +<-16> +<64> +<1> +<-1> +<-1> +<1> +<0> +<0> +<1> +<0> +<3> +<-1> +<0> +<0> +<0> +<0> +<0> +<0> +<-8> +<-8> +<7> +<7> +<-1> +<-1> += BIN << +<0> +<0> +<0> +<0> +<1> +<1> +<2> +<2> +<78179674781384704> +<18639486976> +<2097152> +<-2251799813685248> +<-2251799813685248> +<0> +<1114112> +<-4785074604081152> +<-4785074604081152> +<65> +<64> +<0> +<0> +<-9223372036854775808> +<-2> +<-9223372036854775808> +<-2> +<0> +<0> +<-9007199254740992> +<-2048> +<-9007199254740992> +<-2048> += BIN >> +<0> +<0> +<0> +<0> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<-1> +<-1> +<131071> +<0> +<0> +<-1> +<-1> +<65> +<64> +<-1> +<-4611686018427387904> +<0> +<4611686018427387903> +<-1> +<-1> +<-1024> +<-4503599627370496> +<1023> +<4503599627370495> +<-1> +<-1> +<9007199254740991> += BIN ** +<0> +<2> +<4> +<8> +<16> +<10000> +<10000000000> +<100005> +<10000000000> += LOG OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> += LOG AND +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> += BIN BIT_OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<3327> +<3327> +<17> +<-1> +<-1> +<-1> +<17> +<-1> +<-1> +<-63> +<1088> +<-1> +<-9223372036854775807> +<-1> +<9223372036854775807> +<-1> +<-1> +<-11> +<-9223372036854775797> +<-1> +<9223372036854775807> +<-1> +<-1> += BIN BIT_XOR +<0> +<0> +<1> +<1> +<1> +<1> +<0> +<0> +<3321> +<3321> +<1> +<31> +<31> +<-1> +<1> +<31> +<31> +<-127> +<1088> +<9223372036854775807> +<-9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<9223372036854775797> +<-9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN BIT_AND +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<6> +<6> +<16> +<-32> +<-32> +<0> +<16> +<-32> +<-32> +<64> +<0> +<-9223372036854775808> +<0> +<9223372036854775807> +<1> +<-1> +<1> +<-9223372036854775808> +<0> +<9223372036854775797> +<11> +<-11> +<11> += BIN EQ +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> += BIN NE +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> += BIN LE +<1> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<0> +<1> += BIN GE +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<1> +<0> += BIN LT +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> += BIN GT +<0> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<1> +<0> += PRECEDENCE I +<6> +<2> +<0> +<2> +<-1> +<1> +<1> +<2> +<8> +<7> +<12> +<5> +<10> +<1> +<72> +<48> +<288> +<1> +<3> +<1> +<4> +<76> +<1> +<1> +<1> +<1> +<2> +<2> += PARENS +<6> +<6> +<-4> +<-4> +<2> +<2> +<0> +<0> +<-3> +<-3> +<0> +<0> +<12> +<12> +<10> +<10> +<12> +<48> +<1> +<1> +<6> +<6> +<9> +<9> +<20> +<20> +<21> +<21> +<36864> +<36864> +<6> +<6> +<9> +<9> +<9> +<9> +<0> +<0> +<38> +<38> +<2> +<2> +<32> +<32> +<0> +<0> +<24> +<24> += ASSIGN I +<3><3> +<3><3> +<3><3> +<11><11> +<9><9> +<10><10> +<20><20> +<10><10> +<5><5> +<0><0> +<0><0> +<10><10> +<100><100> +<100><100> +<11><11> +<11><11> +<10><10> +<2><2> +<5><5> +<20><20> +<9223372036854775807><9223372036854775807> += ASSIGN II +<2><3> +<4><3> +<36><5><7> +<1501><100><15> +<3><3> +<10><1><2><3><10> += POSTFIX +<1><2> +<1><2><1> +<10><2><11> +<10><2><11> +<1><0> +<1><0><1> +<10><0><9> +<10><0><9> += PREFIX +<2><2> +<2><2><2> +<22><2><11> +<10><1><10> +<22><2><11> +<0><0> +<0><0><0> +<9><1><9> +<10><1><10> +<0><0><9> += VAR RECUR +<2><1 + 1> +<2><1 + 1> +<3><3> +<2><3> +<3><1 + 1> +<4><1 + 1 * 2> +<5><(1 + 1) * 2> +<3><3><3 / 2> +<2><2> +<2><2> +<3><3> +<4><4> +<5><5> +<5><5><3 / 2> += COMMA +<2> +<3> +<4> +<4> +<133><133> +<10><10> += COND +<3> +<3> +<2> +<3> +<2> +<2> +<111> +<5> +<7> +<5> +<5> +<7> +<7> +<7> +<7> +<5> +-- COND .2 +<-1> +<0> +<1> +<1> +<32> +<32> +<1> +<1> +<32> +-- COND .3 +<3> +<4> +<3> +<3> +<3> +<4> +<3> +<3> +<3> +<4> +<1> +<4> +<5> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<5> +<5> +<8> +<8> +<10> +<10> +<10> +<10> +<10> +-- COND .4 +<5> +<6> +<7> +<8> +<9> +<12> +<10> +<12> +<10> +-- COND .5 +<12> +<10> +<12> +<10> +-- COND .6 +<12> +<9> +<-2> +<-1> +<23> +<26> +<24> +<0> +<23> +<23> +<23> +-- COND .7 +<16><2><3><16><5> +<16><2><3><16><5> +<16><2><3><16><5> +<25><2><3><4><25><> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<16><2><3><16><5> +<9><2><9><4><5><> +<4><4><3><4><5><> +-- COND .8 +<10><2> +<20><0> +<10><10> +<20><0> +<10><10> +<20><0> +<20><20> +-- COND .9 +<2><+E+><1+1> +<2><1+1><+E+> +<2><+E+><2> +<1><1><+E+> +<4><+E+><4> +<4><4><+E+> +-- COND .10 +<-1><+E+><+E+><+E+><-1> +<2><1><2><+E+><+E+> +<3><0><+E+><3><+E+> += WILD I +<14> +<1> +<1> +<1> +<3> +<87> +<2097152> +<20><10> +<100><10> +<0><10> += WILD II +<36><11> +<33><11> +<36><12> +<39><12> +<39><12> +<-33><12> +<-27> +<20><10> +<21><11> +<20><10> +<21><11> +<21><11> +<20><10> +<20><10> +<21> +<21> +<21> += WILD RECUR +<20><20><10> +<10><10> +<11><11> +<21><11> +<10><10> +<1><1> +<6><6><6> +<10><10><5> +<12><12> +<12><12> +<10><11> +<10><11> +<10><10><5> +<6><6> +<10><10> +<10><0><10><20> +<10><6><10><20> +<10><10><10><20> +<50><50><10><20> +<50><50><10><20> +<500><500><10><20> diff --git a/shell/ash_test/ash-arith/bigbadbison.tests b/shell/ash_test/ash-arith/bigbadbison.tests new file mode 100755 index 0000000000..2b15fda7c9 --- /dev/null +++ b/shell/ash_test/ash-arith/bigbadbison.tests @@ -0,0 +1,914 @@ +# make this work with (ba)sh \ +command -v shopt && shopt -s expand_aliases;\ +alias p=printf;alias e=echo;alias s=export +s I=10 J=33 +e '= BASE' +e "<$(())>" +e "<$(( ))>" +e "<$((1))>" +e "<$((0))>" +e "<$((0x0))>" +e "<$((0X0))>" +e "<$((000))>" +e "<$((000000000000001))>" +e "<$((2#00000000000000000000000000000000000001))>" +e "<$((0X00000000000000000000000000000000000000000001))>" +e "<$((999999999999999999999999999999999999999999999))>" +e "<$(( 10 ))>" +e "<$((9191919191919))>" +e "<$((0xD))>" +e "<$((013))>" +e "<$((32#VV))>" +e "<$((36#ZZ))>" +e "<$((36#zz))>" +e "<$(( 64#zzZZ ))>" +e "<$((64#ZZzz))>" +e "<$((I))>" +e "<$((J))>" +e "<$(( I ))>" +e "<$(( J ))>" +e "<$(( (1) ))>" +e "<$((((1))))>" +e "<$(((((1)))))>" +e "<$(( (J) ))>" +e "<$((((J))))>" +e "<$(((((J)))))>" +e "<$(( ( ( ( J ) ) ) ))>" +e '= UNA PLUS/MINUS' +e "<$((+0))>" +e "<$(( + 0 ))>" +e "<$(( +1))>" +e "<$((+ 1 ))>" +e "<$(( + 4221 ))>" +e "<$(( +0x4221 ))>" +e "<$(( + 64#ZZzz ))>" +e "<$(( +64#ZZzz ))>" +e "<$((+ (1) ))>" +e "<$((+((1))))>" +e "<$((+(((1)))))>" +e "<$((-0))>" +e "<$(( - 0 ))>" +e "<$(( -1))>" +e "<$((- 1 ))>" +e "<$(( - 4221 ))>" +e "<$(( -0x4221 ))>" +e "<$(( - 64#ZZzz ))>" +e "<$(( -64#ZZzz ))>" +e "<$((- (1) ))>" +e "<$((-((1))))>" +e "<$((-(((1)))))>" +e "<$((+ -(1) ))>" +e "<$((+(-(-1))))>" +e "<$((+(-(-(-1)))))>" +e '= UNA !' +e "<$((!0))>" +e "<$((! 00000000))>" +e "<$((!1))>" +e "<$((! 0x00001))>" +e "<$((! - 0))>" +e "<$((!-1))>" +e '= UNA ~' +e "<$((~0))>" +e "<$((~ 00000000))>" +e "<$((~1))>" +e "<$((~ 0x00001))>" +e "<$((~ 64#zz))>" +e "<$((~-1))>" +e "<$((~ - 1))>" +e "<$((~-0))>" +e "<$((~ - 0))>" +e "<$((~(-0)))>" +e "<$((~((- 0))))>" +e '= BIN +' +e "<$((0+0))>" +e "<$(( 0 + 0 ))>" +e "<$((0+1))>" +e "<$(( 0 + 1 ))>" +e "<$((1+0))>" +e "<$(( 1 + 0 ))>" +e "<$((1+1))>" +e "<$(( 1 + 1 ))>" +e "<$(( (1 + 1) ))>" +e "<$(((((((-1)))) + (((-1))))))>" +e "<$((1111+2222))>" +e "<$((2222+1111))>" +e "<$(( +0x10 + +0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( +64#10 + -64#11 ))>" +e "<$(( +0x11 + +0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( +64#11 + -64#10 ))>" +e "<$((0x8000000000000000+-1))>" +e "<$((0x8000000000000000+1))>" +e "<$((0x7FFFFFFFFFFFFFFF+-1))>" +e "<$((0x7FFFFFFFFFFFFFFF+1))>" +e "<$((0xFFFFFFFFFFFFFFFF+-1))>" +e "<$((0xFFFFFFFFFFFFFFFF+1))>" +e "<$((0x8000000000000000+-11))>" +e "<$((0x8000000000000000+11))>" +e "<$((0x7FFFFFFFFFFFFFFF+-11))>" +e "<$((0x7FFFFFFFFFFFFFFF+11))>" +e "<$((0xFFFFFFFFFFFFFFFF+-11))>" +e "<$((0xFFFFFFFFFFFFFFFF+11))>" +e '= BIN -' +e "<$((0-0))>" +e "<$(( 0 - 0 ))>" +e "<$((0-1))>" +e "<$(( 0 - 1 ))>" +e "<$((1-0))>" +e "<$(( 1 - 0 ))>" +e "<$((1-1))>" +e "<$(( 1 - 1 ))>" +e "<$(( (1 - 1) ))>" +e "<$(((((((+1)))) - (((+1))))))>" +e "<$((1111-2222))>" +e "<$((2222-1111))>" +e "<$(( +0x10 - +0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( +64#10 - -64#11 ))>" +e "<$(( +0x11 - +0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( +64#11 - -64#10 ))>" +e "<$((0x8000000000000000--1))>" +e "<$((0x8000000000000000-1))>" +e "<$((0x7FFFFFFFFFFFFFFF--1))>" +e "<$((0x7FFFFFFFFFFFFFFF-1))>" +e "<$((0xFFFFFFFFFFFFFFFF--1))>" +e "<$((0xFFFFFFFFFFFFFFFF-1))>" +e "<$((0x8000000000000000--11))>" +e "<$((0x8000000000000000-11))>" +e "<$((0x7FFFFFFFFFFFFFFF--11))>" +e "<$((0x7FFFFFFFFFFFFFFF-11))>" +e "<$((0xFFFFFFFFFFFFFFFF--11))>" +e "<$((0xFFFFFFFFFFFFFFFF-11))>" +e '= BIN *' +e "<$((0*0))>" +e "<$(( 0 * 0 ))>" +e "<$((0*1))>" +e "<$(( 0 * 1 ))>" +e "<$((1*0))>" +e "<$(( 1 * 0 ))>" +e "<$((1*1))>" +e "<$(( 1 * 1 ))>" +e "<$((1111*2222))>" +e "<$((2222*1111))>" +e "<$(( +0x10 * +0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( +64#10 * -64#11 ))>" +e "<$(( +0x11 * +0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( +64#11 * -64#10 ))>" +e "<$((0x8000000000000000*-1))>" +e "<$((0x8000000000000000*1))>" +e "<$((0x7FFFFFFFFFFFFFFF*-1))>" +e "<$((0x7FFFFFFFFFFFFFFF*1))>" +e "<$((0xFFFFFFFFFFFFFFFF*-1))>" +e "<$((0xFFFFFFFFFFFFFFFF*1))>" +e "<$((0x8000000000000000*-11))>" +e "<$((0x8000000000000000*11))>" +e "<$((0x7FFFFFFFFFFFFFFF*-11))>" +e "<$((0x7FFFFFFFFFFFFFFF*11))>" +e "<$((0xFFFFFFFFFFFFFFFF*-11))>" +e "<$((0xFFFFFFFFFFFFFFFF*11))>" +e '= BIN /' +e "<$(( 0 / 1 ))>" +e "<$((1/1))>" +e "<$(( 1 / 1 ))>" +e "<$((1111/2222))>" +e "<$((2222/1111))>" +e "<$(( +0x10 / +0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( +64#10 / -64#11 ))>" +e "<$(( +0x11 / +0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( +64#11 / -64#10 ))>" +e "<$((2/1))>" +e "<$((3/1))>" +e "<$((3/2))>" +e "<$((3/3))>" +e "<$((3/4))>" +e "<$((-1/4))>" +e "<$((0x8000000000000000/-1))>" +e "<$((0x8000000000000000/1))>" +e "<$((0x7FFFFFFFFFFFFFFF/-1))>" +e "<$((0x7FFFFFFFFFFFFFFF/1))>" +e "<$((0xFFFFFFFFFFFFFFFF/-1))>" +e "<$((0xFFFFFFFFFFFFFFFF/1))>" +e "<$((0x8000000000000000/-11))>" +e "<$((0x8000000000000000/11))>" +e "<$((0x7FFFFFFFFFFFFFFF/-11))>" +e "<$((0x7FFFFFFFFFFFFFFF/11))>" +e "<$((0xFFFFFFFFFFFFFFFF/-11))>" +e "<$((0xFFFFFFFFFFFFFFFF/11))>" +e '= BIN %' +e "<$(( 0 % 1 ))>" +e "<$((1%1))>" +e "<$(( 1 % 1 ))>" +e "<$((1111%2222))>" +e "<$((2222%1111))>" +e "<$(( +0x10 % +0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( +64#10 % -64#11 ))>" +e "<$(( +0x11 % +0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( +64#11 % -64#10 ))>" +e "<$((2%1))>" +e "<$((3%1))>" +e "<$((3%2))>" +e "<$((3%3))>" +e "<$((3%4))>" +e "<$((-1%4))>" +e "<$((0x8000000000000000%-1))>" +e "<$((0x8000000000000000%1))>" +e "<$((0x7FFFFFFFFFFFFFFF%-1))>" +e "<$((0x7FFFFFFFFFFFFFFF%1))>" +e "<$((0xFFFFFFFFFFFFFFFF%-1))>" +e "<$((0xFFFFFFFFFFFFFFFF%1))>" +e "<$((0x8000000000000000%-11))>" +e "<$((0x8000000000000000%11))>" +e "<$((0x7FFFFFFFFFFFFFFF%-11))>" +e "<$((0x7FFFFFFFFFFFFFFF%11))>" +e "<$((0xFFFFFFFFFFFFFFFF%-11))>" +e "<$((0xFFFFFFFFFFFFFFFF%11))>" +e '= BIN <<' +e "<$((0<<0))>" +e "<$(( 0 << 0 ))>" +e "<$((0<<1))>" +e "<$(( 0 << 1 ))>" +e "<$((1<<0))>" +e "<$(( 1 << 0 ))>" +e "<$((1<<1))>" +e "<$(( 1 << 1 ))>" +e "<$((1111<<2222))>" +e "<$((2222<<1111))>" +e "<$(( +0x10 << +0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( +64#10 << -64#11 ))>" +e "<$(( +0x11 << +0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( +64#11 << -64#10 ))>" +e "<$(( +64 << +1024 ))>" +e "<$((0x8000000000000000<<-1))>" +e "<$((0x8000000000000000<<1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<1))>" +e "<$((0x8000000000000000<<-11))>" +e "<$((0x8000000000000000<<11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<11))>" +e '= BIN >>' +e "<$((0>>0))>" +e "<$(( 0 >> 0 ))>" +e "<$((0>>1))>" +e "<$(( 0 >> 1 ))>" +e "<$((1>>0))>" +e "<$(( 1 >> 0 ))>" +e "<$((1>>1))>" +e "<$(( 1 >> 1 ))>" +e "<$((1>>>1))>" +e "<$(( 1 >>> 1 ))>" +e "<$((1111>>2222))>" +e "<$((2222>>1111))>" +e "<$((1111>>>2222))>" +e "<$((2222>>>1111))>" +e "<$(( +0x10 >> +0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >>> -0x11 ))>" +e "<$(( +64#10 >> -64#11 ))>" +e "<$(( +0x11 >> +0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( +64#11 >> -64#10 ))>" +e "<$(( +64 >> +1024 ))>" +e "<$((0x8000000000000000>>-1))>" +e "<$((0x8000000000000000>>1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>1))>" +e "<$((0x8000000000000000>>-11))>" +e "<$((0x8000000000000000>>11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" +e '= BIN **' +e "<$((0**1))>" +e "<$((2**1))>" +e "<$((2**2))>" +e "<$((2**3))>" +e "<$((2**4))>" +e "<$((10**4))>" +e "<$((10**10))>" +e "<$((10**5+5))>" +e "<$((10**(5+5)))>" +e '= LOG OR' +e "<$((0||0))>" +e "<$(( 000 || 0X0 ))>" +e "<$((01 || 64#1))>" +e "<$((01 || 64#1))>" +e "<$((0x1234 || 4660))>" +e "<$((0x1234 || 011064))>" +s I=33 J=33;e "<$((I||J))>" +s I=33 J=33;e "<$(( I || J ))>" +e "<$((0||1))>" +e "<$((0||0000000000000000000000001))>" +e "<$((1||2))>" +e "<$((0x1234 || 04660))>" +e "<$((0x1234 || 0x11064))>" +s I=10 J=33;e "<$((I||J))>" +s I=-10 J=-33;e "<$((I||J))>" +s I=-33 J=-33;e "<$((I||J))>" +s I=0 J=-33;e "<$((I||J))>" +s I=33 J=0;e "<$((I||J))>" +e '= LOG AND' +e "<$((0&&0))>" +e "<$(( 000 && 0X0 ))>" +e "<$((01 && 64#1))>" +e "<$((01 && 64#1))>" +e "<$((0x1234 && 4660))>" +e "<$((0x1234 && 011064))>" +s I=33 J=33;e "<$((I&&J))>" +s I=33 J=33;e "<$(( I && J ))>" +e "<$((0&&1))>" +e "<$((0&&0000000000000000000000001))>" +e "<$((1&&2))>" +e "<$((0x1234 && 04660))>" +e "<$((0x1234 && 0x11064))>" +s I=10 J=33;e "<$((I&&J))>" +s I=-10 J=-33;e "<$((I&&J))>" +s I=-33 J=-33;e "<$((I&&J))>" +s I=0 J=-33;e "<$((I&&J))>" +s I=33 J=0;e "<$((I&&J))>" +e '= BIN BIT_OR' +e "<$((0|0))>" +e "<$(( 0 | 0 ))>" +e "<$((0|1))>" +e "<$(( 0 | 1 ))>" +e "<$((1|0))>" +e "<$(( 1 | 0 ))>" +e "<$((1|1))>" +e "<$(( 1 | 1 ))>" +e "<$((1111|2222))>" +e "<$((2222|1111))>" +e "<$(( +0x10 | +0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( +64#10 | -64#11 ))>" +e "<$(( +0x11 | +0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( +64#11 | -64#10 ))>" +e "<$(( +64 | +1024 ))>" +e "<$((0x8000000000000000|-1))>" +e "<$((0x8000000000000000|1))>" +e "<$((0x7FFFFFFFFFFFFFFF|-1))>" +e "<$((0x7FFFFFFFFFFFFFFF|1))>" +e "<$((0xFFFFFFFFFFFFFFFF|-1))>" +e "<$((0xFFFFFFFFFFFFFFFF|1))>" +e "<$((0x8000000000000000|-11))>" +e "<$((0x8000000000000000|11))>" +e "<$((0x7FFFFFFFFFFFFFFF|-11))>" +e "<$((0x7FFFFFFFFFFFFFFF|11))>" +e "<$((0xFFFFFFFFFFFFFFFF|-11))>" +e "<$((0xFFFFFFFFFFFFFFFF|11))>" +e '= BIN BIT_XOR' +e "<$((0^0))>" +e "<$(( 0 ^ 0 ))>" +e "<$((0^1))>" +e "<$(( 0 ^ 1 ))>" +e "<$((1^0))>" +e "<$(( 1 ^ 0 ))>" +e "<$((1^1))>" +e "<$(( 1 ^ 1 ))>" +e "<$((1111^2222))>" +e "<$((2222^1111))>" +e "<$(( +0x10 ^ +0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( +64#10 ^ -64#11 ))>" +e "<$(( +0x11 ^ +0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( +64#11 ^ -64#10 ))>" +e "<$(( +64 ^ +1024 ))>" +e "<$((0x8000000000000000^-1))>" +e "<$((0x8000000000000000^1))>" +e "<$((0x7FFFFFFFFFFFFFFF^-1))>" +e "<$((0x7FFFFFFFFFFFFFFF^1))>" +e "<$((0xFFFFFFFFFFFFFFFF^-1))>" +e "<$((0xFFFFFFFFFFFFFFFF^1))>" +e "<$((0x8000000000000000^-11))>" +e "<$((0x8000000000000000^11))>" +e "<$((0x7FFFFFFFFFFFFFFF^-11))>" +e "<$((0x7FFFFFFFFFFFFFFF^11))>" +e "<$((0xFFFFFFFFFFFFFFFF^-11))>" +e "<$((0xFFFFFFFFFFFFFFFF^11))>" +e '= BIN BIT_AND' +e "<$((0&0))>" +e "<$(( 0 & 0 ))>" +e "<$((0&1))>" +e "<$(( 0 & 1 ))>" +e "<$((1&0))>" +e "<$(( 1 & 0 ))>" +e "<$((1&1))>" +e "<$(( 1 & 1 ))>" +e "<$((1111&2222))>" +e "<$((2222&1111))>" +e "<$(( +0x10 & +0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( +64#10 & -64#11 ))>" +e "<$(( +0x11 & +0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( +64#11 & -64#10 ))>" +e "<$(( +64 & +1024 ))>" +e "<$((0x8000000000000000&-1))>" +e "<$((0x8000000000000000&1))>" +e "<$((0x7FFFFFFFFFFFFFFF&-1))>" +e "<$((0x7FFFFFFFFFFFFFFF&1))>" +e "<$((0xFFFFFFFFFFFFFFFF&-1))>" +e "<$((0xFFFFFFFFFFFFFFFF&1))>" +e "<$((0x8000000000000000&-11))>" +e "<$((0x8000000000000000&11))>" +e "<$((0x7FFFFFFFFFFFFFFF&-11))>" +e "<$((0x7FFFFFFFFFFFFFFF&11))>" +e "<$((0xFFFFFFFFFFFFFFFF&-11))>" +e "<$((0xFFFFFFFFFFFFFFFF&11))>" +e '= BIN EQ' +e "<$((0==0))>" +e "<$(( 000 == 0X0 ))>" +e "<$((01 == 64#1))>" +e "<$((01 == 64#1))>" +e "<$((0x1234 == 4660))>" +e "<$((0x1234 == 011064))>" +s I=33 J=33;e "<$((I==J))>" +s I=33 J=33;e "<$(( I == J ))>" +e "<$((0==1))>" +e "<$((0==0000000000000000000000001))>" +e "<$((1==2))>" +e "<$((0x1234 == 04660))>" +e "<$((0x1234 == 0x11064))>" +s I=10 J=33;e "<$((I==J))>" +s I=-10 J=-33;e "<$((I==J))>" +s I=-33 J=-33;e "<$((I==J))>" +e '= BIN NE' +e "<$((0!=0))>" +e "<$(( 000 != 0X0 ))>" +e "<$((01 != 64#1))>" +e "<$((01 != 64#1))>" +e "<$((0x1234 != 4660))>" +e "<$((0x1234 != 011064))>" +s I=33 J=33;e "<$((I!=J))>" +s I=33 J=33;e "<$(( I != J ))>" +e "<$((0!=1))>" +e "<$((0!=0000000000000000000000001))>" +e "<$((1!=2))>" +e "<$((0x1234 != 04660))>" +e "<$((0x1234 != 0x11064))>" +s I=10 J=33;e "<$((I!=J))>" +s I=-10 J=-33;e "<$((I!=J))>" +s I=-33 J=-33;e "<$((I!=J))>" +e '= BIN LE' +e "<$((0<=0))>" +e "<$(( 000 <= 0X0 ))>" +e "<$((01 <= 64#1))>" +e "<$((01 <= 64#2))>" +e "<$((02 <= 64#1))>" +e "<$((0x1234 <= 4660))>" +e "<$((0x1234 <= 011064))>" +e "<$((0x1233 <= 011064))>" +e "<$((0x1235 <= 011064))>" +s I=33 J=33;e "<$((I<=J))>" +s I=33 J=33;e "<$((I<=J))>" +s I=32 J=33;e "<$((I<=J))>" +s I=34 J=33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-32 J=-33;e "<$((I<=J))>" +s I=-34 J=-33;e "<$((I<=J))>" +e '= BIN GE' +e "<$((0>=0))>" +e "<$(( 000 >= 0X0 ))>" +e "<$((01 >= 64#1))>" +e "<$((01 >= 64#2))>" +e "<$((02 >= 64#1))>" +e "<$((0x1234 >= 4660))>" +e "<$((0x1234 >= 011064))>" +e "<$((0x1233 >= 011064))>" +e "<$((0x1235 >= 011064))>" +s I=33 J=33;e "<$((I>=J))>" +s I=33 J=33;e "<$((I>=J))>" +s I=32 J=33;e "<$((I>=J))>" +s I=34 J=33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-32 J=-33;e "<$((I>=J))>" +s I=-34 J=-33;e "<$((I>=J))>" +e '= BIN LT' +e "<$((0<0))>" +e "<$(( 000 < 0X0 ))>" +e "<$((01 < 64#1))>" +e "<$((01 < 64#2))>" +e "<$((02 < 64#1))>" +e "<$((0x1234 < 4660))>" +e "<$((0x1234 < 011064))>" +e "<$((0x1233 < 011064))>" +e "<$((0x1235 < 011064))>" +s I=33 J=33;e "<$((I" +s I=33 J=33;e "<$((I" +s I=32 J=33;e "<$((I" +s I=34 J=33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-32 J=-33;e "<$((I" +s I=-34 J=-33;e "<$((I" +e '= BIN GT' +e "<$((0>0))>" +e "<$(( 000 > 0X0 ))>" +e "<$((01 > 64#1))>" +e "<$((01 > 64#2))>" +e "<$((02 > 64#1))>" +e "<$((0x1234 > 4660))>" +e "<$((0x1234 > 011064))>" +e "<$((0x1233 > 011064))>" +e "<$((0x1235 > 011064))>" +s I=33 J=33;e "<$((I>J))>" +s I=33 J=33;e "<$((I>J))>" +s I=32 J=33;e "<$((I>J))>" +s I=34 J=33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-32 J=-33;e "<$((I>J))>" +s I=-34 J=-33;e "<$((I>J))>" +# +# COMMA below +e '= PRECEDENCE I' +e "<$(( 1 + 2 + 3 ))>" +e "<$(( 1 - 2 + 3 ))>" +e "<$(( 3 - 2 - 1 ))>" +e "<$(( 3 - 2 + 1 ))>" +e "<$(( - 2 + 1 ))>" +e "<$(( 2 + -1 ))>" +e "<$(( ! 2 + 1 ))>" +e "<$(( 2 + !1 ))>" +e "<$(( 3 * 2 + 2 ))>" +e "<$(( 3 + 2 * 2 ))>" +e "<$(( 3 * 2 * 2 ))>" +e "<$(( 9 / 3 + 2 ))>" +e "<$(( 9 + 3 / 2 ))>" +e "<$(( 9 / 3 / 2 ))>" +e "<$(( 9 << 1 + 2 ))>" +e "<$(( 9 + 3 << 2 ))>" +e "<$(( 9 << 3 << 2 ))>" +e "<$(( 9 >> 1 + 2 ))>" +e "<$(( 9 + 3 >> 2 ))>" +e "<$(( 19 >> 3 >> 1 ))>" +e "<$(( 19 >> 3 << 1 ))>" +e "<$(( 19 << 3 >> 1 ))>" +e "<$(( 2 + 3 < 3 * 2 ))>" +e "<$(( 2 << 3 >= 3 << 2 ))>" +e "<$(( 0xfD & 0xF == 0xF ))>" +e "<$((0xfD&0xF==0xF))>" +e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" +e "<$((3*7,2<<8,9-7))>" +e '= PARENS' +e "<$(((1 + 2) + 3))>" +e "<$(((1+2)+3))>" +e "<$((1 - (2 + 3)))>" +e "<$((1-(2+3)))>" +e "<$((3 - (2 - 1)))>" +e "<$((3-(2-1)))>" +e "<$((3 - ( 2 + 1 )))>" +e "<$((3-(2+1)))>" +e "<$((- (2 + 1)))>" +e "<$((-(2+1)))>" +e "<$((! (2 + 1)))>" +e "<$((!(2+1)))>" +e "<$((3 * (2 + 2)))>" +e "<$((3*(2+2)))>" +e "<$(((3 + 2) * 2))>" +e "<$(((3+2)*2))>" +e "<$((3 * (2 * 2)))>" +e "<$((3*(2*8)))>" +e "<$((9 / (3 + 2)))>" +e "<$((9/(3+2)))>" +e "<$((( 9 + 3 ) / 2))>" +e "<$(((9+3)/2))>" +e "<$((9 / ( 3 / 2 )))>" +e "<$((9/(3/2)))>" +e "<$((( 9 << 1 ) + 2))>" +e "<$(((9<<1)+2))>" +e "<$((9 + (3 << 2)))>" +e "<$((9+(3<<2)))>" +e "<$((9 << (3 << 2)))>" +e "<$((9<<(3<<2)))>" +e "<$(((9 >> 1) + 2))>" +e "<$(((9>>1)+2))>" +e "<$((9 + (3 >> 2)))>" +e "<$((9+(3>>2)))>" +e "<$((19 >> (3 >> 1)))>" +e "<$((19>>(3>>1)))>" +e "<$((19 >> (3 << 1)))>" +e "<$((19>>(3<<1)))>" +e "<$((19 << (3 >> 1)))>" +e "<$((19<<(3>>1)))>" +e "<$((2 + (3 < 3) * 2))>" +e "<$((2+(3<3)*2))>" +e "<$((2 << ((3 >= 3) << 2)))>" +e "<$((2<<((3>=3)<<2)))>" +e "<$(((0xfD & 0xF) == 0xF))>" +e "<$(((0xfD&0xF)==0xF))>" +e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" +e "<$((3*(7,2)<<(8,9-7)))>" +# +# COND BELOW +e '= ASSIGN I' +unset I;p "<$(( I = 3 ))>";e "<$I>" +unset I;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I+=1))>";e "<$I>" +s I=10;p "<$((I-=1))>";e "<$I>" +s I=10;p "<$((I*=1))>";e "<$I>" +s I=10;p "<$((I*=2))>";e "<$I>" +s I=10;p "<$((I/=1))>";e "<$I>" +s I=10;p "<$((I/=2))>";e "<$I>" +s I=10;p "<$((I%=1))>";e "<$I>" +s I=10;p "<$((I%=2))>";e "<$I>" +s I=10;p "<$((I**=1))>";e "<$I>" +s I=10;p "<$((I**=2))>";e "<$I>" +s I=10;p "<$((I**=1+1))>";e "<$I>" +s I=10;p "<$((I|=1))>";e "<$I>" +s I=10;p "<$((I^=1))>";e "<$I>";p "<$((I^=1))>";e "<$I>" +s I=10;p "<$((I&=2))>";e "<$I>" +s I=10;p "<$((I>>=1))>";e "<$I>" +s I=10;p "<$((I<<=1))>";e "<$I>" +s I=-1;p "<$((I>>>=1))>";e "<$I>" +e '= ASSIGN II' +s I=2;p "<$(((I+=1)-1))>";e "<$I>" +s I=4;p "<$(((I-=1)+1))>";e "<$I>" +s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" +s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" +s I=10;p "<$((I=2,I|=1))>";e "<$I>" +s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" +e '= POSTFIX' +s I=1;p "<$((I++))>";e "<$I>" +s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" +s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" +s I=1;p "<$((I--))>";e "<$I>" +s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" +s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" +e '= PREFIX' +s I=1;p "<$((++I))>";e "<$I>" +s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" +s I=1;p "<$((--I))>";e "<$I>" +s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" +s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" +s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" +e '= VAR RECUR' +s I='1 + 1';p "<$((I))>";e "<$I>" +s I='1 + 1';p "<$((+I))>";e "<$I>" +s I='1 + 1';p "<$((++I))>";e "<$I>" +s I='1 + 1';p "<$((I++))>";e "<$I>" +s I='1 + 1';p "<$((1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" +s I='1 + 1';p "<$((I=I))>";e "<$I>" +s I='1 + 1';p "<$((I=+I))>";e "<$I>" +s I='1 + 1';p "<$((I=1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" +e '= COMMA' +e "<$(( 1 , 2 ))>" +e "<$(( 1 , 2 , 3 ))>" +e "<$(( 1 , 2 , 3 , 4 ))>" +e "<$((1,2,3,4))>" +s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" +s I1=I2=10 I2=3;p "<$((I1,I2))>";e "<$I1><$I2>" +e '= COND' +e "<$(( +0 ? 2 : 3 ))>" +e "<$((-0?2:3))>" +e "<$(( +1 ? 2 : 3 ))>" +e "<$(( 1-1 ? 2 : 3 ))>" +e "<$(( 1-0 ? 2 : 3 ))>" +e "<$((-1?2:3))>" +e "<$(( 0x1234 ? 111 : 222 ))>" +e "<$((1**2 ? 5 : 7))>" +e "<$((0**2 ? 5 : 7))>" +e "<$((0**2>=0?5:7))>" +e "<$((-1<=0**2?5:7))>" +e "<$((1<=0**2?5:7))>" +e "<$((1>2||1*0?5:7))>" +e "<$((1>2&&1*0?5:7))>" +e "<$((1<2&&1*0?5:7))>" +e "<$((1<2&&1*0+1?5:7))>" +e '-- COND .2' +e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" +e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" +e "<$((2<1?-1:2>1?1:0))>" +e "<$((4<5 ? 1 : 32))>" +e "<$((4>5 ? 1 : 32))>" +e "<$((4>(2+3) ? 1 : 32))>" +e "<$((4<(2+3) ? 1 : 32))>" +e "<$(((2+2)<(2+3) ? 1 : 32))>" +e "<$(((2+2)>(2+3) ? 1 : 32))>" +## grouping protects precedence in : parts (syntax error tests below) +e '-- COND .3' +e "<$((1-1 < 1 ? 2,4 : 1,3))>" +e "<$((0<1?2,4:(1,3)))>" +e "<$((0,1,2,0?2,4:1,3))>" +e "<$((0,1,2,1?2,4:1,3))>" +e "<$((0,1,2,0?2,4:(1,3)))>" +e "<$((0,1,2,1?2,4:(1,3)))>" +e "<$((0,1,2,0?(2,4):1,3))>" +e "<$((0,1,2,1?(2,4):1,3))>" +e "<$((0,1,2,0?(2,4):(1,3)))>" +e "<$((0,1,2,1?(2,4):(1,3)))>" +e "<$((0?2:((0,3)?1:4)))>" +e "<$((1?2:3,0?1:4))>" +e "<$((1?2:3,0?1:4?5:6))>" +e "<$((1?2:(3,0)?1:4?5:6))>" +e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" +e '-- COND .4' +e "<$((1?2?3?4?5:6:7:8:9))>" +e "<$((1?2?3?0?5:6:7:8:9))>" +e "<$((1?2?0?0?5:6:7:8:9))>" +e "<$((1?0?0?0?5:6:7:8:9))>" +e "<$((0?0?0?0?5:6:7:8:9))>" +e "<$((0?3+4?10:11:5+6?12:13))>" +e "<$((1?3+4?10:11:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" +e '-- COND .5' +e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e '-- COND .6' +e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" +e '-- COND .7' +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$((((I1";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1";\ + e "<$I1><$I2><$I3><$I4><$I5>" +# only first +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(((I1I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +# last not etc. +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3";\ + e "<$I1><$I2><$I3><$I4><$I5>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1>I2)?(I2";\ + e "<$I1><$I2><$I3><$I4><$I5>" +e '-- COND .8' +s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" +s I=0;p "<$((1?20:(I+=2)))>";e "<$I>" +s I=0;p "<$((1?I+=10:(I+=2)))>";e "<$I>" +s I=0;p "<$((0?I+=2:20))>";e "<$I>" +s I=0;p "<$((0?I+=2:(I+=10)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(20)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(I+=20)))>";e "<$I>" +e '-- COND .9' +s I1=+E+ I2=1+1;p "<$((0?I1:I2))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1:I2))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +e '-- COND .10' +s I1=+E+ I2=+E+ I3=+E+ I4=-1;p "<$((0?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=1 I2=2 I3=+E+ I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=0 I2=+E+ I3=3 I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +e '= WILD I' +e "<$(( 3 + ( 11 ) ))>" +e "<$((1 + (2 - 2)))>" +e "<$((1 + (2 - 2)))>" +e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" +e "<$(( 3+((2 * 2))/6 ))>" +e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" +e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" +s I1=I2=10 I2=3;p "<$((I1 + I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 * I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 % I2))>";e "<$I1><$I2>" +e '= WILD II' +s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" +s I=10;p "<$((3+(3*(I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" +e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" +s I=10;p "<$(( +10 + + +I ))>";e "<$I>" +s I=10;p "<$(( +10 + ++I ))>";e "<$I>" +s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" +s I=10;p "<$(( +10 +++ I ))>";e "<$I>" +s I=10;p "<$(( +10+++I ))>";e "<$I>" +s I=10;p "<$((+10++I))>";e "<$I>" +s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" +e "<$(( +10 + + + ++++ +11 ))>" +e "<$(( +10 + + + ++++ ++11 ))>" +e "<$((+10++++++++11))>" +e '= WILD RECUR' # (some yet) +s I1=I2=10 I2=5;p "<$((I1+=I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=10)+1' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=(I2=10)+1)' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=+E+ I2=5;p "<$((I1=10))>";e "<$I1><$I2>" +s I1=I2=+E+ I2=5;p "<$((0?I1:++I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5;p "<$((I2,(1?I1:++I2)))>";e "<$I1><$I2>" +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" diff --git a/shell/hush.c b/shell/hush.c index 051b123e78..be01ed035c 100644 --- a/shell/hush.c +++ b/shell/hush.c @@ -6475,7 +6475,7 @@ static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n, } #if ENABLE_FEATURE_SH_MATH -static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) +static arith_t expand_and_evaluate_arith(const char *arg, char **errmsg_p) { arith_state_t math_state; arith_t res; @@ -6489,8 +6489,13 @@ static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p) free(exp_str); if (errmsg_p) *errmsg_p = math_state.errmsg; - if (math_state.errmsg) + if (math_state.errmsg) { msg_and_die_if_script(math_state.errmsg); +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + if (errmsg_p == NULL) + free(math_state.errmsg); +# endif + } return res; } #endif @@ -6814,11 +6819,15 @@ static NOINLINE int expand_one_var(o_string *output, int n, */ arith_t beg, len; unsigned vallen; - const char *errmsg; + char *errmsg; beg = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(errmsg); +# endif goto empty_result; + } debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg); *p++ = SPECIAL_VAR_SYMBOL; exp_word = p; @@ -6838,8 +6847,12 @@ static NOINLINE int expand_one_var(o_string *output, int n, goto empty_result; } len = expand_and_evaluate_arith(exp_word, &errmsg); - if (errmsg) + if (errmsg) { +# if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(errmsg); +# endif goto empty_result; + } debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len); debug_printf_varexp("from val:'%s'\n", val); if (len < 0) { diff --git a/shell/hush_test/hush-arith/bigbadbison.right b/shell/hush_test/hush-arith/bigbadbison.right new file mode 100644 index 0000000000..a6446c81cd --- /dev/null +++ b/shell/hush_test/hush-arith/bigbadbison.right @@ -0,0 +1,880 @@ += BASE +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<0> +<10> +<9191919191919> +<13> +<11> +<1023> +<1295> +<1295> +<9322365> +<16242915> +<10> +<33> +<10> +<33> +<1> +<1> +<1> +<33> +<33> +<33> +<33> += UNA PLUS/MINUS +<0> +<0> +<1> +<1> +<4221> +<16929> +<16242915> +<16242915> +<1> +<1> +<1> +<0> +<0> +<-1> +<-1> +<-4221> +<-16929> +<-16242915> +<-16242915> +<-1> +<-1> +<-1> +<-1> +<1> +<-1> += UNA ! +<1> +<1> +<0> +<0> +<1> +<0> += UNA ~ +<-1> +<-1> +<-2> +<-2> +<-2276> +<0> +<0> +<-1> +<-1> +<-1> +<-1> += BIN + +<0> +<0> +<1> +<1> +<1> +<1> +<2> +<2> +<2> +<-2> +<3333> +<3333> +<33> +<-33> +<-33> +<-1> +<33> +<-33> +<-33> +<1> +<9223372036854775807> +<-9223372036854775807> +<9223372036854775806> +<-9223372036854775808> +<-2> +<0> +<9223372036854775797> +<-9223372036854775797> +<9223372036854775796> +<-9223372036854775798> +<-12> +<10> += BIN - +<0> +<0> +<-1> +<-1> +<1> +<1> +<0> +<0> +<0> +<0> +<-1111> +<1111> +<-1> +<1> +<1> +<129> +<1> +<-1> +<-1> +<129> +<-9223372036854775807> +<9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<-9223372036854775797> +<9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN * +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<2468642> +<2468642> +<272> +<272> +<272> +<-4160> +<272> +<272> +<272> +<-4160> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775797> +<9223372036854775797> +<11> +<-11> += BIN / +<0> +<1> +<1> +<0> +<2> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<-1> +<2> +<3> +<1> +<1> +<0> +<0> +<-9223372036854775808> +<-9223372036854775808> +<-9223372036854775807> +<9223372036854775807> +<1> +<-1> +<838488366986797800> +<-838488366986797800> +<-838488366986797800> +<838488366986797800> +<0> +<0> += BIN % +<0> +<0> +<0> +<1111> +<0> +<16> +<-16> +<-16> +<64> +<1> +<-1> +<-1> +<1> +<0> +<0> +<1> +<0> +<3> +<-1> +<0> +<0> +<0> +<0> +<0> +<0> +<-8> +<-8> +<7> +<7> +<-1> +<-1> += BIN << +<0> +<0> +<0> +<0> +<1> +<1> +<2> +<2> +<78179674781384704> +<18639486976> +<2097152> +<-2251799813685248> +<-2251799813685248> +<0> +<1114112> +<-4785074604081152> +<-4785074604081152> +<65> +<64> +<0> +<0> +<-9223372036854775808> +<-2> +<-9223372036854775808> +<-2> +<0> +<0> +<-9007199254740992> +<-2048> +<-9007199254740992> +<-2048> += BIN >> +<0> +<0> +<0> +<0> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<-1> +<-1> +<131071> +<0> +<0> +<-1> +<-1> +<65> +<64> +<-1> +<-4611686018427387904> +<0> +<4611686018427387903> +<-1> +<-1> +<-1024> +<-4503599627370496> +<1023> +<4503599627370495> +<-1> +<-1> +<9007199254740991> += BIN ** +<0> +<2> +<4> +<8> +<16> +<10000> +<10000000000> +<100005> +<10000000000> += LOG OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> += LOG AND +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> += BIN BIT_OR +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<3327> +<3327> +<17> +<-1> +<-1> +<-1> +<17> +<-1> +<-1> +<-63> +<1088> +<-1> +<-9223372036854775807> +<-1> +<9223372036854775807> +<-1> +<-1> +<-11> +<-9223372036854775797> +<-1> +<9223372036854775807> +<-1> +<-1> += BIN BIT_XOR +<0> +<0> +<1> +<1> +<1> +<1> +<0> +<0> +<3321> +<3321> +<1> +<31> +<31> +<-1> +<1> +<31> +<31> +<-127> +<1088> +<9223372036854775807> +<-9223372036854775807> +<-9223372036854775808> +<9223372036854775806> +<0> +<-2> +<9223372036854775797> +<-9223372036854775797> +<-9223372036854775798> +<9223372036854775796> +<10> +<-12> += BIN BIT_AND +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<6> +<6> +<16> +<-32> +<-32> +<0> +<16> +<-32> +<-32> +<64> +<0> +<-9223372036854775808> +<0> +<9223372036854775807> +<1> +<-1> +<1> +<-9223372036854775808> +<0> +<9223372036854775797> +<11> +<-11> +<11> += BIN EQ +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> += BIN NE +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<0> +<1> +<1> +<1> +<1> +<1> +<1> +<1> +<0> += BIN LE +<1> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<0> +<1> += BIN GE +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<0> +<1> +<1> +<1> +<1> +<0> += BIN LT +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<0> +<1> += BIN GT +<0> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<0> +<1> +<0> +<0> +<1> +<0> += PRECEDENCE I +<6> +<2> +<0> +<2> +<-1> +<1> +<1> +<2> +<8> +<7> +<12> +<5> +<10> +<1> +<72> +<48> +<288> +<1> +<3> +<1> +<4> +<76> +<1> +<1> +<1> +<1> +<2> +<2> += PARENS +<6> +<6> +<-4> +<-4> +<2> +<2> +<0> +<0> +<-3> +<-3> +<0> +<0> +<12> +<12> +<10> +<10> +<12> +<48> +<1> +<1> +<6> +<6> +<9> +<9> +<20> +<20> +<21> +<21> +<36864> +<36864> +<6> +<6> +<9> +<9> +<9> +<9> +<0> +<0> +<38> +<38> +<2> +<2> +<32> +<32> +<0> +<0> +<24> +<24> += ASSIGN I +<3><3> +<3><3> +<3><3> +<11><11> +<9><9> +<10><10> +<20><20> +<10><10> +<5><5> +<0><0> +<0><0> +<10><10> +<100><100> +<100><100> +<11><11> +<11><11> +<10><10> +<2><2> +<5><5> +<20><20> +<9223372036854775807><9223372036854775807> += ASSIGN II +<2><3> +<4><3> +<36><5><7> +<1501><100><15> +<3><3> +<10><1><2><3><10> += POSTFIX +<1><2> +<1><2><1> +<10><2><11> +<10><2><11> +<1><0> +<1><0><1> +<10><0><9> +<10><0><9> += PREFIX +<2><2> +<2><2><2> +<22><2><11> +<10><1><10> +<22><2><11> +<0><0> +<0><0><0> +<9><1><9> +<10><1><10> +<0><0><9> += VAR RECUR +<2><1 + 1> +<2><1 + 1> +<3><3> +<2><3> +<3><1 + 1> +<4><1 + 1 * 2> +<5><(1 + 1) * 2> +<3><3><3 / 2> +<2><2> +<2><2> +<3><3> +<4><4> +<5><5> +<5><5><3 / 2> += COMMA +<2> +<3> +<4> +<4> +<133><133> +<10><10> += COND +<3> +<3> +<2> +<3> +<2> +<2> +<111> +<5> +<7> +<5> +<5> +<7> +<7> +<7> +<7> +<5> +-- COND .2 +<-1> +<0> +<1> +<1> +<32> +<32> +<1> +<1> +<32> +-- COND .3 +<3> +<4> +<3> +<3> +<3> +<4> +<3> +<3> +<3> +<4> +<1> +<4> +<5> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<10> +<10> +<10> +<10> +<10> +<2> +<5> +<5> +<8> +<8> +<10> +<10> +<10> +<10> +<10> +-- COND .4 +<5> +<6> +<7> +<8> +<9> +<12> +<10> +<12> +<10> +-- COND .5 +<12> +<10> +<12> +<10> +-- COND .6 +<12> +<9> +<-2> +<-1> +<23> +<26> +<24> +<0> +<23> +<23> +<23> +-- COND .7 +<16><2><3><16><5> +<16><2><3><16><5> +<16><2><3><16><5> +<25><2><3><4><25><> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<9><2><9><4><5> +<16><2><3><16><5> +<9><2><9><4><5><> +<4><4><3><4><5><> +-- COND .8 +<10><2> +<20><0> +<10><10> +<20><0> +<10><10> +<20><0> +<20><20> +-- COND .9 +<2><+E+><1+1> +<2><1+1><+E+> +<2><+E+><2> +<1><1><+E+> +<4><+E+><4> +<4><4><+E+> +-- COND .10 +<-1><+E+><+E+><+E+><-1> +<2><1><2><+E+><+E+> +<3><0><+E+><3><+E+> += WILD I +<14> +<1> +<1> +<1> +<3> +<87> +<2097152> +<20><10> +<100><10> +<0><10> += WILD II +<36><11> +<33><11> +<36><12> +<39><12> +<39><12> +<-33><12> +<-27> +<20><10> +<21><11> +<20><10> +<21><11> +<21><11> +<20><10> +<20><10> +<21> +<21> +<21> += WILD RECUR +<20><20><10> +<10><10> +<11><11> +<21><11> +<10><10> +<1><1> +<6><6><6> +<10><10><5> +<12><12> +<12><12> +<10><11> +<10><11> +<10><10><5> +<6><6> +<10><10> +<10><0><10><20> +<10><6><10><20> +<10><10><10><20> +<50><50><10><20> +<50><50><10><20> +<500><500><10><20> diff --git a/shell/hush_test/hush-arith/bigbadbison.tests b/shell/hush_test/hush-arith/bigbadbison.tests new file mode 100755 index 0000000000..2b15fda7c9 --- /dev/null +++ b/shell/hush_test/hush-arith/bigbadbison.tests @@ -0,0 +1,914 @@ +# make this work with (ba)sh \ +command -v shopt && shopt -s expand_aliases;\ +alias p=printf;alias e=echo;alias s=export +s I=10 J=33 +e '= BASE' +e "<$(())>" +e "<$(( ))>" +e "<$((1))>" +e "<$((0))>" +e "<$((0x0))>" +e "<$((0X0))>" +e "<$((000))>" +e "<$((000000000000001))>" +e "<$((2#00000000000000000000000000000000000001))>" +e "<$((0X00000000000000000000000000000000000000000001))>" +e "<$((999999999999999999999999999999999999999999999))>" +e "<$(( 10 ))>" +e "<$((9191919191919))>" +e "<$((0xD))>" +e "<$((013))>" +e "<$((32#VV))>" +e "<$((36#ZZ))>" +e "<$((36#zz))>" +e "<$(( 64#zzZZ ))>" +e "<$((64#ZZzz))>" +e "<$((I))>" +e "<$((J))>" +e "<$(( I ))>" +e "<$(( J ))>" +e "<$(( (1) ))>" +e "<$((((1))))>" +e "<$(((((1)))))>" +e "<$(( (J) ))>" +e "<$((((J))))>" +e "<$(((((J)))))>" +e "<$(( ( ( ( J ) ) ) ))>" +e '= UNA PLUS/MINUS' +e "<$((+0))>" +e "<$(( + 0 ))>" +e "<$(( +1))>" +e "<$((+ 1 ))>" +e "<$(( + 4221 ))>" +e "<$(( +0x4221 ))>" +e "<$(( + 64#ZZzz ))>" +e "<$(( +64#ZZzz ))>" +e "<$((+ (1) ))>" +e "<$((+((1))))>" +e "<$((+(((1)))))>" +e "<$((-0))>" +e "<$(( - 0 ))>" +e "<$(( -1))>" +e "<$((- 1 ))>" +e "<$(( - 4221 ))>" +e "<$(( -0x4221 ))>" +e "<$(( - 64#ZZzz ))>" +e "<$(( -64#ZZzz ))>" +e "<$((- (1) ))>" +e "<$((-((1))))>" +e "<$((-(((1)))))>" +e "<$((+ -(1) ))>" +e "<$((+(-(-1))))>" +e "<$((+(-(-(-1)))))>" +e '= UNA !' +e "<$((!0))>" +e "<$((! 00000000))>" +e "<$((!1))>" +e "<$((! 0x00001))>" +e "<$((! - 0))>" +e "<$((!-1))>" +e '= UNA ~' +e "<$((~0))>" +e "<$((~ 00000000))>" +e "<$((~1))>" +e "<$((~ 0x00001))>" +e "<$((~ 64#zz))>" +e "<$((~-1))>" +e "<$((~ - 1))>" +e "<$((~-0))>" +e "<$((~ - 0))>" +e "<$((~(-0)))>" +e "<$((~((- 0))))>" +e '= BIN +' +e "<$((0+0))>" +e "<$(( 0 + 0 ))>" +e "<$((0+1))>" +e "<$(( 0 + 1 ))>" +e "<$((1+0))>" +e "<$(( 1 + 0 ))>" +e "<$((1+1))>" +e "<$(( 1 + 1 ))>" +e "<$(( (1 + 1) ))>" +e "<$(((((((-1)))) + (((-1))))))>" +e "<$((1111+2222))>" +e "<$((2222+1111))>" +e "<$(( +0x10 + +0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( -0x10 + -0x11 ))>" +e "<$(( +64#10 + -64#11 ))>" +e "<$(( +0x11 + +0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( -0x11 + -0x10 ))>" +e "<$(( +64#11 + -64#10 ))>" +e "<$((0x8000000000000000+-1))>" +e "<$((0x8000000000000000+1))>" +e "<$((0x7FFFFFFFFFFFFFFF+-1))>" +e "<$((0x7FFFFFFFFFFFFFFF+1))>" +e "<$((0xFFFFFFFFFFFFFFFF+-1))>" +e "<$((0xFFFFFFFFFFFFFFFF+1))>" +e "<$((0x8000000000000000+-11))>" +e "<$((0x8000000000000000+11))>" +e "<$((0x7FFFFFFFFFFFFFFF+-11))>" +e "<$((0x7FFFFFFFFFFFFFFF+11))>" +e "<$((0xFFFFFFFFFFFFFFFF+-11))>" +e "<$((0xFFFFFFFFFFFFFFFF+11))>" +e '= BIN -' +e "<$((0-0))>" +e "<$(( 0 - 0 ))>" +e "<$((0-1))>" +e "<$(( 0 - 1 ))>" +e "<$((1-0))>" +e "<$(( 1 - 0 ))>" +e "<$((1-1))>" +e "<$(( 1 - 1 ))>" +e "<$(( (1 - 1) ))>" +e "<$(((((((+1)))) - (((+1))))))>" +e "<$((1111-2222))>" +e "<$((2222-1111))>" +e "<$(( +0x10 - +0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( -0x10 - -0x11 ))>" +e "<$(( +64#10 - -64#11 ))>" +e "<$(( +0x11 - +0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( -0x11 - -0x10 ))>" +e "<$(( +64#11 - -64#10 ))>" +e "<$((0x8000000000000000--1))>" +e "<$((0x8000000000000000-1))>" +e "<$((0x7FFFFFFFFFFFFFFF--1))>" +e "<$((0x7FFFFFFFFFFFFFFF-1))>" +e "<$((0xFFFFFFFFFFFFFFFF--1))>" +e "<$((0xFFFFFFFFFFFFFFFF-1))>" +e "<$((0x8000000000000000--11))>" +e "<$((0x8000000000000000-11))>" +e "<$((0x7FFFFFFFFFFFFFFF--11))>" +e "<$((0x7FFFFFFFFFFFFFFF-11))>" +e "<$((0xFFFFFFFFFFFFFFFF--11))>" +e "<$((0xFFFFFFFFFFFFFFFF-11))>" +e '= BIN *' +e "<$((0*0))>" +e "<$(( 0 * 0 ))>" +e "<$((0*1))>" +e "<$(( 0 * 1 ))>" +e "<$((1*0))>" +e "<$(( 1 * 0 ))>" +e "<$((1*1))>" +e "<$(( 1 * 1 ))>" +e "<$((1111*2222))>" +e "<$((2222*1111))>" +e "<$(( +0x10 * +0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( -0x10 * -0x11 ))>" +e "<$(( +64#10 * -64#11 ))>" +e "<$(( +0x11 * +0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( -0x11 * -0x10 ))>" +e "<$(( +64#11 * -64#10 ))>" +e "<$((0x8000000000000000*-1))>" +e "<$((0x8000000000000000*1))>" +e "<$((0x7FFFFFFFFFFFFFFF*-1))>" +e "<$((0x7FFFFFFFFFFFFFFF*1))>" +e "<$((0xFFFFFFFFFFFFFFFF*-1))>" +e "<$((0xFFFFFFFFFFFFFFFF*1))>" +e "<$((0x8000000000000000*-11))>" +e "<$((0x8000000000000000*11))>" +e "<$((0x7FFFFFFFFFFFFFFF*-11))>" +e "<$((0x7FFFFFFFFFFFFFFF*11))>" +e "<$((0xFFFFFFFFFFFFFFFF*-11))>" +e "<$((0xFFFFFFFFFFFFFFFF*11))>" +e '= BIN /' +e "<$(( 0 / 1 ))>" +e "<$((1/1))>" +e "<$(( 1 / 1 ))>" +e "<$((1111/2222))>" +e "<$((2222/1111))>" +e "<$(( +0x10 / +0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( -0x10 / -0x11 ))>" +e "<$(( +64#10 / -64#11 ))>" +e "<$(( +0x11 / +0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( -0x11 / -0x10 ))>" +e "<$(( +64#11 / -64#10 ))>" +e "<$((2/1))>" +e "<$((3/1))>" +e "<$((3/2))>" +e "<$((3/3))>" +e "<$((3/4))>" +e "<$((-1/4))>" +e "<$((0x8000000000000000/-1))>" +e "<$((0x8000000000000000/1))>" +e "<$((0x7FFFFFFFFFFFFFFF/-1))>" +e "<$((0x7FFFFFFFFFFFFFFF/1))>" +e "<$((0xFFFFFFFFFFFFFFFF/-1))>" +e "<$((0xFFFFFFFFFFFFFFFF/1))>" +e "<$((0x8000000000000000/-11))>" +e "<$((0x8000000000000000/11))>" +e "<$((0x7FFFFFFFFFFFFFFF/-11))>" +e "<$((0x7FFFFFFFFFFFFFFF/11))>" +e "<$((0xFFFFFFFFFFFFFFFF/-11))>" +e "<$((0xFFFFFFFFFFFFFFFF/11))>" +e '= BIN %' +e "<$(( 0 % 1 ))>" +e "<$((1%1))>" +e "<$(( 1 % 1 ))>" +e "<$((1111%2222))>" +e "<$((2222%1111))>" +e "<$(( +0x10 % +0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( -0x10 % -0x11 ))>" +e "<$(( +64#10 % -64#11 ))>" +e "<$(( +0x11 % +0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( -0x11 % -0x10 ))>" +e "<$(( +64#11 % -64#10 ))>" +e "<$((2%1))>" +e "<$((3%1))>" +e "<$((3%2))>" +e "<$((3%3))>" +e "<$((3%4))>" +e "<$((-1%4))>" +e "<$((0x8000000000000000%-1))>" +e "<$((0x8000000000000000%1))>" +e "<$((0x7FFFFFFFFFFFFFFF%-1))>" +e "<$((0x7FFFFFFFFFFFFFFF%1))>" +e "<$((0xFFFFFFFFFFFFFFFF%-1))>" +e "<$((0xFFFFFFFFFFFFFFFF%1))>" +e "<$((0x8000000000000000%-11))>" +e "<$((0x8000000000000000%11))>" +e "<$((0x7FFFFFFFFFFFFFFF%-11))>" +e "<$((0x7FFFFFFFFFFFFFFF%11))>" +e "<$((0xFFFFFFFFFFFFFFFF%-11))>" +e "<$((0xFFFFFFFFFFFFFFFF%11))>" +e '= BIN <<' +e "<$((0<<0))>" +e "<$(( 0 << 0 ))>" +e "<$((0<<1))>" +e "<$(( 0 << 1 ))>" +e "<$((1<<0))>" +e "<$(( 1 << 0 ))>" +e "<$((1<<1))>" +e "<$(( 1 << 1 ))>" +e "<$((1111<<2222))>" +e "<$((2222<<1111))>" +e "<$(( +0x10 << +0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( -0x10 << -0x11 ))>" +e "<$(( +64#10 << -64#11 ))>" +e "<$(( +0x11 << +0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( -0x11 << -0x10 ))>" +e "<$(( +64#11 << -64#10 ))>" +e "<$(( +64 << +1024 ))>" +e "<$((0x8000000000000000<<-1))>" +e "<$((0x8000000000000000<<1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-1))>" +e "<$((0x7FFFFFFFFFFFFFFF<<1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-1))>" +e "<$((0xFFFFFFFFFFFFFFFF<<1))>" +e "<$((0x8000000000000000<<-11))>" +e "<$((0x8000000000000000<<11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<-11))>" +e "<$((0x7FFFFFFFFFFFFFFF<<11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<-11))>" +e "<$((0xFFFFFFFFFFFFFFFF<<11))>" +e '= BIN >>' +e "<$((0>>0))>" +e "<$(( 0 >> 0 ))>" +e "<$((0>>1))>" +e "<$(( 0 >> 1 ))>" +e "<$((1>>0))>" +e "<$(( 1 >> 0 ))>" +e "<$((1>>1))>" +e "<$(( 1 >> 1 ))>" +e "<$((1>>>1))>" +e "<$(( 1 >>> 1 ))>" +e "<$((1111>>2222))>" +e "<$((2222>>1111))>" +e "<$((1111>>>2222))>" +e "<$((2222>>>1111))>" +e "<$(( +0x10 >> +0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >> -0x11 ))>" +e "<$(( -0x10 >>> -0x11 ))>" +e "<$(( +64#10 >> -64#11 ))>" +e "<$(( +0x11 >> +0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( -0x11 >> -0x10 ))>" +e "<$(( +64#11 >> -64#10 ))>" +e "<$(( +64 >> +1024 ))>" +e "<$((0x8000000000000000>>-1))>" +e "<$((0x8000000000000000>>1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-1))>" +e "<$((0x7FFFFFFFFFFFFFFF>>1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-1))>" +e "<$((0xFFFFFFFFFFFFFFFF>>1))>" +e "<$((0x8000000000000000>>-11))>" +e "<$((0x8000000000000000>>11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>-11))>" +e "<$((0x7FFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>-11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>11))>" +e "<$((0xFFFFFFFFFFFFFFFF>>>11))>" +e '= BIN **' +e "<$((0**1))>" +e "<$((2**1))>" +e "<$((2**2))>" +e "<$((2**3))>" +e "<$((2**4))>" +e "<$((10**4))>" +e "<$((10**10))>" +e "<$((10**5+5))>" +e "<$((10**(5+5)))>" +e '= LOG OR' +e "<$((0||0))>" +e "<$(( 000 || 0X0 ))>" +e "<$((01 || 64#1))>" +e "<$((01 || 64#1))>" +e "<$((0x1234 || 4660))>" +e "<$((0x1234 || 011064))>" +s I=33 J=33;e "<$((I||J))>" +s I=33 J=33;e "<$(( I || J ))>" +e "<$((0||1))>" +e "<$((0||0000000000000000000000001))>" +e "<$((1||2))>" +e "<$((0x1234 || 04660))>" +e "<$((0x1234 || 0x11064))>" +s I=10 J=33;e "<$((I||J))>" +s I=-10 J=-33;e "<$((I||J))>" +s I=-33 J=-33;e "<$((I||J))>" +s I=0 J=-33;e "<$((I||J))>" +s I=33 J=0;e "<$((I||J))>" +e '= LOG AND' +e "<$((0&&0))>" +e "<$(( 000 && 0X0 ))>" +e "<$((01 && 64#1))>" +e "<$((01 && 64#1))>" +e "<$((0x1234 && 4660))>" +e "<$((0x1234 && 011064))>" +s I=33 J=33;e "<$((I&&J))>" +s I=33 J=33;e "<$(( I && J ))>" +e "<$((0&&1))>" +e "<$((0&&0000000000000000000000001))>" +e "<$((1&&2))>" +e "<$((0x1234 && 04660))>" +e "<$((0x1234 && 0x11064))>" +s I=10 J=33;e "<$((I&&J))>" +s I=-10 J=-33;e "<$((I&&J))>" +s I=-33 J=-33;e "<$((I&&J))>" +s I=0 J=-33;e "<$((I&&J))>" +s I=33 J=0;e "<$((I&&J))>" +e '= BIN BIT_OR' +e "<$((0|0))>" +e "<$(( 0 | 0 ))>" +e "<$((0|1))>" +e "<$(( 0 | 1 ))>" +e "<$((1|0))>" +e "<$(( 1 | 0 ))>" +e "<$((1|1))>" +e "<$(( 1 | 1 ))>" +e "<$((1111|2222))>" +e "<$((2222|1111))>" +e "<$(( +0x10 | +0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( -0x10 | -0x11 ))>" +e "<$(( +64#10 | -64#11 ))>" +e "<$(( +0x11 | +0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( -0x11 | -0x10 ))>" +e "<$(( +64#11 | -64#10 ))>" +e "<$(( +64 | +1024 ))>" +e "<$((0x8000000000000000|-1))>" +e "<$((0x8000000000000000|1))>" +e "<$((0x7FFFFFFFFFFFFFFF|-1))>" +e "<$((0x7FFFFFFFFFFFFFFF|1))>" +e "<$((0xFFFFFFFFFFFFFFFF|-1))>" +e "<$((0xFFFFFFFFFFFFFFFF|1))>" +e "<$((0x8000000000000000|-11))>" +e "<$((0x8000000000000000|11))>" +e "<$((0x7FFFFFFFFFFFFFFF|-11))>" +e "<$((0x7FFFFFFFFFFFFFFF|11))>" +e "<$((0xFFFFFFFFFFFFFFFF|-11))>" +e "<$((0xFFFFFFFFFFFFFFFF|11))>" +e '= BIN BIT_XOR' +e "<$((0^0))>" +e "<$(( 0 ^ 0 ))>" +e "<$((0^1))>" +e "<$(( 0 ^ 1 ))>" +e "<$((1^0))>" +e "<$(( 1 ^ 0 ))>" +e "<$((1^1))>" +e "<$(( 1 ^ 1 ))>" +e "<$((1111^2222))>" +e "<$((2222^1111))>" +e "<$(( +0x10 ^ +0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( -0x10 ^ -0x11 ))>" +e "<$(( +64#10 ^ -64#11 ))>" +e "<$(( +0x11 ^ +0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( -0x11 ^ -0x10 ))>" +e "<$(( +64#11 ^ -64#10 ))>" +e "<$(( +64 ^ +1024 ))>" +e "<$((0x8000000000000000^-1))>" +e "<$((0x8000000000000000^1))>" +e "<$((0x7FFFFFFFFFFFFFFF^-1))>" +e "<$((0x7FFFFFFFFFFFFFFF^1))>" +e "<$((0xFFFFFFFFFFFFFFFF^-1))>" +e "<$((0xFFFFFFFFFFFFFFFF^1))>" +e "<$((0x8000000000000000^-11))>" +e "<$((0x8000000000000000^11))>" +e "<$((0x7FFFFFFFFFFFFFFF^-11))>" +e "<$((0x7FFFFFFFFFFFFFFF^11))>" +e "<$((0xFFFFFFFFFFFFFFFF^-11))>" +e "<$((0xFFFFFFFFFFFFFFFF^11))>" +e '= BIN BIT_AND' +e "<$((0&0))>" +e "<$(( 0 & 0 ))>" +e "<$((0&1))>" +e "<$(( 0 & 1 ))>" +e "<$((1&0))>" +e "<$(( 1 & 0 ))>" +e "<$((1&1))>" +e "<$(( 1 & 1 ))>" +e "<$((1111&2222))>" +e "<$((2222&1111))>" +e "<$(( +0x10 & +0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( -0x10 & -0x11 ))>" +e "<$(( +64#10 & -64#11 ))>" +e "<$(( +0x11 & +0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( -0x11 & -0x10 ))>" +e "<$(( +64#11 & -64#10 ))>" +e "<$(( +64 & +1024 ))>" +e "<$((0x8000000000000000&-1))>" +e "<$((0x8000000000000000&1))>" +e "<$((0x7FFFFFFFFFFFFFFF&-1))>" +e "<$((0x7FFFFFFFFFFFFFFF&1))>" +e "<$((0xFFFFFFFFFFFFFFFF&-1))>" +e "<$((0xFFFFFFFFFFFFFFFF&1))>" +e "<$((0x8000000000000000&-11))>" +e "<$((0x8000000000000000&11))>" +e "<$((0x7FFFFFFFFFFFFFFF&-11))>" +e "<$((0x7FFFFFFFFFFFFFFF&11))>" +e "<$((0xFFFFFFFFFFFFFFFF&-11))>" +e "<$((0xFFFFFFFFFFFFFFFF&11))>" +e '= BIN EQ' +e "<$((0==0))>" +e "<$(( 000 == 0X0 ))>" +e "<$((01 == 64#1))>" +e "<$((01 == 64#1))>" +e "<$((0x1234 == 4660))>" +e "<$((0x1234 == 011064))>" +s I=33 J=33;e "<$((I==J))>" +s I=33 J=33;e "<$(( I == J ))>" +e "<$((0==1))>" +e "<$((0==0000000000000000000000001))>" +e "<$((1==2))>" +e "<$((0x1234 == 04660))>" +e "<$((0x1234 == 0x11064))>" +s I=10 J=33;e "<$((I==J))>" +s I=-10 J=-33;e "<$((I==J))>" +s I=-33 J=-33;e "<$((I==J))>" +e '= BIN NE' +e "<$((0!=0))>" +e "<$(( 000 != 0X0 ))>" +e "<$((01 != 64#1))>" +e "<$((01 != 64#1))>" +e "<$((0x1234 != 4660))>" +e "<$((0x1234 != 011064))>" +s I=33 J=33;e "<$((I!=J))>" +s I=33 J=33;e "<$(( I != J ))>" +e "<$((0!=1))>" +e "<$((0!=0000000000000000000000001))>" +e "<$((1!=2))>" +e "<$((0x1234 != 04660))>" +e "<$((0x1234 != 0x11064))>" +s I=10 J=33;e "<$((I!=J))>" +s I=-10 J=-33;e "<$((I!=J))>" +s I=-33 J=-33;e "<$((I!=J))>" +e '= BIN LE' +e "<$((0<=0))>" +e "<$(( 000 <= 0X0 ))>" +e "<$((01 <= 64#1))>" +e "<$((01 <= 64#2))>" +e "<$((02 <= 64#1))>" +e "<$((0x1234 <= 4660))>" +e "<$((0x1234 <= 011064))>" +e "<$((0x1233 <= 011064))>" +e "<$((0x1235 <= 011064))>" +s I=33 J=33;e "<$((I<=J))>" +s I=33 J=33;e "<$((I<=J))>" +s I=32 J=33;e "<$((I<=J))>" +s I=34 J=33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-33 J=-33;e "<$((I<=J))>" +s I=-32 J=-33;e "<$((I<=J))>" +s I=-34 J=-33;e "<$((I<=J))>" +e '= BIN GE' +e "<$((0>=0))>" +e "<$(( 000 >= 0X0 ))>" +e "<$((01 >= 64#1))>" +e "<$((01 >= 64#2))>" +e "<$((02 >= 64#1))>" +e "<$((0x1234 >= 4660))>" +e "<$((0x1234 >= 011064))>" +e "<$((0x1233 >= 011064))>" +e "<$((0x1235 >= 011064))>" +s I=33 J=33;e "<$((I>=J))>" +s I=33 J=33;e "<$((I>=J))>" +s I=32 J=33;e "<$((I>=J))>" +s I=34 J=33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-33 J=-33;e "<$((I>=J))>" +s I=-32 J=-33;e "<$((I>=J))>" +s I=-34 J=-33;e "<$((I>=J))>" +e '= BIN LT' +e "<$((0<0))>" +e "<$(( 000 < 0X0 ))>" +e "<$((01 < 64#1))>" +e "<$((01 < 64#2))>" +e "<$((02 < 64#1))>" +e "<$((0x1234 < 4660))>" +e "<$((0x1234 < 011064))>" +e "<$((0x1233 < 011064))>" +e "<$((0x1235 < 011064))>" +s I=33 J=33;e "<$((I" +s I=33 J=33;e "<$((I" +s I=32 J=33;e "<$((I" +s I=34 J=33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-33 J=-33;e "<$((I" +s I=-32 J=-33;e "<$((I" +s I=-34 J=-33;e "<$((I" +e '= BIN GT' +e "<$((0>0))>" +e "<$(( 000 > 0X0 ))>" +e "<$((01 > 64#1))>" +e "<$((01 > 64#2))>" +e "<$((02 > 64#1))>" +e "<$((0x1234 > 4660))>" +e "<$((0x1234 > 011064))>" +e "<$((0x1233 > 011064))>" +e "<$((0x1235 > 011064))>" +s I=33 J=33;e "<$((I>J))>" +s I=33 J=33;e "<$((I>J))>" +s I=32 J=33;e "<$((I>J))>" +s I=34 J=33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-33 J=-33;e "<$((I>J))>" +s I=-32 J=-33;e "<$((I>J))>" +s I=-34 J=-33;e "<$((I>J))>" +# +# COMMA below +e '= PRECEDENCE I' +e "<$(( 1 + 2 + 3 ))>" +e "<$(( 1 - 2 + 3 ))>" +e "<$(( 3 - 2 - 1 ))>" +e "<$(( 3 - 2 + 1 ))>" +e "<$(( - 2 + 1 ))>" +e "<$(( 2 + -1 ))>" +e "<$(( ! 2 + 1 ))>" +e "<$(( 2 + !1 ))>" +e "<$(( 3 * 2 + 2 ))>" +e "<$(( 3 + 2 * 2 ))>" +e "<$(( 3 * 2 * 2 ))>" +e "<$(( 9 / 3 + 2 ))>" +e "<$(( 9 + 3 / 2 ))>" +e "<$(( 9 / 3 / 2 ))>" +e "<$(( 9 << 1 + 2 ))>" +e "<$(( 9 + 3 << 2 ))>" +e "<$(( 9 << 3 << 2 ))>" +e "<$(( 9 >> 1 + 2 ))>" +e "<$(( 9 + 3 >> 2 ))>" +e "<$(( 19 >> 3 >> 1 ))>" +e "<$(( 19 >> 3 << 1 ))>" +e "<$(( 19 << 3 >> 1 ))>" +e "<$(( 2 + 3 < 3 * 2 ))>" +e "<$(( 2 << 3 >= 3 << 2 ))>" +e "<$(( 0xfD & 0xF == 0xF ))>" +e "<$((0xfD&0xF==0xF))>" +e "<$(( 3 * 7 , 2 << 8 , 9 - 7 ))>" +e "<$((3*7,2<<8,9-7))>" +e '= PARENS' +e "<$(((1 + 2) + 3))>" +e "<$(((1+2)+3))>" +e "<$((1 - (2 + 3)))>" +e "<$((1-(2+3)))>" +e "<$((3 - (2 - 1)))>" +e "<$((3-(2-1)))>" +e "<$((3 - ( 2 + 1 )))>" +e "<$((3-(2+1)))>" +e "<$((- (2 + 1)))>" +e "<$((-(2+1)))>" +e "<$((! (2 + 1)))>" +e "<$((!(2+1)))>" +e "<$((3 * (2 + 2)))>" +e "<$((3*(2+2)))>" +e "<$(((3 + 2) * 2))>" +e "<$(((3+2)*2))>" +e "<$((3 * (2 * 2)))>" +e "<$((3*(2*8)))>" +e "<$((9 / (3 + 2)))>" +e "<$((9/(3+2)))>" +e "<$((( 9 + 3 ) / 2))>" +e "<$(((9+3)/2))>" +e "<$((9 / ( 3 / 2 )))>" +e "<$((9/(3/2)))>" +e "<$((( 9 << 1 ) + 2))>" +e "<$(((9<<1)+2))>" +e "<$((9 + (3 << 2)))>" +e "<$((9+(3<<2)))>" +e "<$((9 << (3 << 2)))>" +e "<$((9<<(3<<2)))>" +e "<$(((9 >> 1) + 2))>" +e "<$(((9>>1)+2))>" +e "<$((9 + (3 >> 2)))>" +e "<$((9+(3>>2)))>" +e "<$((19 >> (3 >> 1)))>" +e "<$((19>>(3>>1)))>" +e "<$((19 >> (3 << 1)))>" +e "<$((19>>(3<<1)))>" +e "<$((19 << (3 >> 1)))>" +e "<$((19<<(3>>1)))>" +e "<$((2 + (3 < 3) * 2))>" +e "<$((2+(3<3)*2))>" +e "<$((2 << ((3 >= 3) << 2)))>" +e "<$((2<<((3>=3)<<2)))>" +e "<$(((0xfD & 0xF) == 0xF))>" +e "<$(((0xfD&0xF)==0xF))>" +e "<$((3 * (7 , 2) << (8 , 9 - 7)))>" +e "<$((3*(7,2)<<(8,9-7)))>" +# +# COND BELOW +e '= ASSIGN I' +unset I;p "<$(( I = 3 ))>";e "<$I>" +unset I;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I=3))>";e "<$I>" +s I=10;p "<$((I+=1))>";e "<$I>" +s I=10;p "<$((I-=1))>";e "<$I>" +s I=10;p "<$((I*=1))>";e "<$I>" +s I=10;p "<$((I*=2))>";e "<$I>" +s I=10;p "<$((I/=1))>";e "<$I>" +s I=10;p "<$((I/=2))>";e "<$I>" +s I=10;p "<$((I%=1))>";e "<$I>" +s I=10;p "<$((I%=2))>";e "<$I>" +s I=10;p "<$((I**=1))>";e "<$I>" +s I=10;p "<$((I**=2))>";e "<$I>" +s I=10;p "<$((I**=1+1))>";e "<$I>" +s I=10;p "<$((I|=1))>";e "<$I>" +s I=10;p "<$((I^=1))>";e "<$I>";p "<$((I^=1))>";e "<$I>" +s I=10;p "<$((I&=2))>";e "<$I>" +s I=10;p "<$((I>>=1))>";e "<$I>" +s I=10;p "<$((I<<=1))>";e "<$I>" +s I=-1;p "<$((I>>>=1))>";e "<$I>" +e '= ASSIGN II' +s I=2;p "<$(((I+=1)-1))>";e "<$I>" +s I=4;p "<$(((I-=1)+1))>";e "<$I>" +s I=0 J=0;p "<$(((I=5)*(J=7)+1))>";e "<$I><$J>" +s I=99 J=17;p "<$(((I+=1)*(J-=2)+1))>";e "<$I><$J>" +s I=10;p "<$((I=2,I|=1))>";e "<$I>" +s I=0 J=0 Y=0 Z=0;p "<$((I=1,J=2,Y=3,Z=4,Z+=I+J+Y))>";e "<$I><$J><$Y><$Z>" +e '= POSTFIX' +s I=1;p "<$((I++))>";e "<$I>" +s I=1 J=0;p "<$((J=I++))>";e "<$I><$J>" +s I=1 J=10;p "<$((J++*I++))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J++)*(I++)))>";e "<$I><$J>" +s I=1;p "<$((I--))>";e "<$I>" +s I=1 J=0;p "<$((J=I--))>";e "<$I><$J>" +s I=1 J=10;p "<$((J--*I--))>";e "<$I><$J>" +s I=1 J=10;p "<$(((J--)*(I--)))>";e "<$I><$J>" +e '= PREFIX' +s I=1;p "<$((++I))>";e "<$I>" +s I=1 J=0;p "<$((J=++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++J*++I))>";e "<$I><$J>" +s I=1 J=10;p "<$((++(J)*++(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((++J)*(++I)))>";e "<$I><$J>" +s I=1;p "<$((--I))>";e "<$I>" +s I=1 J=0;p "<$((J=--I))>";e "<$I><$J>" +s I=2 J=10;p "<$((--J*--I))>";e "<$I><$J>" +s I=1 J=10;p "<$((--(J)*--(I)))>";e "<$I><$J>" +s I=1 J=10;p "<$(((--J)*(--I)))>";e "<$I><$J>" +e '= VAR RECUR' +s I='1 + 1';p "<$((I))>";e "<$I>" +s I='1 + 1';p "<$((+I))>";e "<$I>" +s I='1 + 1';p "<$((++I))>";e "<$I>" +s I='1 + 1';p "<$((I++))>";e "<$I>" +s I='1 + 1';p "<$((1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I=I+J))>";e "<$I><$J>" +s I='1 + 1';p "<$((I=I))>";e "<$I>" +s I='1 + 1';p "<$((I=+I))>";e "<$I>" +s I='1 + 1';p "<$((I=1+I))>";e "<$I>" +s I='1 + 1 * 2';p "<$((I=I+1))>";e "<$I>" +s I='(1 + 1) * 2';p "<$((I=I+1))>";e "<$I>" +s I='1 + 1' J='3 / 2';p "<$((I+=I+J))>";e "<$I><$J>" +e '= COMMA' +e "<$(( 1 , 2 ))>" +e "<$(( 1 , 2 , 3 ))>" +e "<$(( 1 , 2 , 3 , 4 ))>" +e "<$((1,2,3,4))>" +s I='1 + 1';p "<$(( I=10 , I+=I, I=I**2, I/=3 ))>";e "<$I>" +s I1=I2=10 I2=3;p "<$((I1,I2))>";e "<$I1><$I2>" +e '= COND' +e "<$(( +0 ? 2 : 3 ))>" +e "<$((-0?2:3))>" +e "<$(( +1 ? 2 : 3 ))>" +e "<$(( 1-1 ? 2 : 3 ))>" +e "<$(( 1-0 ? 2 : 3 ))>" +e "<$((-1?2:3))>" +e "<$(( 0x1234 ? 111 : 222 ))>" +e "<$((1**2 ? 5 : 7))>" +e "<$((0**2 ? 5 : 7))>" +e "<$((0**2>=0?5:7))>" +e "<$((-1<=0**2?5:7))>" +e "<$((1<=0**2?5:7))>" +e "<$((1>2||1*0?5:7))>" +e "<$((1>2&&1*0?5:7))>" +e "<$((1<2&&1*0?5:7))>" +e "<$((1<2&&1*0+1?5:7))>" +e '-- COND .2' +e "<$(( 1 < 2 ? -1 : 1 > 2 ? 1 : 0 ))>" +e "<$((1 < 1 ? -1 : 1 > 1 ? 1 : 0))>" +e "<$((2<1?-1:2>1?1:0))>" +e "<$((4<5 ? 1 : 32))>" +e "<$((4>5 ? 1 : 32))>" +e "<$((4>(2+3) ? 1 : 32))>" +e "<$((4<(2+3) ? 1 : 32))>" +e "<$(((2+2)<(2+3) ? 1 : 32))>" +e "<$(((2+2)>(2+3) ? 1 : 32))>" +## grouping protects precedence in : parts (syntax error tests below) +e '-- COND .3' +e "<$((1-1 < 1 ? 2,4 : 1,3))>" +e "<$((0<1?2,4:(1,3)))>" +e "<$((0,1,2,0?2,4:1,3))>" +e "<$((0,1,2,1?2,4:1,3))>" +e "<$((0,1,2,0?2,4:(1,3)))>" +e "<$((0,1,2,1?2,4:(1,3)))>" +e "<$((0,1,2,0?(2,4):1,3))>" +e "<$((0,1,2,1?(2,4):1,3))>" +e "<$((0,1,2,0?(2,4):(1,3)))>" +e "<$((0,1,2,1?(2,4):(1,3)))>" +e "<$((0?2:((0,3)?1:4)))>" +e "<$((1?2:3,0?1:4))>" +e "<$((1?2:3,0?1:4?5:6))>" +e "<$((1?2:(3,0)?1:4?5:6))>" +e "<$((1?2:3,0?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((1?2:3,1?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?4,5:5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):5,6?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?7,8:9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):9,10))>" +e "<$((1?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,1)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,6)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,6)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?(4,5):(5,0)?(7,8):(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:3,0?4,5:(5,0)?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:5,0?7,8:(9,10)))>" +e "<$((0?2:(3,0)?4,5:(5,0)?7,8:9,10))>" +e '-- COND .4' +e "<$((1?2?3?4?5:6:7:8:9))>" +e "<$((1?2?3?0?5:6:7:8:9))>" +e "<$((1?2?0?0?5:6:7:8:9))>" +e "<$((1?0?0?0?5:6:7:8:9))>" +e "<$((0?0?0?0?5:6:7:8:9))>" +e "<$((0?3+4?10:11:5+6?12:13))>" +e "<$((1?3+4?10:11:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11):((5+6)?12:13)))>" +e '-- COND .5' +e "<$((0?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?10:11?20+1:22*1:5+6?12:13))>" +e "<$((0?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e "<$((1?(3+4)?(10):(11)?(20+1):(22*1):((5+6)?12:13)))>" +e '-- COND .6' +e "<$((0?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((1?3+4?9:11?20+1:22*1:5+6?12:13))>" +e "<$((0?10+11?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?0?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20+1?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?0?22*1?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?0?23**1:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?0:24**1:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:0:25/1?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:0?26%27:56>>1:-1:-2))>" +e "<$((1?10?20?22*1?23**1:24**1:25/1?0:56>>1:-1:-2))>" +e '-- COND .7' +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 < I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 < I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$((((I1";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1";\ + e "<$I1><$I2><$I3><$I4><$I5>" +# only first +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? (I2 > I3) ? I3 *= I3 : (I2 *= I2) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(((I1I3)?I3*=I3:(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( (I1 < I2) ? ((I2 > I3) ? I3 *= I3 : (I2 *= I2)) : (I1 *= I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ + p "<$(( ((I1 < I2) ? ((I2 > I3) ? (I3 *= I3):(I2 *= I2)):(I1 *= I1))))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3>I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +# last not etc. +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I4)?I4*=I4:(I3*=I3):(I2*=I2):(I1*=I1)))>";\ + e "<$I1><$I2><$I3><$I4>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1I3)?(I3";\ + e "<$I1><$I2><$I3><$I4><$I5>" +s I1=2 I2=3 I3=4 I4=5;\ +p "<$(((I1>I2)?(I2";\ + e "<$I1><$I2><$I3><$I4><$I5>" +e '-- COND .8' +s I=0;p "<$((1?I=2:(I=3),8,10))>";e "<$I>" +s I=0;p "<$((1?20:(I+=2)))>";e "<$I>" +s I=0;p "<$((1?I+=10:(I+=2)))>";e "<$I>" +s I=0;p "<$((0?I+=2:20))>";e "<$I>" +s I=0;p "<$((0?I+=2:(I+=10)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(20)))>";e "<$I>" +s I=0;p "<$((0?(I+=2):(I+=20)))>";e "<$I>" +e '-- COND .9' +s I1=+E+ I2=1+1;p "<$((0?I1:I2))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1:I2))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1=1:(I2=2)))>";e "<$I1><$I2>" +s I1=+E+ I2=1+1;p "<$((0?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +s I1=1+1 I2=+E+;p "<$((1?I1*=I1:(I2*=I2)))>";e "<$I1><$I2>" +e '-- COND .10' +s I1=+E+ I2=+E+ I3=+E+ I4=-1;p "<$((0?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=1 I2=2 I3=+E+ I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +s I1=0 I2=+E+ I3=3 I4=+E+;p "<$((1?I1?I2:I3:I4))>";e "<$I1><$I2><$I3><$I4>" +e '= WILD I' +e "<$(( 3 + ( 11 ) ))>" +e "<$((1 + (2 - 2)))>" +e "<$((1 + (2 - 2)))>" +e "<$(( (( 3 / 3 )) + ((1*1*1)) - (( 7 % 6 ))))>" +e "<$(( 3+((2 * 2))/6 ))>" +e "<$(( 1 + 1 - 3 * 3 + 99-88 / 17))>" +e "<$(( 1 << 2 % 1+2 * 4 - (2 + 2 + 1) * 6 / 7 + 4 * 2 + (81/9)))>" +s I1=I2=10 I2=3;p "<$((I1 + I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 * I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=3;p "<$((I1 % I2))>";e "<$I1><$I2>" +e '= WILD II' +s I=10;p "<$((3+(3*(I=11))))>";e "<$I>" +s I=10;p "<$((3+(3*(I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,I++))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,++++I))))>";e "<$I>" +s I=10;p "<$((3+(3*(I=11,+++++++++++++++++++++++-+++++I))))>";e "<$I>" +e "<$((3+(3*(+++++++++++++++++++++++-+++++10))))>" +s I=10;p "<$(( +10 + + +I ))>";e "<$I>" +s I=10;p "<$(( +10 + ++I ))>";e "<$I>" +s I=10;p "<$(( +10 ++ +I ))>";e "<$I>" +s I=10;p "<$(( +10 +++ I ))>";e "<$I>" +s I=10;p "<$(( +10+++I ))>";e "<$I>" +s I=10;p "<$((+10++I))>";e "<$I>" +s I=10;p "<$((+10 + + + ++++ +I))>";e "<$I>" +e "<$(( +10 + + + ++++ +11 ))>" +e "<$(( +10 + + + ++++ ++11 ))>" +e "<$((+10++++++++11))>" +e '= WILD RECUR' # (some yet) +s I1=I2=10 I2=5;p "<$((I1+=I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2=0 I2=5 I3=I2+=1;p "<$((I1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=0?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I1=10 I2=5 I3=I2+=1;p "<$((I1=1?I1:I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=10)+1' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2='(I2=(I2=10)+1)' I2=5 I3=I2+=1;p "<$((I1,I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=10 I2=5 I3=I2+=1;p "<$((I1+I3*I1*I3/I1%I3))>";e "<$I1><$I2><$I3>" +s I1=I2=+E+ I2=5;p "<$((I1=10))>";e "<$I1><$I2>" +s I1=I2=+E+ I2=5;p "<$((0?I1:++I2))>";e "<$I1><$I2>" +s I1=I2=10 I2=5;p "<$((I2,(1?I1:++I2)))>";e "<$I1><$I2>" +s I1=5 I2=10 I3=20;p "<$((I1-=5,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix,1?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((0,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" +s I1=5 Ix=6 I2=10 I3=20;p "<$((I1*=Ix?I2:I3,Ix=21,I1*=Ix?I2:I3))>";e "<$I1><$I2><$I3>" diff --git a/shell/math.c b/shell/math.c index 76d22c9bd5..8ba0d2f7fb 100644 --- a/shell/math.c +++ b/shell/math.c @@ -116,398 +116,6 @@ #include "libbb.h" #include "math.h" -typedef unsigned char operator; - -/* An operator's token id is a bit of a bitfield. The lower 5 bits are the - * precedence, and 3 high bits are an ID unique across operators of that - * precedence. The ID portion is so that multiple operators can have the - * same precedence, ensuring that the leftmost one is evaluated first. - * Consider * and / - */ -#define tok_decl(prec,id) (((id)<<5) | (prec)) -#define PREC(op) ((op) & 0x1F) - -#define TOK_LPAREN tok_decl(0,0) - -#define TOK_COMMA tok_decl(1,0) - -/* All assignments are right associative and have the same precedence, - * but there are 11 of them, which doesn't fit into 3 bits for unique id. - * Abusing another precedence level: - */ -#define TOK_ASSIGN tok_decl(2,0) -#define TOK_AND_ASSIGN tok_decl(2,1) -#define TOK_OR_ASSIGN tok_decl(2,2) -#define TOK_XOR_ASSIGN tok_decl(2,3) -#define TOK_PLUS_ASSIGN tok_decl(2,4) -#define TOK_MINUS_ASSIGN tok_decl(2,5) -#define TOK_LSHIFT_ASSIGN tok_decl(2,6) -#define TOK_RSHIFT_ASSIGN tok_decl(2,7) - -#define TOK_MUL_ASSIGN tok_decl(3,0) -#define TOK_DIV_ASSIGN tok_decl(3,1) -#define TOK_REM_ASSIGN tok_decl(3,2) - -#define fix_assignment_prec(prec) do { if (prec == 3) prec = 2; } while (0) - -/* Ternary conditional operator is right associative too */ -#define TOK_CONDITIONAL tok_decl(4,0) -#define TOK_CONDITIONAL_SEP tok_decl(4,1) - -#define TOK_OR tok_decl(5,0) - -#define TOK_AND tok_decl(6,0) - -#define TOK_BOR tok_decl(7,0) - -#define TOK_BXOR tok_decl(8,0) - -#define TOK_BAND tok_decl(9,0) - -#define TOK_EQ tok_decl(10,0) -#define TOK_NE tok_decl(10,1) - -#define TOK_LT tok_decl(11,0) -#define TOK_GT tok_decl(11,1) -#define TOK_GE tok_decl(11,2) -#define TOK_LE tok_decl(11,3) - -#define TOK_LSHIFT tok_decl(12,0) -#define TOK_RSHIFT tok_decl(12,1) - -#define TOK_ADD tok_decl(13,0) -#define TOK_SUB tok_decl(13,1) - -#define TOK_MUL tok_decl(14,0) -#define TOK_DIV tok_decl(14,1) -#define TOK_REM tok_decl(14,2) - -/* Exponent is right associative */ -#define TOK_EXPONENT tok_decl(15,1) - -/* Unary operators */ -#define UNARYPREC 16 -#define TOK_BNOT tok_decl(UNARYPREC,0) -#define TOK_NOT tok_decl(UNARYPREC,1) - -#define TOK_UMINUS tok_decl(UNARYPREC+1,0) -#define TOK_UPLUS tok_decl(UNARYPREC+1,1) - -#define PREC_PRE (UNARYPREC+2) - -#define TOK_PRE_INC tok_decl(PREC_PRE, 0) -#define TOK_PRE_DEC tok_decl(PREC_PRE, 1) - -#define PREC_POST (UNARYPREC+3) - -#define TOK_POST_INC tok_decl(PREC_POST, 0) -#define TOK_POST_DEC tok_decl(PREC_POST, 1) - -#define SPEC_PREC (UNARYPREC+4) - -#define TOK_NUM tok_decl(SPEC_PREC, 0) -#define TOK_RPAREN tok_decl(SPEC_PREC, 1) - -static int -is_assign_op(operator op) -{ - operator prec = PREC(op); - fix_assignment_prec(prec); - return prec == PREC(TOK_ASSIGN) - || prec == PREC_PRE - || prec == PREC_POST; -} - -static int -is_right_associative(operator prec) -{ - return prec == PREC(TOK_ASSIGN) - || prec == PREC(TOK_EXPONENT) - || prec == PREC(TOK_CONDITIONAL); -} - - -typedef struct { - arith_t val; - /* We acquire second_val only when "expr1 : expr2" part - * of ternary ?: op is evaluated. - * We treat ?: as two binary ops: (expr ? (expr1 : expr2)). - * ':' produces a new value which has two parts, val and second_val; - * then '?' selects one of them based on its left side. - */ - arith_t second_val; - char second_val_present; - /* If NULL then it's just a number, else it's a named variable */ - char *var; -} var_or_num_t; - -typedef struct remembered_name { - struct remembered_name *next; - const char *var; -} remembered_name; - - -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr); - -static const char* -arith_lookup_val(arith_state_t *math_state, var_or_num_t *t) -{ - if (t->var) { - const char *p = math_state->lookupvar(t->var); - if (p) { - remembered_name *cur; - remembered_name cur_save; - - /* did we already see this name? - * testcase: a=b; b=a; echo $((a)) - */ - for (cur = math_state->list_of_recursed_names; cur; cur = cur->next) { - if (strcmp(cur->var, t->var) == 0) { - /* Yes */ - return "expression recursion loop detected"; - } - } - - /* push current var name */ - cur = math_state->list_of_recursed_names; - cur_save.var = t->var; - cur_save.next = cur; - math_state->list_of_recursed_names = &cur_save; - - /* recursively evaluate p as expression */ - t->val = evaluate_string(math_state, p); - - /* pop current var name */ - math_state->list_of_recursed_names = cur; - - return math_state->errmsg; - } - /* treat undefined var as 0 */ - t->val = 0; - } - return 0; -} - -/* "Applying" a token means performing it on the top elements on the integer - * stack. For an unary operator it will only change the top element, but a - * binary operator will pop two arguments and push the result */ -static NOINLINE const char* -arith_apply(arith_state_t *math_state, operator op, var_or_num_t *numstack, var_or_num_t **numstackptr) -{ -#define NUMPTR (*numstackptr) - - var_or_num_t *top_of_stack; - arith_t rez; - const char *err; - - /* There is no operator that can work without arguments */ - if (NUMPTR == numstack) - goto err; - - top_of_stack = NUMPTR - 1; - - /* Resolve name to value, if needed */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - - rez = top_of_stack->val; - if (op == TOK_UMINUS) - rez = -rez; - else if (op == TOK_NOT) - rez = !rez; - else if (op == TOK_BNOT) - rez = ~rez; - else if (op == TOK_POST_INC || op == TOK_PRE_INC) - rez++; - else if (op == TOK_POST_DEC || op == TOK_PRE_DEC) - rez--; - else if (op != TOK_UPLUS) { - /* Binary operators */ - arith_t right_side_val; - char bad_second_val; - - /* Binary operators need two arguments */ - if (top_of_stack == numstack) - goto err; - /* ...and they pop one */ - NUMPTR = top_of_stack; /* this decrements NUMPTR */ - - bad_second_val = top_of_stack->second_val_present; - if (op == TOK_CONDITIONAL) { /* ? operation */ - /* Make next if (...) protect against - * $((expr1 ? expr2)) - that is, missing ": expr" */ - bad_second_val = !bad_second_val; - } - if (bad_second_val) { - /* Protect against $((expr expr1 : expr2)) */ - return "malformed ?: operator"; - } - - top_of_stack--; /* now points to left side */ - - if (op != TOK_ASSIGN) { - /* Resolve left side value (unless the op is '=') */ - err = arith_lookup_val(math_state, top_of_stack); - if (err) - return err; - } - - right_side_val = rez; - rez = top_of_stack->val; - if (op == TOK_CONDITIONAL) /* ? operation */ - rez = (rez ? right_side_val : top_of_stack[1].second_val); - else if (op == TOK_CONDITIONAL_SEP) { /* : operation */ - if (top_of_stack == numstack) { - /* Protect against $((expr : expr)) */ - return "malformed ?: operator"; - } - top_of_stack->second_val_present = op; - top_of_stack->second_val = right_side_val; - } - else if (op == TOK_BOR || op == TOK_OR_ASSIGN) - rez |= right_side_val; - else if (op == TOK_OR) - rez = right_side_val || rez; - else if (op == TOK_BAND || op == TOK_AND_ASSIGN) - rez &= right_side_val; - else if (op == TOK_BXOR || op == TOK_XOR_ASSIGN) - rez ^= right_side_val; - else if (op == TOK_AND) - rez = rez && right_side_val; - else if (op == TOK_EQ) - rez = (rez == right_side_val); - else if (op == TOK_NE) - rez = (rez != right_side_val); - else if (op == TOK_GE) - rez = (rez >= right_side_val); - else if (op == TOK_RSHIFT || op == TOK_RSHIFT_ASSIGN) - rez >>= right_side_val; - else if (op == TOK_LSHIFT || op == TOK_LSHIFT_ASSIGN) - rez <<= right_side_val; - else if (op == TOK_GT) - rez = (rez > right_side_val); - else if (op == TOK_LT) - rez = (rez < right_side_val); - else if (op == TOK_LE) - rez = (rez <= right_side_val); - else if (op == TOK_MUL || op == TOK_MUL_ASSIGN) - rez *= right_side_val; - else if (op == TOK_ADD || op == TOK_PLUS_ASSIGN) - rez += right_side_val; - else if (op == TOK_SUB || op == TOK_MINUS_ASSIGN) - rez -= right_side_val; - else if (op == TOK_ASSIGN || op == TOK_COMMA) - rez = right_side_val; - else if (op == TOK_EXPONENT) { - arith_t c; - if (right_side_val < 0) - return "exponent less than 0"; - c = 1; - while (--right_side_val >= 0) - c *= rez; - rez = c; - } - else if (right_side_val == 0) - return "divide by zero"; - else if (op == TOK_DIV || op == TOK_DIV_ASSIGN - || op == TOK_REM || op == TOK_REM_ASSIGN) { - /* - * bash 4.2.45 x86 64bit: SEGV on 'echo $((2**63 / -1))' - * - * MAX_NEGATIVE_INT / -1 = MAX_POSITIVE_INT+1 - * and thus is not representable. - * Some CPUs segfault trying such op. - * Others overflow MAX_POSITIVE_INT+1 to - * MAX_NEGATIVE_INT (0x7fff+1 = 0x8000). - * Make sure to at least not SEGV here: - */ - if (right_side_val == -1 - && rez << 1 == 0 /* MAX_NEGATIVE_INT or 0 */ - ) { - right_side_val = 1; - } - if (op == TOK_DIV || op == TOK_DIV_ASSIGN) - rez /= right_side_val; - else { - rez %= right_side_val; - } - } - } - - if (is_assign_op(op)) { - char buf[sizeof(arith_t)*3 + 2]; - - if (top_of_stack->var == NULL) { - /* Hmm, 1=2 ? */ - goto err; - } - /* Save to shell variable */ - sprintf(buf, ARITH_FMT, rez); - math_state->setvar(top_of_stack->var, buf); - /* After saving, make previous value for v++ or v-- */ - if (op == TOK_POST_INC) - rez--; - if (op == TOK_POST_DEC) - rez++; - } - - top_of_stack->val = rez; - /* Erase var name, it is just a number now */ - top_of_stack->var = NULL; - return NULL; - err: - return "arithmetic syntax error"; -#undef NUMPTR -} - -/* longest must be first */ -static const char op_tokens[] ALIGN1 = { - '<','<','=',0, TOK_LSHIFT_ASSIGN, - '>','>','=',0, TOK_RSHIFT_ASSIGN, - '<','<', 0, TOK_LSHIFT, - '>','>', 0, TOK_RSHIFT, - '|','|', 0, TOK_OR, - '&','&', 0, TOK_AND, - '!','=', 0, TOK_NE, - '<','=', 0, TOK_LE, - '>','=', 0, TOK_GE, - '=','=', 0, TOK_EQ, - '|','=', 0, TOK_OR_ASSIGN, - '&','=', 0, TOK_AND_ASSIGN, - '*','=', 0, TOK_MUL_ASSIGN, - '/','=', 0, TOK_DIV_ASSIGN, - '%','=', 0, TOK_REM_ASSIGN, - '+','=', 0, TOK_PLUS_ASSIGN, - '-','=', 0, TOK_MINUS_ASSIGN, - '-','-', 0, TOK_POST_DEC, - '^','=', 0, TOK_XOR_ASSIGN, - '+','+', 0, TOK_POST_INC, - '*','*', 0, TOK_EXPONENT, - '!', 0, TOK_NOT, - '<', 0, TOK_LT, - '>', 0, TOK_GT, - '=', 0, TOK_ASSIGN, - '|', 0, TOK_BOR, - '&', 0, TOK_BAND, - '*', 0, TOK_MUL, - '/', 0, TOK_DIV, - '%', 0, TOK_REM, - '+', 0, TOK_ADD, - '-', 0, TOK_SUB, - '^', 0, TOK_BXOR, - /* uniq */ - '~', 0, TOK_BNOT, - ',', 0, TOK_COMMA, - '?', 0, TOK_CONDITIONAL, - ':', 0, TOK_CONDITIONAL_SEP, - ')', 0, TOK_RPAREN, - '(', 0, TOK_LPAREN, - 0 -}; -#define ptr_to_rparen (&op_tokens[sizeof(op_tokens)-7]) - #if ENABLE_FEATURE_SH_MATH_BASE static arith_t strto_arith_t(const char *nptr, char **endptr) { @@ -577,250 +185,99 @@ static arith_t strto_arith_t(const char *nptr, char **endptr) # endif #endif -static arith_t -evaluate_string(arith_state_t *math_state, const char *expr) -{ - operator lasttok; - const char *errmsg; - const char *start_expr = expr = skip_whitespace(expr); - unsigned expr_len = strlen(expr) + 2; - /* Stack of integers */ - /* The proof that there can be no more than strlen(startbuf)/2+1 - * integers in any given correct or incorrect expression - * is left as an exercise to the reader. */ - var_or_num_t *const numstack = alloca((expr_len / 2) * sizeof(numstack[0])); - var_or_num_t *numstackptr = numstack; - /* Stack of operator tokens */ - operator *const stack = alloca(expr_len * sizeof(stack[0])); - operator *stackptr = stack; - - /* Start with a left paren */ - *stackptr++ = lasttok = TOK_LPAREN; - errmsg = NULL; - - while (1) { - const char *p; - operator op; - operator prec; - - expr = skip_whitespace(expr); - if (*expr == '\0') { - if (expr == start_expr) { - /* Null expression */ - numstack->val = 0; - goto ret; - } - - /* This is only reached after all tokens have been extracted from the - * input stream. If there are still tokens on the operator stack, they - * are to be applied in order. At the end, there should be a final - * result on the integer stack */ - - if (expr != ptr_to_rparen + 1) { - /* If we haven't done so already, - * append a closing right paren - * and let the loop process it */ - expr = ptr_to_rparen; -//bb_error_msg("expr=')'"); - continue; - } - /* At this point, we're done with the expression */ - if (numstackptr != numstack + 1) { - /* ...but if there isn't, it's bad */ - goto err; - } - goto ret; - } - - p = endofname(expr); - if (p != expr) { - /* Name */ - size_t var_name_size = (p - expr) + 1; /* +1 for NUL */ - numstackptr->var = alloca(var_name_size); - safe_strncpy(numstackptr->var, expr, var_name_size); -//bb_error_msg("var:'%s'", numstackptr->var); - expr = p; - num: - numstackptr->second_val_present = 0; - numstackptr++; - lasttok = TOK_NUM; - continue; - } - - if (isdigit(*expr)) { - /* Number */ - numstackptr->var = NULL; - errno = 0; - numstackptr->val = strto_arith_t(expr, (char**) &expr); -//bb_error_msg("val:%lld", numstackptr->val); - if (errno) - numstackptr->val = 0; /* bash compat */ - goto num; - } - - /* Should be an operator */ - - /* Special case: XYZ--, XYZ++, --XYZ, ++XYZ are recognized - * only if XYZ is a variable name, not a number or EXPR. IOW: - * "a+++v" is a++ + v. - * "(a)+++7" is ( a ) + + + 7. - * "7+++v" is 7 + ++v, not 7++ + v. - * "--7" is - - 7, not --7. - * "++++a" is + + ++a, not ++ ++a. - */ - if ((expr[0] == '+' || expr[0] == '-') - && (expr[1] == expr[0]) - ) { - if (numstackptr == numstack || !numstackptr[-1].var) { /* not a VAR++ */ - char next = skip_whitespace(expr + 2)[0]; - if (!(isalpha(next) || next == '_')) { /* not a ++VAR */ - //bb_error_msg("special %c%c", expr[0], expr[0]); - op = (expr[0] == '+' ? TOK_ADD : TOK_SUB); - expr++; - goto tok_found1; - } - } - } - - p = op_tokens; - while (1) { - /* Compare expr to current op_tokens[] element */ - const char *e = expr; - while (1) { - if (*p == '\0') { - /* Match: operator is found */ - expr = e; - goto tok_found; - } - if (*p != *e) - break; - p++; - e++; - } - /* No match, go to next element of op_tokens[] */ - while (*p) - p++; - p += 2; /* skip NUL and TOK_foo bytes */ - if (*p == '\0') { - /* No next element, operator not found */ - //math_state->syntax_error_at = expr; - goto err; - } - } - tok_found: - op = p[1]; /* fetch TOK_foo value */ - tok_found1: - /* NB: expr now points past the operator */ - - /* post grammar: a++ reduce to num */ - if (lasttok == TOK_POST_INC || lasttok == TOK_POST_DEC) - lasttok = TOK_NUM; - - /* Plus and minus are binary (not unary) _only_ if the last - * token was a number, or a right paren (which pretends to be - * a number, since it evaluates to one). Think about it. - * It makes sense. */ - if (lasttok != TOK_NUM) { - switch (op) { - case TOK_ADD: - op = TOK_UPLUS; - break; - case TOK_SUB: - op = TOK_UMINUS; - break; - case TOK_POST_INC: - op = TOK_PRE_INC; - break; - case TOK_POST_DEC: - op = TOK_PRE_DEC; - break; - } - } - /* We don't want an unary operator to cause recursive descent on the - * stack, because there can be many in a row and it could cause an - * operator to be evaluated before its argument is pushed onto the - * integer stack. - * But for binary operators, "apply" everything on the operator - * stack until we find an operator with a lesser priority than the - * one we have just extracted. If op is right-associative, - * then stop "applying" on the equal priority too. - * Left paren is given the lowest priority so it will never be - * "applied" in this way. - */ - prec = PREC(op); -//bb_error_msg("prec:%02x", prec); - if ((prec > 0 && prec < UNARYPREC) || prec == SPEC_PREC) { - /* not left paren or unary */ - if (lasttok != TOK_NUM) { - /* binary op must be preceded by a num */ - goto err; - } - /* The algorithm employed here is simple: while we don't - * hit an open paren nor the bottom of the stack, pop - * tokens and apply them */ - while (stackptr != stack) { - operator prev_op = *--stackptr; - if (op == TOK_RPAREN) { -//bb_error_msg("op == TOK_RPAREN"); - if (prev_op == TOK_LPAREN) { -//bb_error_msg("prev_op == TOK_LPAREN"); -//bb_error_msg(" %p %p numstackptr[-1].var:'%s'", numstack, numstackptr-1, numstackptr[-1].var); - if (numstackptr[-1].var) { - /* Expression is (var), lookup now */ - errmsg = arith_lookup_val(math_state, &numstackptr[-1]); - if (errmsg) - goto err_with_custom_msg; - /* Erase var name: (var) is just a number, for example, (var) = 1 is not valid */ - numstackptr[-1].var = NULL; - } - /* Any operator directly after a - * close paren should consider itself binary */ - lasttok = TOK_NUM; - goto next; - } -//bb_error_msg("prev_op != TOK_LPAREN"); - } else { - operator prev_prec = PREC(prev_op); -//bb_error_msg("op != TOK_RPAREN"); - fix_assignment_prec(prec); - fix_assignment_prec(prev_prec); - if (prev_prec < prec - || (prev_prec == prec && is_right_associative(prec)) - ) { - stackptr++; - break; - } - } -//bb_error_msg("arith_apply(prev_op:%02x)", prev_op); - errmsg = arith_apply(math_state, prev_op, numstack, &numstackptr); - if (errmsg) - goto err_with_custom_msg; - } - if (op == TOK_RPAREN) - goto err; - } +#define a_SHEXP_ARITH_COMPAT_SHIMS +# define s64 arith_t +# if ENABLE_FEATURE_SH_MATH_64 +# define S64_MIN LLONG_MIN +# define u64 unsigned long long +# else +# define S64_MIN LONG_MIN +# define u64 unsigned long +# endif +# define savestr(X) xstrdup(X) +# define su_IDEC_STATE_EMASK (1u<<0) +# define su_IDEC_STATE_CONSUMED (1u<<1) +#define a_SHEXP_ARITH_COOKIE arith_state_t * +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK +# define a_SHEXP_ARITH_ERROR_TRACK +#endif - /* Push this operator to the stack and remember it */ -//bb_error_msg("push op:%02x", op); - *stackptr++ = lasttok = op; - next: ; - } /* while (1) */ +#define su_ienc_s64(X,Y,Z) (sprintf(X, ARITH_FMT, Y), X) +#define n_var_vlook(X,Y) (*self->sac_cookie->lookupvar)(X) +#define n_var_vset(X,Y,Z) (*self->sac_cookie->setvar)(X, (char*)(Y)) +#define su_idec_cp(A,B,C,D,E) a_idec_x(A, B, E) + +static inline uint32_t a_idec_x(void *resp, char const *cbuf, + char const **endptr_or_nil){ + uint32_t rv; + arith_t res; + char const *eptr; + + if(endptr_or_nil == NULL) + endptr_or_nil = &eptr; + + errno = 0; + res = strto_arith_t(cbuf, (char**)endptr_or_nil); + rv = 0; + if(errno == 0){ + if(**endptr_or_nil == '\0') + rv = su_IDEC_STATE_CONSUMED; + }else{ + rv = su_IDEC_STATE_EMASK; + res = 0; + } - err: - errmsg = "arithmetic syntax error"; - err_with_custom_msg: - numstack->val = -1; - ret: - math_state->errmsg = errmsg; - return numstack->val; + *(arith_t*)resp = res; + return rv; } +#include "shexp-arith.h" + arith_t FAST_FUNC arith(arith_state_t *math_state, const char *expr) { +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + char *err_rest; +#endif + char const *emsg; + s64 res; + math_state->errmsg = NULL; - math_state->list_of_recursed_names = NULL; - return evaluate_string(math_state, expr); + + switch(a_shexp_arith_eval(math_state, &res, expr, UZ_MAX +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + , &err_rest +#endif + )){ + default: + return res; +#undef a_X +#define a_X(X,N) case CONCAT(a_SHEXP_ARITH_ERR_,X): emsg = N_(N); break + a_X(NOMEM, "out of memory"); + a_X(SYNTAX, "syntax error"); + a_X(ASSIGN_NO_VAR, "assignment without variable"); + a_X(DIV_BY_ZERO, "division by zero"); + a_X(EXP_INVALID, "invalid exponent"); + a_X(NO_OP, "syntax error, expected operand"); + a_X(COND_NO_COLON, "syntax error, incomplete ?: condition"); + a_X(COND_PREC_INVALID, "?: condition, invalid precedence (1:v2:v3=3)"); + a_X(NAME_LOOP, "recursive variable name reference"); + a_X(OP_INVALID, "unknown operator"); + } +#undef a_X + + math_state->errmsg = +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + xasprintf("%s (rest: %s)", emsg, err_rest); +#else + emsg +#endif + ; +#if ENABLE_FEATURE_SH_MATH_ERROR_TRACK + free(err_rest); +#endif + + return -1; } /* diff --git a/shell/math.h b/shell/math.h index 41ef6e8dfa..4bb8d5cdfa 100644 --- a/shell/math.h +++ b/shell/math.h @@ -76,11 +76,14 @@ typedef void FAST_FUNC (*arith_var_set_t)(const char *name, const char *v //typedef const char* FAST_FUNC (*arith_var_endofname_t)(const char *name); typedef struct arith_state_t { - const char *errmsg; + /* ENABLE_FEATURE_SH_MATH_ERROR_TRACK: must be free(3)d if !NULL */ +#if !ENABLE_FEATURE_SH_MATH_ERROR_TRACK + const +#endif + char *errmsg; arith_var_lookup_t lookupvar; arith_var_set_t setvar; // arith_var_endofname_t endofname; - void *list_of_recursed_names; } arith_state_t; arith_t FAST_FUNC arith(arith_state_t *state, const char *expr); diff --git a/shell/shexp-arith.h b/shell/shexp-arith.h new file mode 100644 index 0000000000..5f3655b456 --- /dev/null +++ b/shell/shexp-arith.h @@ -0,0 +1,1292 @@ +/*@ S-nail - a mail user agent derived from Berkeley Mail. + *@ Signed 64-bit sh(1)ell-style $(( ARITH ))metic expression evaluator. + *@ POW2 bases are parsed as unsigned, operation overflow -> limit constant(s), + *@ saturated mode is not supported, division by zero is handled via error. + *@ The expression length limit is ~100.000.000 on 32-bit, U32_MAX otherwise. + *@ After reading on Dijkstra's two stack algorithm, as well as bash:expr.c. + *@ Most heavily inspired by busybox -- conclusion: the Dijkstra algorithm + *@ scales very badly to ternary as are used to implement conditionals and + *@ their ignored sub-expressions. + *@ + *@ #define's: + *@ - a_SHEXP_ARITH_COMPAT_SHIMS: for inclusion in other code bases, setting + *@ this defines most necessary compat macros. + *@ We still need s64, u64, S64_MIN, savestr(CP) <> strdup(3) that does not + *@ return NIL (only with _ERROR_TRACK). Plus stdint.h, ctype.h, string.h. + *@ We need su_idec_cp(), su_ienc_s64(), n_var_vlook() and n_var_vset(). + *@ We need su_IDEC_STATE_EMASK (= 1) and su_IDEC_STATE_CONSUMED (= 2), e.g.: + *@ errno = 0; + *@ res = strto_arith_t(cbuf, (char**)endptr_or_nil); + *@ rv = 0; + *@ if(errno == 0){ + *@ if(**endptr_or_nil == '\0') + *@ rv = su_IDEC_STATE_CONSUMED; + *@ }else{ + *@ rv = su_IDEC_STATE_EMASK; + *@ res = 0; + *@ } + *@ *S(s64*,resp) = res; + *@ - a_SHEXP_ARITH_COOKIE: adds struct a_shexp_arith_ctx:sac_cookie, and + *@ a cookie arg to a_shexp_arith_eval(). + *@ - a_SHEXP_ARITH_ERROR_TRACK: add "char **error_track_or_nil" to + *@ a_shexp_arith_eval(), and according error stack handling, so that users + *@ can be given hint where an error occurred. ("Three stack algorithm.") + * + * Copyright (c) 2022 Steffen Nurpmeso . + * SPDX-License-Identifier: ISC + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Tracking of error location in input */ +#ifdef a_SHEXP_ARITH_ERROR_TRACK +# undef a_SHEXP_ARITH_ERROR_TRACK +# define a_SHEXP_ARITH_ERROR_TRACK(X) X +#else +# define a_SHEXP_ARITH_ERROR_TRACK(X) +#endif + +/* IFS whitespace removal */ +#undef a_SHEXP_ARITH_IFS +#ifdef mx_SOURCE +# define a_SHEXP_ARITH_IFS(X) X +#else +# define a_SHEXP_ARITH_IFS(X) +#endif + +/* (Most necessary) Compat shims */ +#ifdef a_SHEXP_ARITH_COMPAT_SHIMS +# define boole bool +# define FAL0 false +# define TRU1 true +# define u8 uint8_t +# define u16 uint16_t +# define u32 uint32_t +# define U32_MAX UINT32_MAX +# define ul unsigned long +# define up uintptr_t +# define UZ_MAX SIZE_MAX +# define uz size_t +# define a_SHEXP_ISVARC(C) ((C) == '_' || isalnum(S(unsigned char,C))) +# define a_SHEXP_ISVARC_BAD1ST(C) su_cs_is_digit(C) +# define a_SHEXP_ISVARC_BADNST(C) FAL0 +# define ASSERT(X) +# define ASSERT_NYD_EXEC(X,Y) +# define BITENUM_IS(X,Y) X +# define CONCAT(S1,S2) su__CONCAT_1(S1, S2) +# define su__CONCAT_1(S1,S2) su__CONCAT_2(S1, S2) +# define su__CONCAT_2(S1,S2) S1 ## S2 +# define DBGX(X) +# define FALLTHRU +# define N_(X) X +# define NIL NULL +# define NYD_IN S(void,0) +# define NYD2_IN S(void,0) +# define NYD_OU S(void,0) +# define NYD2_OU S(void,0) +# define P2UZ(X) S(size_t,X) +# define S(X,Y) ((X)(Y)) +# define su_ALIGNOF(X) ((sizeof(X) + 15) & ~15) +# define su_COMMA , +# define su_cs_cmp(X,Y) strcmp(X, Y) +# define su_cs_is_digit(X) isdigit(S(unsigned char,X)) +# define su_cs_is_space(X) isspace(S(unsigned char,X)) +# define su_empty "" +# define su_IDEC_STATE_EBASE 0 /* (could cause $CC optimiz.) */ +# define su_IENC_BUFFER_SIZE 80u +# define su_LOFI_ALLOC(X) alloca(X) +# define su_LOFI_FREE(X) +# define su_mem_move(X,Y,Z) memmove(X, Y, Z) +# define STRUCT_ZERO(X,Y) memset(Y, 0, sizeof(X)) +# define UNLIKELY(X) X +# define UNUSED(X) S(void,X) +# if LONG_MAX - 1 > 0x7FFFFFFFl - 1 +# define su_64(X) X +# else +# define su_64(X) +# endif +#endif /* a_SHEXP_ARITH_COMPAT_SHIMS */ + +/* -- >8 -- 8< -- */ + +#if 1 +# define a_SHEXP_ARITH_DBG 0 +# define a_SHEXP_ARITH_L(X) +#else +# define a_SHEXP_ARITH_DBG 1 +# define a_SHEXP_ARITH_L(X) a_shexp__arith_log X +#endif + +/* We parse with base 0: set _RESCAN to allow "I=' -10';$((10#$I))" */ +#define a_SHEXP_ARITH_IDEC_MODE (su_IDEC_MODE_SIGNED_TYPE |\ + su_IDEC_MODE_POW2BASE_UNSIGNED | su_IDEC_MODE_LIMIT_NOERROR |\ + su_IDEC_MODE_BASE0_NUMBER_SIGN_RESCAN) + +enum a_shexp_arith_error{ + a_SHEXP_ARITH_ERR_NONE, + a_SHEXP_ARITH_ERR_NOMEM, /* Out of memory */ + a_SHEXP_ARITH_ERR_SYNTAX, /* General syntax error */ + a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR, /* Assignment without variable */ + a_SHEXP_ARITH_ERR_DIV_BY_ZERO, + a_SHEXP_ARITH_ERR_EXP_INVALID, /* Invalid exponent */ + a_SHEXP_ARITH_ERR_NO_OP, /* Expected an argument here */ + a_SHEXP_ARITH_ERR_COND_NO_COLON, /* Incomplete ?: condition */ + a_SHEXP_ARITH_ERR_COND_PREC_INVALID, /* 1 ? VAR1 : VAR2 = 3 */ + a_SHEXP_ARITH_ERR_NAME_LOOP, /* Variable self-reference loop */ + a_SHEXP_ARITH_ERR_OP_INVALID /* Unknown operator */ +}; + +/* Operators and precedences in increasing precedence order. + * (The operator stack as such is u16: [OP_FLAGS |] (OP<<8) | PREC.) */ +enum a_shexp_arith_ops{ +#undef a_X +#define a_X(N,P,O) \ + CONCAT(a_SHEXP_ARITH_PREC_,N) = CONCAT(P,u),\ + CONCAT(a_SHEXP_ARITH_OP_,N) =\ + (CONCAT(O,u) << 8) | CONCAT(a_SHEXP_ARITH_PREC_,N) + + a_X(PAREN_LEFT, 0, 0), + + a_X(COMMA, 1, 0), + + a_X(ASSIGN, 2, 0), + a_X(ASSIGN_BIT_OR, 2, 1), + a_X(ASSIGN_BIT_XOR, 2, 2), + a_X(ASSIGN_BIT_AND, 2, 3), + a_X(ASSIGN_SHIFT_LEFT, 2, 4), a_X(ASSIGN_SHIFT_RIGHT, 2, 5), + a_X(ASSIGN_SHIFT_RIGHTU, 2, 6), + a_X(ASSIGN_ADD, 2, 7), a_X(ASSIGN_SUB, 2, 8), + a_X(ASSIGN_MUL, 2, 9), a_X(ASSIGN_DIV, 2, 10), a_X(ASSIGN_MOD, 2, 11), + a_X(ASSIGN_EXP, 2, 12), + + a_X(COND, 3, 0), + a_X(COND_COLON, 3, 1), + + a_X(OR, 4, 0), + a_X(AND, 5, 0), + a_X(BIT_OR, 6, 0), + a_X(BIT_XOR, 7, 0), + a_X(BIT_AND, 8, 0), + a_X(EQ, 9, 0), a_X(NE, 9, 1), + a_X(LE, 10, 0), a_X(GE, 10, 1), a_X(LT, 10, 2), a_X(GT, 10, 3), + a_X(SHIFT_LEFT, 11, 0), a_X(SHIFT_RIGHT, 11, 1), a_X(SHIFT_RIGHTU, 11, 2), + a_X(ADD, 12, 0), a_X(SUB, 12, 1), + a_X(MUL, 13, 0), a_X(DIV, 13, 1), a_X(MOD, 13, 2), + a_X(EXP, 14, 0), + + /* Further operators are unary, pre- or postfix */ + a_SHEXP_ARITH_PREC_UNARY = 15, + a_SHEXP_ARITH_PREC_PREFIX = 16, + a_SHEXP_ARITH_PREC_POSTFIX = 18, + + a_X(UNARY_NOT, 15, 0), a_X(UNARY_BIT_NOT, 15, 1), + a_X(PREFIX_INC, 16, 0), a_X(PREFIX_DEC, 16, 1), + a_X(UNARY_PLUS, 17, 1), a_X(UNARY_MINUS, 17, 0), + a_X(POSTFIX_INC, 18, 0), a_X(POSTFIX_DEC, 18, 1), + + /* Beyond operator profanity; the first "is a number" */ + a_SHEXP_ARITH_PREC_SKY = 19, + a_X(NUM, 19, 0), a_X(PAREN_RIGHT, 19, 1), + +#undef a_X +}; + +enum arith_op_flags{ + /* Mask off operator and precision */ + a_SHEXP_ARITH_OP_MASK = 0x1FFF, + a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON = 1u<<13, + a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT = 1u<<14, + a_SHEXP_ARITH_OP_FLAG_WHITEOUT = 1u<<15, + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK = a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT | + a_SHEXP_ARITH_OP_FLAG_WHITEOUT, + a_SHEXP_ARITH_OP_FLAG_MASK = a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON | + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK +}; + +struct a_shexp_arith_name_stack{ + struct a_shexp_arith_name_stack *sans_last; + char const *sans_var; +}; + +struct a_shexp_arith_val{ + s64 sav_val; + char *sav_var; /* Named variable or NIL */ +}; + +struct a_shexp_arith_stack{ + struct a_shexp_arith_val *sas_nums; + struct a_shexp_arith_val *sas_nums_top; + u16 *sas_ops; + u16 *sas_ops_top; + a_SHEXP_ARITH_ERROR_TRACK( + char **sas_error_track; + char **sas_error_track_top; + ) +}; + +struct a_shexp_arith_ctx{ + enum a_shexp_arith_error sac_error; + boole sac_have_error_track; + u8 sac__pad[3]; + s64 sac_rv; + struct a_shexp_arith_stack *sac_stack; + struct a_shexp_arith_name_stack *sac_name_stack; + a_SHEXP_ARITH_ERROR_TRACK( char **sac_error_track_or_nil; ) + a_SHEXP_ARITH_IFS( char const *sac_ifs_ws; ) +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE sac_cookie; +#endif +}; + +/* Sort by ~expected usage -- however, longest first if ambiguous! + * Follow busybox, save space by compressing data in char[] not struct[]! + * (XXX Instead use 1-st byte jump table like for commands) */ +static char const a_shexp_arith_op_toks[] = { +#undef a_X +#define a_X(X) \ + S(char,(CONCAT(a_SHEXP_ARITH_OP_,X) & 0xFF00u) >> 8),\ + S(char,CONCAT(a_SHEXP_ARITH_PREC_,X)) + + '+','+','\0', a_X(POSTFIX_INC), + '+','=','\0', a_X(ASSIGN_ADD), + '+','\0', a_X(ADD), + '-','-','\0', a_X(POSTFIX_DEC), + '-','=','\0', a_X(ASSIGN_SUB), + '-','\0', a_X(SUB), + '*','*','=','\0', a_X(ASSIGN_EXP), + '*','*','\0', a_X(EXP), + '*','=','\0', a_X(ASSIGN_MUL), + '*','\0', a_X(MUL), + '/','=','\0', a_X(ASSIGN_DIV), + '/','\0', a_X(DIV), + '%','=','\0', a_X(ASSIGN_MOD), + '%','\0', a_X(MOD), + '|','|','\0', a_X(OR), + '|','=','\0', a_X(ASSIGN_BIT_OR), + '|','\0', a_X(BIT_OR), + '^','=','\0', a_X(ASSIGN_BIT_XOR), + '^','\0', a_X(BIT_XOR), + '&','&','\0', a_X(AND), + '&','=','\0', a_X(ASSIGN_BIT_AND), + '&','\0', a_X(BIT_AND), + '<','<','=',0, a_X(ASSIGN_SHIFT_LEFT), + '<','<','\0', a_X(SHIFT_LEFT), + '>','>','>','=',0, a_X(ASSIGN_SHIFT_RIGHTU), + '>','>','>','\0', a_X(SHIFT_RIGHTU), + '>','>','=',0, a_X(ASSIGN_SHIFT_RIGHT), + '>','>','\0', a_X(SHIFT_RIGHT), + + '~','\0', a_X(UNARY_BIT_NOT), + '!','=','\0', a_X(NE), + '!','\0', a_X(UNARY_NOT), + + ')','\0', a_X(PAREN_RIGHT), + '(','\0', a_X(PAREN_LEFT), + ',','\0', a_X(COMMA), + + '<','=','\0', a_X(LE), + '>','=','\0', a_X(GE), + '=','=','\0', a_X(EQ), + '<','\0', a_X(LT), + '>','\0', a_X(GT), + '=','\0', a_X(ASSIGN), + + '?','\0', a_X(COND), + ':','\0', a_X(COND_COLON), + + '\0' +#undef a_X +}; + +/* Our "public" entry point. exp_buf can be NIL if exp_len is 0, it need not + * be NUL terminated (stop for NUL or out of length). + * Upon error *error_track_or_nil is set to a "newly allocated" string that + * points to where parse stopped, or NIL upon initial setup failure. */ +static enum a_shexp_arith_error a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len + a_SHEXP_ARITH_ERROR_TRACK( su_COMMA char **error_track_or_nil )); + +static void a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len); + +/* Count non-WS as well as normalized WS ([:"space":]+ -> ' ') in exp_buf, + * return count. If store!=NIL, also copy normalization. + * An all-WS exp_buf returns 0 */ +static uz a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil); + +/* Resolve and evaluate the "self-contained string" savp->sav_var. + * Take care to avoid name lookup loops */ +static boole a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp); + +/* Work top of the stack, which may pop & push etc */ +static boole a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self); + +static boole a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self); + +#if a_SHEXP_ARITH_DBG +static void a_shexp__arith_log(char const *fmt, ...); +#endif + +static enum a_shexp_arith_error +a_shexp_arith_eval( +#ifdef a_SHEXP_ARITH_COOKIE + a_SHEXP_ARITH_COOKIE cookie, +#endif + s64 *resp, char const *exp_buf, uz exp_len + a_SHEXP_ARITH_ERROR_TRACK( su_COMMA char **error_track_or_nil )){ + struct a_shexp_arith_stack sas_stack; + struct a_shexp_arith_ctx self; + NYD_IN; + + a_SHEXP_ARITH_L(("> arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + a_SHEXP_ARITH_ERROR_TRACK(DBGX( + if(error_track_or_nil != NIL) + *error_track_or_nil = NIL; + )); + + STRUCT_ZERO(struct a_shexp_arith_ctx, &self); +#ifdef a_SHEXP_ARITH_COOKIE + self.sac_cookie = cookie; +#endif + a_SHEXP_ARITH_ERROR_TRACK( + if((self.sac_error_track_or_nil = error_track_or_nil) != NIL) + self.sac_have_error_track = TRU1; + ) + + ASSERT_NYD_EXEC(resp != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); + DBGX( *resp = 0; ) + ASSERT_NYD_EXEC(exp_len == 0 || exp_buf != NIL, + self.sac_error = a_SHEXP_ARITH_ERR_NO_OP); + + a_SHEXP_ARITH_IFS( self.sac_ifs_ws = ok_vlook(ifs_ws); ) + self.sac_stack = &sas_stack; + a_shexp__arith_eval(&self, exp_buf, exp_len); + *resp = self.sac_rv; + + a_SHEXP_ARITH_L(("< arith_eval %zu <%.*s> -> <%lld> ERR<%d>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf, self.sac_rv, self.sac_error)); + + NYD_OU; + return self.sac_error; +} + +static void +a_shexp__arith_eval(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len){ + char *ep, *varp, *cp, c; + u16 lop; + struct a_shexp_arith_stack *sasp; + void *mem_p; + NYD2_IN; + + a_SHEXP_ARITH_L((" > _arith_eval %zu <%.*s>\n", + exp_len, S(int,exp_len != UZ_MAX ? exp_len : su_cs_len(exp_buf)), + exp_buf)); + + mem_p = NIL; + sasp = self->sac_stack; + + /* Create a single continuous allocation for anything */ + /* C99 */{ + union {void *v; char *c;} p; + uz i, j, a; + + /* Done for empty expression */ + if((i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, NIL)) == 0) + goto jleave; + ++i; + + /* Overflow check: since arithmetic expressions are rarely long enough + * to come near this limit, xxx laxe & fuzzy, not exact; max U32_MAX! */ + if(su_64( i > U32_MAX || ) i >= UZ_MAX / 2 || + i >= ((UZ_MAX - (i a_SHEXP_ARITH_ERROR_TRACK( * 2))) / + ((su_ALIGNOF(*sasp->sas_nums) + sizeof(*sasp->sas_ops) * 2) + a_SHEXP_ARITH_ERROR_TRACK( + + sizeof(*sasp->sas_error_track) * 2 )) + )){ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + + ++i; + j = su_ALIGNOF(*sasp->sas_nums) * (i >> 1); + a = j + (sizeof(*sasp->sas_ops) * i) + + a_SHEXP_ARITH_ERROR_TRACK( (sizeof(*sasp->sas_error_track) * i) + ) + 1 + (i a_SHEXP_ARITH_ERROR_TRACK( * 2 )); + mem_p = p.v = su_LOFI_ALLOC(a); + if(p.v == NIL){ + /* (For MX LOFI has _MUSTFAIL set though) */ + self->sac_error = a_SHEXP_ARITH_ERR_NOMEM; + goto jleave; + } + sasp->sas_nums = sasp->sas_nums_top = S(struct a_shexp_arith_val*,p.v); + p.c += j; + sasp->sas_ops = sasp->sas_ops_top = S(u16*,p.v); + p.c += sizeof(*sasp->sas_ops) * i; + a_SHEXP_ARITH_ERROR_TRACK( + sasp->sas_error_track_top = sasp->sas_error_track = S(char**,p.v); + p.c += sizeof(*sasp->sas_error_track) * i; + ) + + ep = ++p.c; /* ++ to copy varnames in !_ARITH_ERROR cases */ + i = a_shexp__arith_ws_squeeze(self, exp_buf, exp_len, ep); + varp = &ep[ +#if 0 a_SHEXP_ARITH_ERROR_TRACK( + 1) + i + 1 +#else + -1 +#endif + ]; + + a_SHEXP_ARITH_L((" ! _arith_eval ALLOC <%lu> " + "nums=%p (%lu) ops=%p varp=%p %lu <%s>\n", + S(ul,a), sasp->sas_nums, S(ul,j / su_ALIGNOF(*sasp->sas_nums)), + sasp->sas_ops, varp, S(ul,i - 1), ep)); + } + + /* Start with a left paren */ + a_SHEXP_ARITH_ERROR_TRACK( *sasp->sas_error_track_top++ = ep; ) + *sasp->sas_ops_top++ = lop = a_SHEXP_ARITH_OP_PAREN_LEFT; + + for(;;) Jouter:{ + u16 op; + + a_SHEXP_ARITH_L((" = _arith_eval TICK LOP <0x%02X %u> " + "nums=%lu ops=%lu DATA %lu <%s>\n", + lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(*ep == '\0'){ + /* At the end of the expression pop anything left. + * Assume we have read PAREN_RIGHT */ + if(exp_buf != NIL){ + exp_buf = NIL; + op = a_SHEXP_ARITH_OP_PAREN_RIGHT; + /* Could fail for "1)" (how could that enter at all?) + * ASSERT(sasp->sas_ops_top > sasp->sas_ops); + * Can only be a syntax error! */ + if(sasp->sas_ops_top == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } + goto jtok_go; + } + + /* After PAREN_RIGHT, we must be finished */ + if(sasp->sas_nums_top != &sasp->sas_nums[1]) + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + break; + } + + /* Skip (normalized) WS now */ + if(*ep == ' ') + ++ep; + ASSERT(!su_cs_is_space(*ep)); + + /* A number? */ + if(su_cs_is_digit(*ep)){ + BITENUM_IS(u32,su_idec_state) is; + + is = su_idec_cp(&sasp->sas_nums_top->sav_val, ep, 0, + a_SHEXP_ARITH_IDEC_MODE, S(char const**,&ep)); + if((is &= su_IDEC_STATE_EMASK) && is != su_IDEC_STATE_EBASE) + sasp->sas_nums_top->sav_val = 0; + sasp->sas_nums_top->sav_var = NIL; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval NUM <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + continue; + } + + /* Is it a variable name? */ + for(cp = ep; (c = *cp, a_SHEXP_ISVARC(c)); ++cp) + if(cp == ep && a_SHEXP_ISVARC_BAD1ST(c)) + break; + + if(cp != ep){ + for(;;){ + c = cp[-1]; + /* (For example, hyphen-minus as a sh(1) extension!) */ + if(!a_SHEXP_ISVARC_BADNST(c)) + break; + if(--cp == ep){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + } + + /* Copy over to pre-allocated var storage */ + /* C99 */{ + uz i; + + i = P2UZ(cp - ep); + /* (move not copy for !_ARITH_ERROR cases (says ISO C?)) */ + su_mem_move(sasp->sas_nums_top->sav_var = varp, ep, i); + varp += i; + *varp++ = '\0'; + } + ep = cp; + + ++sasp->sas_nums_top; + lop = a_SHEXP_ARITH_OP_NUM; + + a_SHEXP_ARITH_L((" + _arith_eval VAR <%s>\n", + sasp->sas_nums_top[-1].sav_var)); + continue; + } + + /* An operator. + * We turn prefix operators to multiple unary plus/minus if + * not pre- or post-attached to a variable name (++10 -> + + 10). + * (We adjust postfix to prefix below) */ + if((ep[0] == '+' || ep[0] == '-') && (ep[1] == ep[0])){ + if(sasp->sas_nums_top == sasp->sas_nums || + sasp->sas_nums_top[-1].sav_var == NIL){ + if((c = ep[2]) == ' ') + c = ep[3]; + + if(c != '\0' && (!a_SHEXP_ISVARC(c) || a_SHEXP_ISVARC_BAD1ST(c))){ + op = (ep[0] == '+') ? a_SHEXP_ARITH_OP_ADD + : a_SHEXP_ARITH_OP_SUB; + ++ep; + a_SHEXP_ARITH_L((" + _arith_eval OP PREFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", ep[0], ep[0], ep[0])); + goto jtok_go; + } + } + } + + /* Operator search */ + /* C99 */{ + char const *tokp; + + /* 3=NUL+OP+PREC */ + for(tokp = a_shexp_arith_op_toks; *tokp != '\0'; tokp += 3){ + for(cp = ep;; ++tokp, ++cp){ + if(*tokp == '\0'){ + ep = cp; + op = (S(u16,tokp[1]) << 8) | S(u8,tokp[2]); + goto jtok_go; + }else if(*tokp != *cp) + break; + } + + while(*tokp != '\0') + ++tokp; + } + self->sac_error = a_SHEXP_ARITH_ERR_OP_INVALID; + goto jleave; + } + +jtok_go:/* C99 */{ + u8 prec; + + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP <0x%02X %u> LOP <0x%02X %u> " + "nums=%lu ops=%lu %lu <%s>\n", + op, prec, lop, lop & 0xFF, S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops), S(ul,su_cs_len(ep)), ep)); + + if(op == a_SHEXP_ARITH_OP_UNARY_PLUS){ + a_SHEXP_ARITH_L((" + _arith_eval IGNORE UNARY PLUS\n")); + continue; + } + + /* Correct our understanding of what there is. + * Post grammar: VAR++ reduces to num */ + if((lop & 0xFF) == a_SHEXP_ARITH_PREC_POSTFIX){ + lop = a_SHEXP_ARITH_OP_NUM; + a_SHEXP_ARITH_L((" + _arith_eval LOP POSTFIX REDUCED to NUM\n")); + } + /* Adjust some binary/postfix operators to make them flow */ + else if(lop != a_SHEXP_ARITH_OP_NUM){ + switch(op){ + case a_SHEXP_ARITH_OP_ADD: + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST: IGNORE UNARY PLUS\n")); + continue; + case a_SHEXP_ARITH_OP_SUB: + op = a_SHEXP_ARITH_OP_UNARY_MINUS; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_INC: + op = a_SHEXP_ARITH_OP_PREFIX_INC; + goto junapre; + case a_SHEXP_ARITH_OP_POSTFIX_DEC: + op = a_SHEXP_ARITH_OP_PREFIX_DEC; +junapre: + prec = a_SHEXP_ARITH_PREC_PREFIX; + a_SHEXP_ARITH_L((" + _arith_eval OP ADJUST TO UNARY/PREFIX\n")); + break; + } + } + /* Special: +10++VAR -> +10 + +VAR. (Since we do handle +10++11 + * correctly via "prefix split", we should also handle this) */ + else if(prec == a_SHEXP_ARITH_PREC_POSTFIX){ + ASSERT(lop == a_SHEXP_ARITH_OP_NUM); + if((c = ep[0]) == ' ') + c = ep[1]; + if(c != '\0' && (a_SHEXP_ISVARC(c) && !a_SHEXP_ISVARC_BAD1ST(c))){ + c = *--ep; + op = (c == '+') ? a_SHEXP_ARITH_OP_ADD : a_SHEXP_ARITH_OP_SUB; + prec = op & 0xFF; + a_SHEXP_ARITH_L((" + _arith_eval OP POSTFIX INC/DEC SPLIT " + "<%c%c> -> <%c>\n", c, c, c)); + } + } + + /* Check whether we can work it a bit */ + if((prec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + prec < a_SHEXP_ARITH_PREC_UNARY) || + prec >= a_SHEXP_ARITH_PREC_SKY){ + if(lop != a_SHEXP_ARITH_OP_NUM){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + + /* Pop as much as possible */ + while(sasp->sas_ops_top != sasp->sas_ops){ + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + + a_SHEXP_ARITH_L((" + _arith_eval TRY POP - OP " + "<0x%02X %u>, NEW LOP <0x%02X %u 0x%X> nums=%lu ops=%lu\n", + op, op & 0xFF, lop, lop & 0xFF, + (*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK), + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* Special-case parenthesis groups */ + if(op == a_SHEXP_ARITH_OP_PAREN_RIGHT){ + if(lop == a_SHEXP_ARITH_OP_PAREN_LEFT){ + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + /* Resolve VAR to NUM */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + ASSERT(!(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK)); + if(!a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + } + sasp->sas_nums_top[-1].sav_var = NIL; + a_SHEXP_ARITH_L((" + _arith_eval OP () RESOLVED <%lld>\n", + sasp->sas_nums_top[-1].sav_val)); + lop = a_SHEXP_ARITH_OP_NUM; + goto Jouter; + } + }else{ + u8 lprec; + + lprec = lop & 0xFF; + + /* */ + if(op == a_SHEXP_ARITH_OP_COND){ + u16 x; + + x = *sasp->sas_ops_top; + x &= a_SHEXP_ARITH_OP_FLAG_MASK; + if(x & a_SHEXP_ARITH_OP_FLAG_WHITEOUT){ + x ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + x |= a_SHEXP_ARITH_OP_FLAG_OUTER_WHITEOUT; + } + op |= x; + + /* Resolve as resolve can, need to assert our condition! */ + while(lprec > a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + /* Evaluate condition assertion */ + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + --sasp->sas_nums_top; + + if(sasp->sas_nums_top->sav_var != NIL){ + if(!(op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + !a_shexp__arith_val_eval(self, sasp->sas_nums_top)) + goto jleave; + sasp->sas_nums_top->sav_var = NIL; + } + + if((sasp->sas_nums_top)->sav_val == 0) + op |= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + op |= *sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_MASK; + + /* Delay ternary: this ? op will last until we can resolve + * the entire condition, its number stack position is used + * as storage for the actual condition result */ + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + break; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + uz recur; + u16 *opsp, x; + boole delay; + + delay = TRU1; + + /* Find our counterpart ? so we can toggle whiteout */ + opsp = sasp->sas_ops_top; + for(recur = 1;; --opsp){ + if(opsp == sasp->sas_ops){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + x = *opsp & a_SHEXP_ARITH_OP_MASK; + if(x == a_SHEXP_ARITH_OP_COND_COLON) + ++recur; + else if(x == a_SHEXP_ARITH_OP_COND && --recur == 0){ + *opsp |= a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON; + break; + } + } + op |= *opsp & a_SHEXP_ARITH_OP_FLAG_MASK; + op ^= a_SHEXP_ARITH_OP_FLAG_WHITEOUT; + + /* Resolve innermost condition asap. + * In "1?0?5:6:3", resolve innermost upon :3 */ + while(lprec > a_SHEXP_ARITH_PREC_PAREN_LEFT && + lprec != a_SHEXP_ARITH_PREC_COND){ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + lop = *--sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + } + + /* If at a COLON we have to resolve further, otherwise syntax + * error would happen for 1?2?3:6:7 (due to how Dijkstra's + * algorithm applies, and our squeezing of ?: constructs) */ + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + delay = FAL0; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + lop = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + } + + if(lop != a_SHEXP_ARITH_OP_COND){ + self->sac_error = a_SHEXP_ARITH_ERR_SYNTAX; + goto jleave; + } + + if(delay){ + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + } + a_SHEXP_ARITH_L((" + _arith_eval %sTERNARY ?:%s\n", + (delay ? "DELAY " : su_empty), + ((op & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) + ? " WHITEOUT" : su_empty))); + break; + } + /* Is this a right-associative operation? */ + else{ + boole doit; + + doit = FAL0; + if(lprec < prec){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY PRECEDENCE\n")); + }else if(lprec == prec && prec == a_SHEXP_ARITH_PREC_ASSIGN){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY RIGHT ASSOC\n")); + }else if(lprec == a_SHEXP_ARITH_PREC_COND){ + if(lop == a_SHEXP_ARITH_OP_COND){ + doit = TRU1; + a_SHEXP_ARITH_L((" + _arith_eval DELAY CONDITION\n")); + } + /* Without massive rewrite this is the location to detect + * in-whiteout precedence bugs as in + * $((0?I1=10:(1?I3:I2=12))) + * which would be parsed like (1?I3:I2)=12 without error + * (different to 0?I3:I2=12) otherwise */ + else if(op != a_SHEXP_ARITH_OP_COMMA){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_PREC_INVALID; + goto jleave; + } + } + + if(doit){ + /* If we are about to delay and LHV is a VAR, expand that + * immediately to expand in correct order things like + * I1=I2=10 I2=3; echo $((I1,I2)) + * I1=I2=10 I2=3; echo $((I1+=I2)) */ + if(sasp->sas_nums_top[-1].sav_var != NIL){ + if(op != a_SHEXP_ARITH_OP_ASSIGN && + !(*sasp->sas_ops_top & + a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) && + !a_shexp__arith_val_eval(self, + &sasp->sas_nums_top[-1])) + goto jleave; + if(prec != a_SHEXP_ARITH_PREC_ASSIGN) + sasp->sas_nums_top[-1].sav_var = NIL; + } + + a_SHEXP_ARITH_ERROR_TRACK( ++sasp->sas_error_track_top; ) + ++sasp->sas_ops_top; + break; + } + } + } + + /* */ + if(!a_shexp__arith_op_apply(self)) + goto jleave; + + if(lop == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops && + &sasp->sas_ops_top[-1] > sasp->sas_ops); + ASSERT((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND); + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + --sasp->sas_ops_top; + } + } + + /* Should have been catched in *ep==\0,exp_buf!=NIL case */ + ASSERT(op != a_SHEXP_ARITH_OP_PAREN_RIGHT); + } + + /* Push this operator to the stack and remember it */ + a_SHEXP_ARITH_ERROR_TRACK( *sasp->sas_error_track_top++ = ep; ) + if(sasp->sas_ops_top > sasp->sas_ops && + (op & 0xFF) != a_SHEXP_ARITH_PREC_COND) + op |= sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_FLAG_MASK; + *sasp->sas_ops_top++ = op; + lop = op & a_SHEXP_ARITH_OP_MASK; + a_SHEXP_ARITH_L((" + _arith_eval OP PUSH <0x%02X %u> nums=%lu ops=%lu\n", + op, (op & 0xFF), S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + } + } + + self->sac_rv = sasp->sas_nums->sav_val; + +jleave: +#if 0 a_SHEXP_ARITH_ERROR_TRACK( + 1 ) + if(self->sac_error != a_SHEXP_ARITH_ERR_NONE && mem_p != NIL && + self->sac_have_error_track){ + if(sasp->sas_error_track_top > sasp->sas_error_track) + --sasp->sas_error_track_top; + *self->sac_error_track_or_nil = savestr(*sasp->sas_error_track_top); + } +#endif + + if(mem_p != NIL) + su_LOFI_FREE(mem_p); + + a_SHEXP_ARITH_L((" < _arith_eval <%lld> ERR<%d>\n", + self->sac_rv, self->sac_error)); + NYD2_OU; +} + +static uz +a_shexp__arith_ws_squeeze(struct a_shexp_arith_ctx *self, + char const *exp_buf, uz exp_len, char *store_or_nil){ + a_SHEXP_ARITH_IFS( char const *ifs_ws; ) + char c; + boole last_ws, ws; + uz rv; + NYD2_IN; + UNUSED(self); + + rv = 0; + a_SHEXP_ARITH_IFS( ifs_ws = self->sac_ifs_ws; ) + + for(;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + goto jleave; + if(!(su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + )) + break; + } + + for(last_ws = FAL0;; ++exp_buf, --exp_len){ + if(UNLIKELY(exp_len == 0) || UNLIKELY((c = *exp_buf) == '\0')) + break; + + ws = (su_cs_is_space(c) + a_SHEXP_ARITH_IFS( || su_cs_find_c(ifs_ws, c) != NIL ) + ); + if(ws){ + if(last_ws) + continue; + c = ' '; + } + last_ws = ws; + + ++rv; + if(store_or_nil != NIL) + *store_or_nil++ = c; + } + + if(last_ws){ + --rv; + if(store_or_nil != NIL) + --store_or_nil; + } + +jleave: + if(store_or_nil != NIL) + *store_or_nil = '\0'; + + NYD2_OU; + return rv; +} + +static boole +a_shexp__arith_val_eval(struct a_shexp_arith_ctx *self, + struct a_shexp_arith_val *savp){ + struct a_shexp_arith_name_stack sans_stack, *sansp; + struct a_shexp_arith_stack sas_stack, *sasp; + char const *cp; + NYD_IN; + ASSERT(savp->sav_var != NIL); + + a_SHEXP_ARITH_L(("> _arith_val_eval %p <%s>\n", savp, savp->sav_var)); + + savp->sav_val = 0; + + /* Also look in program environment XXX configurable? */ + cp = n_var_vlook(savp->sav_var, TRU1); + if(cp == NIL) + goto jleave; + + for(sansp = self->sac_name_stack; sansp != NIL; sansp = sansp->sans_last){ + if(!su_cs_cmp(sansp->sans_var, savp->sav_var)){ + self->sac_error = a_SHEXP_ARITH_ERR_NAME_LOOP; + goto jleave; + } + } + + /* cp must be a self-contained expression. + * However, in most cases it solely consists of an integer, shortcut that */ + if(su_idec_cp(&savp->sav_val, cp, 0, a_SHEXP_ARITH_IDEC_MODE, NIL + ) & su_IDEC_STATE_CONSUMED){ + a_SHEXP_ARITH_L((" + _arith_val_eval NUM DIRECT <%lld>\n", + savp->sav_val)); + }else{ + sasp = self->sac_stack; + self->sac_stack = &sas_stack; + + sans_stack.sans_last = sansp = self->sac_name_stack; + sans_stack.sans_var = savp->sav_var; + self->sac_name_stack = &sans_stack; + + a_shexp__arith_eval(self, cp, UZ_MAX); + savp->sav_val = self->sac_rv; + /* .sav_var may be needed further on for updating purposes */ + + self->sac_stack = sasp; + self->sac_name_stack = sansp; + } + + cp = NIL; +jleave: + a_SHEXP_ARITH_L(("< _arith_val_eval %p <%s> <%lld> -> OK <%d>\n", + savp, savp->sav_var, savp->sav_val, + (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE))); + + NYD_OU; + return (cp == NIL && self->sac_error == a_SHEXP_ARITH_ERR_NONE); +} + +static boole +a_shexp__arith_op_apply(struct a_shexp_arith_ctx *self){ + struct a_shexp_arith_val *nums_top; + u8 prec; + u16 op; + struct a_shexp_arith_stack *sasp; + s64 val; + boole rv, ign; + NYD_IN; + + rv = FAL0; + val = 0; + sasp = self->sac_stack; + op = *sasp->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + ign = ((*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_WHITE_MASK) != 0); + + a_SHEXP_ARITH_L((" > _arith_op_apply %s<0x%02X %u> " + "nums_top=%p (%lu) ops_top=%p (%lu)\n", + (ign ? "WHITEOUT " : su_empty), op, (op & 0xFF), sasp->sas_nums_top, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + sasp->sas_ops_top, S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + /* At least one argument is always needed */ + if((nums_top = sasp->sas_nums_top) == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + --nums_top; + + /* Resolve name ([R]VAL) to value as necessary */ + if(!ign && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + val = nums_top->sav_val; + prec = op & 0xFF; + + /* Not a binary operator? */ + if(prec >= a_SHEXP_ARITH_PREC_UNARY && prec < a_SHEXP_ARITH_PREC_SKY){ + if(ign) + goto jquick; + + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_UNARY_NOT: val = !val; break; + case a_SHEXP_ARITH_OP_UNARY_BIT_NOT: val = ~val; break; + case a_SHEXP_ARITH_OP_UNARY_MINUS: val = -val; break; + case a_SHEXP_ARITH_OP_PREFIX_INC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_INC: ++val; break; + case a_SHEXP_ARITH_OP_PREFIX_DEC: FALLTHRU + case a_SHEXP_ARITH_OP_POSTFIX_DEC: --val; break; + } + }else if(op == a_SHEXP_ARITH_OP_COND){ + if(!(*sasp->sas_ops_top & a_SHEXP_ARITH_OP_FLAG_COND_SAW_COLON)){ + self->sac_error = a_SHEXP_ARITH_ERR_COND_NO_COLON; + goto jleave; + } + goto jquick; + }else if(op == a_SHEXP_ARITH_OP_COND_COLON){ + ASSERT(sasp->sas_ops_top > sasp->sas_ops); + ASSERT(nums_top > sasp->sas_nums); + + if(!ign){ + /* Move the ternary value over to LHV where we find it as a result, + * and ensure LHV's name is forgotten so not to evaluate it (for + * example in 0?I1:I2 I1 would be evaluated when resolving the virtual + * outer group, because it still exists on number stack) */ + nums_top[-1].sav_val = nums_top[0].sav_val; + nums_top[-1].sav_var = NIL; + } + DBGX( else val = -1; ) + + sasp->sas_nums_top = nums_top; + + if((sasp->sas_ops_top[-1] & a_SHEXP_ARITH_OP_MASK + ) == a_SHEXP_ARITH_OP_COND_COLON){ + a_SHEXP_ARITH_ERROR_TRACK( --sasp->sas_error_track_top; ) + --sasp->sas_ops_top; + if(!a_shexp__arith_op_apply_colons(self)) + goto jleave; + ASSERT(sasp->sas_nums_top > sasp->sas_nums); + if(!ign) + sasp->sas_nums_top[-1].sav_val = val; + } + }else{ + /* Binaries need two numbers: one is popped, the other replaced */ + s64 rval; + + if(nums_top == sasp->sas_nums){ + self->sac_error = a_SHEXP_ARITH_ERR_NO_OP; + goto jleave; + } + sasp->sas_nums_top = nums_top--; + + if(ign) + goto jquick; + + /* Resolve LHV as necessary */ + if(op != a_SHEXP_ARITH_OP_ASSIGN && nums_top->sav_var != NIL && + !a_shexp__arith_val_eval(self, nums_top)) + goto jleave; + + rval = val; + val = nums_top->sav_val; /* (may be bogus for assign, fixed soon) */ + + /* In precedence order (excluding assignments) */ + switch(op){ + default: break; + case a_SHEXP_ARITH_OP_COMMA: FALLTHRU + + case a_SHEXP_ARITH_OP_ASSIGN: val = rval; break; + + case a_SHEXP_ARITH_OP_OR: val = (val != 0 || rval != 0); break; + case a_SHEXP_ARITH_OP_AND: val = (val != 0 && rval != 0); break; + + case a_SHEXP_ARITH_OP_BIT_OR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_OR: val |= rval; break; + case a_SHEXP_ARITH_OP_BIT_XOR: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_XOR: val ^= rval; break; + case a_SHEXP_ARITH_OP_BIT_AND: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_BIT_AND: val &= rval; break; + + case a_SHEXP_ARITH_OP_EQ: val = (val == rval); break; + case a_SHEXP_ARITH_OP_NE: val = (val != rval); break; + + case a_SHEXP_ARITH_OP_LE: val = (val <= rval); break; + case a_SHEXP_ARITH_OP_GE: val = (val >= rval); break; + case a_SHEXP_ARITH_OP_LT: val = (val < rval); break; + case a_SHEXP_ARITH_OP_GT: val = (val > rval); break; + + case a_SHEXP_ARITH_OP_SHIFT_LEFT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_LEFT: val <<= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHT: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHT: val >>= rval; break; + + case a_SHEXP_ARITH_OP_SHIFT_RIGHTU: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SHIFT_RIGHTU: + val = S(s64,S(u64,val) >> rval); + break; + + case a_SHEXP_ARITH_OP_ADD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_ADD: val += rval; break; + case a_SHEXP_ARITH_OP_SUB: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_SUB: val -= rval; break; + + case a_SHEXP_ARITH_OP_MUL: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MUL: val *= rval; break; + /* For /,%, avoid lvh=S64_MIN, rhv=-1: + * CHANGES, bash 4.3 [ac50fbac377e32b98d2de396f016ea81e8ee9961]: + * Fixed a bug that caused floating-point exceptions and + * overflow errors for the / and % arithmetic operators when + * using INTMAX_MIN and -1. */ + case a_SHEXP_ARITH_OP_DIV: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_DIV: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val != S64_MIN || rval != -1) + val /= rval; + break; + case a_SHEXP_ARITH_OP_MOD: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_MOD: + if(rval == 0){ + self->sac_error = a_SHEXP_ARITH_ERR_DIV_BY_ZERO; + goto jleave; + }else if(val == S64_MIN && rval == -1) + val = 0; + else + val %= rval; + break; + + case a_SHEXP_ARITH_OP_EXP: FALLTHRU + case a_SHEXP_ARITH_OP_ASSIGN_EXP: + if(rval < 0){ + self->sac_error = a_SHEXP_ARITH_ERR_EXP_INVALID; + goto jleave; + }else{ + s64 i; + + for(i = 1; rval > 0; --rval) + i *= val; + val = i; + } + break; + } + } + + /* Assignment updates a variable, which must exist. + * For prefix and postfix operators, too: we already turned them into + * multiple unary plus/minus unless we had seen a variable name */ +jquick: + if(prec == a_SHEXP_ARITH_PREC_ASSIGN || prec == a_SHEXP_ARITH_PREC_PREFIX || + prec == a_SHEXP_ARITH_PREC_POSTFIX){ + char buf[su_IENC_BUFFER_SIZE], *bp; + + if(nums_top->sav_var == NIL){ + self->sac_error = a_SHEXP_ARITH_ERR_ASSIGN_NO_VAR; + goto jleave; + } + + if(!ign){ + bp = su_ienc_s64(buf, val, 10); + n_var_vset(nums_top->sav_var, S(up,bp), FAL0); + } + + /* And restore the stack value again for postfix operators */ + if(op == a_SHEXP_ARITH_OP_POSTFIX_INC) + --val; + else if(op == a_SHEXP_ARITH_OP_POSTFIX_DEC) + ++val; + + if(!ign) + a_SHEXP_ARITH_L((" + _arith_op_apply VAR <%s> SET <%s> VAL <%lld>\n", + nums_top->sav_var, bp, val)); + } + + nums_top->sav_val = val; + nums_top->sav_var = NIL; + + rv = TRU1; +jleave: + a_SHEXP_ARITH_L((" < _arith_op_apply RV %d <0x%02X %u> RES<%lld> ERR<%d> " + "nums=%lu ops=%lu\n", + rv, op, op & 0xFF, val, self->sac_error, + S(ul,sasp->sas_nums_top - sasp->sas_nums), + S(ul,sasp->sas_ops_top - sasp->sas_ops))); + + NYD_OU; + return rv; +} + +static boole +a_shexp__arith_op_apply_colons(struct a_shexp_arith_ctx *self){ + u16 lop, lprec; + boole next_stop; + NYD_IN; + + for(next_stop = FAL0;;){ + if(!a_shexp__arith_op_apply(self)){ + next_stop = FAL0; + break; + } + if(next_stop) + break; + a_SHEXP_ARITH_ERROR_TRACK( --self->sac_stack->sas_error_track_top; ) + lop = *--self->sac_stack->sas_ops_top & a_SHEXP_ARITH_OP_MASK; + lprec = lop & 0xFF; + next_stop = (lprec == a_SHEXP_ARITH_PREC_PAREN_LEFT || + lop == a_SHEXP_ARITH_OP_COND); + } + + NYD_OU; + return next_stop; +} + +#if a_SHEXP_ARITH_DBG +static void +a_shexp__arith_log(char const *fmt, ...){ + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); +} +#endif + +#undef a_SHEXP_ARITH_ERROR_TRACK +#undef a_SHEXP_ARITH_IFS +#undef a_SHEXP_ARITH_DBG +#undef a_SHEXP_ARITH_L +#undef a_SHEXP_ARITH_IDEC_MODE + +/* s-it-mode */ -- 2.37.2 --steffen | |Der Kragenbaer, The moon bear, |der holt sich munter he cheerfully and one by one |einen nach dem anderen runter wa.ks himself off |(By Robert Gernhardt)