Re: [PATCH v13 00/24] Modernize U-Boot shell

2024-01-17 Thread Francis Laniel
Hi!


Le mercredi 17 janvier 2024, 17:05:28 +07 Patrice CHOTARD a écrit :
> On 1/16/24 18:25, Francis Laniel wrote:
> > Hi!
> > 
> > Le mardi 16 janvier 2024, 00:34:24 +07 Patrice CHOTARD a écrit :
> >> On 1/11/24 18:04, Francesco Dolcini wrote:
> >>> Hello Tom, Francis
> >>> 
> >>> On Fri, Dec 29, 2023 at 07:55:37PM +0100, Francis Laniel wrote:
> >>>> Le jeudi 28 décembre 2023, 21:58:59 CET Tom Rini a écrit :
> >>>>> On Fri, 22 Dec 2023 22:02:20 +0100, Francis Laniel wrote:
> >>>>>> During 2021 summer, Sean Anderson wrote a contribution to add a new
> >>>>>> shell,
> >>>>>> based on LIL, to U-Boot [1, 2].
> >>>>>> While one of the goals of this contribution was to address the fact
> >>>>>> actual
> >>>>>> U-Boot shell, which is based on Busybox hush, is old there was a
> >>>>>> discussion
> >>>>>> about adding a new shell versus updating the actual one [3, 4].
> >>>>>> 
> >>>>>> So, in this series, with Harald Seiler, we updated the actual U-Boot
> >>>>>> shell
> >>>>>> to reflect what is currently in Busybox source code.
> >>>>>> Basically, this contribution is about taking a snapshot of Busybox
> >>>>>> shell/hush.c file (as it exists in commit 37460f5da) and adapt it to
> >>>>>> suit
> >>>>>> U-Boot needs.
> >>>>>> 
> >>>>>> [...]
> >>>>> 
> >>>>> Applied to u-boot/next, thanks!
> >>>> 
> >>>> Thank you for the merge!
> >>>> If there is any problem, do not hesitate to mail me and I will take
> >>>> care
> >>>> of
> >>>> it!
> >>> 
> >>> This change, specifically setting the modern hush shell as default, is
> >>> breaking our boot script, just noticed since the current U-Boot master
> >>> has a regression for us.
> >>> 
> >>> We still need to figure out the exact details, here [1] you can find the
> >>> boot script (that has some placeholder that is replaced during build).
> >>> 
> >>> and the error is something like:
> >>> 
> >>> ```
> >>> ## Executing script at 9028
> >>> Loading DeviceTree: k3-am625-verdin-nonwifi-dev.dtb
> >>> 69025 bytes read in 11 ms (6 MiB/s)
> >>> 82 bytes read in 9 ms (8.8 KiB/s)
> >>> Working FDT set to 9020
> >>> syntax error at 'done'HUSH died!
> >>> resetting ...
> >>> ```
> >>> 
> >>> that I _assume_ comes from this line
> >>> 
> >>> env set set_apply_overlays 'env set apply_overlays "for overlay_file
> >>> in \\${fdt_overlays}; do echo Applying Overlay: \\${overlay_file} &&
> >>> ${load_cmd} \\${loadaddr} \\${overlays_prefix}\\${overlay_file} &&
> >>> fdt apply \\${loadaddr}; env set overlay_file; done; true"'>
> >>> 
> >>> [1]
> >>> https://git.toradex.com/cgit/meta-toradex-bsp-common.git/tree/recipes-bs
> >>> p
> >>> /u-boot/u-boot-distro-boot/boot.cmd.in
> >>> 
> >>> Francesco
> >> 
> >> Hi all
> >> 
> >> I observed a similar issue with STM32MP157c-DK2 board.
> >> Since commit 78912cfde281 ("cmd: Set modern hush as default shell")
> >> U-Boot
> >> crashes :
> >> 
> >> 
> >> U-Boot 2024.01-00486-g697758e7c81-dirty (Jan 15 2024 - 18:23:52 +0100)
> >> 
> >> CPU: STM32MP157CAC Rev.B
> >> Model: STMicroelectronics STM32MP157C-DK2 Discovery Board
> >> Board: stm32mp1 in trusted mode (st,stm32mp157c-dk2)
> >> Board: MB1272 Var2.0 Rev.C-01
> >> DRAM:  512 MiB
> >> Clocks:
> >> - MPU : 650 MHz
> >> - MCU : 208.878 MHz
> >> - AXI : 266.500 MHz
> >> - PER : 24 MHz
> >> - DDR : 533 MHz
> >> optee optee: OP-TEE: revision 4.0 (e92de4ca)
> >> I/TC: Reserved shared memory is disabled
> >> I/TC: Dynamic shared memory is enabled
> >> I/TC: Normal World virtualization support is disabled
> >> I/TC: Asynchronous notifications are disabled
> >> Core:  311 devices, 40 uclasses, devicetree: board
> >> WDT:   Started watchdog@5a002000 with servicing every 1000ms (32s
> >> timeout)
&g

Re: [PATCH v13 00/24] Modernize U-Boot shell

2024-01-16 Thread Francis Laniel
Hi!


Le mardi 16 janvier 2024, 00:34:24 +07 Patrice CHOTARD a écrit :
> On 1/11/24 18:04, Francesco Dolcini wrote:
> > Hello Tom, Francis
> > 
> > On Fri, Dec 29, 2023 at 07:55:37PM +0100, Francis Laniel wrote:
> >> Le jeudi 28 décembre 2023, 21:58:59 CET Tom Rini a écrit :
> >>> On Fri, 22 Dec 2023 22:02:20 +0100, Francis Laniel wrote:
> >>>> During 2021 summer, Sean Anderson wrote a contribution to add a new
> >>>> shell,
> >>>> based on LIL, to U-Boot [1, 2].
> >>>> While one of the goals of this contribution was to address the fact
> >>>> actual
> >>>> U-Boot shell, which is based on Busybox hush, is old there was a
> >>>> discussion
> >>>> about adding a new shell versus updating the actual one [3, 4].
> >>>> 
> >>>> So, in this series, with Harald Seiler, we updated the actual U-Boot
> >>>> shell
> >>>> to reflect what is currently in Busybox source code.
> >>>> Basically, this contribution is about taking a snapshot of Busybox
> >>>> shell/hush.c file (as it exists in commit 37460f5da) and adapt it to
> >>>> suit
> >>>> U-Boot needs.
> >>>> 
> >>>> [...]
> >>> 
> >>> Applied to u-boot/next, thanks!
> >> 
> >> Thank you for the merge!
> >> If there is any problem, do not hesitate to mail me and I will take care
> >> of
> >> it!
> > 
> > This change, specifically setting the modern hush shell as default, is
> > breaking our boot script, just noticed since the current U-Boot master
> > has a regression for us.
> > 
> > We still need to figure out the exact details, here [1] you can find the
> > boot script (that has some placeholder that is replaced during build).
> > 
> > and the error is something like:
> > 
> > ```
> > ## Executing script at 9028
> > Loading DeviceTree: k3-am625-verdin-nonwifi-dev.dtb
> > 69025 bytes read in 11 ms (6 MiB/s)
> > 82 bytes read in 9 ms (8.8 KiB/s)
> > Working FDT set to 9020
> > syntax error at 'done'HUSH died!
> > resetting ...
> > ```
> > 
> > that I _assume_ comes from this line
> > 
> > env set set_apply_overlays 'env set apply_overlays "for overlay_file
> > in \\${fdt_overlays}; do echo Applying Overlay: \\${overlay_file} &&
> > ${load_cmd} \\${loadaddr} \\${overlays_prefix}\\${overlay_file} &&
> > fdt apply \\${loadaddr}; env set overlay_file; done; true"'> 
> > [1]
> > https://git.toradex.com/cgit/meta-toradex-bsp-common.git/tree/recipes-bsp
> > /u-boot/u-boot-distro-boot/boot.cmd.in
> > 
> > Francesco
> 
> Hi all
> 
> I observed a similar issue with STM32MP157c-DK2 board.
> Since commit 78912cfde281 ("cmd: Set modern hush as default shell") U-Boot
> crashes :
> 
> 
> U-Boot 2024.01-00486-g697758e7c81-dirty (Jan 15 2024 - 18:23:52 +0100)
> 
> CPU: STM32MP157CAC Rev.B
> Model: STMicroelectronics STM32MP157C-DK2 Discovery Board
> Board: stm32mp1 in trusted mode (st,stm32mp157c-dk2)
> Board: MB1272 Var2.0 Rev.C-01
> DRAM:  512 MiB
> Clocks:
> - MPU : 650 MHz
> - MCU : 208.878 MHz
> - AXI : 266.500 MHz
> - PER : 24 MHz
> - DDR : 533 MHz
> optee optee: OP-TEE: revision 4.0 (e92de4ca)
> I/TC: Reserved shared memory is disabled
> I/TC: Dynamic shared memory is enabled
> I/TC: Normal World virtualization support is disabled
> I/TC: Asynchronous notifications are disabled
> Core:  311 devices, 40 uclasses, devicetree: board
> WDT:   Started watchdog@5a002000 with servicing every 1000ms (32s timeout)
> NAND:  0 MiB
> MMC:   STM32 SD/MMC: 0
> Loading Environment from MMC... OK
> In:No input devices available!
> Out:   No output devices available!
> Err:   No error devices available!
> Net:   eth0: ethernet@5800a000
> Hit any key to stop autoboot:  0
> Boot over mmc0!
> switch to partitions #0, OK
> mmc0 is current device
> Scanning mmc 0:8...
> data abort
> pc : []  lr : []
> reloc pc : []lr : []
> sp : dbafc848  ip : ddbfc578 fp : ddbedf18
> r10:   r9 : dbb15e70 r8 : 
> r7 : dbb5bf98  r6 : dbb5de10 r5 : dbb5bf98  r4 : ddbeda78
> r3 : dbb5dc08  r2 : 33f8 r1 : 0071  r0 : ddbede00
> Flags: nzcv  IRQs off  FIQs off  Mode SVC_32 (T)
> Code: 3138 1c48 f854 0030 (eb04) 05c1
> Resetting CPU ...
> 
> 
> It crashes in blkcache_fill() , i didn't investigate deeply into this issue
> yet, but i can reproduce this issue by stopping autoboot by

Re: [PATCH v13 00/24] Modernize U-Boot shell

2024-01-16 Thread Francis Laniel
Hi!


Le vendredi 12 janvier 2024, 00:04:18 +07 Francesco Dolcini a écrit :
> Hello Tom, Francis
> 
> On Fri, Dec 29, 2023 at 07:55:37PM +0100, Francis Laniel wrote:
> > Le jeudi 28 décembre 2023, 21:58:59 CET Tom Rini a écrit :
> > > On Fri, 22 Dec 2023 22:02:20 +0100, Francis Laniel wrote:
> > > > During 2021 summer, Sean Anderson wrote a contribution to add a new
> > > > shell,
> > > > based on LIL, to U-Boot [1, 2].
> > > > While one of the goals of this contribution was to address the fact
> > > > actual
> > > > U-Boot shell, which is based on Busybox hush, is old there was a
> > > > discussion
> > > > about adding a new shell versus updating the actual one [3, 4].
> > > > 
> > > > So, in this series, with Harald Seiler, we updated the actual U-Boot
> > > > shell
> > > > to reflect what is currently in Busybox source code.
> > > > Basically, this contribution is about taking a snapshot of Busybox
> > > > shell/hush.c file (as it exists in commit 37460f5da) and adapt it to
> > > > suit
> > > > U-Boot needs.
> > > > 
> > > > [...]
> > > 
> > > Applied to u-boot/next, thanks!
> > 
> > Thank you for the merge!
> > If there is any problem, do not hesitate to mail me and I will take care
> > of
> > it!
> 
> This change, specifically setting the modern hush shell as default, is
> breaking our boot script, just noticed since the current U-Boot master
> has a regression for us.
> 
> We still need to figure out the exact details, here [1] you can find the
> boot script (that has some placeholder that is replaced during build).
> 
> and the error is something like:
> 
> ```
> ## Executing script at 9028
> Loading DeviceTree: k3-am625-verdin-nonwifi-dev.dtb
> 69025 bytes read in 11 ms (6 MiB/s)
> 82 bytes read in 9 ms (8.8 KiB/s)
> Working FDT set to 9020
> syntax error at 'done'HUSH died!
> resetting ...
> ```
> 
> that I _assume_ comes from this line
> 
> env set set_apply_overlays 'env set apply_overlays "for overlay_file in
> \\${fdt_overlays}; do echo Applying Overlay: \\${overlay_file} &&
> ${load_cmd} \\${loadaddr} \\${overlays_prefix}\\${overlay_file} && fdt
> apply \\${loadaddr}; env set overlay_file; done; true"'

Sorry for the inconvenience, I tried to reproduce on my side and noticed some 
strange behavior.
For the moment, can you please try to only escape "$" with "\$ and not "\\$"?
I would like to confirm some insights I have.

> [1]
> https://git.toradex.com/cgit/meta-toradex-bsp-common.git/tree/recipes-bsp/u
> -boot/u-boot-distro-boot/boot.cmd.in
> 
> Francesco

Best regards.




Re: [PATCH v13 00/24] Modernize U-Boot shell

2023-12-29 Thread Francis Laniel
Le jeudi 28 décembre 2023, 21:58:59 CET Tom Rini a écrit :
> On Fri, 22 Dec 2023 22:02:20 +0100, Francis Laniel wrote:
> > During 2021 summer, Sean Anderson wrote a contribution to add a new shell,
> > based on LIL, to U-Boot [1, 2].
> > While one of the goals of this contribution was to address the fact actual
> > U-Boot shell, which is based on Busybox hush, is old there was a
> > discussion
> > about adding a new shell versus updating the actual one [3, 4].
> > 
> > So, in this series, with Harald Seiler, we updated the actual U-Boot shell
> > to reflect what is currently in Busybox source code.
> > Basically, this contribution is about taking a snapshot of Busybox
> > shell/hush.c file (as it exists in commit 37460f5da) and adapt it to suit
> > U-Boot needs.
> > 
> > [...]
> 
> Applied to u-boot/next, thanks!

Thank you for the merge!
If there is any problem, do not hesitate to mail me and I will take care of 
it!




Re: [PATCH v13 15/24] cli: add modern hush as parser for run_command*()

2023-12-29 Thread Francis Laniel
Hi!

Le mardi 26 décembre 2023, 10:46:48 CET Simon Glass a écrit :
> Hi,
> 
> On Fri, Dec 22, 2023 at 9:23 PM Tom Rini  wrote:
> > On Fri, Dec 22, 2023 at 10:10:42PM +0100, Francis Laniel wrote:
> > > Hi!
> > > 
> > > Le vendredi 22 décembre 2023, 22:02:35 CET Francis Laniel a écrit :
> > > > Enables using, in code, modern hush as parser for run_command function
> > > > family. It also enables the command run to be used by CLI user of
> > > > modern
> > > > hush.
> > > > 
> > > > Reviewed-by: Simon Glass 
> > > > Signed-off-by: Francis Laniel 
> > 
> > [snip]
> > 
> > > > diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
> > > > index a9b555c779..104f49deef 100644
> > > > --- a/test/boot/bootflow.c
> > > > +++ b/test/boot/bootflow.c
> > > > @@ -710,7 +710,21 @@ static int bootflow_scan_menu_boot(struct
> > > > unit_test_state *uts) ut_assert_skip_to_line("(2 bootflows, 2
> > > > valid)");
> > > > 
> > > > ut_assert_nextline("Selected: Armbian");
> > > > 
> > > > -   ut_assert_skip_to_line("Boot failed (err=-14)");
> > > > +
> > > > +   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
> > > > +   /*
> > > > +* With old hush, despite booti failing to boot, i.e.
> > > > returning
> > > > +* CMD_RET_FAILURE, run_command() returns 0 which leads
> > > 
> > > bootflow_boot(),
> > > 
> > > > as + * we are using bootmeth_script here, to return
> > > > -EFAULT. +*/
> > > > +   ut_assert_skip_to_line("Boot failed (err=-14)");
> > > > +   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
> > > > +   /*
> > > > +* While with modern one, run_command() propagates
> > > 
> > > CMD_RET_FAILURE
> > > 
> > > > returned +   * by booti, so we get 1 here.
> > > > +*/
> > > > +   ut_assert_skip_to_line("Boot failed (err=1)");
> > > > +   }
> > > 
> > > I would like to give a bit of context here.
> > > With the following patch:
> > > diff --git a/test/py/tests/test_ut.py b/test/py/tests/test_ut.py
> > > index c169c835e3..cc5adda0a3 100644
> > > --- a/test/py/tests/test_ut.py
> > > +++ b/test/py/tests/test_ut.py
> > > @@ -173,7 +173,7 @@ else
> > > 
> > > fi
> > >  
> > >  fi
> > >  booti ${kernel_addr_r} ${ramdisk_addr_r} ${fdt_addr_r}
> > > 
> > > -
> > > +echo $?
> > > 
> > >  # Recompile with:
> > >  # mkimage -C none -A arm -T script -d /boot/boot.cmd /boot/boot.scr
> > >  ''' % (mmc_dev)
> > > 
> > > We can easily see that booti is failing while running the test:
> > > $ ./test/py/test.py -o log_cli=true -s --build -v -k
> > > 'test_ut[ut_bootstd_bootflow_scan_menu_boot'
> > > ...
> > > Aborting!
> > > Failed to load '/boot/dtb/rockchip/overlay/-fixup.scr'
> > > 1
> > > 
> > > The problem with old hush, is that the 1 returned here, which
> > > corresponds to CMD_RET_FAILURE, is not propagated as the return value
> > > of run_command(). So, this lead the -EFAULT branch here to be taken:
> > > int bootflow_boot(struct bootflow *bflow)
> > > {
> > > 
> > >   /* ... */
> > >   
> > >   ret = bootmeth_boot(bflow->method, bflow);
> > >   if (ret)
> > >   
> > >   return log_msg_ret("boot", ret);
> > >   
> > >   /*
> > >   
> > >* internal error, should not get here since we should have booted
> > >* something or returned an error
> > >*/
> > >   
> > >   return log_msg_ret("end", -EFAULT);
> > > 
> > > }
> > > 
> > > With modern hush, CMD_RET_FAILURE is propagated as the return value of
> > > run_command().
> > > As a consequence, we return with log_mst_ret("boot", 1), which leaded to
> > > this test to fail.
> > > The above modification consists in adapting the expected output to the
> > > current shell flavor.
> > > I think this is the good thing to do, as I find modern hush behavior
> > > better
> > > than the old one, i.e. it propagates CMD_RET_FAILURE as return of
> > > run_command().
> > 
> > Oh very nice, thanks for digging in to this and explaining!
> 
> Yes, thank you from me too!

You are welcome!
 
> 
> - Simon

Best regards and have a nice end of the year!




Re: [PATCH v13 15/24] cli: add modern hush as parser for run_command*()

2023-12-22 Thread Francis Laniel
Hi!


Le vendredi 22 décembre 2023, 22:02:35 CET Francis Laniel a écrit :
> Enables using, in code, modern hush as parser for run_command function
> family. It also enables the command run to be used by CLI user of modern
> hush.
> 
> Reviewed-by: Simon Glass 
> Signed-off-by: Francis Laniel 
> ---
>  common/cli.c   | 67 --
>  common/cli_hush_upstream.c |  2 +-
>  test/boot/bootflow.c   | 16 -
>  3 files changed, 66 insertions(+), 19 deletions(-)
> 
> diff --git a/common/cli.c b/common/cli.c
> index b3eb512b62..a34938294e 100644
> --- a/common/cli.c
> +++ b/common/cli.c
> @@ -25,6 +25,14 @@
>  #include 
> 
>  #ifdef CONFIG_CMDLINE
> +
> +static inline bool use_hush_old(void)
> +{
> + return IS_ENABLED(CONFIG_HUSH_SELECTABLE) ?
> + gd->flags & GD_FLG_HUSH_OLD_PARSER :
> + IS_ENABLED(CONFIG_HUSH_OLD_PARSER);
> +}
> +
>  /*
>   * Run a command using the selected parser.
>   *
> @@ -43,15 +51,30 @@ int run_command(const char *cmd, int flag)
>   return 1;
> 
>   return 0;
> -#elif CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
> - int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
> -
> - if (flag & CMD_FLAG_ENV)
> - hush_flags |= FLAG_CONT_ON_NEWLINE;
> - return parse_string_outer(cmd, hush_flags);
> -#else /* HUSH_MODERN_PARSER */
> - /* Not yet implemented. */
> - return 1;
> +#else
> + if (use_hush_old()) {
> + int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
> +
> + if (flag & CMD_FLAG_ENV)
> + hush_flags |= FLAG_CONT_ON_NEWLINE;
> + return parse_string_outer(cmd, hush_flags);
> + }
> + /*
> +  * Possible values for flags are the following:
> +  * FLAG_EXIT_FROM_LOOP: This flags ensures we exit from loop in
> +  * parse_and_run_stream() after first iteration while normal
> +  * behavior, * i.e. when called from cli_loop(), is to loop
> +  * infinitely.
> +  * FLAG_PARSE_SEMICOLON: modern Hush parses ';' and does not stop
> +  * first time it sees one. So, I think we do not need this flag.
> +  * FLAG_REPARSING: For the moment, I do not understand the goal
> +  * of this flag.
> +  * FLAG_CONT_ON_NEWLINE: This flag seems to be used to continue
> +  * parsing even when reading '\n' when coming from
> +  * run_command(). In this case, modern Hush reads until it finds
> +  * '\0'. So, I think we do not need this flag.
> +  */
> + return parse_string_outer_modern(cmd, FLAG_EXIT_FROM_LOOP);
>  #endif
>  }
> 
> @@ -67,12 +90,23 @@ int run_command_repeatable(const char *cmd, int flag)
>  #ifndef CONFIG_HUSH_PARSER
>   return cli_simple_run_command(cmd, flag);
>  #else
> + int ret;
> +
> + if (use_hush_old()) {
> + ret = parse_string_outer(cmd,
> +  FLAG_PARSE_SEMICOLON
> +  | FLAG_EXIT_FROM_LOOP);
> + } else {
> + ret = parse_string_outer_modern(cmd,
> +   FLAG_PARSE_SEMICOLON
> +   | FLAG_EXIT_FROM_LOOP);
> + }
> +
>   /*
>* parse_string_outer() returns 1 for failure, so clean up
>* its result.
>*/
> - if (parse_string_outer(cmd,
> -FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP))
> + if (ret)
>   return -1;
> 
>   return 0;
> @@ -111,12 +145,11 @@ int run_command_list(const char *cmd, int len, int
> flag) buff[len] = '\0';
>   }
>  #ifdef CONFIG_HUSH_PARSER
> -#if CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
> - rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
> -#else /* HUSH_MODERN_PARSER */
> - /* Not yet implemented. */
> - rcode = 1;
> -#endif
> + if (use_hush_old()) {
> + rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
> + } else {
> + rcode = parse_string_outer_modern(buff, FLAG_PARSE_SEMICOLON);
> + }
>  #else
>   /*
>* This function will overwrite any \n it sees with a \0, which
> diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
> index 7f6b058e6c..11870c51e8 100644
> --- a/common/cli_hush_upstream.c
> +++ b/common/cli_hush_upstream.c
> @@ -8022,7 +8022,7 @@ static int parse_and_run_string(const char *s)
>  }
> 
>  #ifdef __U_BOOT__
> -int parse_string_outer(const char *cmd, int flags)
> +int parse_string_outer_modern(const char *cmd, int flags)
>  {
>   int ret;
>   int old_flags;

[PATCH v13 24/24] configs: Use old hush for several boards

2023-12-22 Thread Francis Laniel
The keymile board family is not compatible with modern hush parser.
Indeed, This boards used set_local_var() to store some variables as local shell.
They then used get_local_var() to retrieve the variables values.
Sadly, this two functions do not exist with CONFIG_HUSH_MODERN_PARSER.
A patch was proposed to use environment variables rather than local variables
but it does not tackle the problem, so complementary work is needed to make
this boards use CONFIG_HUSH_MODERN_PARSER.

Also, with CONFIG_HUSH_MODERN_PARSER, kirkwoord sheevaplug and phytec bk4r1 hit
their board limits, so better to stick with old hush.

Cc: Holger Brunck 
Link: https://marc.info/?l=u-boot=165541917618725=2
Signed-off-by: Francis Laniel 
---
 configs/kmcent2_defconfig  | 1 +
 configs/kmcoge5ne_defconfig| 1 +
 configs/kmeter1_defconfig  | 1 +
 configs/kmopti2_defconfig  | 1 +
 configs/kmsupx5_defconfig  | 1 +
 configs/kmtepr2_defconfig  | 1 +
 configs/pg_wcom_expu1_defconfig| 1 +
 configs/pg_wcom_expu1_update_defconfig | 1 +
 configs/pg_wcom_seli8_defconfig| 1 +
 configs/pg_wcom_seli8_update_defconfig | 1 +
 configs/socfpga_secu1_defconfig| 1 +
 configs/tuge1_defconfig| 1 +
 configs/tuxx1_defconfig| 1 +
 13 files changed, 13 insertions(+)

diff --git a/configs/kmcent2_defconfig b/configs/kmcent2_defconfig
index 2cf9565fc9..ac272b3840 100644
--- a/configs/kmcent2_defconfig
+++ b/configs/kmcent2_defconfig
@@ -111,3 +111,4 @@ CONFIG_BCH=y
 CONFIG_PANIC_HANG=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmcoge5ne_defconfig b/configs/kmcoge5ne_defconfig
index 257ceeca90..ace5080690 100644
--- a/configs/kmcoge5ne_defconfig
+++ b/configs/kmcoge5ne_defconfig
@@ -202,3 +202,4 @@ CONFIG_QE=y
 CONFIG_SYS_NS16550=y
 CONFIG_BCH=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmeter1_defconfig b/configs/kmeter1_defconfig
index 46e0370e35..56b83c085d 100644
--- a/configs/kmeter1_defconfig
+++ b/configs/kmeter1_defconfig
@@ -173,3 +173,4 @@ CONFIG_DM_ETH_PHY=y
 CONFIG_QE_UEC=y
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmopti2_defconfig b/configs/kmopti2_defconfig
index c6c021adde..08c7602f5d 100644
--- a/configs/kmopti2_defconfig
+++ b/configs/kmopti2_defconfig
@@ -183,3 +183,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmsupx5_defconfig b/configs/kmsupx5_defconfig
index 25642e7018..72db26f320 100644
--- a/configs/kmsupx5_defconfig
+++ b/configs/kmsupx5_defconfig
@@ -166,3 +166,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmtepr2_defconfig b/configs/kmtepr2_defconfig
index ea37a29060..ed908d3c77 100644
--- a/configs/kmtepr2_defconfig
+++ b/configs/kmtepr2_defconfig
@@ -182,3 +182,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_expu1_defconfig b/configs/pg_wcom_expu1_defconfig
index 455b439151..0337447f79 100644
--- a/configs/pg_wcom_expu1_defconfig
+++ b/configs/pg_wcom_expu1_defconfig
@@ -105,3 +105,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_expu1_update_defconfig 
b/configs/pg_wcom_expu1_update_defconfig
index 269116cd0d..e5daa306ab 100644
--- a/configs/pg_wcom_expu1_update_defconfig
+++ b/configs/pg_wcom_expu1_update_defconfig
@@ -103,3 +103,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_seli8_defconfig b/configs/pg_wcom_seli8_defconfig
index 678bc10070..e86a17abdf 100644
--- a/configs/pg_wcom_seli8_defconfig
+++ b/configs/pg_wcom_seli8_defconfig
@@ -105,3 +105,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_seli8_update_defconfig 
b/configs/pg_wcom_seli8_update_defconfig
index 7c7b001903..886334a043 100644
--- a/configs/pg_wcom_seli8_update_defconfig
+++ b/configs/pg_wcom_seli8_update_defconfig
@@ -103,3 +103,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/socfpga_secu1_defconfig b/configs/socfpga_secu1_defconfig
index 6a4106a559..0f8a65c42f 100644
--- a/configs/socfpga_secu1_defconfig
+++ b/configs/socfpga_secu1_defconfig
@@ -113,3 +113,4 @@ CONFIG_DESIGNWARE_WATCHDOG=y
 CONFIG_WDT=y
 CONFIG_SYS_TIMER_COUNTS_DOWN=y
 # CONFIG_GZIP is not set
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/tuge1_defconfig b/configs/tuge1_defconfig
index 9ff5d1599f..5c4d33235e 100644
--- a/configs/tuge1_defconfig
+++ b/configs/tuge1_defconfig
@@ -166,3 +166,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y

[PATCH v13 23/24] cmd: Set modern hush as default shell

2023-12-22 Thread Francis Laniel
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig | 6 +-
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index e25578cde3..26ad03 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -27,21 +27,17 @@ depends on HUSH_PARSER
 
 config HUSH_OLD_PARSER
bool "Use hush old parser"
-   default y
help
  This option enables the old flavor of hush based on hush Busybox from
  2005.
 
- It is actually the default U-Boot shell when decided to use hush as 
shell.
-
 config HUSH_MODERN_PARSER
bool "Use hush modern parser"
+   default y
help
  This option enables the new flavor of hush based on hush upstream
  Busybox.
 
- This parser is experimental and not well tested.
-
 config HUSH_SELECTABLE
bool
default y if HUSH_OLD_PARSER && HUSH_MODERN_PARSER
-- 
2.34.1



[PATCH v13 22/24] cli: modern_hush: Add upstream commits up to 2nd October 2023.

2023-12-22 Thread Francis Laniel
This commit adds the following hush busybox upstream commits:
791b222dd55d ("sleep: fix "sleep -- ARGS"")
5353df91cba7 ("Update applet size estimates")
e41e481fd571 ("hush: fix a compile failure")
07a95cfcabb0 ("ash: disable check for "good" function name, bash does not check 
this")
e5692e2342c6 ("hush: quote values in "readonly" output")
96769486e20f ("shell: move varcmp() to shell_common.h and use it in hush")
bab8828b0dad ("hush: fix expansion of space in "a=${a:+$a }c" construct")
b5be8da350b5 ("hush: make "false" built-in")
6824298ab4d3 ("hush: fix ELIF cmd1;cmd2 THEN ... not executing cmd2, closes 
15571")
3a7f00eadcf4 ("hush: add comment about abort on syntax error %{^}")
acae889dd972 ("ash,hush: tab completion of functions and aliases")
90b607d79a13 ("hush: quote variable values printed by "set" (match ash 
behavior)")
6748e6494c22 ("hush (NOMMU): fix LINENO in execed children")
fd5fb2d2b596 ("hush: speed up "big heredoc" code")
1409432d072e ("hush: add TODO comment")
93ae7464e6e4 ("hush: restore SIGHUP handling, this time explain why we do what 
we do")
1fdb33bd07e5 ("hush: restore tty pgrp on SIGHUP")
6101b6d3eaa0 ("hush: remove special handling of SIGHUP")
93e0898c663a ("shell: fix SIGWINCH and SIGCHLD (in hush) interrupting line 
input, closes 15256")
969e00816835 ("hush: code shrink")
27be0e8cfeb6 ("shell: fix compile failures in some configs")
7d1c7d833785 ("ash,hush: use HOME for tab completion and prompts")
21afddefd258 ("hush: fix "error: invalid preprocessing directive ##"")
e53c7dbafc78 ("hush: fix set -n to act immediately, not just after run_list()")
574b9c446da1 ("hush: fix var_LINENO3.tests failure")
49bcf9f40cff ("hush: speed up ${x//\*/|} too")
53b2fdcdba4c ("*: add NOINLINEs where code noticeably shrinks")
7c3e96d4b3d4 ("shell: use more compact SHELL_ASH / HUSH config defines. no code 
changes")
62f1eed1e191 ("hush: in a comment, document what -i might be doing")
aaf3d5ba74c5 ("shell: tweak --help")
db5546ca1018 ("libbb: code shrink: introduce and use [_]exit_SUCCESS()")
931c55f9e2b4 ("libbb: invert the meaning of SETUP_ENV_NO_CHDIR -> 
SETUP_ENV_CHDIR")
12566e7f9b5e ("ash,hush: fix handling of SIGINT while waiting for interactive 
input")
987be932ed3c ("*: slap on a few ALIGN_PTR where appropriate")

Signed-off-by: Francis Laniel 
---
 common/cli_hush_modern.c   |  20 +-
 common/cli_hush_upstream.c | 407 ++---
 2 files changed, 304 insertions(+), 123 deletions(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index fb578c3853..9a1bc977d5 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -26,7 +26,7 @@
 /*
  * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
  */
-#define BB_VER "1.34.0.git37460f5daff9"
+#define BB_VER "1.35.0.git7d1c7d833785"
 
 /*
  * Define hush features by the names used upstream.
@@ -236,6 +236,24 @@ static size_t list_size(char **list)
return size;
 }
 
+static int varcmp(const char *p, const char *q)
+{
+   int c, d;
+
+   while ((c = *p) == (d = *q)) {
+   if (c == '\0' || c == '=')
+   goto out;
+   p++;
+   q++;
+   }
+   if (c == '=')
+   c = '\0';
+   if (d == '=')
+   d = '\0';
+out:
+   return c - d;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 748edc9d2f..ca40bbb2cd 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -91,7 +91,7 @@
  * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
  */
 //config:config HUSH
-//config:  bool "hush (68 kb)"
+//config:  bool "hush (70 kb)"
 //config:  default y
 //config:  select SHELL_HUSH
 //config:  help
@@ -339,7 +339,7 @@
  * therefore we don't show them either.
  */
 //usage:#define hush_trivial_usage
-//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
+//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
 //usage:#define hush_full_usage "\n\n"
 //usage:   "Unix shell interpreter"
 
@@ -374,7 +374,7 @@
 # define F_DUPFD_CLOEXEC F_DUPFD
 #endif
 
-#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !(ENABLE_ASH || ENABLE_SH_IS_ASH || 
ENABLE_BASH_IS_ASH)
+#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !ENABLE_SHELL_ASH
 # include "embe

[PATCH v13 21/24] test: hush: Fix loop tests for modern hush

2023-12-22 Thread Francis Laniel
Modifies return code got from while loop as modern hush always returns 0 from
while loop.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/loop.c | 34 ++
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/test/hush/loop.c b/test/hush/loop.c
index ca777e38fe..80a9071a80 100644
--- a/test/hush/loop.c
+++ b/test/hush/loop.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_for(struct unit_test_state *uts)
 {
@@ -21,7 +24,12 @@ static int hush_test_for(struct unit_test_state *uts)
ut_assert_nextline("quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_i=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_i and it cannot 
be unset!");
+   }
 
return 0;
 }
@@ -31,12 +39,30 @@ static int hush_test_while(struct unit_test_state *uts)
 {
console_record_reset_enable();
 
-   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
-   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* Hush 2021 always returns 0 from while loop...
+* You can see code snippet near this line to have a better
+* understanding:
+* debug_printf_exec(": while expr is false: breaking 
(exitcode:EXIT_SUCCESS)\n");
+*/
+   ut_assertok(run_command("while test -z \"$loop_foo\"; do echo 
bar; loop_foo=quux; done", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   /*
+* Exit status is that of test, so 1 since test is false to quit
+* the loop.
+*/
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do 
echo bar; loop_foo=quux; done", 0));
+   }
ut_assert_nextline("bar");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
-- 
2.34.1



[PATCH v13 20/24] cli: hush_modern: Enable loops

2023-12-22 Thread Francis Laniel
Enables the use of for, while and until loops for command line as
well as with run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_modern.c   |  1 +
 common/cli_hush_upstream.c | 15 ++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index 6360747f21..fb578c3853 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -34,6 +34,7 @@
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
 #define ENABLE_HUSH_IF 1
+#define ENABLE_HUSH_LOOPS  1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 06af538a48..748edc9d2f 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -10348,7 +10348,7 @@ static int run_list(struct pipe *pi)
 #ifndef __U_BOOT__
for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) 
pi->next) {
 #else /* __U_BOOT__ */
-   for (; pi; pi = pi->next) {
+   for (; pi; pi = rword == RES_DONE ? loop_top : pi->next) {
 #endif /* __U_BOOT__ */
int r;
int sv_errexit_depth;
@@ -10450,7 +10450,20 @@ static int run_list(struct pipe *pi)
}
/* Insert next value from for_lcur */
/* note: *for_lcur already has quotes removed, $var 
expanded, etc */
+#ifndef __U_BOOT__
set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], 
*for_lcur++), /*flag:*/ 0);
+#else /* __U_BOOT__ */
+   /* We cannot use xasprintf, so we emulate it. */
+   char *full_var;
+   char *var = pi->cmds[0].argv[0];
+   char *val = *for_lcur++;
+
+   /* + 1 to take into account =. */
+   full_var = xmalloc(strlen(var) + strlen(val) + 1);
+   sprintf(full_var, "%s=%s", var, val);
+
+   set_local_var_modern(full_var, /*flag:*/ 0);
+#endif /* __U_BOOT__ */
continue;
}
if (rword == RES_IN) {
-- 
2.34.1



[PATCH v13 19/24] cli: hush_modern: Enable if keyword

2023-12-22 Thread Francis Laniel
Adds support for "if then else" construct both for command line interface and
through run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_modern.c   | 11 +++
 common/cli_hush_upstream.c | 12 
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index 15bb1f0d92..6360747f21 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -33,6 +33,7 @@
  */
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
+#define ENABLE_HUSH_IF 1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
@@ -124,6 +125,11 @@ static void bb_error_msg(const char *s, ...)
va_end(p);
 }
 
+static void bb_simple_error_msg(const char *s)
+{
+   bb_error_msg("%s", s);
+}
+
 static void *xmalloc(size_t size)
 {
void *p = NULL;
@@ -147,6 +153,11 @@ static void *xrealloc(void *ptr, size_t size)
return p;
 }
 
+static void *xmemdup(const void *s, int n)
+{
+   return memcpy(xmalloc(n), s, n);
+}
+
 #define xstrdupstrdup
 #define xstrndup   strndup
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index a56ea6c5c8..06af538a48 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1487,7 +1487,6 @@ static void msg_and_die_if_script(unsigned lineno, const 
char *fmt, ...)
die_if_script();
 }
 
-#ifndef __U_BOOT__
 static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
 {
if (msg)
@@ -1496,7 +1495,6 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, 
const char *msg)
bb_simple_error_msg("syntax error");
die_if_script();
 }
-#endif /* !__U_BOOT__ */
 
 static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
 {
@@ -3961,7 +3959,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[PIPE_OR ] = "OR" ,
[PIPE_BG ] = "BG" ,
};
-#ifndef __U_BOOT__
static const char *RES[] = {
[RES_NONE ] = "NONE" ,
 # if ENABLE_HUSH_IF
@@ -3991,7 +3988,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[RES_ ] = "" ,
[RES_SNTX ] = "SNTX" ,
};
-#endif /* !__U_BOOT__ */
static const char *const CMDTYPE[] = {
"{}",
"()",
@@ -4009,10 +4005,8 @@ static void debug_print_tree(struct pipe *pi, int lvl)
lvl*2, "",
pin,
pi->num_cmds,
-#ifdef __U_BOOT__
(IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
RES[pi->res_word],
-#endif /* !__U_BOOT__ */
pi->followup, PIPE[pi->followup]
);
prn = 0;
@@ -9832,6 +9826,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
rcode = 1; /* exitcode if redir failed */
 #ifndef __U_BOOT__
if (setup_redirects(command, ) == 0) {
+#endif /* !__U_BOOT__ */
debug_printf_exec(": run_list\n");
 //FIXME: we need to pass squirrel down into run_list()
 //for SH_STANDALONE case, or else this construct:
@@ -9840,10 +9835,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
 //and in SH_STANDALONE mode, "find" is not execed,
 //therefore CLOEXEC on saved fd does not help.
rcode = run_list(command->group) & 0xff;
+#ifndef __U_BOOT__
}
restore_redirects(squirrel);
-   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
 #endif /* !__U_BOOT__ */
+   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
debug_leave();
debug_printf_exec("run_pipe: return %d\n", rcode);
return rcode;
@@ -10362,12 +10358,12 @@ static int run_list(struct pipe *pi)
break;
if (G_flag_return_in_progress == 1)
break;
+#endif /* !__U_BOOT__ */
 
IF_HAS_KEYWORDS(rword = pi->res_word;)
debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
rword, cond_code, last_rword);
 
-#endif /* !__U_BOOT__ */
sv_errexit_depth = G.errexit_depth;
if (
 #if ENABLE_HUSH_IF
-- 
2.34.1



[PATCH v13 18/24] cli: hush_modern: Enable using < and > as string compare operators

2023-12-22 Thread Francis Laniel
In Busybox hush, '<' and '>' are used as redirection operators.
For example, cat foo > bar will write content of file foo inside file bar.
In U-Boot, we do not have file system, so we can hardly redirect command output
inside a file.

But, in actual U-Boot hush, these operators ('<' and '>') are used as string
compare operators.
For example, test aaa < bbb returns 0 as aaa is before bbb in the dictionary.
Busybox hush also permits this, but operators need to be escaped ('\<' and
'\>').
Indeed, if escaping is needed it permits the developer to think about its code,
as in a lot of case, we want to compare integers (using '-lt' or '-gt') rather
than strings.

As testing in U-Boot is handled by the test command, we will stick with the
original behaviour and not adapt to Busybox one.

Nonetheless, if one day we decide to implement test with '[[ ]]', we will then
stick to upstream Busybox behavior.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 23 ++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 11870c51e8..a56ea6c5c8 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -6159,7 +6159,28 @@ static struct pipe *parse_stream(char **pstring,
if (parse_redirect(, redir_fd, redir_style, input))
goto parse_error_exitcode1;
continue; /* get next char */
-#endif /* !__U_BOOT__ */
+#else /* __U_BOOT__ */
+   /*
+* In U-Boot, '<' and '>' can be used in test command 
to test if
+* a string is, alphabetically, before or after another.
+* In 2021 Busybox hush, we will keep the same behavior 
and so not treat
+* them as redirection operator.
+*
+* Indeed, in U-Boot, tests are handled by the test 
command and not by the
+* shell code.
+* So, better to give this character as input to test 
command.
+*
+* NOTE In my opinion, when you use '<' or '>' I am 
almost sure
+* you wanted to use "-gt" or "-lt" in place, so 
thinking to
+* escape these will make you should check your code 
(sh syntax
+* at this level is, for me, error prone).
+*/
+   case '>':
+   fallthrough;
+   case '<':
+   o_addQchr(, ch);
+   continue;
+#endif /* __U_BOOT__ */
case '#':
if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
/* skip "#comment" */
-- 
2.34.1



[PATCH v13 17/24] test: hush: Fix variable expansion tests for modern hush

2023-12-22 Thread Francis Laniel
Modifies the expected result for modern hush.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As modern hush is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/dollar.c | 79 --
 1 file changed, 69 insertions(+), 10 deletions(-)

diff --git a/test/hush/dollar.c b/test/hush/dollar.c
index defb2c3fd0..7ab7ea7084 100644
--- a/test/hush/dollar.c
+++ b/test/hush/dollar.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_simple_dollar(struct unit_test_state *uts)
 {
@@ -51,13 +54,29 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
/* Next line contains error message */
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* For some strange reasons, the console is not empty after
+* running above command.
+* So, we reset it to not have side effects for other tests.
+*/
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
/* Two next lines contain error message */
ut_assert_skipline();
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* See above comments. */
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
 
@@ -71,17 +90,35 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
 */
console_record_reset_enable();
 
-   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
-   /* Next line contains error message */
-   ut_assert_skipline();
-   ut_assert_console_end();
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* Old parser returns an error because it waits for closing
+* '\'', but this behavior is wrong as the '\'' is surrounded by
+* '"', so no need to wait for a closing one.
+*/
+   ut_assertok(run_command("dollar_foo=\"bar 'quux\"", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar 'quux");
+   ut_assert_console_end();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar quux'", 0));
ut_assertok(run_command("echo $dollar_foo", 0));
ut_assert_nextline("bar quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable dollar_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("dollar_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable dollar_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -109,7 +146,12 @@ static int hush_test_env_dollar(struct unit_test_state 
*uts)
/* Clean up setting the variable */
env_set("env_foo", NULL);
 
-   puts("Beware: this test set local variable env_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("env_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable env_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -144,7 +186,18 @@ static int hush_test_command_dollar(struct unit_test_state 
*uts)
ut_assertok(run_command("dollar_bar='echo bar\\n'", 0));
 
ut_assertok(run_command("$dollar_bar", 0));
-   ut_assert_nextline("barn");
+
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug 

[PATCH v13 16/24] test: hush: Fix instructions list tests for modern hush

2023-12-22 Thread Francis Laniel
Modifies the expected result for modern hush.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As modern hush is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/list.c | 69 +---
 1 file changed, 65 insertions(+), 4 deletions(-)

diff --git a/test/hush/list.c b/test/hush/list.c
index 052cf2783c..73ae36e88f 100644
--- a/test/hush/list.c
+++ b/test/hush/list.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 
 static int hush_test_semicolon(struct unit_test_state *uts)
 {
@@ -46,12 +47,43 @@ static int hush_test_or(struct unit_test_state *uts)
 }
 HUSH_TEST(hush_test_or, 0);
 
+DECLARE_GLOBAL_DATA_PTR;
+
 static int hush_test_and_or(struct unit_test_state *uts)
 {
/* A && B || C truth table. */
ut_asserteq(1, run_command("false && false || false", 0));
-   ut_asserteq(1, run_command("false && false || true", 0));
-   ut_asserteq(1, run_command("false && true || true", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && false || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && false) || true
+* So, (false && false) returns 1, the second false is not
+* executed, and true is executed because of ||.
+*/
+   ut_assertok(run_command("false && false || true", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && true || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && true) || true
+* So, (false && true) returns 1, the true is not executed, and
+* true is executed because of ||.
+*/
+   ut_assertok(run_command("false && true || true", 0));
+   }
+
ut_asserteq(1, run_command("false && true || false", 0));
ut_assertok(run_command("true && true || false", 0));
ut_asserteq(1, run_command("true && false || false", 0));
@@ -69,8 +101,37 @@ static int hush_test_or_and(struct unit_test_state *uts)
ut_asserteq(1, run_command("false || false && true", 0));
ut_assertok(run_command("false || true && true", 0));
ut_asserteq(1, run_command("false || true && false", 0));
-   ut_assertok(run_command("true || true && false", 0));
-   ut_assertok(run_command("true || false && false", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || true && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || true) && false
+* So, (true || true) returns 0, the second true is not
+* executed, and then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || true && false", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || false && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || false) && false
+* So, (true || false) returns 0, the false is not executed, and
+* then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || false && false", 0));
+   }
+
ut_assertok(run_command("true || false && true", 0));
ut_assertok(run_command("true || true && true", 0));
 
-- 
2.34.1



[PATCH v13 15/24] cli: add modern hush as parser for run_command*()

2023-12-22 Thread Francis Laniel
Enables using, in code, modern hush as parser for run_command function family.
It also enables the command run to be used by CLI user of modern hush.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c   | 67 --
 common/cli_hush_upstream.c |  2 +-
 test/boot/bootflow.c   | 16 -
 3 files changed, 66 insertions(+), 19 deletions(-)

diff --git a/common/cli.c b/common/cli.c
index b3eb512b62..a34938294e 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -25,6 +25,14 @@
 #include 
 
 #ifdef CONFIG_CMDLINE
+
+static inline bool use_hush_old(void)
+{
+   return IS_ENABLED(CONFIG_HUSH_SELECTABLE) ?
+   gd->flags & GD_FLG_HUSH_OLD_PARSER :
+   IS_ENABLED(CONFIG_HUSH_OLD_PARSER);
+}
+
 /*
  * Run a command using the selected parser.
  *
@@ -43,15 +51,30 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#elif CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
-
-   if (flag & CMD_FLAG_ENV)
-   hush_flags |= FLAG_CONT_ON_NEWLINE;
-   return parse_string_outer(cmd, hush_flags);
-#else /* HUSH_MODERN_PARSER */
-   /* Not yet implemented. */
-   return 1;
+#else
+   if (use_hush_old()) {
+   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
+
+   if (flag & CMD_FLAG_ENV)
+   hush_flags |= FLAG_CONT_ON_NEWLINE;
+   return parse_string_outer(cmd, hush_flags);
+   }
+   /*
+* Possible values for flags are the following:
+* FLAG_EXIT_FROM_LOOP: This flags ensures we exit from loop in
+* parse_and_run_stream() after first iteration while normal
+* behavior, * i.e. when called from cli_loop(), is to loop
+* infinitely.
+* FLAG_PARSE_SEMICOLON: modern Hush parses ';' and does not stop
+* first time it sees one. So, I think we do not need this flag.
+* FLAG_REPARSING: For the moment, I do not understand the goal
+* of this flag.
+* FLAG_CONT_ON_NEWLINE: This flag seems to be used to continue
+* parsing even when reading '\n' when coming from
+* run_command(). In this case, modern Hush reads until it finds
+* '\0'. So, I think we do not need this flag.
+*/
+   return parse_string_outer_modern(cmd, FLAG_EXIT_FROM_LOOP);
 #endif
 }
 
@@ -67,12 +90,23 @@ int run_command_repeatable(const char *cmd, int flag)
 #ifndef CONFIG_HUSH_PARSER
return cli_simple_run_command(cmd, flag);
 #else
+   int ret;
+
+   if (use_hush_old()) {
+   ret = parse_string_outer(cmd,
+FLAG_PARSE_SEMICOLON
+| FLAG_EXIT_FROM_LOOP);
+   } else {
+   ret = parse_string_outer_modern(cmd,
+ FLAG_PARSE_SEMICOLON
+ | FLAG_EXIT_FROM_LOOP);
+   }
+
/*
 * parse_string_outer() returns 1 for failure, so clean up
 * its result.
 */
-   if (parse_string_outer(cmd,
-  FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP))
+   if (ret)
return -1;
 
return 0;
@@ -111,12 +145,11 @@ int run_command_list(const char *cmd, int len, int flag)
buff[len] = '\0';
}
 #ifdef CONFIG_HUSH_PARSER
-#if CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
-#else /* HUSH_MODERN_PARSER */
-   /* Not yet implemented. */
-   rcode = 1;
-#endif
+   if (use_hush_old()) {
+   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
+   } else {
+   rcode = parse_string_outer_modern(buff, FLAG_PARSE_SEMICOLON);
+   }
 #else
/*
 * This function will overwrite any \n it sees with a \0, which
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 7f6b058e6c..11870c51e8 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -8022,7 +8022,7 @@ static int parse_and_run_string(const char *s)
 }
 
 #ifdef __U_BOOT__
-int parse_string_outer(const char *cmd, int flags)
+int parse_string_outer_modern(const char *cmd, int flags)
 {
int ret;
int old_flags;
diff --git a/test/boot/bootflow.c b/test/boot/bootflow.c
index a9b555c779..104f49deef 100644
--- a/test/boot/bootflow.c
+++ b/test/boot/bootflow.c
@@ -710,7 +710,21 @@ static int bootflow_scan_menu_boot(struct unit_test_state 
*uts)
ut_assert_skip_to_line("(2 bootflows, 2 valid)");
 
ut_assert_nextline("Selected: Armbian");
-   ut_assert_skip_to_line("Boot failed (err=-14)");
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   /*
+* With old hush, despite booti failing to boo

[PATCH v13 13/24] cli: hush_modern: Enable variables expansion for modern hush

2023-12-22 Thread Francis Laniel
Enables variables expansion for modern hush, both for local and environment
variables.
So the following commands:
foo=bar
echo $foo
setenv bar foo
echo $bar
leads to "bar" and "foo" being printed on console output.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_modern.c   | 17 +++
 common/cli_hush_upstream.c | 91 +++---
 2 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index 626fed089b..15bb1f0d92 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -207,6 +207,23 @@ static const char* endofname(const char *name)
return name;
 }
 
+/**
+ * list_size() - returns the number of elements in char ** before NULL.
+ *
+ * Argument must contain NULL to signalize its end.
+ *
+ * @list The list to count the number of element.
+ * @return The number of element in list.
+ */
+static size_t list_size(char **list)
+{
+   size_t size;
+
+   for (size = 0; list[size] != NULL; size++);
+
+   return size;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 4b6ab20f3b..ff4a57c6e3 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -3485,7 +3485,6 @@ static int o_get_last_ptr(o_string *o, int n)
return ((int)(uintptr_t)list[n-1]) + string_start;
 }
 
-#ifndef __U_BOOT__
 /*
  * Globbing routines.
  *
@@ -3740,8 +3739,10 @@ static int glob_needed(const char *s)
  */
 static int perform_glob(o_string *o, int n)
 {
+#ifndef __U_BOOT__
glob_t globdata;
int gr;
+#endif /* __U_BOOT__ */
char *pattern;
 
debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
@@ -3750,13 +3751,16 @@ static int perform_glob(o_string *o, int n)
pattern = o->data + o_get_last_ptr(o, n);
debug_printf_glob("glob pattern '%s'\n", pattern);
if (!glob_needed(pattern)) {
+#ifndef __U_BOOT__
  literal:
+#endif /* __U_BOOT__ */
/* unbackslash last string in o in place, fix length */
o->length = unbackslash(pattern) - o->data;
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
return o_save_ptr_helper(o, n);
}
 
+#ifndef __U_BOOT__
memset(, 0, sizeof(globdata));
/* Can't use GLOB_NOCHECK: it does not unescape the string.
 * If we glob "*.\*" and don't find anything, we need
@@ -3792,16 +3796,22 @@ static int perform_glob(o_string *o, int n)
if (DEBUG_GLOB)
debug_print_list("perform_glob returning", o, n);
return n;
+#else /* __U_BOOT__ */
+   /*
+* NOTE We only use perform glob to call unbackslash to remove backslash
+* from string once expanded.
+* So, it seems OK to return this if no previous return was done.
+*/
+   return o_save_ptr_helper(o, n);
+#endif /* __U_BOOT__ */
 }
 
-#endif /* !__U_BOOT__ */
 #endif /* !HUSH_BRACE_EXPANSION */
 
 /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
  * Otherwise, just finish current list[] and start new */
 static int o_save_ptr(o_string *o, int n)
 {
-#ifndef __U_BOOT__
if (o->o_expflags & EXP_FLAG_GLOB) {
/* If o->has_empty_slot, list[n] was already globbed
 * (if it was requested back then when it was filled)
@@ -3809,7 +3819,6 @@ static int o_save_ptr(o_string *o, int n)
if (!o->has_empty_slot)
return perform_glob(o, n); /* o_save_ptr_helper is 
inside */
}
-#endif /* !__U_BOOT__ */
return o_save_ptr_helper(o, n);
 }
 
@@ -5455,7 +5464,20 @@ static int parse_dollar(o_string *as_string,
nommu_addchr(as_string, ch);
if (ch == '}')
break;
+#ifndef __U_BOOT__
if (!isalnum(ch) && ch != '_') {
+#else /* __U_BOOT__ */
+   /*
+* In several places in U-Boot, we use variable like
+* foo# (e.g. serial#), particularly in env.
+* So, we need to authorize # to appear inside
+* variable name and then expand this variable.
+* NOTE Having # in variable name is not permitted in
+* upstream hush but expansion will be done (even though
+* the result will be empty).
+*/
+   if (!isalnum(ch) && ch != '_' && ch != '#') {
+#endif /* __U_BOOT__ */
unsigned end_ch;
 #ifndef __U_BOOT__
unsigned char last_ch;
@@ -7030,7 +7052,20 @@ static NOINLINE int 

[PATCH v13 14/24] cli: hush_modern: Add functions to be called from run_command()

2023-12-22 Thread Francis Laniel
run_command() is called internally by the command run and it can also be called
directly from U-Boot code, e.g. to do unit tests.
This commit adds this path to go to modern hush.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 66 --
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index ff4a57c6e3..7f6b058e6c 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1011,6 +1011,7 @@ struct globals {
 #ifdef __U_BOOT__
int flag_repeat;
int do_repeat;
+   int run_command_flags;
 #endif /* __U_BOOT__ */
char *ifs_whitespace; /* = G.ifs or malloced */
 #ifndef __U_BOOT__
@@ -3004,7 +3005,24 @@ static int i_getch(struct in_str *i)
if (i->p && *i->p != '\0') {
ch = (unsigned char)*i->p++;
goto out;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* There are two ways for command to be called:
+* 1. The first one is when they are typed by the user.
+* 2. The second one is through run_command() (NOTE command run
+* internally calls run_command()).
+*
+* In the second case, we do not get input from the user, so once we
+* get a '\0', it means we need to stop.
+* NOTE G.run_command_flags is only set on run_command call stack, so
+* we use this to know if we come from user input or run_command().
+*/
+   } else if (i->p && *i->p == '\0' && G.run_command_flags){
+   return EOF;
+   }
+#endif /* __U_BOOT__ */
 #endif
 #ifndef __U_BOOT__
/* peek_buf[] is an int array, not char. Can contain EOF. */
@@ -3163,7 +3181,6 @@ static void setup_file_in_str(struct in_str *i)
 #endif /* !__U_BOOT__ */
 }
 
-#ifndef __U_BOOT__
 static void setup_string_in_str(struct in_str *i, const char *s)
 {
memset(i, 0, sizeof(*i));
@@ -3171,7 +3188,6 @@ static void setup_string_in_str(struct in_str *i, const 
char *s)
i->p = s;
 }
 
-#endif /* !__U_BOOT__ */
 
 /*
  * o_string support
@@ -7910,7 +7926,11 @@ static int run_and_free_list(struct pipe *pi);
  * NUL: parse all, execute, return
  * ';': parse till ';' or newline, execute, repeat till EOF
  */
+#ifndef __U_BOOT__
 static void parse_and_run_stream(struct in_str *inp, int end_trigger)
+#else /* __U_BOOT__ */
+static int parse_and_run_stream(struct in_str *inp, int end_trigger)
+#endif /* __U_BOOT__ */
 {
/* Why we need empty flag?
 * An obscure corner case "false; ``; echo $?":
@@ -7919,7 +7939,11 @@ static void parse_and_run_stream(struct in_str *inp, int 
end_trigger)
 * this breaks "false; echo `echo $?`" case.
 */
bool empty = 1;
+#ifndef __U_BOOT__
while (1) {
+#else /* __U_BOOT__ */
+   do {
+#endif /* __U_BOOT__ */
struct pipe *pipe_list;
 
 #if ENABLE_HUSH_INTERACTIVE
@@ -7966,21 +7990,57 @@ static void parse_and_run_stream(struct in_str *inp, 
int end_trigger)
empty = 0;
if (G_flag_return_in_progress == 1)
break;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* This do/while is needed by run_command to avoid looping on a command
+* with syntax error.
+*/
+   } while (!(G.run_command_flags & FLAG_EXIT_FROM_LOOP));
+
+   return G.last_exitcode;
+#endif /* __U_BOOT__ */
 }
 
 #ifndef __U_BOOT__
 static void parse_and_run_string(const char *s)
+#else /* __U_BOOT__ */
+static int parse_and_run_string(const char *s)
+#endif /* __U_BOOT__ */
 {
struct in_str input;
//IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
 
setup_string_in_str(, s);
+#ifndef __U_BOOT__
parse_and_run_stream(, '\0');
+#else /* __U_BOOT__ */
+   return parse_and_run_stream(, '\0');
+#endif /* __U_BOOT__ */
//IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
 }
-#endif /* !__U_BOOT__ */
 
+#ifdef __U_BOOT__
+int parse_string_outer(const char *cmd, int flags)
+{
+   int ret;
+   int old_flags;
+
+   /*
+* Keep old values of run_command to be able to restore them once
+* command was executed.
+*/
+   old_flags = G.run_command_flags;
+   G.run_command_flags = flags;
+
+   ret = parse_and_run_string(cmd);
+
+   G.run_command_flags = old_flags;
+
+   return ret;
+}
+#endif /* __U_BOOT__ */
 #ifndef __U_BOOT__
 static void parse_and_run_file(HFILE *fp)
 #else /* __U_BOOT__ */
-- 
2.34.1



[PATCH v13 12/24] cli: Enables using modern hush parser as command line parser

2023-12-22 Thread Francis Laniel
If one defines HUSH_MODERN_PARSER, it is then possible to use modern parser 
with:
=> cli get
old
=> cli set modern
=> cli get
modern

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig   | 12 
 cmd/Makefile  |  2 +-
 cmd/cli.c | 28 ++---
 common/Makefile   |  1 +
 common/cli.c  | 38 +++
 common/cli_hush_modern.c  |  3 ++
 common/cli_hush_upstream.c| 46 +---
 doc/usage/cmd/cli.rst | 19 ++--
 include/asm-generic/global_data.h |  4 +++
 include/cli_hush.h| 51 +--
 10 files changed, 184 insertions(+), 20 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 15715ac6ad..e25578cde3 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -33,6 +33,18 @@ config HUSH_OLD_PARSER
  2005.
 
  It is actually the default U-Boot shell when decided to use hush as 
shell.
+
+config HUSH_MODERN_PARSER
+   bool "Use hush modern parser"
+   help
+ This option enables the new flavor of hush based on hush upstream
+ Busybox.
+
+ This parser is experimental and not well tested.
+
+config HUSH_SELECTABLE
+   bool
+   default y if HUSH_OLD_PARSER && HUSH_MODERN_PARSER
 endmenu
 
 config CMDLINE_EDITING
diff --git a/cmd/Makefile b/cmd/Makefile
index 477b86cf23..e2a2b16ab2 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -229,7 +229,7 @@ obj-$(CONFIG_CMD_AVB) += avb.o
 # Foundries.IO SCP03
 obj-$(CONFIG_CMD_SCP03) += scp03.o
 
-obj-$(CONFIG_HUSH_PARSER) += cli.o
+obj-$(CONFIG_HUSH_SELECTABLE) += cli.o
 
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_RISCV) += riscv/
diff --git a/cmd/cli.c b/cmd/cli.c
index 86c6471aa4..b93cc952ed 100644
--- a/cmd/cli.c
+++ b/cmd/cli.c
@@ -12,6 +12,8 @@ static const char *gd_flags_to_parser_name(void)
 {
if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
return "old";
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER)
+   return "modern";
return NULL;
 }
 
@@ -34,18 +36,31 @@ static int parser_string_to_gd_flags(const char *parser)
 {
if (!strcmp(parser, "old"))
return GD_FLG_HUSH_OLD_PARSER;
+   if (!strcmp(parser, "modern"))
+   return GD_FLG_HUSH_MODERN_PARSER;
+   return -1;
+}
+
+static int gd_flags_to_parser_config(int flag)
+{
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   return CONFIG_VAL(HUSH_OLD_PARSER);
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER)
+   return CONFIG_VAL(HUSH_MODERN_PARSER);
return -1;
 }
 
 static void reset_parser_gd_flags(void)
 {
gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+   gd->flags &= ~GD_FLG_HUSH_MODERN_PARSER;
 }
 
 static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
  char *const argv[])
 {
char *parser_name;
+   int parser_config;
int parser_flag;
 
if (argc < 2)
@@ -59,9 +74,14 @@ static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int 
argc,
return CMD_RET_USAGE;
}
 
-   if (parser_flag == GD_FLG_HUSH_OLD_PARSER &&
-   !CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) {
-   printf("Want to set current parser to old, but its code was not 
compiled!\n");
+   parser_config = gd_flags_to_parser_config(parser_flag);
+   switch (parser_config) {
+   case -1:
+   printf("Bad value for parser flags: %d\n", parser_flag);
+   return CMD_RET_FAILURE;
+   case 0:
+   printf("Want to set current parser to %s, but its code was not 
compiled!\n",
+   parser_name);
return CMD_RET_FAILURE;
}
 
@@ -102,7 +122,7 @@ static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
 #if CONFIG_IS_ENABLED(SYS_LONGHELP)
 static char cli_help_text[] =
"get - print current cli\n"
-   "set - set the current cli, possible value is: old"
+   "set - set the current cli, possible value are: old, modern"
;
 #endif
 
diff --git a/common/Makefile b/common/Makefile
index 3bb33b4e36..f010c2a1b9 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -10,6 +10,7 @@ obj-y += main.o
 obj-y += exports.o
 obj-y += cli_getch.o cli_simple.o cli_readline.o
 obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_MODERN_PARSER) += cli_hush_modern.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 obj-y += version.o
 
diff --git a/common/cli.c b/common/cli.c
index d419671e8c..b3eb512b62 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -43,12 +43,15 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#else
+#elif CONFIG_IS_ENABLED(HUSH_OL

[PATCH v13 11/24] cmd: Add new cli command

2023-12-22 Thread Francis Laniel
This command can be used to print the current parser with 'cli get'.
It can also be used to set the current parser with 'cli set'.
For the moment, only one value is valid for set: old.

Signed-off-by: Francis Laniel 
---
 cmd/Makefile  |   2 +
 cmd/cli.c | 114 ++
 common/cli.c  |   3 +-
 doc/usage/cmd/cli.rst |  59 ++
 doc/usage/index.rst   |   1 +
 5 files changed, 178 insertions(+), 1 deletion(-)
 create mode 100644 cmd/cli.c
 create mode 100644 doc/usage/cmd/cli.rst

diff --git a/cmd/Makefile b/cmd/Makefile
index 5ed0e4011d..477b86cf23 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -229,6 +229,8 @@ obj-$(CONFIG_CMD_AVB) += avb.o
 # Foundries.IO SCP03
 obj-$(CONFIG_CMD_SCP03) += scp03.o
 
+obj-$(CONFIG_HUSH_PARSER) += cli.o
+
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_RISCV) += riscv/
 obj-$(CONFIG_SANDBOX) += sandbox/
diff --git a/cmd/cli.c b/cmd/cli.c
new file mode 100644
index 00..86c6471aa4
--- /dev/null
+++ b/cmd/cli.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *gd_flags_to_parser_name(void)
+{
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   return "old";
+   return NULL;
+}
+
+static int do_cli_get(struct cmd_tbl *cmdtp, int flag, int argc,
+char *const argv[])
+{
+   const char *current = gd_flags_to_parser_name();
+
+   if (!current) {
+   printf("current cli value is not valid, this should not 
happen!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   printf("%s\n", current);
+
+   return CMD_RET_SUCCESS;
+}
+
+static int parser_string_to_gd_flags(const char *parser)
+{
+   if (!strcmp(parser, "old"))
+   return GD_FLG_HUSH_OLD_PARSER;
+   return -1;
+}
+
+static void reset_parser_gd_flags(void)
+{
+   gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+}
+
+static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   char *parser_name;
+   int parser_flag;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   parser_name = argv[1];
+
+   parser_flag = parser_string_to_gd_flags(parser_name);
+   if (parser_flag == -1) {
+   printf("Bad value for parser name: %s\n", parser_name);
+   return CMD_RET_USAGE;
+   }
+
+   if (parser_flag == GD_FLG_HUSH_OLD_PARSER &&
+   !CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) {
+   printf("Want to set current parser to old, but its code was not 
compiled!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   reset_parser_gd_flags();
+   gd->flags |= parser_flag;
+
+   cli_init();
+   cli_loop();
+
+   /* cli_loop() should never return. */
+   return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl parser_sub[] = {
+   U_BOOT_CMD_MKENT(get, 1, 1, do_cli_get, "", ""),
+   U_BOOT_CMD_MKENT(set, 2, 1, do_cli_set, "", ""),
+};
+
+static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   struct cmd_tbl *cp;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   /* drop initial "parser" arg */
+   argc--;
+   argv++;
+
+   cp = find_cmd_tbl(argv[0], parser_sub, ARRAY_SIZE(parser_sub));
+   if (cp)
+   return cp->cmd(cmdtp, flag, argc, argv);
+
+   return CMD_RET_USAGE;
+}
+
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+static char cli_help_text[] =
+   "get - print current cli\n"
+   "set - set the current cli, possible value is: old"
+   ;
+#endif
+
+U_BOOT_CMD(cli, 3, 1, do_cli,
+  "cli",
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+  cli_help_text
+#endif
+);
diff --git a/common/cli.c b/common/cli.c
index e5fe1060d0..d419671e8c 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,7 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
-   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   && CONFIG_IS_ENABLED(HUSH_OLD_PARSER))
gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
diff --git a/doc/usage/cmd/cli.rst b/doc/usage/cmd/cli.rst
new file mode 100644
index 00..89ece3203d
--- /dev/null
+++ b/doc/usage/cmd/cli.rst
@@ -0,0 +1,59 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+cli command
+===
+
+Synopis
+---
+
+::
+
+cli get
+cli set cli_flavor
+
+Description
+---
+
+The cli command permits getting and changing the current parser at runtime.
+
+cli get
+~~~
+
+It shows the current value of the parser used by the CLI.
+
+cli se

[PATCH v13 10/24] global_data.h: add GD_FLG_HUSH_OLD_PARSER flag

2023-12-22 Thread Francis Laniel
This flag is used to indicate we are using the hush parser.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c  | 2 ++
 include/asm-generic/global_data.h | 4 
 2 files changed, 6 insertions(+)

diff --git a/common/cli.c b/common/cli.c
index 3916a7b10a..e5fe1060d0 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,6 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
 
diff --git a/include/asm-generic/global_data.h 
b/include/asm-generic/global_data.h
index e8c6412e3f..0a9b6bd92a 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -697,6 +697,10 @@ enum gd_flags {
 * @GD_FLG_BLOBLIST_READY: bloblist is ready for use
 */
GD_FLG_BLOBLIST_READY = 0x80,
+   /**
+* @GD_FLG_HUSH_OLD_PARSER: Use hush old parser.
+*/
+   GD_FLG_HUSH_OLD_PARSER = 0x100,
 };
 
 #endif /* __ASSEMBLY__ */
-- 
2.34.1



[PATCH v13 08/24] cli: Port upstream Busybox hush to U-Boot

2023-12-22 Thread Francis Laniel
Adds new file cli_hush_upstream.c, it is a copy of Busybox hush file as it was 
of
time to commit 37460f5da.
This commit modifies Busybox hush to not compile some part specific to Busybox
and adds some code needed by U-Boot.
The modifications consists mainly on adding code #if(n)def guards.

For the moment, this refurbished flavor of hush only permits running command
without any keywords (i.e., if and for are not recognized) or variable expansion
(i.e., echo $foo prints foo and not value stored in variable foo).

A new file was also added to define some functions specific to U-Boot.

Signed-off-by: Francis Laniel 
Signed-off-by: Harald Seiler 
---
 common/cli_hush_modern.c   | 274 
 common/cli_hush_upstream.c | 502 -
 2 files changed, 774 insertions(+), 2 deletions(-)
 create mode 100644 common/cli_hush_modern.c

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
new file mode 100644
index 00..34278fdca2
--- /dev/null
+++ b/common/cli_hush_modern.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file defines the compilation unit for the new hush shell version.  The
+ * actual implementation from upstream BusyBox can be found in
+ * `cli_hush_upstream.c` which is included at the end of this file.
+ *
+ * This "wrapper" technique is used to keep the changes to the upstream version
+ * as minmal as possible.  Instead, all defines and redefines necessary are 
done
+ * here, outside the upstream sources.  This will hopefully make upgrades to
+ * newer revisions much easier.
+ *
+ * Copyright (c) 2021, Harald Seiler, DENX Software Engineering, h...@denx.de
+ */
+
+#include  /* readline */
+#include 
+#include  /* malloc, free, realloc*/
+#include /* isalpha, isdigit */
+#include 
+#include 
+#include 
+#include 
+#include /* find_cmd */
+#include 
+
+/*
+ * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
+ */
+#define BB_VER "1.34.0.git37460f5daff9"
+
+/*
+ * Define hush features by the names used upstream.
+ */
+#define ENABLE_HUSH_INTERACTIVE1
+#define ENABLE_FEATURE_EDITING 1
+/* No MMU in U-Boot */
+#define BB_MMU 0
+#define USE_FOR_NOMMU(...) __VA_ARGS__
+#define USE_FOR_MMU(...)
+
+/*
+ * Size-saving "small" ints (arch-dependent)
+ */
+#if CONFIG_IS_ENABLED(X86) || CONFIG_IS_ENABLED(X86_64) || 
CONFIG_IS_ENABLED(MIPS)
+/* add other arches which benefit from this... */
+typedef signed char smallint;
+typedef unsigned char smalluint;
+#else
+/* for arches where byte accesses generate larger code: */
+typedef int smallint;
+typedef unsigned smalluint;
+#endif
+
+/*
+ * Alignment defines used by BusyBox.
+ */
+#define ALIGN1 __attribute__((aligned(1)))
+#define ALIGN2 __attribute__((aligned(2)))
+#define ALIGN4 __attribute__((aligned(4)))
+#define ALIGN8 __attribute__((aligned(8)))
+#define ALIGN_PTR  __attribute__((aligned(sizeof(void*
+
+/*
+ * Miscellaneous compiler/platform defines.
+ */
+#define FAST_FUNC /* not used in U-Boot */
+#define UNUSED_PARAM   __always_unused
+#define ALWAYS_INLINE  __always_inline
+#define NOINLINE   noinline
+
+/*
+ * Defines to provide equivalents to what libc/BusyBox defines.
+ */
+#define EOF(-1)
+#define EXIT_SUCCESS   0
+#define EXIT_FAILURE   1
+
+/*
+ * Stubs to provide libc/BusyBox functions based on U-Boot equivalents where it
+ * makes sense.
+ */
+#define utoa   simple_itoa
+
+static void __noreturn xfunc_die(void)
+{
+   panic("HUSH died!");
+}
+
+#define bb_error_msg_and_die(format, ...) do { \
+panic("HUSH: " format, __VA_ARGS__); \
+} while (0);
+
+#define bb_simple_error_msg_and_die(msg) do { \
+panic_str("HUSH: " msg); \
+} while (0);
+
+/* fdprintf() is used for debug output. */
+static int __maybe_unused fdprintf(int fd, const char *format, ...)
+{
+   va_list args;
+   uint i;
+
+   assert(fd == 2);
+
+   va_start(args, format);
+   i = vprintf(format, args);
+   va_end(args);
+
+   return i;
+}
+
+static void bb_verror_msg(const char *s, va_list p, const char* strerr)
+{
+   /* TODO: what to do with strerr arg? */
+   vprintf(s, p);
+}
+
+static void bb_error_msg(const char *s, ...)
+{
+   va_list p;
+
+   va_start(p, s);
+   bb_verror_msg(s, p, NULL);
+   va_end(p);
+}
+
+static void *xmalloc(size_t size)
+{
+   void *p = NULL;
+   if (!(p = malloc(size)))
+   panic("out of memory");
+   return p;
+}
+
+static void *xzalloc(size_t size)
+{
+   void *p = xmalloc(size);
+   memset(p, 0, size);
+   return p;
+}
+
+static void *xrealloc(void *ptr, size_t size)
+{
+   void *p = NULL;
+   if (!(p = realloc(ptr, size)))
+   panic("out

[PATCH v13 09/24] cli: Add menu for hush parser

2023-12-22 Thread Francis Laniel
For the moment, the menu contains only entry: HUSH_OLD_PARSER which is the
default.
The goal is to prepare the field to add a new hush parser which guarantees
actual behavior is still correct.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig | 13 +
 common/Makefile |  2 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 5a7678f0ac..15715ac6ad 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -22,6 +22,19 @@ config HUSH_PARSER
  If disabled, you get the old, much simpler behaviour with a somewhat
  smaller memory footprint.
 
+menu "Hush flavor to use"
+depends on HUSH_PARSER
+
+config HUSH_OLD_PARSER
+   bool "Use hush old parser"
+   default y
+   help
+ This option enables the old flavor of hush based on hush Busybox from
+ 2005.
+
+ It is actually the default U-Boot shell when decided to use hush as 
shell.
+endmenu
+
 config CMDLINE_EDITING
bool "Enable command line editing"
default y
diff --git a/common/Makefile b/common/Makefile
index 1495436d5d..3bb33b4e36 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -9,7 +9,7 @@ obj-y += init/
 obj-y += main.o
 obj-y += exports.o
 obj-y += cli_getch.o cli_simple.o cli_readline.o
-obj-$(CONFIG_HUSH_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 obj-y += version.o
 
-- 
2.34.1



[PATCH v13 06/24] test: hush: Test hush loops

2023-12-22 Thread Francis Laniel
The added tests verifies correct behavior of for, while and until loops.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/loop.c   | 65 ++
 2 files changed, 66 insertions(+)
 create mode 100644 test/hush/loop.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index ff4fe7700b..a2d98815e5 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -7,3 +7,4 @@ obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
 obj-y += list.o
+obj-y += loop.o
diff --git a/test/hush/loop.c b/test/hush/loop.c
new file mode 100644
index 00..ca777e38fe
--- /dev/null
+++ b/test/hush/loop.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_for(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   ut_assertok(run_command("for loop_i in foo bar quux quux; do echo 
$loop_i; done", 0));
+   ut_assert_nextline("foo");
+   ut_assert_nextline("bar");
+   ut_assert_nextline("quux");
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_for, 0);
+
+static int hush_test_while(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_while, 0);
+
+static int hush_test_until(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+   env_set("loop_bar", "bar");
+
+   /*
+* WARNING We have to use environment variable because it is not 
possible
+* resetting local variable.
+*/
+   ut_assertok(run_command("until test -z \"$loop_bar\"; do echo quux; 
setenv loop_bar; done", 0));
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   /*
+* Loop normally resets foo environment variable, but we reset it here 
in
+* case the test failed.
+*/
+   env_set("loop_bar", NULL);
+   return 0;
+}
+HUSH_TEST(hush_test_until, 0);
-- 
2.34.1



[PATCH v13 05/24] test: hush: Test hush commands list

2023-12-22 Thread Francis Laniel
Verifies behavior of commands separated by ';', '&&' and '||'.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/list.c   | 79 ++
 2 files changed, 80 insertions(+)
 create mode 100644 test/hush/list.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index feb4f71956..ff4fe7700b 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -6,3 +6,4 @@
 obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
+obj-y += list.o
diff --git a/test/hush/list.c b/test/hush/list.c
new file mode 100644
index 00..052cf2783c
--- /dev/null
+++ b/test/hush/list.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_semicolon(struct unit_test_state *uts)
+{
+   /* A; B = B truth table. */
+   ut_asserteq(1, run_command("false; false", 0));
+   ut_assertok(run_command("false; true", 0));
+   ut_assertok(run_command("true; true", 0));
+   ut_asserteq(1, run_command("true; false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_semicolon, 0);
+
+static int hush_test_and(struct unit_test_state *uts)
+{
+   /* A && B truth table. */
+   ut_asserteq(1, run_command("false && false", 0));
+   ut_asserteq(1, run_command("false && true", 0));
+   ut_assertok(run_command("true && true", 0));
+   ut_asserteq(1, run_command("true && false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and, 0);
+
+static int hush_test_or(struct unit_test_state *uts)
+{
+   /* A || B truth table. */
+   ut_asserteq(1, run_command("false || false", 0));
+   ut_assertok(run_command("false || true", 0));
+   ut_assertok(run_command("true || true", 0));
+   ut_assertok(run_command("true || false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or, 0);
+
+static int hush_test_and_or(struct unit_test_state *uts)
+{
+   /* A && B || C truth table. */
+   ut_asserteq(1, run_command("false && false || false", 0));
+   ut_asserteq(1, run_command("false && false || true", 0));
+   ut_asserteq(1, run_command("false && true || true", 0));
+   ut_asserteq(1, run_command("false && true || false", 0));
+   ut_assertok(run_command("true && true || false", 0));
+   ut_asserteq(1, run_command("true && false || false", 0));
+   ut_assertok(run_command("true && false || true", 0));
+   ut_assertok(run_command("true && true || true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and_or, 0);
+
+static int hush_test_or_and(struct unit_test_state *uts)
+{
+   /* A || B && C truth table. */
+   ut_asserteq(1, run_command("false || false && false", 0));
+   ut_asserteq(1, run_command("false || false && true", 0));
+   ut_assertok(run_command("false || true && true", 0));
+   ut_asserteq(1, run_command("false || true && false", 0));
+   ut_assertok(run_command("true || true && false", 0));
+   ut_assertok(run_command("true || false && false", 0));
+   ut_assertok(run_command("true || false && true", 0));
+   ut_assertok(run_command("true || true && true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or_and, 0);
-- 
2.34.1



[PATCH v13 04/24] test: hush: Test hush variable expansion

2023-12-22 Thread Francis Laniel
Verifies shell variables are replaced by their values.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/Makefile   |   1 +
 test/hush/dollar.c   | 167 +++
 test/py/tests/test_ut.py |   8 +-
 3 files changed, 175 insertions(+), 1 deletion(-)
 create mode 100644 test/hush/dollar.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index a3c9ae5106..feb4f71956 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -5,3 +5,4 @@
 
 obj-y += cmd_ut_hush.o
 obj-y += if.o
+obj-y += dollar.o
diff --git a/test/hush/dollar.c b/test/hush/dollar.c
new file mode 100644
index 00..defb2c3fd0
--- /dev/null
+++ b/test/hush/dollar.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_simple_dollar(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline_empty();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${dollar_foo}", 0));
+   ut_assert_nextline_empty();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo=bar", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${dollar_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo=\\$bar", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("$bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='$bar'", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("$bar");
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo=bar quux", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
+   /* Two next lines contain error message */
+   ut_assert_skipline();
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   /*
+* This one is buggy.
+* ut_assert_nextline("bar \"quux");
+* ut_assert_console_end();
+*
+* So, let's reset output:
+*/
+   console_record_reset_enable();
+
+   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='bar quux'", 0));
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar quux");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable dollar_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_simple_dollar, 0);
+
+static int hush_test_env_dollar(struct unit_test_state *uts)
+{
+   env_set("env_foo", "bar");
+   console_record_reset_enable();
+
+   ut_assertok(run_command("echo $env_foo", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${env_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   /* Environment variables have priority over local variable */
+   ut_assertok(run_command("env_foo=quux", 0));
+   ut_assertok(run_command("echo ${env_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   /* Clean up setting the variable */
+   env_set("env_foo", NULL);
+
+   puts("Beware: this test set local variable env_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_env_dollar, 0);
+
+static int hush_test_command_dollar(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   ut_assertok(run_command("dollar_bar=\"echo bar\"", 0));
+
+   ut_assertok(run_command("$dollar_bar", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end(

[PATCH v13 03/24] test/py: hush_if_test: Remove the test file

2023-12-22 Thread Francis Laniel
5804ebfeb1ce ("test: hush: Test hush if/else") translated this test to a C test,
so this python file is no more needed.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/py/tests/test_hush_if_test.py | 197 -
 1 file changed, 197 deletions(-)
 delete mode 100644 test/py/tests/test_hush_if_test.py

diff --git a/test/py/tests/test_hush_if_test.py 
b/test/py/tests/test_hush_if_test.py
deleted file mode 100644
index 3b4b6fcaf4..00
--- a/test/py/tests/test_hush_if_test.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
-
-# Test operation of the "if" shell command.
-
-import os
-import os.path
-import pytest
-
-# TODO: These tests should be converted to a C test.
-# For more information please take a look at the thread
-# https://lists.denx.de/pipermail/u-boot/2019-October/388732.html
-
-pytestmark = pytest.mark.buildconfigspec('hush_parser')
-
-# The list of "if test" conditions to test.
-subtests = (
-# Base if functionality.
-
-('true', True),
-('false', False),
-
-# Basic operators.
-
-('test aaa = aaa', True),
-('test aaa = bbb', False),
-
-('test aaa != bbb', True),
-('test aaa != aaa', False),
-
-('test aaa < bbb', True),
-('test bbb < aaa', False),
-
-('test bbb > aaa', True),
-('test aaa > bbb', False),
-
-('test 123 -eq 123', True),
-('test 123 -eq 456', False),
-
-('test 123 -ne 456', True),
-('test 123 -ne 123', False),
-
-('test 123 -lt 456', True),
-('test 123 -lt 123', False),
-('test 456 -lt 123', False),
-
-('test 123 -le 456', True),
-('test 123 -le 123', True),
-('test 456 -le 123', False),
-
-('test 456 -gt 123', True),
-('test 123 -gt 123', False),
-('test 123 -gt 456', False),
-
-('test 456 -ge 123', True),
-('test 123 -ge 123', True),
-('test 123 -ge 456', False),
-
-# Octal tests
-
-('test 010 -eq 010', True),
-('test 010 -eq 011', False),
-
-('test 010 -ne 011', True),
-('test 010 -ne 010', False),
-
-# Hexadecimal tests
-
-('test 0x200 -gt 0x201', False),
-('test 0x200 -gt 0x200', False),
-('test 0x200 -gt 0x1ff', True),
-
-# Mixed tests
-
-('test 010 -eq 10', False),
-('test 010 -ne 10', True),
-('test 0xa -eq 10', True),
-('test 0xa -eq 012', True),
-
-('test 200 -gt 0x1ff', False),
-('test 0x200 -gt 1ff', True),
-('test 0x200 -lt 1ff', False),
-('test 0x200 -eq 200', False),
-('test 0x200 -ne 200', True),
-
-('test -z ""', True),
-('test -z "aaa"', False),
-
-('test -n "aaa"', True),
-('test -n ""', False),
-
-# Inversion of simple tests.
-
-('test ! aaa = aaa', False),
-('test ! aaa = bbb', True),
-('test ! ! aaa = aaa', True),
-('test ! ! aaa = bbb', False),
-
-# Binary operators.
-
-('test aaa != aaa -o bbb != bbb', False),
-('test aaa != aaa -o bbb = bbb', True),
-('test aaa = aaa -o bbb != bbb', True),
-('test aaa = aaa -o bbb = bbb', True),
-
-('test aaa != aaa -a bbb != bbb', False),
-('test aaa != aaa -a bbb = bbb', False),
-('test aaa = aaa -a bbb != bbb', False),
-('test aaa = aaa -a bbb = bbb', True),
-
-# Inversion within binary operators.
-
-('test ! aaa != aaa -o ! bbb != bbb', True),
-('test ! aaa != aaa -o ! bbb = bbb', True),
-('test ! aaa = aaa -o ! bbb != bbb', True),
-('test ! aaa = aaa -o ! bbb = bbb', False),
-
-('test ! ! aaa != aaa -o ! ! bbb != bbb', False),
-('test ! ! aaa != aaa -o ! ! bbb = bbb', True),
-('test ! ! aaa = aaa -o ! ! bbb != bbb', True),
-('test ! ! aaa = aaa -o ! ! bbb = bbb', True),
-)
-
-def exec_hush_if(u_boot_console, expr, result):
-"""Execute a shell "if" command, and validate its result."""
-
-config = u_boot_console.config.buildconfig
-maxargs = int(config.get('config_sys_maxargs', '0'))
-args = len(expr.split(' ')) - 1
-if args > maxargs:
-u_boot_console.log.warning('CONFIG_SYS_MAXARGS too low; need ' +
-str(args))
-pytest.skip()
-
-cmd = 'if ' + expr + '; then echo true; else echo false; fi'
-response = u_boot_console.run_command(cmd)
-assert response.strip() == str(result).lower()
-
-@pytest.mark.buildconfigspec('cmd_echo')
-@pytest.mark.parametrize('expr,result', subtests)
-def test_hush_if_test(u_boot_console, expr, result):
-"""Test a single "if test" condition."""
-
-exec_hush_if(u_boot_console, expr, result)
-
-def test_hush_z(u_boot_console):
-"""Test the -z operator"""
-u_boot_console.run_command('setenv ut_var_nonexistent')
-u_boot_console.run_command('set

[PATCH v13 02/24] test: hush: Test hush if/else

2023-12-22 Thread Francis Laniel
As asked in commit 9c6bf1715f6a ("test/py: hush_if_test: Add tests to cover
octal/hex values"), this commit translates test_hush_if_test.py to a C test.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |   1 +
 test/hush/if.c | 316 +
 2 files changed, 317 insertions(+)
 create mode 100644 test/hush/if.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index dfa2a92615..a3c9ae5106 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -4,3 +4,4 @@
 # Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
 
 obj-y += cmd_ut_hush.o
+obj-y += if.o
diff --git a/test/hush/if.c b/test/hush/if.c
new file mode 100644
index 00..b7200e32ec
--- /dev/null
+++ b/test/hush/if.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * All tests will execute the following:
+ * if condition_to_test; then
+ *   true
+ * else
+ *   false
+ * fi
+ * If condition is true, command returns 1, 0 otherwise.
+ */
+const char *if_format = "if %s; then true; else false; fi";
+
+static int hush_test_if_base(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_format, "true");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "false");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_if_base, 0);
+
+static int hush_test_if_basic_operators(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_format, "test aaa = aaa");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa = bbb");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa != bbb");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa != aaa");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa < bbb");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test bbb < aaa");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test bbb > aaa");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa > bbb");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -eq 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -eq 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ne 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ne 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -lt 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -lt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -lt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -le 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -le 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -le 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -gt 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -gt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -gt 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -ge 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ge 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ge 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_if_basic_operators, 0);
+
+static int hush_test_if_octal(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_f

[PATCH v13 01/24] test: Add framework to test hush behavior

2023-12-22 Thread Francis Laniel
Introduce a new subcommand to ut: ut hush.
For the moment, this command does nothing, future commits will add tests which
will be run on command call.

Note that CONFIG_HUSH_PARSER must be defined to compile this new subcommand.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 include/test/hush.h | 15 +++
 include/test/suites.h   |  1 +
 test/Makefile   |  3 +++
 test/cmd_ut.c   |  6 ++
 test/hush/Makefile  |  6 ++
 test/hush/cmd_ut_hush.c | 20 
 6 files changed, 51 insertions(+)
 create mode 100644 include/test/hush.h
 create mode 100644 test/hush/Makefile
 create mode 100644 test/hush/cmd_ut_hush.c

diff --git a/include/test/hush.h b/include/test/hush.h
new file mode 100644
index 00..cca66544a0
--- /dev/null
+++ b/include/test/hush.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#ifndef __TEST_HUSH_H__
+#define __TEST_HUSH_H__
+
+#include 
+
+/* Declare a new environment test */
+#define HUSH_TEST(_name, _flags)   UNIT_TEST(_name, _flags, hush_test)
+
+#endif /* __TEST_HUSH_H__ */
diff --git a/include/test/suites.h b/include/test/suites.h
index 49224d8ea2..365d5f20df 100644
--- a/include/test/suites.h
+++ b/include/test/suites.h
@@ -43,6 +43,7 @@ int do_ut_env(struct cmd_tbl *cmdtp, int flag, int argc, char 
*const argv[]);
 int do_ut_exit(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_font(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+int do_ut_hush(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_lib(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_loadm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_log(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
diff --git a/test/Makefile b/test/Makefile
index 6b8a1506f5..9aeef02f9e 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -17,6 +17,9 @@ obj-$(CONFIG_FUZZ) += fuzz/
 ifndef CONFIG_SANDBOX_VPL
 obj-$(CONFIG_UNIT_TEST) += lib/
 endif
+ifneq ($(CONFIG_HUSH_PARSER),)
+obj-$(CONFIG_$(SPL_)CMDLINE) += hush/
+endif
 obj-$(CONFIG_$(SPL_)CMDLINE) += print_ut.o
 obj-$(CONFIG_$(SPL_)CMDLINE) += str_ut.o
 obj-$(CONFIG_UT_TIME) += time_ut.o
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 1b934b2329..0677ce0cd1 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -121,6 +121,9 @@ static struct cmd_tbl cmd_ut_sub[] = {
 #ifdef CONFIG_CMD_ADDRMAP
U_BOOT_CMD_MKENT(addrmap, CONFIG_SYS_MAXARGS, 1, do_ut_addrmap, "", ""),
 #endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+   U_BOOT_CMD_MKENT(hush, CONFIG_SYS_MAXARGS, 1, do_ut_hush, "", ""),
+#endif
 #ifdef CONFIG_CMD_LOADM
U_BOOT_CMD_MKENT(loadm, CONFIG_SYS_MAXARGS, 1, do_ut_loadm, "", ""),
 #endif
@@ -216,6 +219,9 @@ U_BOOT_LONGHELP(ut,
 #ifdef CONFIG_CONSOLE_TRUETYPE
"\nfont - font command"
 #endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+   "\nhush - Test hush behavior"
+#endif
 #ifdef CONFIG_CMD_LOADM
"\nloadm - loadm command parameters and loading memory blob"
 #endif
diff --git a/test/hush/Makefile b/test/hush/Makefile
new file mode 100644
index 00..dfa2a92615
--- /dev/null
+++ b/test/hush/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2021
+# Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+
+obj-y += cmd_ut_hush.o
diff --git a/test/hush/cmd_ut_hush.c b/test/hush/cmd_ut_hush.c
new file mode 100644
index 00..48a1adbf28
--- /dev/null
+++ b/test/hush/cmd_ut_hush.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+int do_ut_hush(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+   struct unit_test *tests = UNIT_TEST_SUITE_START(hush_test);
+   const int n_ents = UNIT_TEST_SUITE_COUNT(hush_test);
+
+   return cmd_ut_category("hush", "hush_test_",
+  tests, n_ents, argc, argv);
+}
-- 
2.34.1



[PATCH v13 00/24] Modernize U-Boot shell

2023-12-22 Thread Francis Laniel
tream busybox hush commits until 2nd October 2023.
 v10:
  * Fixed a build error in commit adding cli command.
  * Add new CONFIG_HUSH_SELECTABLE to only build cli command if the two shell
  flavors are selected.
 v11:
  * Renamed everything containing 2021 to modern.
  * Fixed cmd Kconfig indentation.
  * Set modern parser as default for majority of boards except some.
 v12:
  * Rebased on top of 36d3db6c2c06.
  * Fixed bootflow_scan_menu_boot for modern hush.

Francis Laniel (24):
  test: Add framework to test hush behavior
  test: hush: Test hush if/else
  test/py: hush_if_test: Remove the test file
  test: hush: Test hush variable expansion
  test: hush: Test hush commands list
  test: hush: Test hush loops
  cli: Add Busybox upstream hush.c file
  cli: Port upstream Busybox hush to U-Boot
  cli: Add menu for hush parser
  global_data.h: add GD_FLG_HUSH_OLD_PARSER flag
  cmd: Add new cli command
  cli: Enables using modern hush parser as command line parser
  cli: hush_modern: Enable variables expansion for modern hush
  cli: hush_modern: Add functions to be called from run_command()
  cli: add modern hush as parser for run_command*()
  test: hush: Fix instructions list tests for modern hush
  test: hush: Fix variable expansion tests for modern hush
  cli: hush_modern: Enable using < and > as string compare operators
  cli: hush_modern: Enable if keyword
  cli: hush_modern: Enable loops
  test: hush: Fix loop tests for modern hush
  cli: modern_hush: Add upstream commits up to 2nd October 2023.
  cmd: Set modern hush as default shell
  configs: Use old hush for several boards

 cmd/Kconfig|21 +
 cmd/Makefile   | 2 +
 cmd/cli.c  |   134 +
 common/Makefile| 3 +-
 common/cli.c   |82 +-
 common/cli_hush_modern.c   |   324 +
 common/cli_hush_upstream.c | 13030 +++
 configs/kmcent2_defconfig  | 1 +
 configs/kmcoge5ne_defconfig| 1 +
 configs/kmeter1_defconfig  | 1 +
 configs/kmopti2_defconfig  | 1 +
 configs/kmsupx5_defconfig  | 1 +
 configs/kmtepr2_defconfig  | 1 +
 configs/pg_wcom_expu1_defconfig| 1 +
 configs/pg_wcom_expu1_update_defconfig | 1 +
 configs/pg_wcom_seli8_defconfig| 1 +
 configs/pg_wcom_seli8_update_defconfig | 1 +
 configs/socfpga_secu1_defconfig| 1 +
 configs/tuge1_defconfig| 1 +
 configs/tuxx1_defconfig| 1 +
 doc/usage/cmd/cli.rst  |74 +
 doc/usage/index.rst| 1 +
 include/asm-generic/global_data.h  | 8 +
 include/cli_hush.h |51 +-
 include/test/hush.h|15 +
 include/test/suites.h  | 1 +
 test/Makefile  | 3 +
 test/boot/bootflow.c   |16 +-
 test/cmd_ut.c  | 6 +
 test/hush/Makefile |10 +
 test/hush/cmd_ut_hush.c|20 +
 test/hush/dollar.c |   226 +
 test/hush/if.c |   316 +
 test/hush/list.c   |   140 +
 test/hush/loop.c   |91 +
 test/py/tests/test_hush_if_test.py |   197 -
 test/py/tests/test_ut.py   | 8 +-
 37 files changed, 14580 insertions(+), 212 deletions(-)
 create mode 100644 cmd/cli.c
 create mode 100644 common/cli_hush_modern.c
 create mode 100644 common/cli_hush_upstream.c
 create mode 100644 doc/usage/cmd/cli.rst
 create mode 100644 include/test/hush.h
 create mode 100644 test/hush/Makefile
 create mode 100644 test/hush/cmd_ut_hush.c
 create mode 100644 test/hush/dollar.c
 create mode 100644 test/hush/if.c
 create mode 100644 test/hush/list.c
 create mode 100644 test/hush/loop.c
 delete mode 100644 test/py/tests/test_hush_if_test.py


Have a nice end of the year and thank you in advance!
---
[1] https://lists.denx.de/pipermail/u-boot/2021-July/453347.html
[2] https://runtimeterror.com/tech/lil/
[3] https://lists.denx.de/pipermail/u-boot/2021-July/453790.html
[4] https://lists.denx.de/pipermail/u-boot/2021-July/453848.html
[5] https://lists.denx.de/pipermail/u-boot/2021-August/458569.html
[6] https://lists.denx.de/pipermail/u-boot/2023-November/536768.html
[7] https://lists.denx.de/pipermail/u-boot/2023-May/518140.html
[8] https://lists.denx.de/pipermail/u-boot/2023-May/518147.html
[9] https://lists.denx.de/pipermail/u-boot/2023-November/536952.html
--
2.34.1



Re: [PATCH v12 00/24] Modernize U-Boot shell

2023-12-12 Thread Francis Laniel
Hi!


Le dimanche 10 décembre 2023, 00:16:31 CET Tom Rini a écrit :
> On Fri, Dec 08, 2023 at 11:30:01PM +0100, Francis Laniel wrote:
> > Hi.
> > 
> > 
> > During 2021 summer, Sean Anderson wrote a contribution to add a new shell,
> > based on LIL, to U-Boot [1, 2].
> > While one of the goals of this contribution was to address the fact actual
> > U-Boot shell, which is based on Busybox hush, is old there was a
> > discussion
> > about adding a new shell versus updating the actual one [3, 4].
> > 
> > So, in this series, with Harald Seiler, we updated the actual U-Boot shell
> > to reflect what is currently in Busybox source code.
> > Basically, this contribution is about taking a snapshot of Busybox
> > shell/hush.c file (as it exists in commit 37460f5da) and adapt it to suit
> > U-Boot needs.
> > 
> > This contribution was written to be as backward-compatible as possible to
> > avoid breaking the existing.
> > So, the modern hush flavor offers the same as the actual, that is to say:
> > 1. Variable expansion.
> > 2. Instruction lists (;, && and ||).
> > 3. If, then and else.
> > 4. Loops (for, while and until).
> > No new features offered by Busybox hush were implemented (e.g. functions).
> > 
> > It is possible to change the parser at runtime using the "cli" command:
> > => cli print
> > old
> > => cli set modern
> > => cli print
> > modern
> > => cli set old
> > The default parser is the old one.
> > Note that to use both parser, you would need to set both
> > CONFIG_HUSH_MODERN_PARSER and CONFIG_HUSH_OLD_PARSER.
> > 
> > In terms of testing, new unit tests were added to ut to ensure the new
> > behavior is the same as the old one and it does not add regression.
> > Nonetheless, if old behavior was buggy and fixed upstream, the fix is then
> > added to U-Boot [5].
> > In sandbox, all of these tests pass smoothly:
> > => printenv board
> > board=sandbox
> > => ut hush
> > Running 20 hush tests
> > ...
> > Failures: 0
> > => cli set modern
> > => ut hush
> > Running 20 hush tests
> > ...
> > Failures: 0
> > 
> > Thanks to the effort of Harald Seiler, I was successful booting a board:
> > => printenv fdtfile
> > fdtfile=amlogic/meson-gxl-s905x-libretech-cc.dtb
> > => cli get
> > old
> > => boot
> > ...
> > root@lepotato:~#
> > root@lepotato:~# reboot
> > ...
> > => cli set modern
> > => cli get
> > modern
> > => printenv fdtfile
> > fdtfile=amlogic/meson-gxl-s905x-libretech-cc.dtb
> > => boot
> > ...
> > root@lepotato:~#
> > 
> > This contribution indeed adds a lot of code and there were concern about
> > its size [6, 7].
> > With regard to the amount of code added, the cli_hush_upstream.c is 13030
> > lines long but it seems a smaller subset is really used:
> > gcc -D__U_BOOT__ -E common/cli_hush_upstream.c | wc -l
> > 2870
> > Despite this, it is better to still have the whole upstream code for the
> > sake of easing maintenance.
> > With regard to memory size, I conducted some experiments for version 8 of
> > this series and for a subset of arm64 boards and found the worst case to
> > be 4K [8]. Tom Rini conducted more research on this and also found the
> > increase to be acceptable [9].
> > 
> > If you want to review it - your review will really be appreciated - here
> > are some information regarding the commits:
> > * commits marked as "test:" deal with unit tests.
> > * commit "cli: Add Busybox upstream hush.c file." copies Busybox
> > shell/hush.c into U-Boot tree, this explain why this commit contains
> > around 12000 additions. * commit "cli: Port Busybox 2021 hush to U-Boot."
> > modifies previously added file to permit us to use this as new shell.
> > The really good idea of #include'ing Busybox code into a wrapper file to
> > define some particular functions while minimizing modifications to
> > upstream code comes from Harald Seiler.
> > * commit "cmd: Add new parser command" adds a new command which permits
> > selecting parser at runtime.
> > I am not really satisfied with the fact it calls cli_init() and cli_loop()
> > each time the parser is set, so your reviews would be welcomed.
> > * Other commits focus on enabling features we need (e.g. if).
> > 
> > Changes since:
> >  v2:
> >   * Added a small fix to compile sandbox with NO_SDL=1.
> >   * Added a comman

[PATCH v12 24/24] configs: Use old hush for several boards

2023-12-08 Thread Francis Laniel
The keymile board family is not compatible with modern hush parser.
Indeed, This boards used set_local_var() to store some variables as local shell.
They then used get_local_var() to retrieve the variables values.
Sadly, this two functions do not exist with CONFIG_HUSH_MODERN_PARSER.
A patch was proposed to use environment variables rather than local variables
but it does not tackle the problem, so complementary work is needed to make
this boards use CONFIG_HUSH_MODERN_PARSER.

Also, with CONFIG_HUSH_MODERN_PARSER, kirkwoord sheevaplug and phytec bk4r1 hit
their board limits, so better to stick with old hush.

Cc: Holger Brunck 
Link: https://marc.info/?l=u-boot=165541917618725=2
Signed-off-by: Francis Laniel 
---
 configs/kmcent2_defconfig  | 1 +
 configs/kmcoge5ne_defconfig| 1 +
 configs/kmeter1_defconfig  | 1 +
 configs/kmopti2_defconfig  | 1 +
 configs/kmsupx5_defconfig  | 1 +
 configs/kmtepr2_defconfig  | 1 +
 configs/pg_wcom_expu1_defconfig| 1 +
 configs/pg_wcom_expu1_update_defconfig | 1 +
 configs/pg_wcom_seli8_defconfig| 1 +
 configs/pg_wcom_seli8_update_defconfig | 1 +
 configs/socfpga_secu1_defconfig| 1 +
 configs/tuge1_defconfig| 1 +
 configs/tuxx1_defconfig| 1 +
 13 files changed, 13 insertions(+)

diff --git a/configs/kmcent2_defconfig b/configs/kmcent2_defconfig
index 2cf9565fc9..ac272b3840 100644
--- a/configs/kmcent2_defconfig
+++ b/configs/kmcent2_defconfig
@@ -111,3 +111,4 @@ CONFIG_BCH=y
 CONFIG_PANIC_HANG=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmcoge5ne_defconfig b/configs/kmcoge5ne_defconfig
index 257ceeca90..ace5080690 100644
--- a/configs/kmcoge5ne_defconfig
+++ b/configs/kmcoge5ne_defconfig
@@ -202,3 +202,4 @@ CONFIG_QE=y
 CONFIG_SYS_NS16550=y
 CONFIG_BCH=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmeter1_defconfig b/configs/kmeter1_defconfig
index 46e0370e35..56b83c085d 100644
--- a/configs/kmeter1_defconfig
+++ b/configs/kmeter1_defconfig
@@ -173,3 +173,4 @@ CONFIG_DM_ETH_PHY=y
 CONFIG_QE_UEC=y
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmopti2_defconfig b/configs/kmopti2_defconfig
index c6c021adde..08c7602f5d 100644
--- a/configs/kmopti2_defconfig
+++ b/configs/kmopti2_defconfig
@@ -183,3 +183,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmsupx5_defconfig b/configs/kmsupx5_defconfig
index 25642e7018..72db26f320 100644
--- a/configs/kmsupx5_defconfig
+++ b/configs/kmsupx5_defconfig
@@ -166,3 +166,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/kmtepr2_defconfig b/configs/kmtepr2_defconfig
index ea37a29060..ed908d3c77 100644
--- a/configs/kmtepr2_defconfig
+++ b/configs/kmtepr2_defconfig
@@ -182,3 +182,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_expu1_defconfig b/configs/pg_wcom_expu1_defconfig
index 455b439151..0337447f79 100644
--- a/configs/pg_wcom_expu1_defconfig
+++ b/configs/pg_wcom_expu1_defconfig
@@ -105,3 +105,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_expu1_update_defconfig 
b/configs/pg_wcom_expu1_update_defconfig
index 269116cd0d..e5daa306ab 100644
--- a/configs/pg_wcom_expu1_update_defconfig
+++ b/configs/pg_wcom_expu1_update_defconfig
@@ -103,3 +103,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_seli8_defconfig b/configs/pg_wcom_seli8_defconfig
index 678bc10070..e86a17abdf 100644
--- a/configs/pg_wcom_seli8_defconfig
+++ b/configs/pg_wcom_seli8_defconfig
@@ -105,3 +105,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/pg_wcom_seli8_update_defconfig 
b/configs/pg_wcom_seli8_update_defconfig
index 7c7b001903..886334a043 100644
--- a/configs/pg_wcom_seli8_update_defconfig
+++ b/configs/pg_wcom_seli8_update_defconfig
@@ -103,3 +103,4 @@ CONFIG_DM_SERIAL=y
 CONFIG_SYS_NS16550=y
 CONFIG_LZO=y
 CONFIG_POST=y
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/socfpga_secu1_defconfig b/configs/socfpga_secu1_defconfig
index b8052f1dee..2622bb0432 100644
--- a/configs/socfpga_secu1_defconfig
+++ b/configs/socfpga_secu1_defconfig
@@ -113,3 +113,4 @@ CONFIG_DESIGNWARE_WATCHDOG=y
 CONFIG_WDT=y
 CONFIG_SYS_TIMER_COUNTS_DOWN=y
 # CONFIG_GZIP is not set
+CONFIG_HUSH_OLD_PARSER=y
diff --git a/configs/tuge1_defconfig b/configs/tuge1_defconfig
index 9ff5d1599f..5c4d33235e 100644
--- a/configs/tuge1_defconfig
+++ b/configs/tuge1_defconfig
@@ -166,3 +166,4 @@ CONFIG_QE_UEC=y
 # CONFIG_PINCTRL_FULL is not set
 CONFIG_QE=y
 CONFIG_SYS_NS16550=y

[PATCH v12 22/24] cli: modern_hush: Add upstream commits up to 2nd October 2023.

2023-12-08 Thread Francis Laniel
This commit adds the following hush busybox upstream commits:
791b222dd55d ("sleep: fix "sleep -- ARGS"")
5353df91cba7 ("Update applet size estimates")
e41e481fd571 ("hush: fix a compile failure")
07a95cfcabb0 ("ash: disable check for "good" function name, bash does not check 
this")
e5692e2342c6 ("hush: quote values in "readonly" output")
96769486e20f ("shell: move varcmp() to shell_common.h and use it in hush")
bab8828b0dad ("hush: fix expansion of space in "a=${a:+$a }c" construct")
b5be8da350b5 ("hush: make "false" built-in")
6824298ab4d3 ("hush: fix ELIF cmd1;cmd2 THEN ... not executing cmd2, closes 
15571")
3a7f00eadcf4 ("hush: add comment about abort on syntax error %{^}")
acae889dd972 ("ash,hush: tab completion of functions and aliases")
90b607d79a13 ("hush: quote variable values printed by "set" (match ash 
behavior)")
6748e6494c22 ("hush (NOMMU): fix LINENO in execed children")
fd5fb2d2b596 ("hush: speed up "big heredoc" code")
1409432d072e ("hush: add TODO comment")
93ae7464e6e4 ("hush: restore SIGHUP handling, this time explain why we do what 
we do")
1fdb33bd07e5 ("hush: restore tty pgrp on SIGHUP")
6101b6d3eaa0 ("hush: remove special handling of SIGHUP")
93e0898c663a ("shell: fix SIGWINCH and SIGCHLD (in hush) interrupting line 
input, closes 15256")
969e00816835 ("hush: code shrink")
27be0e8cfeb6 ("shell: fix compile failures in some configs")
7d1c7d833785 ("ash,hush: use HOME for tab completion and prompts")
21afddefd258 ("hush: fix "error: invalid preprocessing directive ##"")
e53c7dbafc78 ("hush: fix set -n to act immediately, not just after run_list()")
574b9c446da1 ("hush: fix var_LINENO3.tests failure")
49bcf9f40cff ("hush: speed up ${x//\*/|} too")
53b2fdcdba4c ("*: add NOINLINEs where code noticeably shrinks")
7c3e96d4b3d4 ("shell: use more compact SHELL_ASH / HUSH config defines. no code 
changes")
62f1eed1e191 ("hush: in a comment, document what -i might be doing")
aaf3d5ba74c5 ("shell: tweak --help")
db5546ca1018 ("libbb: code shrink: introduce and use [_]exit_SUCCESS()")
931c55f9e2b4 ("libbb: invert the meaning of SETUP_ENV_NO_CHDIR -> 
SETUP_ENV_CHDIR")
12566e7f9b5e ("ash,hush: fix handling of SIGINT while waiting for interactive 
input")
987be932ed3c ("*: slap on a few ALIGN_PTR where appropriate")

Signed-off-by: Francis Laniel 
---
 common/cli_hush_modern.c   |  20 +-
 common/cli_hush_upstream.c | 407 ++---
 2 files changed, 304 insertions(+), 123 deletions(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index fb578c3853..9a1bc977d5 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -26,7 +26,7 @@
 /*
  * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
  */
-#define BB_VER "1.34.0.git37460f5daff9"
+#define BB_VER "1.35.0.git7d1c7d833785"
 
 /*
  * Define hush features by the names used upstream.
@@ -236,6 +236,24 @@ static size_t list_size(char **list)
return size;
 }
 
+static int varcmp(const char *p, const char *q)
+{
+   int c, d;
+
+   while ((c = *p) == (d = *q)) {
+   if (c == '\0' || c == '=')
+   goto out;
+   p++;
+   q++;
+   }
+   if (c == '=')
+   c = '\0';
+   if (d == '=')
+   d = '\0';
+out:
+   return c - d;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 748edc9d2f..ca40bbb2cd 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -91,7 +91,7 @@
  * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
  */
 //config:config HUSH
-//config:  bool "hush (68 kb)"
+//config:  bool "hush (70 kb)"
 //config:  default y
 //config:  select SHELL_HUSH
 //config:  help
@@ -339,7 +339,7 @@
  * therefore we don't show them either.
  */
 //usage:#define hush_trivial_usage
-//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
+//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
 //usage:#define hush_full_usage "\n\n"
 //usage:   "Unix shell interpreter"
 
@@ -374,7 +374,7 @@
 # define F_DUPFD_CLOEXEC F_DUPFD
 #endif
 
-#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !(ENABLE_ASH || ENABLE_SH_IS_ASH || 
ENABLE_BASH_IS_ASH)
+#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !ENABLE_SHELL_ASH
 # include "embe

[PATCH v12 23/24] cmd: Set modern hush as default shell

2023-12-08 Thread Francis Laniel
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig | 6 +-
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 8b49a97aca..c9d4375ebb 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -28,21 +28,17 @@ depends on HUSH_PARSER
 
 config HUSH_OLD_PARSER
bool "Use hush old parser"
-   default y
help
  This option enables the old flavor of hush based on hush Busybox from
  2005.
 
- It is actually the default U-Boot shell when decided to use hush as 
shell.
-
 config HUSH_MODERN_PARSER
bool "Use hush modern parser"
+   default y
help
  This option enables the new flavor of hush based on hush upstream
  Busybox.
 
- This parser is experimental and not well tested.
-
 config HUSH_SELECTABLE
bool
default y if HUSH_OLD_PARSER && HUSH_MODERN_PARSER
-- 
2.34.1



[PATCH v12 21/24] test: hush: Fix loop tests for modern hush

2023-12-08 Thread Francis Laniel
Modifies return code got from while loop as modern hush always returns 0 from
while loop.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/loop.c | 34 ++
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/test/hush/loop.c b/test/hush/loop.c
index ca777e38fe..80a9071a80 100644
--- a/test/hush/loop.c
+++ b/test/hush/loop.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_for(struct unit_test_state *uts)
 {
@@ -21,7 +24,12 @@ static int hush_test_for(struct unit_test_state *uts)
ut_assert_nextline("quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_i=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_i and it cannot 
be unset!");
+   }
 
return 0;
 }
@@ -31,12 +39,30 @@ static int hush_test_while(struct unit_test_state *uts)
 {
console_record_reset_enable();
 
-   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
-   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* Hush 2021 always returns 0 from while loop...
+* You can see code snippet near this line to have a better
+* understanding:
+* debug_printf_exec(": while expr is false: breaking 
(exitcode:EXIT_SUCCESS)\n");
+*/
+   ut_assertok(run_command("while test -z \"$loop_foo\"; do echo 
bar; loop_foo=quux; done", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   /*
+* Exit status is that of test, so 1 since test is false to quit
+* the loop.
+*/
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do 
echo bar; loop_foo=quux; done", 0));
+   }
ut_assert_nextline("bar");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
-- 
2.34.1



[PATCH v12 15/24] cli: add modern hush as parser for run_command*()

2023-12-08 Thread Francis Laniel
Enables using, in code, modern hush as parser for run_command function family.
It also enables the command run to be used by CLI user of modern hush.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c   | 67 --
 common/cli_hush_upstream.c |  2 +-
 2 files changed, 51 insertions(+), 18 deletions(-)

diff --git a/common/cli.c b/common/cli.c
index b3eb512b62..a34938294e 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -25,6 +25,14 @@
 #include 
 
 #ifdef CONFIG_CMDLINE
+
+static inline bool use_hush_old(void)
+{
+   return IS_ENABLED(CONFIG_HUSH_SELECTABLE) ?
+   gd->flags & GD_FLG_HUSH_OLD_PARSER :
+   IS_ENABLED(CONFIG_HUSH_OLD_PARSER);
+}
+
 /*
  * Run a command using the selected parser.
  *
@@ -43,15 +51,30 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#elif CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
-
-   if (flag & CMD_FLAG_ENV)
-   hush_flags |= FLAG_CONT_ON_NEWLINE;
-   return parse_string_outer(cmd, hush_flags);
-#else /* HUSH_MODERN_PARSER */
-   /* Not yet implemented. */
-   return 1;
+#else
+   if (use_hush_old()) {
+   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
+
+   if (flag & CMD_FLAG_ENV)
+   hush_flags |= FLAG_CONT_ON_NEWLINE;
+   return parse_string_outer(cmd, hush_flags);
+   }
+   /*
+* Possible values for flags are the following:
+* FLAG_EXIT_FROM_LOOP: This flags ensures we exit from loop in
+* parse_and_run_stream() after first iteration while normal
+* behavior, * i.e. when called from cli_loop(), is to loop
+* infinitely.
+* FLAG_PARSE_SEMICOLON: modern Hush parses ';' and does not stop
+* first time it sees one. So, I think we do not need this flag.
+* FLAG_REPARSING: For the moment, I do not understand the goal
+* of this flag.
+* FLAG_CONT_ON_NEWLINE: This flag seems to be used to continue
+* parsing even when reading '\n' when coming from
+* run_command(). In this case, modern Hush reads until it finds
+* '\0'. So, I think we do not need this flag.
+*/
+   return parse_string_outer_modern(cmd, FLAG_EXIT_FROM_LOOP);
 #endif
 }
 
@@ -67,12 +90,23 @@ int run_command_repeatable(const char *cmd, int flag)
 #ifndef CONFIG_HUSH_PARSER
return cli_simple_run_command(cmd, flag);
 #else
+   int ret;
+
+   if (use_hush_old()) {
+   ret = parse_string_outer(cmd,
+FLAG_PARSE_SEMICOLON
+| FLAG_EXIT_FROM_LOOP);
+   } else {
+   ret = parse_string_outer_modern(cmd,
+ FLAG_PARSE_SEMICOLON
+ | FLAG_EXIT_FROM_LOOP);
+   }
+
/*
 * parse_string_outer() returns 1 for failure, so clean up
 * its result.
 */
-   if (parse_string_outer(cmd,
-  FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP))
+   if (ret)
return -1;
 
return 0;
@@ -111,12 +145,11 @@ int run_command_list(const char *cmd, int len, int flag)
buff[len] = '\0';
}
 #ifdef CONFIG_HUSH_PARSER
-#if CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
-#else /* HUSH_MODERN_PARSER */
-   /* Not yet implemented. */
-   rcode = 1;
-#endif
+   if (use_hush_old()) {
+   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
+   } else {
+   rcode = parse_string_outer_modern(buff, FLAG_PARSE_SEMICOLON);
+   }
 #else
/*
 * This function will overwrite any \n it sees with a \0, which
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 7f6b058e6c..11870c51e8 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -8022,7 +8022,7 @@ static int parse_and_run_string(const char *s)
 }
 
 #ifdef __U_BOOT__
-int parse_string_outer(const char *cmd, int flags)
+int parse_string_outer_modern(const char *cmd, int flags)
 {
int ret;
int old_flags;
-- 
2.34.1



[PATCH v12 20/24] cli: hush_modern: Enable loops

2023-12-08 Thread Francis Laniel
Enables the use of for, while and until loops for command line as
well as with run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_modern.c   |  1 +
 common/cli_hush_upstream.c | 15 ++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index 6360747f21..fb578c3853 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -34,6 +34,7 @@
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
 #define ENABLE_HUSH_IF 1
+#define ENABLE_HUSH_LOOPS  1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 06af538a48..748edc9d2f 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -10348,7 +10348,7 @@ static int run_list(struct pipe *pi)
 #ifndef __U_BOOT__
for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) 
pi->next) {
 #else /* __U_BOOT__ */
-   for (; pi; pi = pi->next) {
+   for (; pi; pi = rword == RES_DONE ? loop_top : pi->next) {
 #endif /* __U_BOOT__ */
int r;
int sv_errexit_depth;
@@ -10450,7 +10450,20 @@ static int run_list(struct pipe *pi)
}
/* Insert next value from for_lcur */
/* note: *for_lcur already has quotes removed, $var 
expanded, etc */
+#ifndef __U_BOOT__
set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], 
*for_lcur++), /*flag:*/ 0);
+#else /* __U_BOOT__ */
+   /* We cannot use xasprintf, so we emulate it. */
+   char *full_var;
+   char *var = pi->cmds[0].argv[0];
+   char *val = *for_lcur++;
+
+   /* + 1 to take into account =. */
+   full_var = xmalloc(strlen(var) + strlen(val) + 1);
+   sprintf(full_var, "%s=%s", var, val);
+
+   set_local_var_modern(full_var, /*flag:*/ 0);
+#endif /* __U_BOOT__ */
continue;
}
if (rword == RES_IN) {
-- 
2.34.1



[PATCH v12 17/24] test: hush: Fix variable expansion tests for modern hush

2023-12-08 Thread Francis Laniel
Modifies the expected result for modern hush.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As modern hush is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/dollar.c | 79 --
 1 file changed, 69 insertions(+), 10 deletions(-)

diff --git a/test/hush/dollar.c b/test/hush/dollar.c
index defb2c3fd0..7ab7ea7084 100644
--- a/test/hush/dollar.c
+++ b/test/hush/dollar.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_simple_dollar(struct unit_test_state *uts)
 {
@@ -51,13 +54,29 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
/* Next line contains error message */
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* For some strange reasons, the console is not empty after
+* running above command.
+* So, we reset it to not have side effects for other tests.
+*/
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
/* Two next lines contain error message */
ut_assert_skipline();
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* See above comments. */
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
 
@@ -71,17 +90,35 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
 */
console_record_reset_enable();
 
-   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
-   /* Next line contains error message */
-   ut_assert_skipline();
-   ut_assert_console_end();
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* Old parser returns an error because it waits for closing
+* '\'', but this behavior is wrong as the '\'' is surrounded by
+* '"', so no need to wait for a closing one.
+*/
+   ut_assertok(run_command("dollar_foo=\"bar 'quux\"", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar 'quux");
+   ut_assert_console_end();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar quux'", 0));
ut_assertok(run_command("echo $dollar_foo", 0));
ut_assert_nextline("bar quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable dollar_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("dollar_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable dollar_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -109,7 +146,12 @@ static int hush_test_env_dollar(struct unit_test_state 
*uts)
/* Clean up setting the variable */
env_set("env_foo", NULL);
 
-   puts("Beware: this test set local variable env_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("env_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable env_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -144,7 +186,18 @@ static int hush_test_command_dollar(struct unit_test_state 
*uts)
ut_assertok(run_command("dollar_bar='echo bar\\n'", 0));
 
ut_assertok(run_command("$dollar_bar", 0));
-   ut_assert_nextline("barn");
+
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug 

[PATCH v12 19/24] cli: hush_modern: Enable if keyword

2023-12-08 Thread Francis Laniel
Adds support for "if then else" construct both for command line interface and
through run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_modern.c   | 11 +++
 common/cli_hush_upstream.c | 12 
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index 15bb1f0d92..6360747f21 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -33,6 +33,7 @@
  */
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
+#define ENABLE_HUSH_IF 1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
@@ -124,6 +125,11 @@ static void bb_error_msg(const char *s, ...)
va_end(p);
 }
 
+static void bb_simple_error_msg(const char *s)
+{
+   bb_error_msg("%s", s);
+}
+
 static void *xmalloc(size_t size)
 {
void *p = NULL;
@@ -147,6 +153,11 @@ static void *xrealloc(void *ptr, size_t size)
return p;
 }
 
+static void *xmemdup(const void *s, int n)
+{
+   return memcpy(xmalloc(n), s, n);
+}
+
 #define xstrdupstrdup
 #define xstrndup   strndup
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index a56ea6c5c8..06af538a48 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1487,7 +1487,6 @@ static void msg_and_die_if_script(unsigned lineno, const 
char *fmt, ...)
die_if_script();
 }
 
-#ifndef __U_BOOT__
 static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
 {
if (msg)
@@ -1496,7 +1495,6 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, 
const char *msg)
bb_simple_error_msg("syntax error");
die_if_script();
 }
-#endif /* !__U_BOOT__ */
 
 static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
 {
@@ -3961,7 +3959,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[PIPE_OR ] = "OR" ,
[PIPE_BG ] = "BG" ,
};
-#ifndef __U_BOOT__
static const char *RES[] = {
[RES_NONE ] = "NONE" ,
 # if ENABLE_HUSH_IF
@@ -3991,7 +3988,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[RES_ ] = "" ,
[RES_SNTX ] = "SNTX" ,
};
-#endif /* !__U_BOOT__ */
static const char *const CMDTYPE[] = {
"{}",
"()",
@@ -4009,10 +4005,8 @@ static void debug_print_tree(struct pipe *pi, int lvl)
lvl*2, "",
pin,
pi->num_cmds,
-#ifdef __U_BOOT__
(IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
RES[pi->res_word],
-#endif /* !__U_BOOT__ */
pi->followup, PIPE[pi->followup]
);
prn = 0;
@@ -9832,6 +9826,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
rcode = 1; /* exitcode if redir failed */
 #ifndef __U_BOOT__
if (setup_redirects(command, ) == 0) {
+#endif /* !__U_BOOT__ */
debug_printf_exec(": run_list\n");
 //FIXME: we need to pass squirrel down into run_list()
 //for SH_STANDALONE case, or else this construct:
@@ -9840,10 +9835,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
 //and in SH_STANDALONE mode, "find" is not execed,
 //therefore CLOEXEC on saved fd does not help.
rcode = run_list(command->group) & 0xff;
+#ifndef __U_BOOT__
}
restore_redirects(squirrel);
-   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
 #endif /* !__U_BOOT__ */
+   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
debug_leave();
debug_printf_exec("run_pipe: return %d\n", rcode);
return rcode;
@@ -10362,12 +10358,12 @@ static int run_list(struct pipe *pi)
break;
if (G_flag_return_in_progress == 1)
break;
+#endif /* !__U_BOOT__ */
 
IF_HAS_KEYWORDS(rword = pi->res_word;)
debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
rword, cond_code, last_rword);
 
-#endif /* !__U_BOOT__ */
sv_errexit_depth = G.errexit_depth;
if (
 #if ENABLE_HUSH_IF
-- 
2.34.1



[PATCH v12 18/24] cli: hush_modern: Enable using < and > as string compare operators

2023-12-08 Thread Francis Laniel
In Busybox hush, '<' and '>' are used as redirection operators.
For example, cat foo > bar will write content of file foo inside file bar.
In U-Boot, we do not have file system, so we can hardly redirect command output
inside a file.

But, in actual U-Boot hush, these operators ('<' and '>') are used as string
compare operators.
For example, test aaa < bbb returns 0 as aaa is before bbb in the dictionary.
Busybox hush also permits this, but operators need to be escaped ('\<' and
'\>').
Indeed, if escaping is needed it permits the developer to think about its code,
as in a lot of case, we want to compare integers (using '-lt' or '-gt') rather
than strings.

As testing in U-Boot is handled by the test command, we will stick with the
original behaviour and not adapt to Busybox one.

Nonetheless, if one day we decide to implement test with '[[ ]]', we will then
stick to upstream Busybox behavior.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 23 ++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 11870c51e8..a56ea6c5c8 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -6159,7 +6159,28 @@ static struct pipe *parse_stream(char **pstring,
if (parse_redirect(, redir_fd, redir_style, input))
goto parse_error_exitcode1;
continue; /* get next char */
-#endif /* !__U_BOOT__ */
+#else /* __U_BOOT__ */
+   /*
+* In U-Boot, '<' and '>' can be used in test command 
to test if
+* a string is, alphabetically, before or after another.
+* In 2021 Busybox hush, we will keep the same behavior 
and so not treat
+* them as redirection operator.
+*
+* Indeed, in U-Boot, tests are handled by the test 
command and not by the
+* shell code.
+* So, better to give this character as input to test 
command.
+*
+* NOTE In my opinion, when you use '<' or '>' I am 
almost sure
+* you wanted to use "-gt" or "-lt" in place, so 
thinking to
+* escape these will make you should check your code 
(sh syntax
+* at this level is, for me, error prone).
+*/
+   case '>':
+   fallthrough;
+   case '<':
+   o_addQchr(, ch);
+   continue;
+#endif /* __U_BOOT__ */
case '#':
if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
/* skip "#comment" */
-- 
2.34.1



[PATCH v12 14/24] cli: hush_modern: Add functions to be called from run_command()

2023-12-08 Thread Francis Laniel
run_command() is called internally by the command run and it can also be called
directly from U-Boot code, e.g. to do unit tests.
This commit adds this path to go to modern hush.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 66 --
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index ff4a57c6e3..7f6b058e6c 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1011,6 +1011,7 @@ struct globals {
 #ifdef __U_BOOT__
int flag_repeat;
int do_repeat;
+   int run_command_flags;
 #endif /* __U_BOOT__ */
char *ifs_whitespace; /* = G.ifs or malloced */
 #ifndef __U_BOOT__
@@ -3004,7 +3005,24 @@ static int i_getch(struct in_str *i)
if (i->p && *i->p != '\0') {
ch = (unsigned char)*i->p++;
goto out;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* There are two ways for command to be called:
+* 1. The first one is when they are typed by the user.
+* 2. The second one is through run_command() (NOTE command run
+* internally calls run_command()).
+*
+* In the second case, we do not get input from the user, so once we
+* get a '\0', it means we need to stop.
+* NOTE G.run_command_flags is only set on run_command call stack, so
+* we use this to know if we come from user input or run_command().
+*/
+   } else if (i->p && *i->p == '\0' && G.run_command_flags){
+   return EOF;
+   }
+#endif /* __U_BOOT__ */
 #endif
 #ifndef __U_BOOT__
/* peek_buf[] is an int array, not char. Can contain EOF. */
@@ -3163,7 +3181,6 @@ static void setup_file_in_str(struct in_str *i)
 #endif /* !__U_BOOT__ */
 }
 
-#ifndef __U_BOOT__
 static void setup_string_in_str(struct in_str *i, const char *s)
 {
memset(i, 0, sizeof(*i));
@@ -3171,7 +3188,6 @@ static void setup_string_in_str(struct in_str *i, const 
char *s)
i->p = s;
 }
 
-#endif /* !__U_BOOT__ */
 
 /*
  * o_string support
@@ -7910,7 +7926,11 @@ static int run_and_free_list(struct pipe *pi);
  * NUL: parse all, execute, return
  * ';': parse till ';' or newline, execute, repeat till EOF
  */
+#ifndef __U_BOOT__
 static void parse_and_run_stream(struct in_str *inp, int end_trigger)
+#else /* __U_BOOT__ */
+static int parse_and_run_stream(struct in_str *inp, int end_trigger)
+#endif /* __U_BOOT__ */
 {
/* Why we need empty flag?
 * An obscure corner case "false; ``; echo $?":
@@ -7919,7 +7939,11 @@ static void parse_and_run_stream(struct in_str *inp, int 
end_trigger)
 * this breaks "false; echo `echo $?`" case.
 */
bool empty = 1;
+#ifndef __U_BOOT__
while (1) {
+#else /* __U_BOOT__ */
+   do {
+#endif /* __U_BOOT__ */
struct pipe *pipe_list;
 
 #if ENABLE_HUSH_INTERACTIVE
@@ -7966,21 +7990,57 @@ static void parse_and_run_stream(struct in_str *inp, 
int end_trigger)
empty = 0;
if (G_flag_return_in_progress == 1)
break;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* This do/while is needed by run_command to avoid looping on a command
+* with syntax error.
+*/
+   } while (!(G.run_command_flags & FLAG_EXIT_FROM_LOOP));
+
+   return G.last_exitcode;
+#endif /* __U_BOOT__ */
 }
 
 #ifndef __U_BOOT__
 static void parse_and_run_string(const char *s)
+#else /* __U_BOOT__ */
+static int parse_and_run_string(const char *s)
+#endif /* __U_BOOT__ */
 {
struct in_str input;
//IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
 
setup_string_in_str(, s);
+#ifndef __U_BOOT__
parse_and_run_stream(, '\0');
+#else /* __U_BOOT__ */
+   return parse_and_run_stream(, '\0');
+#endif /* __U_BOOT__ */
//IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
 }
-#endif /* !__U_BOOT__ */
 
+#ifdef __U_BOOT__
+int parse_string_outer(const char *cmd, int flags)
+{
+   int ret;
+   int old_flags;
+
+   /*
+* Keep old values of run_command to be able to restore them once
+* command was executed.
+*/
+   old_flags = G.run_command_flags;
+   G.run_command_flags = flags;
+
+   ret = parse_and_run_string(cmd);
+
+   G.run_command_flags = old_flags;
+
+   return ret;
+}
+#endif /* __U_BOOT__ */
 #ifndef __U_BOOT__
 static void parse_and_run_file(HFILE *fp)
 #else /* __U_BOOT__ */
-- 
2.34.1



[PATCH v12 16/24] test: hush: Fix instructions list tests for modern hush

2023-12-08 Thread Francis Laniel
Modifies the expected result for modern hush.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As modern hush is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/list.c | 69 +---
 1 file changed, 65 insertions(+), 4 deletions(-)

diff --git a/test/hush/list.c b/test/hush/list.c
index 052cf2783c..73ae36e88f 100644
--- a/test/hush/list.c
+++ b/test/hush/list.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 
 static int hush_test_semicolon(struct unit_test_state *uts)
 {
@@ -46,12 +47,43 @@ static int hush_test_or(struct unit_test_state *uts)
 }
 HUSH_TEST(hush_test_or, 0);
 
+DECLARE_GLOBAL_DATA_PTR;
+
 static int hush_test_and_or(struct unit_test_state *uts)
 {
/* A && B || C truth table. */
ut_asserteq(1, run_command("false && false || false", 0));
-   ut_asserteq(1, run_command("false && false || true", 0));
-   ut_asserteq(1, run_command("false && true || true", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && false || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && false) || true
+* So, (false && false) returns 1, the second false is not
+* executed, and true is executed because of ||.
+*/
+   ut_assertok(run_command("false && false || true", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && true || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && true) || true
+* So, (false && true) returns 1, the true is not executed, and
+* true is executed because of ||.
+*/
+   ut_assertok(run_command("false && true || true", 0));
+   }
+
ut_asserteq(1, run_command("false && true || false", 0));
ut_assertok(run_command("true && true || false", 0));
ut_asserteq(1, run_command("true && false || false", 0));
@@ -69,8 +101,37 @@ static int hush_test_or_and(struct unit_test_state *uts)
ut_asserteq(1, run_command("false || false && true", 0));
ut_assertok(run_command("false || true && true", 0));
ut_asserteq(1, run_command("false || true && false", 0));
-   ut_assertok(run_command("true || true && false", 0));
-   ut_assertok(run_command("true || false && false", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || true && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || true) && false
+* So, (true || true) returns 0, the second true is not
+* executed, and then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || true && false", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || false && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_MODERN_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || false) && false
+* So, (true || false) returns 0, the false is not executed, and
+* then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || false && false", 0));
+   }
+
ut_assertok(run_command("true || false && true", 0));
ut_assertok(run_command("true || true && true", 0));
 
-- 
2.34.1



[PATCH v12 13/24] cli: hush_modern: Enable variables expansion for modern hush

2023-12-08 Thread Francis Laniel
Enables variables expansion for modern hush, both for local and environment
variables.
So the following commands:
foo=bar
echo $foo
setenv bar foo
echo $bar
leads to "bar" and "foo" being printed on console output.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_modern.c   | 17 +++
 common/cli_hush_upstream.c | 91 +++---
 2 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
index 626fed089b..15bb1f0d92 100644
--- a/common/cli_hush_modern.c
+++ b/common/cli_hush_modern.c
@@ -207,6 +207,23 @@ static const char* endofname(const char *name)
return name;
 }
 
+/**
+ * list_size() - returns the number of elements in char ** before NULL.
+ *
+ * Argument must contain NULL to signalize its end.
+ *
+ * @list The list to count the number of element.
+ * @return The number of element in list.
+ */
+static size_t list_size(char **list)
+{
+   size_t size;
+
+   for (size = 0; list[size] != NULL; size++);
+
+   return size;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 4b6ab20f3b..ff4a57c6e3 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -3485,7 +3485,6 @@ static int o_get_last_ptr(o_string *o, int n)
return ((int)(uintptr_t)list[n-1]) + string_start;
 }
 
-#ifndef __U_BOOT__
 /*
  * Globbing routines.
  *
@@ -3740,8 +3739,10 @@ static int glob_needed(const char *s)
  */
 static int perform_glob(o_string *o, int n)
 {
+#ifndef __U_BOOT__
glob_t globdata;
int gr;
+#endif /* __U_BOOT__ */
char *pattern;
 
debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
@@ -3750,13 +3751,16 @@ static int perform_glob(o_string *o, int n)
pattern = o->data + o_get_last_ptr(o, n);
debug_printf_glob("glob pattern '%s'\n", pattern);
if (!glob_needed(pattern)) {
+#ifndef __U_BOOT__
  literal:
+#endif /* __U_BOOT__ */
/* unbackslash last string in o in place, fix length */
o->length = unbackslash(pattern) - o->data;
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
return o_save_ptr_helper(o, n);
}
 
+#ifndef __U_BOOT__
memset(, 0, sizeof(globdata));
/* Can't use GLOB_NOCHECK: it does not unescape the string.
 * If we glob "*.\*" and don't find anything, we need
@@ -3792,16 +3796,22 @@ static int perform_glob(o_string *o, int n)
if (DEBUG_GLOB)
debug_print_list("perform_glob returning", o, n);
return n;
+#else /* __U_BOOT__ */
+   /*
+* NOTE We only use perform glob to call unbackslash to remove backslash
+* from string once expanded.
+* So, it seems OK to return this if no previous return was done.
+*/
+   return o_save_ptr_helper(o, n);
+#endif /* __U_BOOT__ */
 }
 
-#endif /* !__U_BOOT__ */
 #endif /* !HUSH_BRACE_EXPANSION */
 
 /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
  * Otherwise, just finish current list[] and start new */
 static int o_save_ptr(o_string *o, int n)
 {
-#ifndef __U_BOOT__
if (o->o_expflags & EXP_FLAG_GLOB) {
/* If o->has_empty_slot, list[n] was already globbed
 * (if it was requested back then when it was filled)
@@ -3809,7 +3819,6 @@ static int o_save_ptr(o_string *o, int n)
if (!o->has_empty_slot)
return perform_glob(o, n); /* o_save_ptr_helper is 
inside */
}
-#endif /* !__U_BOOT__ */
return o_save_ptr_helper(o, n);
 }
 
@@ -5455,7 +5464,20 @@ static int parse_dollar(o_string *as_string,
nommu_addchr(as_string, ch);
if (ch == '}')
break;
+#ifndef __U_BOOT__
if (!isalnum(ch) && ch != '_') {
+#else /* __U_BOOT__ */
+   /*
+* In several places in U-Boot, we use variable like
+* foo# (e.g. serial#), particularly in env.
+* So, we need to authorize # to appear inside
+* variable name and then expand this variable.
+* NOTE Having # in variable name is not permitted in
+* upstream hush but expansion will be done (even though
+* the result will be empty).
+*/
+   if (!isalnum(ch) && ch != '_' && ch != '#') {
+#endif /* __U_BOOT__ */
unsigned end_ch;
 #ifndef __U_BOOT__
unsigned char last_ch;
@@ -7030,7 +7052,20 @@ static NOINLINE int 

[PATCH v12 12/24] cli: Enables using modern hush parser as command line parser

2023-12-08 Thread Francis Laniel
If one defines HUSH_MODERN_PARSER, it is then possible to use modern parser 
with:
=> cli get
old
=> cli set modern
=> cli get
modern

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig   | 12 
 cmd/Makefile  |  2 +-
 cmd/cli.c | 28 ++---
 common/Makefile   |  1 +
 common/cli.c  | 38 +++
 common/cli_hush_modern.c  |  3 ++
 common/cli_hush_upstream.c| 46 +---
 doc/usage/cmd/cli.rst | 19 ++--
 include/asm-generic/global_data.h |  4 +++
 include/cli_hush.h| 51 +--
 10 files changed, 184 insertions(+), 20 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 30ea081a10..8b49a97aca 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -34,6 +34,18 @@ config HUSH_OLD_PARSER
  2005.
 
  It is actually the default U-Boot shell when decided to use hush as 
shell.
+
+config HUSH_MODERN_PARSER
+   bool "Use hush modern parser"
+   help
+ This option enables the new flavor of hush based on hush upstream
+ Busybox.
+
+ This parser is experimental and not well tested.
+
+config HUSH_SELECTABLE
+   bool
+   default y if HUSH_OLD_PARSER && HUSH_MODERN_PARSER
 endmenu
 
 config CMDLINE_EDITING
diff --git a/cmd/Makefile b/cmd/Makefile
index 620f69c1c5..e260c81405 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -227,7 +227,7 @@ obj-$(CONFIG_CMD_AVB) += avb.o
 # Foundries.IO SCP03
 obj-$(CONFIG_CMD_SCP03) += scp03.o
 
-obj-$(CONFIG_HUSH_PARSER) += cli.o
+obj-$(CONFIG_HUSH_SELECTABLE) += cli.o
 
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_RISCV) += riscv/
diff --git a/cmd/cli.c b/cmd/cli.c
index 86c6471aa4..b93cc952ed 100644
--- a/cmd/cli.c
+++ b/cmd/cli.c
@@ -12,6 +12,8 @@ static const char *gd_flags_to_parser_name(void)
 {
if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
return "old";
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER)
+   return "modern";
return NULL;
 }
 
@@ -34,18 +36,31 @@ static int parser_string_to_gd_flags(const char *parser)
 {
if (!strcmp(parser, "old"))
return GD_FLG_HUSH_OLD_PARSER;
+   if (!strcmp(parser, "modern"))
+   return GD_FLG_HUSH_MODERN_PARSER;
+   return -1;
+}
+
+static int gd_flags_to_parser_config(int flag)
+{
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   return CONFIG_VAL(HUSH_OLD_PARSER);
+   if (gd->flags & GD_FLG_HUSH_MODERN_PARSER)
+   return CONFIG_VAL(HUSH_MODERN_PARSER);
return -1;
 }
 
 static void reset_parser_gd_flags(void)
 {
gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+   gd->flags &= ~GD_FLG_HUSH_MODERN_PARSER;
 }
 
 static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
  char *const argv[])
 {
char *parser_name;
+   int parser_config;
int parser_flag;
 
if (argc < 2)
@@ -59,9 +74,14 @@ static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int 
argc,
return CMD_RET_USAGE;
}
 
-   if (parser_flag == GD_FLG_HUSH_OLD_PARSER &&
-   !CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) {
-   printf("Want to set current parser to old, but its code was not 
compiled!\n");
+   parser_config = gd_flags_to_parser_config(parser_flag);
+   switch (parser_config) {
+   case -1:
+   printf("Bad value for parser flags: %d\n", parser_flag);
+   return CMD_RET_FAILURE;
+   case 0:
+   printf("Want to set current parser to %s, but its code was not 
compiled!\n",
+   parser_name);
return CMD_RET_FAILURE;
}
 
@@ -102,7 +122,7 @@ static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
 #if CONFIG_IS_ENABLED(SYS_LONGHELP)
 static char cli_help_text[] =
"get - print current cli\n"
-   "set - set the current cli, possible value is: old"
+   "set - set the current cli, possible value are: old, modern"
;
 #endif
 
diff --git a/common/Makefile b/common/Makefile
index 23851a68e2..0d6075361a 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -9,6 +9,7 @@ obj-y += init/
 obj-y += main.o
 obj-y += exports.o
 obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_MODERN_PARSER) += cli_hush_modern.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 
 # # boards
diff --git a/common/cli.c b/common/cli.c
index d419671e8c..b3eb512b62 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -43,12 +43,15 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#else
+#elif CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
int hush_flags = FLAG_PARS

[PATCH v12 11/24] cmd: Add new cli command

2023-12-08 Thread Francis Laniel
This command can be used to print the current parser with 'cli get'.
It can also be used to set the current parser with 'cli set'.
For the moment, only one value is valid for set: old.

Signed-off-by: Francis Laniel 
---
 cmd/Makefile  |   2 +
 cmd/cli.c | 114 ++
 common/cli.c  |   3 +-
 doc/usage/cmd/cli.rst |  59 ++
 doc/usage/index.rst   |   1 +
 5 files changed, 178 insertions(+), 1 deletion(-)
 create mode 100644 cmd/cli.c
 create mode 100644 doc/usage/cmd/cli.rst

diff --git a/cmd/Makefile b/cmd/Makefile
index 9a6790cc17..620f69c1c5 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -227,6 +227,8 @@ obj-$(CONFIG_CMD_AVB) += avb.o
 # Foundries.IO SCP03
 obj-$(CONFIG_CMD_SCP03) += scp03.o
 
+obj-$(CONFIG_HUSH_PARSER) += cli.o
+
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_RISCV) += riscv/
 obj-$(CONFIG_SANDBOX) += sandbox/
diff --git a/cmd/cli.c b/cmd/cli.c
new file mode 100644
index 00..86c6471aa4
--- /dev/null
+++ b/cmd/cli.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *gd_flags_to_parser_name(void)
+{
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   return "old";
+   return NULL;
+}
+
+static int do_cli_get(struct cmd_tbl *cmdtp, int flag, int argc,
+char *const argv[])
+{
+   const char *current = gd_flags_to_parser_name();
+
+   if (!current) {
+   printf("current cli value is not valid, this should not 
happen!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   printf("%s\n", current);
+
+   return CMD_RET_SUCCESS;
+}
+
+static int parser_string_to_gd_flags(const char *parser)
+{
+   if (!strcmp(parser, "old"))
+   return GD_FLG_HUSH_OLD_PARSER;
+   return -1;
+}
+
+static void reset_parser_gd_flags(void)
+{
+   gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+}
+
+static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   char *parser_name;
+   int parser_flag;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   parser_name = argv[1];
+
+   parser_flag = parser_string_to_gd_flags(parser_name);
+   if (parser_flag == -1) {
+   printf("Bad value for parser name: %s\n", parser_name);
+   return CMD_RET_USAGE;
+   }
+
+   if (parser_flag == GD_FLG_HUSH_OLD_PARSER &&
+   !CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) {
+   printf("Want to set current parser to old, but its code was not 
compiled!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   reset_parser_gd_flags();
+   gd->flags |= parser_flag;
+
+   cli_init();
+   cli_loop();
+
+   /* cli_loop() should never return. */
+   return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl parser_sub[] = {
+   U_BOOT_CMD_MKENT(get, 1, 1, do_cli_get, "", ""),
+   U_BOOT_CMD_MKENT(set, 2, 1, do_cli_set, "", ""),
+};
+
+static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   struct cmd_tbl *cp;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   /* drop initial "parser" arg */
+   argc--;
+   argv++;
+
+   cp = find_cmd_tbl(argv[0], parser_sub, ARRAY_SIZE(parser_sub));
+   if (cp)
+   return cp->cmd(cmdtp, flag, argc, argv);
+
+   return CMD_RET_USAGE;
+}
+
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+static char cli_help_text[] =
+   "get - print current cli\n"
+   "set - set the current cli, possible value is: old"
+   ;
+#endif
+
+U_BOOT_CMD(cli, 3, 1, do_cli,
+  "cli",
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+  cli_help_text
+#endif
+);
diff --git a/common/cli.c b/common/cli.c
index e5fe1060d0..d419671e8c 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,7 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
-   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   && CONFIG_IS_ENABLED(HUSH_OLD_PARSER))
gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
diff --git a/doc/usage/cmd/cli.rst b/doc/usage/cmd/cli.rst
new file mode 100644
index 00..89ece3203d
--- /dev/null
+++ b/doc/usage/cmd/cli.rst
@@ -0,0 +1,59 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+cli command
+===
+
+Synopis
+---
+
+::
+
+cli get
+cli set cli_flavor
+
+Description
+---
+
+The cli command permits getting and changing the current parser at runtime.
+
+cli get
+~~~
+
+It shows the current value of the parser used by the CLI.
+
+cli se

[PATCH v12 10/24] global_data.h: add GD_FLG_HUSH_OLD_PARSER flag

2023-12-08 Thread Francis Laniel
This flag is used to indicate we are using the hush parser.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c  | 2 ++
 include/asm-generic/global_data.h | 4 
 2 files changed, 6 insertions(+)

diff --git a/common/cli.c b/common/cli.c
index 3916a7b10a..e5fe1060d0 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,6 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
 
diff --git a/include/asm-generic/global_data.h 
b/include/asm-generic/global_data.h
index e8c6412e3f..0a9b6bd92a 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -697,6 +697,10 @@ enum gd_flags {
 * @GD_FLG_BLOBLIST_READY: bloblist is ready for use
 */
GD_FLG_BLOBLIST_READY = 0x80,
+   /**
+* @GD_FLG_HUSH_OLD_PARSER: Use hush old parser.
+*/
+   GD_FLG_HUSH_OLD_PARSER = 0x100,
 };
 
 #endif /* __ASSEMBLY__ */
-- 
2.34.1



[PATCH v12 09/24] cli: Add menu for hush parser

2023-12-08 Thread Francis Laniel
For the moment, the menu contains only entry: HUSH_OLD_PARSER which is the
default.
The goal is to prepare the field to add a new hush parser which guarantees
actual behavior is still correct.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig | 13 +
 common/Makefile |  2 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index df6d71c103..30ea081a10 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -23,6 +23,19 @@ config HUSH_PARSER
  If disabled, you get the old, much simpler behaviour with a somewhat
  smaller memory footprint.
 
+menu "Hush flavor to use"
+depends on HUSH_PARSER
+
+config HUSH_OLD_PARSER
+   bool "Use hush old parser"
+   default y
+   help
+ This option enables the old flavor of hush based on hush Busybox from
+ 2005.
+
+ It is actually the default U-Boot shell when decided to use hush as 
shell.
+endmenu
+
 config CMDLINE_EDITING
bool "Enable command line editing"
depends on CMDLINE
diff --git a/common/Makefile b/common/Makefile
index cdeadf7202..23851a68e2 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -8,7 +8,7 @@ ifndef CONFIG_SPL_BUILD
 obj-y += init/
 obj-y += main.o
 obj-y += exports.o
-obj-$(CONFIG_HUSH_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 
 # # boards
-- 
2.34.1



[PATCH v12 08/24] cli: Port upstream Busybox hush to U-Boot

2023-12-08 Thread Francis Laniel
Adds new file cli_hush_upstream.c, it is a copy of Busybox hush file as it was 
of
time to commit 37460f5da.
This commit modifies Busybox hush to not compile some part specific to Busybox
and adds some code needed by U-Boot.
The modifications consists mainly on adding code #if(n)def guards.

For the moment, this refurbished flavor of hush only permits running command
without any keywords (i.e., if and for are not recognized) or variable expansion
(i.e., echo $foo prints foo and not value stored in variable foo).

A new file was also added to define some functions specific to U-Boot.

Signed-off-by: Francis Laniel 
Signed-off-by: Harald Seiler 
---
 common/cli_hush_modern.c   | 274 
 common/cli_hush_upstream.c | 502 -
 2 files changed, 774 insertions(+), 2 deletions(-)
 create mode 100644 common/cli_hush_modern.c

diff --git a/common/cli_hush_modern.c b/common/cli_hush_modern.c
new file mode 100644
index 00..34278fdca2
--- /dev/null
+++ b/common/cli_hush_modern.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file defines the compilation unit for the new hush shell version.  The
+ * actual implementation from upstream BusyBox can be found in
+ * `cli_hush_upstream.c` which is included at the end of this file.
+ *
+ * This "wrapper" technique is used to keep the changes to the upstream version
+ * as minmal as possible.  Instead, all defines and redefines necessary are 
done
+ * here, outside the upstream sources.  This will hopefully make upgrades to
+ * newer revisions much easier.
+ *
+ * Copyright (c) 2021, Harald Seiler, DENX Software Engineering, h...@denx.de
+ */
+
+#include  /* readline */
+#include 
+#include  /* malloc, free, realloc*/
+#include /* isalpha, isdigit */
+#include 
+#include 
+#include 
+#include 
+#include /* find_cmd */
+#include 
+
+/*
+ * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
+ */
+#define BB_VER "1.34.0.git37460f5daff9"
+
+/*
+ * Define hush features by the names used upstream.
+ */
+#define ENABLE_HUSH_INTERACTIVE1
+#define ENABLE_FEATURE_EDITING 1
+/* No MMU in U-Boot */
+#define BB_MMU 0
+#define USE_FOR_NOMMU(...) __VA_ARGS__
+#define USE_FOR_MMU(...)
+
+/*
+ * Size-saving "small" ints (arch-dependent)
+ */
+#if CONFIG_IS_ENABLED(X86) || CONFIG_IS_ENABLED(X86_64) || 
CONFIG_IS_ENABLED(MIPS)
+/* add other arches which benefit from this... */
+typedef signed char smallint;
+typedef unsigned char smalluint;
+#else
+/* for arches where byte accesses generate larger code: */
+typedef int smallint;
+typedef unsigned smalluint;
+#endif
+
+/*
+ * Alignment defines used by BusyBox.
+ */
+#define ALIGN1 __attribute__((aligned(1)))
+#define ALIGN2 __attribute__((aligned(2)))
+#define ALIGN4 __attribute__((aligned(4)))
+#define ALIGN8 __attribute__((aligned(8)))
+#define ALIGN_PTR  __attribute__((aligned(sizeof(void*
+
+/*
+ * Miscellaneous compiler/platform defines.
+ */
+#define FAST_FUNC /* not used in U-Boot */
+#define UNUSED_PARAM   __always_unused
+#define ALWAYS_INLINE  __always_inline
+#define NOINLINE   noinline
+
+/*
+ * Defines to provide equivalents to what libc/BusyBox defines.
+ */
+#define EOF(-1)
+#define EXIT_SUCCESS   0
+#define EXIT_FAILURE   1
+
+/*
+ * Stubs to provide libc/BusyBox functions based on U-Boot equivalents where it
+ * makes sense.
+ */
+#define utoa   simple_itoa
+
+static void __noreturn xfunc_die(void)
+{
+   panic("HUSH died!");
+}
+
+#define bb_error_msg_and_die(format, ...) do { \
+panic("HUSH: " format, __VA_ARGS__); \
+} while (0);
+
+#define bb_simple_error_msg_and_die(msg) do { \
+panic_str("HUSH: " msg); \
+} while (0);
+
+/* fdprintf() is used for debug output. */
+static int __maybe_unused fdprintf(int fd, const char *format, ...)
+{
+   va_list args;
+   uint i;
+
+   assert(fd == 2);
+
+   va_start(args, format);
+   i = vprintf(format, args);
+   va_end(args);
+
+   return i;
+}
+
+static void bb_verror_msg(const char *s, va_list p, const char* strerr)
+{
+   /* TODO: what to do with strerr arg? */
+   vprintf(s, p);
+}
+
+static void bb_error_msg(const char *s, ...)
+{
+   va_list p;
+
+   va_start(p, s);
+   bb_verror_msg(s, p, NULL);
+   va_end(p);
+}
+
+static void *xmalloc(size_t size)
+{
+   void *p = NULL;
+   if (!(p = malloc(size)))
+   panic("out of memory");
+   return p;
+}
+
+static void *xzalloc(size_t size)
+{
+   void *p = xmalloc(size);
+   memset(p, 0, size);
+   return p;
+}
+
+static void *xrealloc(void *ptr, size_t size)
+{
+   void *p = NULL;
+   if (!(p = realloc(ptr, size)))
+   panic("out

[PATCH v12 06/24] test: hush: Test hush loops

2023-12-08 Thread Francis Laniel
The added tests verifies correct behavior of for, while and until loops.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/loop.c   | 65 ++
 2 files changed, 66 insertions(+)
 create mode 100644 test/hush/loop.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index ff4fe7700b..a2d98815e5 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -7,3 +7,4 @@ obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
 obj-y += list.o
+obj-y += loop.o
diff --git a/test/hush/loop.c b/test/hush/loop.c
new file mode 100644
index 00..ca777e38fe
--- /dev/null
+++ b/test/hush/loop.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_for(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   ut_assertok(run_command("for loop_i in foo bar quux quux; do echo 
$loop_i; done", 0));
+   ut_assert_nextline("foo");
+   ut_assert_nextline("bar");
+   ut_assert_nextline("quux");
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_for, 0);
+
+static int hush_test_while(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_while, 0);
+
+static int hush_test_until(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+   env_set("loop_bar", "bar");
+
+   /*
+* WARNING We have to use environment variable because it is not 
possible
+* resetting local variable.
+*/
+   ut_assertok(run_command("until test -z \"$loop_bar\"; do echo quux; 
setenv loop_bar; done", 0));
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   /*
+* Loop normally resets foo environment variable, but we reset it here 
in
+* case the test failed.
+*/
+   env_set("loop_bar", NULL);
+   return 0;
+}
+HUSH_TEST(hush_test_until, 0);
-- 
2.34.1



[PATCH v12 05/24] test: hush: Test hush commands list

2023-12-08 Thread Francis Laniel
Verifies behavior of commands separated by ';', '&&' and '||'.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/list.c   | 79 ++
 2 files changed, 80 insertions(+)
 create mode 100644 test/hush/list.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index feb4f71956..ff4fe7700b 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -6,3 +6,4 @@
 obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
+obj-y += list.o
diff --git a/test/hush/list.c b/test/hush/list.c
new file mode 100644
index 00..052cf2783c
--- /dev/null
+++ b/test/hush/list.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_semicolon(struct unit_test_state *uts)
+{
+   /* A; B = B truth table. */
+   ut_asserteq(1, run_command("false; false", 0));
+   ut_assertok(run_command("false; true", 0));
+   ut_assertok(run_command("true; true", 0));
+   ut_asserteq(1, run_command("true; false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_semicolon, 0);
+
+static int hush_test_and(struct unit_test_state *uts)
+{
+   /* A && B truth table. */
+   ut_asserteq(1, run_command("false && false", 0));
+   ut_asserteq(1, run_command("false && true", 0));
+   ut_assertok(run_command("true && true", 0));
+   ut_asserteq(1, run_command("true && false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and, 0);
+
+static int hush_test_or(struct unit_test_state *uts)
+{
+   /* A || B truth table. */
+   ut_asserteq(1, run_command("false || false", 0));
+   ut_assertok(run_command("false || true", 0));
+   ut_assertok(run_command("true || true", 0));
+   ut_assertok(run_command("true || false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or, 0);
+
+static int hush_test_and_or(struct unit_test_state *uts)
+{
+   /* A && B || C truth table. */
+   ut_asserteq(1, run_command("false && false || false", 0));
+   ut_asserteq(1, run_command("false && false || true", 0));
+   ut_asserteq(1, run_command("false && true || true", 0));
+   ut_asserteq(1, run_command("false && true || false", 0));
+   ut_assertok(run_command("true && true || false", 0));
+   ut_asserteq(1, run_command("true && false || false", 0));
+   ut_assertok(run_command("true && false || true", 0));
+   ut_assertok(run_command("true && true || true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and_or, 0);
+
+static int hush_test_or_and(struct unit_test_state *uts)
+{
+   /* A || B && C truth table. */
+   ut_asserteq(1, run_command("false || false && false", 0));
+   ut_asserteq(1, run_command("false || false && true", 0));
+   ut_assertok(run_command("false || true && true", 0));
+   ut_asserteq(1, run_command("false || true && false", 0));
+   ut_assertok(run_command("true || true && false", 0));
+   ut_assertok(run_command("true || false && false", 0));
+   ut_assertok(run_command("true || false && true", 0));
+   ut_assertok(run_command("true || true && true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or_and, 0);
-- 
2.34.1



[PATCH v12 04/24] test: hush: Test hush variable expansion

2023-12-08 Thread Francis Laniel
Verifies shell variables are replaced by their values.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/Makefile   |   1 +
 test/hush/dollar.c   | 167 +++
 test/py/tests/test_ut.py |   8 +-
 3 files changed, 175 insertions(+), 1 deletion(-)
 create mode 100644 test/hush/dollar.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index a3c9ae5106..feb4f71956 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -5,3 +5,4 @@
 
 obj-y += cmd_ut_hush.o
 obj-y += if.o
+obj-y += dollar.o
diff --git a/test/hush/dollar.c b/test/hush/dollar.c
new file mode 100644
index 00..defb2c3fd0
--- /dev/null
+++ b/test/hush/dollar.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_simple_dollar(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline_empty();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${dollar_foo}", 0));
+   ut_assert_nextline_empty();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo=bar", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${dollar_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo=\\$bar", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("$bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='$bar'", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("$bar");
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo=bar quux", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
+   /* Two next lines contain error message */
+   ut_assert_skipline();
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   /*
+* This one is buggy.
+* ut_assert_nextline("bar \"quux");
+* ut_assert_console_end();
+*
+* So, let's reset output:
+*/
+   console_record_reset_enable();
+
+   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='bar quux'", 0));
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar quux");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable dollar_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_simple_dollar, 0);
+
+static int hush_test_env_dollar(struct unit_test_state *uts)
+{
+   env_set("env_foo", "bar");
+   console_record_reset_enable();
+
+   ut_assertok(run_command("echo $env_foo", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${env_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   /* Environment variables have priority over local variable */
+   ut_assertok(run_command("env_foo=quux", 0));
+   ut_assertok(run_command("echo ${env_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   /* Clean up setting the variable */
+   env_set("env_foo", NULL);
+
+   puts("Beware: this test set local variable env_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_env_dollar, 0);
+
+static int hush_test_command_dollar(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   ut_assertok(run_command("dollar_bar=\"echo bar\"", 0));
+
+   ut_assertok(run_command("$dollar_bar", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end(

[PATCH v12 03/24] test/py: hush_if_test: Remove the test file

2023-12-08 Thread Francis Laniel
5804ebfeb1ce ("test: hush: Test hush if/else") translated this test to a C test,
so this python file is no more needed.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/py/tests/test_hush_if_test.py | 197 -
 1 file changed, 197 deletions(-)
 delete mode 100644 test/py/tests/test_hush_if_test.py

diff --git a/test/py/tests/test_hush_if_test.py 
b/test/py/tests/test_hush_if_test.py
deleted file mode 100644
index 3b4b6fcaf4..00
--- a/test/py/tests/test_hush_if_test.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
-
-# Test operation of the "if" shell command.
-
-import os
-import os.path
-import pytest
-
-# TODO: These tests should be converted to a C test.
-# For more information please take a look at the thread
-# https://lists.denx.de/pipermail/u-boot/2019-October/388732.html
-
-pytestmark = pytest.mark.buildconfigspec('hush_parser')
-
-# The list of "if test" conditions to test.
-subtests = (
-# Base if functionality.
-
-('true', True),
-('false', False),
-
-# Basic operators.
-
-('test aaa = aaa', True),
-('test aaa = bbb', False),
-
-('test aaa != bbb', True),
-('test aaa != aaa', False),
-
-('test aaa < bbb', True),
-('test bbb < aaa', False),
-
-('test bbb > aaa', True),
-('test aaa > bbb', False),
-
-('test 123 -eq 123', True),
-('test 123 -eq 456', False),
-
-('test 123 -ne 456', True),
-('test 123 -ne 123', False),
-
-('test 123 -lt 456', True),
-('test 123 -lt 123', False),
-('test 456 -lt 123', False),
-
-('test 123 -le 456', True),
-('test 123 -le 123', True),
-('test 456 -le 123', False),
-
-('test 456 -gt 123', True),
-('test 123 -gt 123', False),
-('test 123 -gt 456', False),
-
-('test 456 -ge 123', True),
-('test 123 -ge 123', True),
-('test 123 -ge 456', False),
-
-# Octal tests
-
-('test 010 -eq 010', True),
-('test 010 -eq 011', False),
-
-('test 010 -ne 011', True),
-('test 010 -ne 010', False),
-
-# Hexadecimal tests
-
-('test 0x200 -gt 0x201', False),
-('test 0x200 -gt 0x200', False),
-('test 0x200 -gt 0x1ff', True),
-
-# Mixed tests
-
-('test 010 -eq 10', False),
-('test 010 -ne 10', True),
-('test 0xa -eq 10', True),
-('test 0xa -eq 012', True),
-
-('test 200 -gt 0x1ff', False),
-('test 0x200 -gt 1ff', True),
-('test 0x200 -lt 1ff', False),
-('test 0x200 -eq 200', False),
-('test 0x200 -ne 200', True),
-
-('test -z ""', True),
-('test -z "aaa"', False),
-
-('test -n "aaa"', True),
-('test -n ""', False),
-
-# Inversion of simple tests.
-
-('test ! aaa = aaa', False),
-('test ! aaa = bbb', True),
-('test ! ! aaa = aaa', True),
-('test ! ! aaa = bbb', False),
-
-# Binary operators.
-
-('test aaa != aaa -o bbb != bbb', False),
-('test aaa != aaa -o bbb = bbb', True),
-('test aaa = aaa -o bbb != bbb', True),
-('test aaa = aaa -o bbb = bbb', True),
-
-('test aaa != aaa -a bbb != bbb', False),
-('test aaa != aaa -a bbb = bbb', False),
-('test aaa = aaa -a bbb != bbb', False),
-('test aaa = aaa -a bbb = bbb', True),
-
-# Inversion within binary operators.
-
-('test ! aaa != aaa -o ! bbb != bbb', True),
-('test ! aaa != aaa -o ! bbb = bbb', True),
-('test ! aaa = aaa -o ! bbb != bbb', True),
-('test ! aaa = aaa -o ! bbb = bbb', False),
-
-('test ! ! aaa != aaa -o ! ! bbb != bbb', False),
-('test ! ! aaa != aaa -o ! ! bbb = bbb', True),
-('test ! ! aaa = aaa -o ! ! bbb != bbb', True),
-('test ! ! aaa = aaa -o ! ! bbb = bbb', True),
-)
-
-def exec_hush_if(u_boot_console, expr, result):
-"""Execute a shell "if" command, and validate its result."""
-
-config = u_boot_console.config.buildconfig
-maxargs = int(config.get('config_sys_maxargs', '0'))
-args = len(expr.split(' ')) - 1
-if args > maxargs:
-u_boot_console.log.warning('CONFIG_SYS_MAXARGS too low; need ' +
-str(args))
-pytest.skip()
-
-cmd = 'if ' + expr + '; then echo true; else echo false; fi'
-response = u_boot_console.run_command(cmd)
-assert response.strip() == str(result).lower()
-
-@pytest.mark.buildconfigspec('cmd_echo')
-@pytest.mark.parametrize('expr,result', subtests)
-def test_hush_if_test(u_boot_console, expr, result):
-"""Test a single "if test" condition."""
-
-exec_hush_if(u_boot_console, expr, result)
-
-def test_hush_z(u_boot_console):
-"""Test the -z operator"""
-u_boot_console.run_command('setenv ut_var_nonexistent')
-u_boot_console.run_command('set

[PATCH v12 02/24] test: hush: Test hush if/else

2023-12-08 Thread Francis Laniel
As asked in commit 9c6bf1715f6a ("test/py: hush_if_test: Add tests to cover
octal/hex values"), this commit translates test_hush_if_test.py to a C test.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |   1 +
 test/hush/if.c | 316 +
 2 files changed, 317 insertions(+)
 create mode 100644 test/hush/if.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index dfa2a92615..a3c9ae5106 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -4,3 +4,4 @@
 # Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
 
 obj-y += cmd_ut_hush.o
+obj-y += if.o
diff --git a/test/hush/if.c b/test/hush/if.c
new file mode 100644
index 00..b7200e32ec
--- /dev/null
+++ b/test/hush/if.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * All tests will execute the following:
+ * if condition_to_test; then
+ *   true
+ * else
+ *   false
+ * fi
+ * If condition is true, command returns 1, 0 otherwise.
+ */
+const char *if_format = "if %s; then true; else false; fi";
+
+static int hush_test_if_base(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_format, "true");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "false");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_if_base, 0);
+
+static int hush_test_if_basic_operators(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_format, "test aaa = aaa");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa = bbb");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa != bbb");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa != aaa");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa < bbb");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test bbb < aaa");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test bbb > aaa");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa > bbb");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -eq 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -eq 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ne 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ne 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -lt 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -lt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -lt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -le 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -le 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -le 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -gt 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -gt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -gt 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -ge 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ge 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ge 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_if_basic_operators, 0);
+
+static int hush_test_if_octal(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_f

[PATCH v12 01/24] test: Add framework to test hush behavior

2023-12-08 Thread Francis Laniel
Introduce a new subcommand to ut: ut hush.
For the moment, this command does nothing, future commits will add tests which
will be run on command call.

Note that CONFIG_HUSH_PARSER must be defined to compile this new subcommand.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 include/test/hush.h | 15 +++
 include/test/suites.h   |  1 +
 test/Makefile   |  3 +++
 test/cmd_ut.c   |  6 ++
 test/hush/Makefile  |  6 ++
 test/hush/cmd_ut_hush.c | 20 
 6 files changed, 51 insertions(+)
 create mode 100644 include/test/hush.h
 create mode 100644 test/hush/Makefile
 create mode 100644 test/hush/cmd_ut_hush.c

diff --git a/include/test/hush.h b/include/test/hush.h
new file mode 100644
index 00..cca66544a0
--- /dev/null
+++ b/include/test/hush.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#ifndef __TEST_HUSH_H__
+#define __TEST_HUSH_H__
+
+#include 
+
+/* Declare a new environment test */
+#define HUSH_TEST(_name, _flags)   UNIT_TEST(_name, _flags, hush_test)
+
+#endif /* __TEST_HUSH_H__ */
diff --git a/include/test/suites.h b/include/test/suites.h
index ad4fc926f4..fc0f9334c0 100644
--- a/include/test/suites.h
+++ b/include/test/suites.h
@@ -42,6 +42,7 @@ int do_ut_env(struct cmd_tbl *cmdtp, int flag, int argc, char 
*const argv[]);
 int do_ut_exit(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_font(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+int do_ut_hush(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_lib(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_loadm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_log(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
diff --git a/test/Makefile b/test/Makefile
index 8e1fed2c28..10cf6a7976 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -17,6 +17,9 @@ obj-$(CONFIG_FUZZ) += fuzz/
 ifndef CONFIG_SANDBOX_VPL
 obj-$(CONFIG_UNIT_TEST) += lib/
 endif
+ifneq ($(CONFIG_HUSH_PARSER),)
+obj-$(CONFIG_$(SPL_)CMDLINE) += hush/
+endif
 obj-$(CONFIG_$(SPL_)CMDLINE) += print_ut.o
 obj-$(CONFIG_$(SPL_)CMDLINE) += str_ut.o
 obj-$(CONFIG_UT_TIME) += time_ut.o
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 2d5b80f992..2f96dfa934 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -118,6 +118,9 @@ static struct cmd_tbl cmd_ut_sub[] = {
 #ifdef CONFIG_CMD_ADDRMAP
U_BOOT_CMD_MKENT(addrmap, CONFIG_SYS_MAXARGS, 1, do_ut_addrmap, "", ""),
 #endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+   U_BOOT_CMD_MKENT(hush, CONFIG_SYS_MAXARGS, 1, do_ut_hush, "", ""),
+#endif
 #ifdef CONFIG_CMD_LOADM
U_BOOT_CMD_MKENT(loadm, CONFIG_SYS_MAXARGS, 1, do_ut_loadm, "", ""),
 #endif
@@ -210,6 +213,9 @@ U_BOOT_LONGHELP(ut,
 #ifdef CONFIG_CONSOLE_TRUETYPE
"\nfont - font command"
 #endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+   "\nhush - Test hush behavior"
+#endif
 #ifdef CONFIG_CMD_LOADM
"\nloadm - loadm command parameters and loading memory blob"
 #endif
diff --git a/test/hush/Makefile b/test/hush/Makefile
new file mode 100644
index 00..dfa2a92615
--- /dev/null
+++ b/test/hush/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2021
+# Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+
+obj-y += cmd_ut_hush.o
diff --git a/test/hush/cmd_ut_hush.c b/test/hush/cmd_ut_hush.c
new file mode 100644
index 00..48a1adbf28
--- /dev/null
+++ b/test/hush/cmd_ut_hush.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+int do_ut_hush(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+   struct unit_test *tests = UNIT_TEST_SUITE_START(hush_test);
+   const int n_ents = UNIT_TEST_SUITE_COUNT(hush_test);
+
+   return cmd_ut_category("hush", "hush_test_",
+  tests, n_ents, argc, argv);
+}
-- 
2.34.1



[PATCH v12 00/24] Modernize U-Boot shell

2023-12-08 Thread Francis Laniel
tream busybox hush commits until 2nd October 2023.
 v10:
  * Fixed a build error in commit adding cli command.
  * Add new CONFIG_HUSH_SELECTABLE to only build cli command if the two shell
  flavors are selected.
 v11:
  * Renamed everything containing 2021 to modern.
  * Fixed cmd Kconfig indentation.
  * Set modern parser as default for majority of boards except some.

Francis Laniel (24):
  test: Add framework to test hush behavior
  test: hush: Test hush if/else
  test/py: hush_if_test: Remove the test file
  test: hush: Test hush variable expansion
  test: hush: Test hush commands list
  test: hush: Test hush loops
  cli: Add Busybox upstream hush.c file
  cli: Port upstream Busybox hush to U-Boot
  cli: Add menu for hush parser
  global_data.h: add GD_FLG_HUSH_OLD_PARSER flag
  cmd: Add new cli command
  cli: Enables using modern hush parser as command line parser
  cli: hush_modern: Enable variables expansion for modern hush
  cli: hush_modern: Add functions to be called from run_command()
  cli: add modern hush as parser for run_command*()
  test: hush: Fix instructions list tests for modern hush
  test: hush: Fix variable expansion tests for modern hush
  cli: hush_modern: Enable using < and > as string compare operators
  cli: hush_modern: Enable if keyword
  cli: hush_modern: Enable loops
  test: hush: Fix loop tests for modern hush
  cli: modern_hush: Add upstream commits up to 2nd October 2023.
  cmd: Set modern hush as default shell
  configs: Use old hush for several boards

 cmd/Kconfig|21 +
 cmd/Makefile   | 2 +
 cmd/cli.c  |   134 +
 common/Makefile| 3 +-
 common/cli.c   |82 +-
 common/cli_hush_modern.c   |   324 +
 common/cli_hush_upstream.c | 13030 +++
 configs/kmcent2_defconfig  | 1 +
 configs/kmcoge5ne_defconfig| 1 +
 configs/kmeter1_defconfig  | 1 +
 configs/kmopti2_defconfig  | 1 +
 configs/kmsupx5_defconfig  | 1 +
 configs/kmtepr2_defconfig  | 1 +
 configs/pg_wcom_expu1_defconfig| 1 +
 configs/pg_wcom_expu1_update_defconfig | 1 +
 configs/pg_wcom_seli8_defconfig| 1 +
 configs/pg_wcom_seli8_update_defconfig | 1 +
 configs/socfpga_secu1_defconfig| 1 +
 configs/tuge1_defconfig| 1 +
 configs/tuxx1_defconfig| 1 +
 doc/usage/cmd/cli.rst  |74 +
 doc/usage/index.rst| 1 +
 include/asm-generic/global_data.h  | 8 +
 include/cli_hush.h |51 +-
 include/test/hush.h|15 +
 include/test/suites.h  | 1 +
 test/Makefile  | 3 +
 test/cmd_ut.c  | 6 +
 test/hush/Makefile |10 +
 test/hush/cmd_ut_hush.c|20 +
 test/hush/dollar.c |   226 +
 test/hush/if.c |   316 +
 test/hush/list.c   |   140 +
 test/hush/loop.c   |91 +
 test/py/tests/test_hush_if_test.py |   197 -
 test/py/tests/test_ut.py   | 8 +-
 36 files changed, 14565 insertions(+), 211 deletions(-)
 create mode 100644 cmd/cli.c
 create mode 100644 common/cli_hush_modern.c
 create mode 100644 common/cli_hush_upstream.c
 create mode 100644 doc/usage/cmd/cli.rst
 create mode 100644 include/test/hush.h
 create mode 100644 test/hush/Makefile
 create mode 100644 test/hush/cmd_ut_hush.c
 create mode 100644 test/hush/dollar.c
 create mode 100644 test/hush/if.c
 create mode 100644 test/hush/list.c
 create mode 100644 test/hush/loop.c
 delete mode 100644 test/py/tests/test_hush_if_test.py


Best regards and thank you in advance.
---
[1] https://lists.denx.de/pipermail/u-boot/2021-July/453347.html
[2] https://runtimeterror.com/tech/lil/
[3] https://lists.denx.de/pipermail/u-boot/2021-July/453790.html
[4] https://lists.denx.de/pipermail/u-boot/2021-July/453848.html
[5] https://lists.denx.de/pipermail/u-boot/2021-August/458569.html
[6] https://lists.denx.de/pipermail/u-boot/2023-November/536768.html
[7] https://lists.denx.de/pipermail/u-boot/2023-May/518140.html
[8] https://lists.denx.de/pipermail/u-boot/2023-May/518147.html
[9] https://lists.denx.de/pipermail/u-boot/2023-November/536952.html
--
2.34.1



[PATCH v11 12/24] cli: Enables using hush 2021 parser as command line parser

2023-11-07 Thread Francis Laniel
If one defines HUSH_2021_PARSER, it is then possible to use 2021 parser with:
=> cli get
old
=> cli set 2021
=> cli get
2021

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig   | 12 
 cmd/Makefile  |  2 +-
 cmd/cli.c | 28 ++---
 common/Makefile   |  1 +
 common/cli.c  | 38 +++
 common/cli_hush_2021.c|  3 ++
 common/cli_hush_upstream.c| 46 +---
 doc/usage/cmd/cli.rst | 17 ++-
 include/asm-generic/global_data.h |  4 +++
 include/cli_hush.h| 51 +--
 10 files changed, 183 insertions(+), 19 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index f3a00ee59b..4641333809 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -34,6 +34,18 @@ menu "Hush flavor to use"
  2005.
 
  It is actually the default U-Boot shell when decided to use 
hush as shell.
+
+   config HUSH_2021_PARSER
+   bool "Use hush 2021 parser"
+   help
+ This option enables the new flavor of hush based on hush 
Busybox from
+ 2021.
+
+ This parser is experimental and not well tested.
+
+   config HUSH_SELECTABLE
+   bool
+   default y if HUSH_OLD_PARSER && HUSH_2021_PARSER
 endmenu
 
 config CMDLINE_EDITING
diff --git a/cmd/Makefile b/cmd/Makefile
index 620f69c1c5..e260c81405 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -227,7 +227,7 @@ obj-$(CONFIG_CMD_AVB) += avb.o
 # Foundries.IO SCP03
 obj-$(CONFIG_CMD_SCP03) += scp03.o
 
-obj-$(CONFIG_HUSH_PARSER) += cli.o
+obj-$(CONFIG_HUSH_SELECTABLE) += cli.o
 
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_RISCV) += riscv/
diff --git a/cmd/cli.c b/cmd/cli.c
index 86c6471aa4..7da196186a 100644
--- a/cmd/cli.c
+++ b/cmd/cli.c
@@ -12,6 +12,8 @@ static const char *gd_flags_to_parser_name(void)
 {
if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
return "old";
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER)
+   return "2021";
return NULL;
 }
 
@@ -34,18 +36,31 @@ static int parser_string_to_gd_flags(const char *parser)
 {
if (!strcmp(parser, "old"))
return GD_FLG_HUSH_OLD_PARSER;
+   if (!strcmp(parser, "2021"))
+   return GD_FLG_HUSH_2021_PARSER;
+   return -1;
+}
+
+static int gd_flags_to_parser_config(int flag)
+{
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   return CONFIG_VAL(HUSH_OLD_PARSER);
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER)
+   return CONFIG_VAL(HUSH_2021_PARSER);
return -1;
 }
 
 static void reset_parser_gd_flags(void)
 {
gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+   gd->flags &= ~GD_FLG_HUSH_2021_PARSER;
 }
 
 static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
  char *const argv[])
 {
char *parser_name;
+   int parser_config;
int parser_flag;
 
if (argc < 2)
@@ -59,9 +74,14 @@ static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int 
argc,
return CMD_RET_USAGE;
}
 
-   if (parser_flag == GD_FLG_HUSH_OLD_PARSER &&
-   !CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) {
-   printf("Want to set current parser to old, but its code was not 
compiled!\n");
+   parser_config = gd_flags_to_parser_config(parser_flag);
+   switch (parser_config) {
+   case -1:
+   printf("Bad value for parser flags: %d\n", parser_flag);
+   return CMD_RET_FAILURE;
+   case 0:
+   printf("Want to set current parser to %s, but its code was not 
compiled!\n",
+   parser_name);
return CMD_RET_FAILURE;
}
 
@@ -102,7 +122,7 @@ static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
 #if CONFIG_IS_ENABLED(SYS_LONGHELP)
 static char cli_help_text[] =
"get - print current cli\n"
-   "set - set the current cli, possible value is: old"
+   "set - set the current cli, possible value are: old, 2021"
;
 #endif
 
diff --git a/common/Makefile b/common/Makefile
index 23851a68e2..b487421133 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -9,6 +9,7 @@ obj-y += init/
 obj-y += main.o
 obj-y += exports.o
 obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_2021_PARSER) += cli_hush_2021.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 
 # # boards
diff --git a/common/cli.c b/common/cli.c
index d419671e8c..e3e2bc7fe1 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -43,12 +43,15 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#else
+#elif CONFIG_IS_ENABL

Re: [RFC PATCH v10 00/24] Modernize U-Boot shell

2023-11-07 Thread Francis Laniel
Hi!


Le lundi 9 octobre 2023, 20:56:30 EET Simon Glass a écrit :
> Hi Francis,
> 
> On Wed, 4 Oct 2023 at 10:42, Francis Laniel <
> 
> francis.lan...@amarulasolutions.com> wrote:
> > Hi.
> > 
> > 
> > During 2021 summer, Sean Anderson wrote a contribution to add a new
> 
> shell, based
> 
> > on LIL, to U-Boot [1, 2].
> > While one of the goals of this contribution was to address the fact actual
> > U-Boot shell, which is based on Busybox hush, is old there was a
> 
> discussion
> 
> > about adding a new shell versus updating the actual one [3, 4].
> > 
> > So, in this series, with Harald Seiler, we updated the actual U-Boot
> 
> shell to
> 
> > reflect what is currently in Busybox source code.
> > Basically, this contribution is about taking a snapshot of Busybox
> 
> shell/hush.c
> 
> > file (as it exists in commit 37460f5da) and adapt it to suit U-Boot needs.
> > 
> > This contribution was written to be as backward-compatible as possible to
> 
> avoid
> 
> > breaking the existing.
> > So, the 2021 hush flavor offers the same as the actual, that is to say:
> > 1. Variable expansion.
> > 2. Instruction lists (;, && and ||).
> > 3. If, then and else.
> > 4. Loops (for, while and until).
> > No new features offered by Busybox hush were implemented (e.g. functions).
> > 
> > It is possible to change the parser at runtime using the "cli" command:
> > => cli print
> > old
> > => cli set 2021
> > => cli print
> > 2021
> > => cli set old
> > The default parser is the old one.
> > Note that to use both parser, you would need to set both
> 
> CONFIG_HUSH_2021_PARSER
> 
> > and CONFIG_HUSH_OLD_PARSER.
> > 
> > In terms of testing, new unit tests were added to ut to ensure the new
> 
> behavior
> 
> > is the same as the old one and it does not add regression.
> > Nonetheless, if old behavior was buggy and fixed upstream, the fix is
> 
> then added
> 
> > to U-Boot [5].
> > In sandbox, all of these tests pass smoothly:
> > => printenv board
> > board=sandbox
> > => ut hush
> > Running 20 hush tests
> > ...
> > Failures: 0
> > => parser set 2021
> > => ut hush
> > Running 20 hush tests
> > ...
> > Failures: 0
> > 
> > Thanks to the effort of Harald Seiler, I was successful booting a board:
> > => printenv fdtfile
> > fdtfile=amlogic/meson-gxl-s905x-libretech-cc.dtb
> > => cli get
> > old
> 
> This is a really nice integration.
> 
> I think it could benefit from a new Kconfig like CONFIG_HUSH_SELECTABLE
> which enables the cli command and selection of parser...this could perhaps
> be enabled automatically if two parsers are selected. Then the code growth
> (about 1KB on Thumb 2) would mostly go away.

Excellent idea!
I added this in v11 but did not find a way to count how many options were set, 
so HUSH_SELECTABLE is set when the two hush flavors are selected.

> For checking parsers, the code you use:
> 
>if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
>   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
> 
>   if (flag & CMD_FLAG_ENV)
>  hush_flags |= FLAG_CONT_ON_NEWLINE;
>   return parse_string_outer(cmd, hush_flags);
>} else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
> ...
>   return parse_string_outer_2021(cmd, FLAG_EXIT_FROM_LOOP);
>}
> 
> could be written to reduce code size...i.e. there is only a need to check
> the gd flag if HUSH_SELECTABLE is enabled, so:
> 
> static inline bool use_hush_old(void)
> {
>  return IS_ENABLED(CONFIG_HUSH_SELECTABLE) ?
>  gd->flags & GD_FLG_HUSH_OLD_PARSER :
>  IS_ENABLD(CONFIG_HUSH_OLD_PARSER);
> }
> 
> Then use that function in the code able.

Indeed! Thank you for the suggestion!

> Size growth when swtiching to the the new parser is only 3KB, from my test.
> I thought it would be much more?
> 
> > => boot
> > ...
> > root@lepotato:~#
> > root@lepotato:~# reboot
> > ...
> > => cli set 2021
> > => cli get
> > 2021
> > => printenv fdtfile
> > fdtfile=amlogic/meson-gxl-s905x-libretech-cc.dtb
> > => boot
> > ...
> > root@lepotato:~#
> > 
> > I had to not use CONFIG_HUSH_2021_PARSER for the keymile board.
> > Indeed, the keymile board family is the only set of boards to call
> > get_local_var(), set_local_var() and unset_local_var().
> > Sadly, these functions are static in this contribution.
> > I could 

Re: [RFC PATCH v10 11/24] cmd: Add new cli command

2023-11-07 Thread Francis Laniel
Hi!


Le jeudi 5 octobre 2023, 02:26:52 EET Heinrich Schuchardt a écrit :
> On 10/4/23 18:42, Francis Laniel wrote:
> > This command can be used to print the current parser with 'cli print'.
> 
> Please, provide a commit message that matches the code.
> 
> > It can also be used to set the current parser with 'cli set'.
> > For the moment, only one value is valid for set: old.
> 
> If there is only one valid value, we should not provide the 'cli'
> command to save code size. The patch seems to be in the wrong spot in
> the series.

Thank you for your feedback! I normally addressed this problem in v11.

> > Signed-off-by: Francis Laniel 
> > ---
> > 
> >   cmd/Makefile  |   2 +
> >   cmd/cli.c | 120 ++
> >   common/cli.c  |   3 +-
> >   doc/usage/cmd/cli.rst |  59 +
> >   doc/usage/index.rst   |   1 +
> >   5 files changed, 184 insertions(+), 1 deletion(-)
> >   create mode 100644 cmd/cli.c
> >   create mode 100644 doc/usage/cmd/cli.rst
> > 
> > diff --git a/cmd/Makefile b/cmd/Makefile
> > index 9bebf321c3..d468cc5065 100644
> > --- a/cmd/Makefile
> > +++ b/cmd/Makefile
> > @@ -226,6 +226,8 @@ obj-$(CONFIG_CMD_AVB) += avb.o
> > 
> >   # Foundries.IO SCP03
> >   obj-$(CONFIG_CMD_SCP03) += scp03.o
> > 
> > +obj-$(CONFIG_HUSH_PARSER) += cli.o
> 
> Don't waste binary code size. We only need the command if at least two
> parsers are available.
> 
> ifeq ($(CONFIG_HUSH_OLD_PARSER)$(HUSH_2021_PARSER),yy)
> obj-y += cli.o
> endif
> 
> The symbol CONFIG_HUSH_PARSER should be eliminated.
> 
> > +
> > 
> >   obj-$(CONFIG_ARM) += arm/
> >   obj-$(CONFIG_RISCV) += riscv/
> >   obj-$(CONFIG_SANDBOX) += sandbox/
> > 
> > diff --git a/cmd/cli.c b/cmd/cli.c
> > new file mode 100644
> > index 00..7671785b83
> > --- /dev/null
> > +++ b/cmd/cli.c
> > @@ -0,0 +1,120 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +#include 
> > +
> > +DECLARE_GLOBAL_DATA_PTR;
> > +
> > +static const char *gd_flags_to_parser(void)
> > +{
> > +   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
> > +   return "old";
> > +   return NULL;
> > +}
> > +
> > +static int do_cli_get(struct cmd_tbl *cmdtp, int flag, int argc,
> > +char *const argv[])
> > +{
> > +   const char *current = gd_flags_to_parser();
> > +
> > +   if (!current) {
> > +   printf("current cli value is not valid, this should not happen!
\n");
> > +   return CMD_RET_FAILURE;
> > +   }
> > +
> > +   printf("%s\n", current);
> > +
> > +   return CMD_RET_SUCCESS;
> > +}
> > +
> 
> Please, describe this function (and all the others) in Sphinx style.
> 
> /**
>   * parser_string_to_gd_flags() - converts parser name to bit mask
>   *
>   * @parser:  parser name
>   * Return:   valid bit mask or -1
>   */
> 
> > +static int parser_string_to_gd_flags(const char *parser)
> > +{
> > +   if (!strcmp(parser, "old"))
> > +   return GD_FLG_HUSH_OLD_PARSER;
> 
> Please, do not return an invalid bit mask.
> 
> if (CONFIG_IS_ENABLED(HUSH_OLD_PARSER) && !strcmp(parser, "old"))
>   return GD_FLG_HUSH_OLD_PARSER;
> if (CONFIG_IS_ENABLED(HUSH_2021_PARSER) && !strcmp(parser, "2021"))
>   return GD_FLG_HUSH_201_PARSER;
> 
> > +   return -1;
> > +}
> > +
> > +static void reset_parser_gd_flags(void)
> > +{
> > +   gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
> 
> gd->flags &= ~(GD_FLG_HUSH_OLD_PARSER | GD_FLG_HUSH_2021_PARSER);
> 
> If there were only one parser, we wouldn't create this command.
> 
> > +}
> > +
> > +static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
> > + char *const argv[])
> > +{
> > +   char *parser_name;
> > +   int parser_flag;
> > +
> > +   if (argc < 2)
> > +   return CMD_RET_USAGE;
> > +
> > +   parser_name = argv[1];
> > +
> > +   parser_flag = parser_string_to_gd_flags(parser_name);
> 
> This function should return -1 for for any value that is not supported
> be it 'foo', 'old', or '2021'. Then you can eliminate a bunch of error
> checking code below.
> 
> > +   if (parser_flag == -1) {
> > +   printf("Bad value for parser name: 

[PATCH v11 24/24] DO NOT MERGE: ci: Build the world in any case.

2023-11-07 Thread Francis Laniel
The CI can fails previously for reasons which are not tied to hush 2021.
Let's build the world in any case to check everything is OK there too.

Signed-off-by: Francis Laniel 
---
 .azure-pipelines.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index d6f3fa423c..4eaf0e9062 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -469,6 +469,7 @@ stages:
 retryCountOnTaskFailure: 2 # QEMU may be too slow, etc.
 
 - stage: world_build
+  condition: always()
   jobs:
   - job: build_the_world
 timeoutInMinutes: 0 # Use the maximum allowed
-- 
2.34.1



[PATCH v11 23/24] DO NOT MERGE: only to make CI happy

2023-11-07 Thread Francis Laniel
This commit set CONFIG_HUSH_PARSER_2021 as the default to trigger the CI with
this parser.

Nonetheless, the keymile (i.e. VENDOR_KM) board family is not compatible with
new 2021 hush parser.
Indeed, This boards used set_local_var() to store some variables as local shell.
They then used get_local_var() to retrieve the variables values.
Sadly, this two functions do not exist with CONFIG_HUSH_PARSER_2021.
A patch was proposed to use environment variables rather than local variables
but it does not tackle the problem, so complementary work is needed to make
this boards use CONFIG_HUSH_PARSER_2021 [1].

We also remove a #undef of CONFIG_FEATURE_SH_STANDALONE as it does not exist in
U-Boot and causes troubles in the CI.

We also set CONFIG_LTO for kirkwoord sheevaplug and phytec bk4r1, otherwise it
hits its board size limit.

We also disable some check for pylint as it was not able to find future for
commit object.

Acked-by: Tony Dinh 
Signed-off-by: Francis Laniel 
[1] https://marc.info/?l=u-boot=165541917618725=2
---
 cmd/Kconfig  | 3 ++-
 common/cli_hush_upstream.c   | 1 -
 configs/sheevaplug_defconfig | 1 +
 tools/patman/series.py   | 4 
 4 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 4641333809..3c36833f4d 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -28,7 +28,7 @@ menu "Hush flavor to use"
 
config HUSH_OLD_PARSER
bool "Use hush old parser"
-   default y
+   default y if VENDOR_KM
help
  This option enables the old flavor of hush based on hush 
Busybox from
  2005.
@@ -37,6 +37,7 @@ menu "Hush flavor to use"
 
config HUSH_2021_PARSER
bool "Use hush 2021 parser"
+   default y if !VENDOR_KM
help
  This option enables the new flavor of hush based on hush 
Busybox from
  2021.
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 709c055d2f..243710ab51 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -427,7 +427,6 @@
 #include "NUM_APPLETS.h"
 #if NUM_APPLETS == 1
 /* STANDALONE does not make sense, and won't compile */
-# undef CONFIG_FEATURE_SH_STANDALONE
 # undef ENABLE_FEATURE_SH_STANDALONE
 # undef IF_FEATURE_SH_STANDALONE
 # undef IF_NOT_FEATURE_SH_STANDALONE
diff --git a/configs/sheevaplug_defconfig b/configs/sheevaplug_defconfig
index 2e4901b840..365f779cc8 100644
--- a/configs/sheevaplug_defconfig
+++ b/configs/sheevaplug_defconfig
@@ -16,6 +16,7 @@ CONFIG_ENV_OFFSET=0x8
 CONFIG_DEFAULT_DEVICE_TREE="kirkwood-sheevaplug"
 CONFIG_IDENT_STRING="\nMarvell-Sheevaplug"
 CONFIG_SYS_LOAD_ADDR=0x80
+CONFIG_LTO=y
 CONFIG_HAS_BOARD_SIZE_LIMIT=y
 CONFIG_BOARD_SIZE_LIMIT=524288
 CONFIG_BOOTDELAY=3
diff --git a/tools/patman/series.py b/tools/patman/series.py
index 6866e1dbd0..f99818e33a 100644
--- a/tools/patman/series.py
+++ b/tools/patman/series.py
@@ -316,6 +316,8 @@ class Series(dict):
 # Show progress any commits that are taking forever
 lastlen = 0
 while True:
+# pylint does not find future which is set above.
+# pylint: disable=E1101
 left = [commit for commit in self.commits
 if not commit.future.done()]
 if not left:
@@ -333,6 +335,8 @@ class Series(dict):
 print('Cc processing complete')
 
 for commit in self.commits:
+# pylint does not find future which is set above.
+# pylint: disable=E1101
 cc = commit.future.result()
 all_ccs += cc
 print(commit.patch, '\0'.join(sorted(set(cc))), file=fd)
-- 
2.34.1



[PATCH v11 22/24] cli: hush_2021: Add upstream commits up to 2nd October 2023.

2023-11-07 Thread Francis Laniel
This commit adds the following hush busybox upstream commits:
791b222dd55d ("sleep: fix "sleep -- ARGS"")
5353df91cba7 ("Update applet size estimates")
e41e481fd571 ("hush: fix a compile failure")
07a95cfcabb0 ("ash: disable check for "good" function name, bash does not check 
this")
e5692e2342c6 ("hush: quote values in "readonly" output")
96769486e20f ("shell: move varcmp() to shell_common.h and use it in hush")
bab8828b0dad ("hush: fix expansion of space in "a=${a:+$a }c" construct")
b5be8da350b5 ("hush: make "false" built-in")
6824298ab4d3 ("hush: fix ELIF cmd1;cmd2 THEN ... not executing cmd2, closes 
15571")
3a7f00eadcf4 ("hush: add comment about abort on syntax error %{^}")
acae889dd972 ("ash,hush: tab completion of functions and aliases")
90b607d79a13 ("hush: quote variable values printed by "set" (match ash 
behavior)")
6748e6494c22 ("hush (NOMMU): fix LINENO in execed children")
fd5fb2d2b596 ("hush: speed up "big heredoc" code")
1409432d072e ("hush: add TODO comment")
93ae7464e6e4 ("hush: restore SIGHUP handling, this time explain why we do what 
we do")
1fdb33bd07e5 ("hush: restore tty pgrp on SIGHUP")
6101b6d3eaa0 ("hush: remove special handling of SIGHUP")
93e0898c663a ("shell: fix SIGWINCH and SIGCHLD (in hush) interrupting line 
input, closes 15256")
969e00816835 ("hush: code shrink")
27be0e8cfeb6 ("shell: fix compile failures in some configs")
7d1c7d833785 ("ash,hush: use HOME for tab completion and prompts")
21afddefd258 ("hush: fix "error: invalid preprocessing directive ##"")
e53c7dbafc78 ("hush: fix set -n to act immediately, not just after run_list()")
574b9c446da1 ("hush: fix var_LINENO3.tests failure")
49bcf9f40cff ("hush: speed up ${x//\*/|} too")
53b2fdcdba4c ("*: add NOINLINEs where code noticeably shrinks")
7c3e96d4b3d4 ("shell: use more compact SHELL_ASH / HUSH config defines. no code 
changes")
62f1eed1e191 ("hush: in a comment, document what -i might be doing")
aaf3d5ba74c5 ("shell: tweak --help")
db5546ca1018 ("libbb: code shrink: introduce and use [_]exit_SUCCESS()")
931c55f9e2b4 ("libbb: invert the meaning of SETUP_ENV_NO_CHDIR -> 
SETUP_ENV_CHDIR")
12566e7f9b5e ("ash,hush: fix handling of SIGINT while waiting for interactive 
input")
987be932ed3c ("*: slap on a few ALIGN_PTR where appropriate")

Signed-off-by: Francis Laniel 
---
 common/cli_hush_2021.c |  20 +-
 common/cli_hush_upstream.c | 407 ++---
 2 files changed, 304 insertions(+), 123 deletions(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index 0a207d147b..15a5d3134e 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -26,7 +26,7 @@
 /*
  * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
  */
-#define BB_VER "1.34.0.git37460f5daff9"
+#define BB_VER "1.35.0.git7d1c7d833785"
 
 /*
  * Define hush features by the names used upstream.
@@ -236,6 +236,24 @@ static size_t list_size(char **list)
return size;
 }
 
+static int varcmp(const char *p, const char *q)
+{
+   int c, d;
+
+   while ((c = *p) == (d = *q)) {
+   if (c == '\0' || c == '=')
+   goto out;
+   p++;
+   q++;
+   }
+   if (c == '=')
+   c = '\0';
+   if (d == '=')
+   d = '\0';
+out:
+   return c - d;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 23392939e1..709c055d2f 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -91,7 +91,7 @@
  * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
  */
 //config:config HUSH
-//config:  bool "hush (68 kb)"
+//config:  bool "hush (70 kb)"
 //config:  default y
 //config:  select SHELL_HUSH
 //config:  help
@@ -339,7 +339,7 @@
  * therefore we don't show them either.
  */
 //usage:#define hush_trivial_usage
-//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
+//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
 //usage:#define hush_full_usage "\n\n"
 //usage:   "Unix shell interpreter"
 
@@ -374,7 +374,7 @@
 # define F_DUPFD_CLOEXEC F_DUPFD
 #endif
 
-#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !(ENABLE_ASH || ENABLE_SH_IS_ASH || 
ENABLE_BASH_IS_ASH)
+#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !ENABLE_SHELL_ASH
 # include "embedded_scripts.h"

[PATCH v11 21/24] test: hush: Fix loop tests for hush 2021

2023-11-07 Thread Francis Laniel
Modifies return code got from while loop as hush 2021 always returns 0 from
while loop.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/loop.c | 34 ++
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/test/hush/loop.c b/test/hush/loop.c
index ca777e38fe..add5402a32 100644
--- a/test/hush/loop.c
+++ b/test/hush/loop.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_for(struct unit_test_state *uts)
 {
@@ -21,7 +24,12 @@ static int hush_test_for(struct unit_test_state *uts)
ut_assert_nextline("quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_i=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_i and it cannot 
be unset!");
+   }
 
return 0;
 }
@@ -31,12 +39,30 @@ static int hush_test_while(struct unit_test_state *uts)
 {
console_record_reset_enable();
 
-   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
-   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* Hush 2021 always returns 0 from while loop...
+* You can see code snippet near this line to have a better
+* understanding:
+* debug_printf_exec(": while expr is false: breaking 
(exitcode:EXIT_SUCCESS)\n");
+*/
+   ut_assertok(run_command("while test -z \"$loop_foo\"; do echo 
bar; loop_foo=quux; done", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   /*
+* Exit status is that of test, so 1 since test is false to quit
+* the loop.
+*/
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do 
echo bar; loop_foo=quux; done", 0));
+   }
ut_assert_nextline("bar");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
-- 
2.34.1



[PATCH v11 20/24] cli: hush_2021: Enable loops

2023-11-07 Thread Francis Laniel
Enables the use of for, while and until loops for command line as
well as with run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_2021.c |  1 +
 common/cli_hush_upstream.c | 15 ++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index aba5dcbbcd..0a207d147b 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -34,6 +34,7 @@
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
 #define ENABLE_HUSH_IF 1
+#define ENABLE_HUSH_LOOPS  1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 9b65dcbde1..23392939e1 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -10349,7 +10349,7 @@ static int run_list(struct pipe *pi)
 #ifndef __U_BOOT__
for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) 
pi->next) {
 #else /* __U_BOOT__ */
-   for (; pi; pi = pi->next) {
+   for (; pi; pi = rword == RES_DONE ? loop_top : pi->next) {
 #endif /* __U_BOOT__ */
int r;
int sv_errexit_depth;
@@ -10451,7 +10451,20 @@ static int run_list(struct pipe *pi)
}
/* Insert next value from for_lcur */
/* note: *for_lcur already has quotes removed, $var 
expanded, etc */
+#ifndef __U_BOOT__
set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], 
*for_lcur++), /*flag:*/ 0);
+#else /* __U_BOOT__ */
+   /* We cannot use xasprintf, so we emulate it. */
+   char *full_var;
+   char *var = pi->cmds[0].argv[0];
+   char *val = *for_lcur++;
+
+   /* + 1 to take into account =. */
+   full_var = xmalloc(strlen(var) + strlen(val) + 1);
+   sprintf(full_var, "%s=%s", var, val);
+
+   set_local_var_2021(full_var, /*flag:*/ 0);
+#endif /* __U_BOOT__ */
continue;
}
if (rword == RES_IN) {
-- 
2.34.1



[PATCH v11 19/24] cli: hush_2021: Enable if keyword

2023-11-07 Thread Francis Laniel
Adds support for "if then else" construct both for command line interface and
through run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_2021.c | 11 +++
 common/cli_hush_upstream.c | 12 
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index 7dd30ea0ef..aba5dcbbcd 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -33,6 +33,7 @@
  */
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
+#define ENABLE_HUSH_IF 1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
@@ -124,6 +125,11 @@ static void bb_error_msg(const char *s, ...)
va_end(p);
 }
 
+static void bb_simple_error_msg(const char *s)
+{
+   bb_error_msg("%s", s);
+}
+
 static void *xmalloc(size_t size)
 {
void *p = NULL;
@@ -147,6 +153,11 @@ static void *xrealloc(void *ptr, size_t size)
return p;
 }
 
+static void *xmemdup(const void *s, int n)
+{
+   return memcpy(xmalloc(n), s, n);
+}
+
 #define xstrdupstrdup
 #define xstrndup   strndup
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 68a9a4bb72..9b65dcbde1 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1488,7 +1488,6 @@ static void msg_and_die_if_script(unsigned lineno, const 
char *fmt, ...)
die_if_script();
 }
 
-#ifndef __U_BOOT__
 static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
 {
if (msg)
@@ -1497,7 +1496,6 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, 
const char *msg)
bb_simple_error_msg("syntax error");
die_if_script();
 }
-#endif /* !__U_BOOT__ */
 
 static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
 {
@@ -3962,7 +3960,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[PIPE_OR ] = "OR" ,
[PIPE_BG ] = "BG" ,
};
-#ifndef __U_BOOT__
static const char *RES[] = {
[RES_NONE ] = "NONE" ,
 # if ENABLE_HUSH_IF
@@ -3992,7 +3989,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[RES_ ] = "" ,
[RES_SNTX ] = "SNTX" ,
};
-#endif /* !__U_BOOT__ */
static const char *const CMDTYPE[] = {
"{}",
"()",
@@ -4010,10 +4006,8 @@ static void debug_print_tree(struct pipe *pi, int lvl)
lvl*2, "",
pin,
pi->num_cmds,
-#ifdef __U_BOOT__
(IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
RES[pi->res_word],
-#endif /* !__U_BOOT__ */
pi->followup, PIPE[pi->followup]
);
prn = 0;
@@ -9833,6 +9827,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
rcode = 1; /* exitcode if redir failed */
 #ifndef __U_BOOT__
if (setup_redirects(command, ) == 0) {
+#endif /* !__U_BOOT__ */
debug_printf_exec(": run_list\n");
 //FIXME: we need to pass squirrel down into run_list()
 //for SH_STANDALONE case, or else this construct:
@@ -9841,10 +9836,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
 //and in SH_STANDALONE mode, "find" is not execed,
 //therefore CLOEXEC on saved fd does not help.
rcode = run_list(command->group) & 0xff;
+#ifndef __U_BOOT__
}
restore_redirects(squirrel);
-   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
 #endif /* !__U_BOOT__ */
+   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
debug_leave();
debug_printf_exec("run_pipe: return %d\n", rcode);
return rcode;
@@ -10363,12 +10359,12 @@ static int run_list(struct pipe *pi)
break;
if (G_flag_return_in_progress == 1)
break;
+#endif /* !__U_BOOT__ */
 
IF_HAS_KEYWORDS(rword = pi->res_word;)
debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
rword, cond_code, last_rword);
 
-#endif /* !__U_BOOT__ */
sv_errexit_depth = G.errexit_depth;
if (
 #if ENABLE_HUSH_IF
-- 
2.34.1



[PATCH v11 18/24] cli: hush_2021: Enable using < and > as string compare operators

2023-11-07 Thread Francis Laniel
In Busybox hush, '<' and '>' are used as redirection operators.
For example, cat foo > bar will write content of file foo inside file bar.
In U-Boot, we do not have file system, so we can hardly redirect command output
inside a file.

But, in actual U-Boot hush, these operators ('<' and '>') are used as string
compare operators.
For example, test aaa < bbb returns 0 as aaa is before bbb in the dictionary.
Busybox hush also permits this, but operators need to be escaped ('\<' and
'\>').
Indeed, if escaping is needed it permits the developer to think about its code,
as in a lot of case, we want to compare integers (using '-lt' or '-gt') rather
than strings.

As testing in U-Boot is handled by the test command, we will stick with the
original behaviour and not adapt to Busybox one.

Nonetheless, if one day we decide to implement test with '[[ ]]', we will then
stick to upstream Busybox behavior.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 23 ++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index dbf77ad503..68a9a4bb72 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -6160,7 +6160,28 @@ static struct pipe *parse_stream(char **pstring,
if (parse_redirect(, redir_fd, redir_style, input))
goto parse_error_exitcode1;
continue; /* get next char */
-#endif /* !__U_BOOT__ */
+#else /* __U_BOOT__ */
+   /*
+* In U-Boot, '<' and '>' can be used in test command 
to test if
+* a string is, alphabetically, before or after another.
+* In 2021 Busybox hush, we will keep the same behavior 
and so not treat
+* them as redirection operator.
+*
+* Indeed, in U-Boot, tests are handled by the test 
command and not by the
+* shell code.
+* So, better to give this character as input to test 
command.
+*
+* NOTE In my opinion, when you use '<' or '>' I am 
almost sure
+* you wanted to use "-gt" or "-lt" in place, so 
thinking to
+* escape these will make you should check your code 
(sh syntax
+* at this level is, for me, error prone).
+*/
+   case '>':
+   fallthrough;
+   case '<':
+   o_addQchr(, ch);
+   continue;
+#endif /* __U_BOOT__ */
case '#':
if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
/* skip "#comment" */
-- 
2.34.1



[PATCH v11 17/24] test: hush: Fix variable expansion tests for hush 2021

2023-11-07 Thread Francis Laniel
Modifies the expected result for hush 2021.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As hush 2021 is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/dollar.c | 79 --
 1 file changed, 69 insertions(+), 10 deletions(-)

diff --git a/test/hush/dollar.c b/test/hush/dollar.c
index defb2c3fd0..f23392c72d 100644
--- a/test/hush/dollar.c
+++ b/test/hush/dollar.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_simple_dollar(struct unit_test_state *uts)
 {
@@ -51,13 +54,29 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
/* Next line contains error message */
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* For some strange reasons, the console is not empty after
+* running above command.
+* So, we reset it to not have side effects for other tests.
+*/
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
/* Two next lines contain error message */
ut_assert_skipline();
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* See above comments. */
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
 
@@ -71,17 +90,35 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
 */
console_record_reset_enable();
 
-   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
-   /* Next line contains error message */
-   ut_assert_skipline();
-   ut_assert_console_end();
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* Old parser returns an error because it waits for closing
+* '\'', but this behavior is wrong as the '\'' is surrounded by
+* '"', so no need to wait for a closing one.
+*/
+   ut_assertok(run_command("dollar_foo=\"bar 'quux\"", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar 'quux");
+   ut_assert_console_end();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar quux'", 0));
ut_assertok(run_command("echo $dollar_foo", 0));
ut_assert_nextline("bar quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable dollar_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("dollar_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable dollar_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -109,7 +146,12 @@ static int hush_test_env_dollar(struct unit_test_state 
*uts)
/* Clean up setting the variable */
env_set("env_foo", NULL);
 
-   puts("Beware: this test set local variable env_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("env_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable env_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -144,7 +186,18 @@ static int hush_test_command_dollar(struct unit_test_state 
*uts)
ut_assertok(run_command("dollar_bar='echo bar\\n'", 0));
 
ut_assertok(run_command("$dollar_bar", 0));
-   ut_assert_nextline("barn");
+
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+  

[PATCH v11 16/24] test: hush: Fix instructions list tests for hush 2021

2023-11-07 Thread Francis Laniel
Modifies the expected result for hush 2021.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As hush 2021 is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/list.c | 69 +---
 1 file changed, 65 insertions(+), 4 deletions(-)

diff --git a/test/hush/list.c b/test/hush/list.c
index 052cf2783c..6f8f10f15e 100644
--- a/test/hush/list.c
+++ b/test/hush/list.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 
 static int hush_test_semicolon(struct unit_test_state *uts)
 {
@@ -46,12 +47,43 @@ static int hush_test_or(struct unit_test_state *uts)
 }
 HUSH_TEST(hush_test_or, 0);
 
+DECLARE_GLOBAL_DATA_PTR;
+
 static int hush_test_and_or(struct unit_test_state *uts)
 {
/* A && B || C truth table. */
ut_asserteq(1, run_command("false && false || false", 0));
-   ut_asserteq(1, run_command("false && false || true", 0));
-   ut_asserteq(1, run_command("false && true || true", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && false || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && false) || true
+* So, (false && false) returns 1, the second false is not
+* executed, and true is executed because of ||.
+*/
+   ut_assertok(run_command("false && false || true", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && true || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && true) || true
+* So, (false && true) returns 1, the true is not executed, and
+* true is executed because of ||.
+*/
+   ut_assertok(run_command("false && true || true", 0));
+   }
+
ut_asserteq(1, run_command("false && true || false", 0));
ut_assertok(run_command("true && true || false", 0));
ut_asserteq(1, run_command("true && false || false", 0));
@@ -69,8 +101,37 @@ static int hush_test_or_and(struct unit_test_state *uts)
ut_asserteq(1, run_command("false || false && true", 0));
ut_assertok(run_command("false || true && true", 0));
ut_asserteq(1, run_command("false || true && false", 0));
-   ut_assertok(run_command("true || true && false", 0));
-   ut_assertok(run_command("true || false && false", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || true && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || true) && false
+* So, (true || true) returns 0, the second true is not
+* executed, and then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || true && false", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || false && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || false) && false
+* So, (true || false) returns 0, the false is not executed, and
+* then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || false && false", 0));
+   }
+
ut_assertok(run_command("true || false && true", 0));
ut_assertok(run_command("true || true && true", 0));
 
-- 
2.34.1



[PATCH v11 15/24] cli: add hush 2021 as parser for run_command*()

2023-11-07 Thread Francis Laniel
Enables using, in code, hush 2021 as parser for run_command function family.
It also enables the command run to be used by CLI user of hush 2021.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c   | 67 --
 common/cli_hush_upstream.c |  2 +-
 2 files changed, 51 insertions(+), 18 deletions(-)

diff --git a/common/cli.c b/common/cli.c
index e3e2bc7fe1..e332bdd670 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -25,6 +25,14 @@
 #include 
 
 #ifdef CONFIG_CMDLINE
+
+static inline bool use_hush_old(void)
+{
+   return IS_ENABLED(CONFIG_HUSH_SELECTABLE) ?
+   gd->flags & GD_FLG_HUSH_OLD_PARSER :
+   IS_ENABLED(CONFIG_HUSH_OLD_PARSER);
+}
+
 /*
  * Run a command using the selected parser.
  *
@@ -43,15 +51,30 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#elif CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
-
-   if (flag & CMD_FLAG_ENV)
-   hush_flags |= FLAG_CONT_ON_NEWLINE;
-   return parse_string_outer(cmd, hush_flags);
-#else /* HUSH_2021_PARSER */
-   /* Not yet implemented. */
-   return 1;
+#else
+   if (use_hush_old()) {
+   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
+
+   if (flag & CMD_FLAG_ENV)
+   hush_flags |= FLAG_CONT_ON_NEWLINE;
+   return parse_string_outer(cmd, hush_flags);
+   }
+   /*
+* Possible values for flags are the following:
+* FLAG_EXIT_FROM_LOOP: This flags ensures we exit from loop in
+* parse_and_run_stream() after first iteration while normal
+* behavior, * i.e. when called from cli_loop(), is to loop
+* infinitely.
+* FLAG_PARSE_SEMICOLON: Hush 2021 parses ';' and does not stop
+* first time it sees one. So, I think we do not need this flag.
+* FLAG_REPARSING: For the moment, I do not understand the goal
+* of this flag.
+* FLAG_CONT_ON_NEWLINE: This flag seems to be used to continue
+* parsing even when reading '\n' when coming from
+* run_command(). In this case, Hush 2021 reads until it finds
+* '\0'. So, I think we do not need this flag.
+*/
+   return parse_string_outer_2021(cmd, FLAG_EXIT_FROM_LOOP);
 #endif
 }
 
@@ -67,12 +90,23 @@ int run_command_repeatable(const char *cmd, int flag)
 #ifndef CONFIG_HUSH_PARSER
return cli_simple_run_command(cmd, flag);
 #else
+   int ret;
+
+   if (use_hush_old()) {
+   ret = parse_string_outer(cmd,
+FLAG_PARSE_SEMICOLON
+| FLAG_EXIT_FROM_LOOP);
+   } else {
+   ret = parse_string_outer_2021(cmd,
+ FLAG_PARSE_SEMICOLON
+ | FLAG_EXIT_FROM_LOOP);
+   }
+
/*
 * parse_string_outer() returns 1 for failure, so clean up
 * its result.
 */
-   if (parse_string_outer(cmd,
-  FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP))
+   if (ret)
return -1;
 
return 0;
@@ -111,12 +145,11 @@ int run_command_list(const char *cmd, int len, int flag)
buff[len] = '\0';
}
 #ifdef CONFIG_HUSH_PARSER
-#if CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
-#else /* HUSH_2021_PARSER */
-   /* Not yet implemented. */
-   rcode = 1;
-#endif
+   if (use_hush_old()) {
+   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
+   } else {
+   rcode = parse_string_outer_2021(buff, FLAG_PARSE_SEMICOLON);
+   }
 #else
/*
 * This function will overwrite any \n it sees with a \0, which
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index b11445c3ac..dbf77ad503 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -8023,7 +8023,7 @@ static int parse_and_run_string(const char *s)
 }
 
 #ifdef __U_BOOT__
-int parse_string_outer(const char *cmd, int flags)
+int parse_string_outer_2021(const char *cmd, int flags)
 {
int ret;
int old_flags;
-- 
2.34.1



[PATCH v11 14/24] cli: hush_2021: Add functions to be called from run_command()

2023-11-07 Thread Francis Laniel
run_command() is called internally by the command run and it can also be called
directly from U-Boot code, e.g. to do unit tests.
This commit adds this path to go to hush 2021.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 66 --
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index b806c5c653..b11445c3ac 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1012,6 +1012,7 @@ struct globals {
 #ifdef __U_BOOT__
int flag_repeat;
int do_repeat;
+   int run_command_flags;
 #endif /* __U_BOOT__ */
char *ifs_whitespace; /* = G.ifs or malloced */
 #ifndef __U_BOOT__
@@ -3005,7 +3006,24 @@ static int i_getch(struct in_str *i)
if (i->p && *i->p != '\0') {
ch = (unsigned char)*i->p++;
goto out;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* There are two ways for command to be called:
+* 1. The first one is when they are typed by the user.
+* 2. The second one is through run_command() (NOTE command run
+* internally calls run_command()).
+*
+* In the second case, we do not get input from the user, so once we
+* get a '\0', it means we need to stop.
+* NOTE G.run_command_flags is only set on run_command call stack, so
+* we use this to know if we come from user input or run_command().
+*/
+   } else if (i->p && *i->p == '\0' && G.run_command_flags){
+   return EOF;
+   }
+#endif /* __U_BOOT__ */
 #endif
 #ifndef __U_BOOT__
/* peek_buf[] is an int array, not char. Can contain EOF. */
@@ -3164,7 +3182,6 @@ static void setup_file_in_str(struct in_str *i)
 #endif /* !__U_BOOT__ */
 }
 
-#ifndef __U_BOOT__
 static void setup_string_in_str(struct in_str *i, const char *s)
 {
memset(i, 0, sizeof(*i));
@@ -3172,7 +3189,6 @@ static void setup_string_in_str(struct in_str *i, const 
char *s)
i->p = s;
 }
 
-#endif /* !__U_BOOT__ */
 
 /*
  * o_string support
@@ -7911,7 +7927,11 @@ static int run_and_free_list(struct pipe *pi);
  * NUL: parse all, execute, return
  * ';': parse till ';' or newline, execute, repeat till EOF
  */
+#ifndef __U_BOOT__
 static void parse_and_run_stream(struct in_str *inp, int end_trigger)
+#else /* __U_BOOT__ */
+static int parse_and_run_stream(struct in_str *inp, int end_trigger)
+#endif /* __U_BOOT__ */
 {
/* Why we need empty flag?
 * An obscure corner case "false; ``; echo $?":
@@ -7920,7 +7940,11 @@ static void parse_and_run_stream(struct in_str *inp, int 
end_trigger)
 * this breaks "false; echo `echo $?`" case.
 */
bool empty = 1;
+#ifndef __U_BOOT__
while (1) {
+#else /* __U_BOOT__ */
+   do {
+#endif /* __U_BOOT__ */
struct pipe *pipe_list;
 
 #if ENABLE_HUSH_INTERACTIVE
@@ -7967,21 +7991,57 @@ static void parse_and_run_stream(struct in_str *inp, 
int end_trigger)
empty = 0;
if (G_flag_return_in_progress == 1)
break;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* This do/while is needed by run_command to avoid looping on a command
+* with syntax error.
+*/
+   } while (!(G.run_command_flags & FLAG_EXIT_FROM_LOOP));
+
+   return G.last_exitcode;
+#endif /* __U_BOOT__ */
 }
 
 #ifndef __U_BOOT__
 static void parse_and_run_string(const char *s)
+#else /* __U_BOOT__ */
+static int parse_and_run_string(const char *s)
+#endif /* __U_BOOT__ */
 {
struct in_str input;
//IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
 
setup_string_in_str(, s);
+#ifndef __U_BOOT__
parse_and_run_stream(, '\0');
+#else /* __U_BOOT__ */
+   return parse_and_run_stream(, '\0');
+#endif /* __U_BOOT__ */
//IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
 }
-#endif /* !__U_BOOT__ */
 
+#ifdef __U_BOOT__
+int parse_string_outer(const char *cmd, int flags)
+{
+   int ret;
+   int old_flags;
+
+   /*
+* Keep old values of run_command to be able to restore them once
+* command was executed.
+*/
+   old_flags = G.run_command_flags;
+   G.run_command_flags = flags;
+
+   ret = parse_and_run_string(cmd);
+
+   G.run_command_flags = old_flags;
+
+   return ret;
+}
+#endif /* __U_BOOT__ */
 #ifndef __U_BOOT__
 static void parse_and_run_file(HFILE *fp)
 #else /* __U_BOOT__ */
-- 
2.34.1



[PATCH v11 13/24] cli: hush_2021: Enable variables expansion for hush 2021

2023-11-07 Thread Francis Laniel
Enables variables expansion for hush 2021, both for local and environment
variables.
So the following commands:
foo=bar
echo $foo
setenv bar foo
echo $bar
leads to "bar" and "foo" being printed on console output.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_2021.c | 17 +++
 common/cli_hush_upstream.c | 91 +++---
 2 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index 653ea52929..7dd30ea0ef 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -207,6 +207,23 @@ static const char* endofname(const char *name)
return name;
 }
 
+/**
+ * list_size() - returns the number of elements in char ** before NULL.
+ *
+ * Argument must contain NULL to signalize its end.
+ *
+ * @list The list to count the number of element.
+ * @return The number of element in list.
+ */
+static size_t list_size(char **list)
+{
+   size_t size;
+
+   for (size = 0; list[size] != NULL; size++);
+
+   return size;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 84227a248e..b806c5c653 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -3486,7 +3486,6 @@ static int o_get_last_ptr(o_string *o, int n)
return ((int)(uintptr_t)list[n-1]) + string_start;
 }
 
-#ifndef __U_BOOT__
 /*
  * Globbing routines.
  *
@@ -3741,8 +3740,10 @@ static int glob_needed(const char *s)
  */
 static int perform_glob(o_string *o, int n)
 {
+#ifndef __U_BOOT__
glob_t globdata;
int gr;
+#endif /* __U_BOOT__ */
char *pattern;
 
debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
@@ -3751,13 +3752,16 @@ static int perform_glob(o_string *o, int n)
pattern = o->data + o_get_last_ptr(o, n);
debug_printf_glob("glob pattern '%s'\n", pattern);
if (!glob_needed(pattern)) {
+#ifndef __U_BOOT__
  literal:
+#endif /* __U_BOOT__ */
/* unbackslash last string in o in place, fix length */
o->length = unbackslash(pattern) - o->data;
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
return o_save_ptr_helper(o, n);
}
 
+#ifndef __U_BOOT__
memset(, 0, sizeof(globdata));
/* Can't use GLOB_NOCHECK: it does not unescape the string.
 * If we glob "*.\*" and don't find anything, we need
@@ -3793,16 +3797,22 @@ static int perform_glob(o_string *o, int n)
if (DEBUG_GLOB)
debug_print_list("perform_glob returning", o, n);
return n;
+#else /* __U_BOOT__ */
+   /*
+* NOTE We only use perform glob to call unbackslash to remove backslash
+* from string once expanded.
+* So, it seems OK to return this if no previous return was done.
+*/
+   return o_save_ptr_helper(o, n);
+#endif /* __U_BOOT__ */
 }
 
-#endif /* !__U_BOOT__ */
 #endif /* !HUSH_BRACE_EXPANSION */
 
 /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
  * Otherwise, just finish current list[] and start new */
 static int o_save_ptr(o_string *o, int n)
 {
-#ifndef __U_BOOT__
if (o->o_expflags & EXP_FLAG_GLOB) {
/* If o->has_empty_slot, list[n] was already globbed
 * (if it was requested back then when it was filled)
@@ -3810,7 +3820,6 @@ static int o_save_ptr(o_string *o, int n)
if (!o->has_empty_slot)
return perform_glob(o, n); /* o_save_ptr_helper is 
inside */
}
-#endif /* !__U_BOOT__ */
return o_save_ptr_helper(o, n);
 }
 
@@ -5456,7 +5465,20 @@ static int parse_dollar(o_string *as_string,
nommu_addchr(as_string, ch);
if (ch == '}')
break;
+#ifndef __U_BOOT__
if (!isalnum(ch) && ch != '_') {
+#else /* __U_BOOT__ */
+   /*
+* In several places in U-Boot, we use variable like
+* foo# (e.g. serial#), particularly in env.
+* So, we need to authorize # to appear inside
+* variable name and then expand this variable.
+* NOTE Having # in variable name is not permitted in
+* upstream hush but expansion will be done (even though
+* the result will be empty).
+*/
+   if (!isalnum(ch) && ch != '_' && ch != '#') {
+#endif /* __U_BOOT__ */
unsigned end_ch;
 #ifndef __U_BOOT__
unsigned char last_ch;
@@ -7031,7 +7053,20 @@ static NOINLINE int 

[PATCH v11 11/24] cmd: Add new cli command

2023-11-07 Thread Francis Laniel
This command can be used to print the current parser with 'cli get'.
It can also be used to set the current parser with 'cli set'.
For the moment, only one value is valid for set: old.

Signed-off-by: Francis Laniel 
---
 cmd/Makefile  |   2 +
 cmd/cli.c | 114 ++
 common/cli.c  |   3 +-
 doc/usage/cmd/cli.rst |  59 ++
 doc/usage/index.rst   |   1 +
 5 files changed, 178 insertions(+), 1 deletion(-)
 create mode 100644 cmd/cli.c
 create mode 100644 doc/usage/cmd/cli.rst

diff --git a/cmd/Makefile b/cmd/Makefile
index 9a6790cc17..620f69c1c5 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -227,6 +227,8 @@ obj-$(CONFIG_CMD_AVB) += avb.o
 # Foundries.IO SCP03
 obj-$(CONFIG_CMD_SCP03) += scp03.o
 
+obj-$(CONFIG_HUSH_PARSER) += cli.o
+
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_RISCV) += riscv/
 obj-$(CONFIG_SANDBOX) += sandbox/
diff --git a/cmd/cli.c b/cmd/cli.c
new file mode 100644
index 00..86c6471aa4
--- /dev/null
+++ b/cmd/cli.c
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *gd_flags_to_parser_name(void)
+{
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   return "old";
+   return NULL;
+}
+
+static int do_cli_get(struct cmd_tbl *cmdtp, int flag, int argc,
+char *const argv[])
+{
+   const char *current = gd_flags_to_parser_name();
+
+   if (!current) {
+   printf("current cli value is not valid, this should not 
happen!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   printf("%s\n", current);
+
+   return CMD_RET_SUCCESS;
+}
+
+static int parser_string_to_gd_flags(const char *parser)
+{
+   if (!strcmp(parser, "old"))
+   return GD_FLG_HUSH_OLD_PARSER;
+   return -1;
+}
+
+static void reset_parser_gd_flags(void)
+{
+   gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+}
+
+static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   char *parser_name;
+   int parser_flag;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   parser_name = argv[1];
+
+   parser_flag = parser_string_to_gd_flags(parser_name);
+   if (parser_flag == -1) {
+   printf("Bad value for parser name: %s\n", parser_name);
+   return CMD_RET_USAGE;
+   }
+
+   if (parser_flag == GD_FLG_HUSH_OLD_PARSER &&
+   !CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) {
+   printf("Want to set current parser to old, but its code was not 
compiled!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   reset_parser_gd_flags();
+   gd->flags |= parser_flag;
+
+   cli_init();
+   cli_loop();
+
+   /* cli_loop() should never return. */
+   return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl parser_sub[] = {
+   U_BOOT_CMD_MKENT(get, 1, 1, do_cli_get, "", ""),
+   U_BOOT_CMD_MKENT(set, 2, 1, do_cli_set, "", ""),
+};
+
+static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   struct cmd_tbl *cp;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   /* drop initial "parser" arg */
+   argc--;
+   argv++;
+
+   cp = find_cmd_tbl(argv[0], parser_sub, ARRAY_SIZE(parser_sub));
+   if (cp)
+   return cp->cmd(cmdtp, flag, argc, argv);
+
+   return CMD_RET_USAGE;
+}
+
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+static char cli_help_text[] =
+   "get - print current cli\n"
+   "set - set the current cli, possible value is: old"
+   ;
+#endif
+
+U_BOOT_CMD(cli, 3, 1, do_cli,
+  "cli",
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+  cli_help_text
+#endif
+);
diff --git a/common/cli.c b/common/cli.c
index e5fe1060d0..d419671e8c 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,7 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
-   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   && CONFIG_IS_ENABLED(HUSH_OLD_PARSER))
gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
diff --git a/doc/usage/cmd/cli.rst b/doc/usage/cmd/cli.rst
new file mode 100644
index 00..89ece3203d
--- /dev/null
+++ b/doc/usage/cmd/cli.rst
@@ -0,0 +1,59 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+cli command
+===
+
+Synopis
+---
+
+::
+
+cli get
+cli set cli_flavor
+
+Description
+---
+
+The cli command permits getting and changing the current parser at runtime.
+
+cli get
+~~~
+
+It shows the current value of the parser used by the CLI.
+
+cli se

[PATCH v11 10/24] global_data.h: add GD_FLG_HUSH_OLD_PARSER flag

2023-11-07 Thread Francis Laniel
This flag is used to indicate we are using the hush parser.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c  | 2 ++
 include/asm-generic/global_data.h | 4 
 2 files changed, 6 insertions(+)

diff --git a/common/cli.c b/common/cli.c
index 3916a7b10a..e5fe1060d0 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,6 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
 
diff --git a/include/asm-generic/global_data.h 
b/include/asm-generic/global_data.h
index e8c6412e3f..0a9b6bd92a 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -697,6 +697,10 @@ enum gd_flags {
 * @GD_FLG_BLOBLIST_READY: bloblist is ready for use
 */
GD_FLG_BLOBLIST_READY = 0x80,
+   /**
+* @GD_FLG_HUSH_OLD_PARSER: Use hush old parser.
+*/
+   GD_FLG_HUSH_OLD_PARSER = 0x100,
 };
 
 #endif /* __ASSEMBLY__ */
-- 
2.34.1



[PATCH v11 08/24] cli: Port Busybox 2021 hush to U-Boot

2023-11-07 Thread Francis Laniel
Adds new file cli_hush_2021.c, it is a copy of Busybox hush file as it was of
time to commit 37460f5da.
This commit modifies Busybox hush to not compile some part specific to Busybox
and adds some code needed by U-Boot.
The modifications consists mainly on adding code #if(n)def guards.

For the moment, this refurbished flavor of hush only permits running command
without any keywords (i.e., if and for are not recognized) or variable expansion
(i.e., echo $foo prints foo and not value stored in variable foo).

A new file was also added to define some functions specific to U-Boot.

Signed-off-by: Francis Laniel 
Signed-off-by: Harald Seiler 
---
 common/cli_hush_2021.c | 274 
 common/cli_hush_upstream.c | 501 -
 2 files changed, 774 insertions(+), 1 deletion(-)
 create mode 100644 common/cli_hush_2021.c

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
new file mode 100644
index 00..6d109933b8
--- /dev/null
+++ b/common/cli_hush_2021.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file defines the compilation unit for the new hush shell version.  The
+ * actual implementation from upstream BusyBox can be found in
+ * `cli_hush_2021_upstream.c` which is included at the end of this file.
+ *
+ * This "wrapper" technique is used to keep the changes to the upstream version
+ * as minmal as possible.  Instead, all defines and redefines necessary are 
done
+ * here, outside the upstream sources.  This will hopefully make upgrades to
+ * newer revisions much easier.
+ *
+ * Copyright (c) 2021, Harald Seiler, DENX Software Engineering, h...@denx.de
+ */
+
+#include  /* readline */
+#include 
+#include  /* malloc, free, realloc*/
+#include /* isalpha, isdigit */
+#include 
+#include 
+#include 
+#include 
+#include /* find_cmd */
+#include 
+
+/*
+ * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
+ */
+#define BB_VER "1.34.0.git37460f5daff9"
+
+/*
+ * Define hush features by the names used upstream.
+ */
+#define ENABLE_HUSH_INTERACTIVE1
+#define ENABLE_FEATURE_EDITING 1
+/* No MMU in U-Boot */
+#define BB_MMU 0
+#define USE_FOR_NOMMU(...) __VA_ARGS__
+#define USE_FOR_MMU(...)
+
+/*
+ * Size-saving "small" ints (arch-dependent)
+ */
+#if CONFIG_IS_ENABLED(X86) || CONFIG_IS_ENABLED(X86_64) || 
CONFIG_IS_ENABLED(MIPS)
+/* add other arches which benefit from this... */
+typedef signed char smallint;
+typedef unsigned char smalluint;
+#else
+/* for arches where byte accesses generate larger code: */
+typedef int smallint;
+typedef unsigned smalluint;
+#endif
+
+/*
+ * Alignment defines used by BusyBox.
+ */
+#define ALIGN1 __attribute__((aligned(1)))
+#define ALIGN2 __attribute__((aligned(2)))
+#define ALIGN4 __attribute__((aligned(4)))
+#define ALIGN8 __attribute__((aligned(8)))
+#define ALIGN_PTR  __attribute__((aligned(sizeof(void*
+
+/*
+ * Miscellaneous compiler/platform defines.
+ */
+#define FAST_FUNC /* not used in U-Boot */
+#define UNUSED_PARAM   __always_unused
+#define ALWAYS_INLINE  __always_inline
+#define NOINLINE   noinline
+
+/*
+ * Defines to provide equivalents to what libc/BusyBox defines.
+ */
+#define EOF(-1)
+#define EXIT_SUCCESS   0
+#define EXIT_FAILURE   1
+
+/*
+ * Stubs to provide libc/BusyBox functions based on U-Boot equivalents where it
+ * makes sense.
+ */
+#define utoa   simple_itoa
+
+static void __noreturn xfunc_die(void)
+{
+   panic("HUSH died!");
+}
+
+#define bb_error_msg_and_die(format, ...) do { \
+panic("HUSH: " format, __VA_ARGS__); \
+} while (0);
+
+#define bb_simple_error_msg_and_die(msg) do { \
+panic_str("HUSH: " msg); \
+} while (0);
+
+/* fdprintf() is used for debug output. */
+static int __maybe_unused fdprintf(int fd, const char *format, ...)
+{
+   va_list args;
+   uint i;
+
+   assert(fd == 2);
+
+   va_start(args, format);
+   i = vprintf(format, args);
+   va_end(args);
+
+   return i;
+}
+
+static void bb_verror_msg(const char *s, va_list p, const char* strerr)
+{
+   /* TODO: what to do with strerr arg? */
+   vprintf(s, p);
+}
+
+static void bb_error_msg(const char *s, ...)
+{
+   va_list p;
+
+   va_start(p, s);
+   bb_verror_msg(s, p, NULL);
+   va_end(p);
+}
+
+static void *xmalloc(size_t size)
+{
+   void *p = NULL;
+   if (!(p = malloc(size)))
+   panic("out of memory");
+   return p;
+}
+
+static void *xzalloc(size_t size)
+{
+   void *p = xmalloc(size);
+   memset(p, 0, size);
+   return p;
+}
+
+static void *xrealloc(void *ptr, size_t size)
+{
+   void *p = NULL;
+   if (!(p = realloc(ptr, size)))
+   panic("out of memor

[PATCH v11 09/24] cli: Add menu for hush parser

2023-11-07 Thread Francis Laniel
For the moment, the menu contains only entry: HUSH_OLD_PARSER which is the
default.
The goal is to prepare the field to add a new hush parser which guarantees
actual behavior is still correct.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig | 13 +
 common/Makefile |  2 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index fd16c3a48e..f3a00ee59b 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -23,6 +23,19 @@ config HUSH_PARSER
  If disabled, you get the old, much simpler behaviour with a somewhat
  smaller memory footprint.
 
+menu "Hush flavor to use"
+   depends on HUSH_PARSER
+
+   config HUSH_OLD_PARSER
+   bool "Use hush old parser"
+   default y
+   help
+ This option enables the old flavor of hush based on hush 
Busybox from
+ 2005.
+
+ It is actually the default U-Boot shell when decided to use 
hush as shell.
+endmenu
+
 config CMDLINE_EDITING
bool "Enable command line editing"
depends on CMDLINE
diff --git a/common/Makefile b/common/Makefile
index cdeadf7202..23851a68e2 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -8,7 +8,7 @@ ifndef CONFIG_SPL_BUILD
 obj-y += init/
 obj-y += main.o
 obj-y += exports.o
-obj-$(CONFIG_HUSH_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 
 # # boards
-- 
2.34.1



[PATCH v11 05/24] test: hush: Test hush commands list

2023-11-07 Thread Francis Laniel
Verifies behavior of commands separated by ';', '&&' and '||'.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/list.c   | 79 ++
 2 files changed, 80 insertions(+)
 create mode 100644 test/hush/list.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index feb4f71956..ff4fe7700b 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -6,3 +6,4 @@
 obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
+obj-y += list.o
diff --git a/test/hush/list.c b/test/hush/list.c
new file mode 100644
index 00..052cf2783c
--- /dev/null
+++ b/test/hush/list.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_semicolon(struct unit_test_state *uts)
+{
+   /* A; B = B truth table. */
+   ut_asserteq(1, run_command("false; false", 0));
+   ut_assertok(run_command("false; true", 0));
+   ut_assertok(run_command("true; true", 0));
+   ut_asserteq(1, run_command("true; false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_semicolon, 0);
+
+static int hush_test_and(struct unit_test_state *uts)
+{
+   /* A && B truth table. */
+   ut_asserteq(1, run_command("false && false", 0));
+   ut_asserteq(1, run_command("false && true", 0));
+   ut_assertok(run_command("true && true", 0));
+   ut_asserteq(1, run_command("true && false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and, 0);
+
+static int hush_test_or(struct unit_test_state *uts)
+{
+   /* A || B truth table. */
+   ut_asserteq(1, run_command("false || false", 0));
+   ut_assertok(run_command("false || true", 0));
+   ut_assertok(run_command("true || true", 0));
+   ut_assertok(run_command("true || false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or, 0);
+
+static int hush_test_and_or(struct unit_test_state *uts)
+{
+   /* A && B || C truth table. */
+   ut_asserteq(1, run_command("false && false || false", 0));
+   ut_asserteq(1, run_command("false && false || true", 0));
+   ut_asserteq(1, run_command("false && true || true", 0));
+   ut_asserteq(1, run_command("false && true || false", 0));
+   ut_assertok(run_command("true && true || false", 0));
+   ut_asserteq(1, run_command("true && false || false", 0));
+   ut_assertok(run_command("true && false || true", 0));
+   ut_assertok(run_command("true && true || true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and_or, 0);
+
+static int hush_test_or_and(struct unit_test_state *uts)
+{
+   /* A || B && C truth table. */
+   ut_asserteq(1, run_command("false || false && false", 0));
+   ut_asserteq(1, run_command("false || false && true", 0));
+   ut_assertok(run_command("false || true && true", 0));
+   ut_asserteq(1, run_command("false || true && false", 0));
+   ut_assertok(run_command("true || true && false", 0));
+   ut_assertok(run_command("true || false && false", 0));
+   ut_assertok(run_command("true || false && true", 0));
+   ut_assertok(run_command("true || true && true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or_and, 0);
-- 
2.34.1



[PATCH v11 06/24] test: hush: Test hush loops

2023-11-07 Thread Francis Laniel
The added tests verifies correct behavior of for, while and until loops.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/loop.c   | 65 ++
 2 files changed, 66 insertions(+)
 create mode 100644 test/hush/loop.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index ff4fe7700b..a2d98815e5 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -7,3 +7,4 @@ obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
 obj-y += list.o
+obj-y += loop.o
diff --git a/test/hush/loop.c b/test/hush/loop.c
new file mode 100644
index 00..ca777e38fe
--- /dev/null
+++ b/test/hush/loop.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_for(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   ut_assertok(run_command("for loop_i in foo bar quux quux; do echo 
$loop_i; done", 0));
+   ut_assert_nextline("foo");
+   ut_assert_nextline("bar");
+   ut_assert_nextline("quux");
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_for, 0);
+
+static int hush_test_while(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_while, 0);
+
+static int hush_test_until(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+   env_set("loop_bar", "bar");
+
+   /*
+* WARNING We have to use environment variable because it is not 
possible
+* resetting local variable.
+*/
+   ut_assertok(run_command("until test -z \"$loop_bar\"; do echo quux; 
setenv loop_bar; done", 0));
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   /*
+* Loop normally resets foo environment variable, but we reset it here 
in
+* case the test failed.
+*/
+   env_set("loop_bar", NULL);
+   return 0;
+}
+HUSH_TEST(hush_test_until, 0);
-- 
2.34.1



[PATCH v11 04/24] test: hush: Test hush variable expansion

2023-11-07 Thread Francis Laniel
Verifies shell variables are replaced by their values.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/Makefile   |   1 +
 test/hush/dollar.c   | 167 +++
 test/py/tests/test_ut.py |   8 +-
 3 files changed, 175 insertions(+), 1 deletion(-)
 create mode 100644 test/hush/dollar.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index a3c9ae5106..feb4f71956 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -5,3 +5,4 @@
 
 obj-y += cmd_ut_hush.o
 obj-y += if.o
+obj-y += dollar.o
diff --git a/test/hush/dollar.c b/test/hush/dollar.c
new file mode 100644
index 00..defb2c3fd0
--- /dev/null
+++ b/test/hush/dollar.c
@@ -0,0 +1,167 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_simple_dollar(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline_empty();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${dollar_foo}", 0));
+   ut_assert_nextline_empty();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo=bar", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${dollar_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo=\\$bar", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("$bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='$bar'", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("$bar");
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo=bar quux", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
+   /* Two next lines contain error message */
+   ut_assert_skipline();
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   /*
+* This one is buggy.
+* ut_assert_nextline("bar \"quux");
+* ut_assert_console_end();
+*
+* So, let's reset output:
+*/
+   console_record_reset_enable();
+
+   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+
+   ut_assertok(run_command("dollar_foo='bar quux'", 0));
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar quux");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable dollar_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_simple_dollar, 0);
+
+static int hush_test_env_dollar(struct unit_test_state *uts)
+{
+   env_set("env_foo", "bar");
+   console_record_reset_enable();
+
+   ut_assertok(run_command("echo $env_foo", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   ut_assertok(run_command("echo ${env_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   /* Environment variables have priority over local variable */
+   ut_assertok(run_command("env_foo=quux", 0));
+   ut_assertok(run_command("echo ${env_foo}", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   /* Clean up setting the variable */
+   env_set("env_foo", NULL);
+
+   puts("Beware: this test set local variable env_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_env_dollar, 0);
+
+static int hush_test_command_dollar(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   ut_assertok(run_command("dollar_bar=\"echo bar\"", 0));
+
+   ut_assertok(run_command("$dollar_bar", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end(

[PATCH v11 03/24] test/py: hush_if_test: Remove the test file

2023-11-07 Thread Francis Laniel
5804ebfeb1ce ("test: hush: Test hush if/else") translated this test to a C test,
so this python file is no more needed.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/py/tests/test_hush_if_test.py | 197 -
 1 file changed, 197 deletions(-)
 delete mode 100644 test/py/tests/test_hush_if_test.py

diff --git a/test/py/tests/test_hush_if_test.py 
b/test/py/tests/test_hush_if_test.py
deleted file mode 100644
index 3b4b6fcaf4..00
--- a/test/py/tests/test_hush_if_test.py
+++ /dev/null
@@ -1,197 +0,0 @@
-# SPDX-License-Identifier: GPL-2.0
-# Copyright (c) 2015-2016, NVIDIA CORPORATION. All rights reserved.
-
-# Test operation of the "if" shell command.
-
-import os
-import os.path
-import pytest
-
-# TODO: These tests should be converted to a C test.
-# For more information please take a look at the thread
-# https://lists.denx.de/pipermail/u-boot/2019-October/388732.html
-
-pytestmark = pytest.mark.buildconfigspec('hush_parser')
-
-# The list of "if test" conditions to test.
-subtests = (
-# Base if functionality.
-
-('true', True),
-('false', False),
-
-# Basic operators.
-
-('test aaa = aaa', True),
-('test aaa = bbb', False),
-
-('test aaa != bbb', True),
-('test aaa != aaa', False),
-
-('test aaa < bbb', True),
-('test bbb < aaa', False),
-
-('test bbb > aaa', True),
-('test aaa > bbb', False),
-
-('test 123 -eq 123', True),
-('test 123 -eq 456', False),
-
-('test 123 -ne 456', True),
-('test 123 -ne 123', False),
-
-('test 123 -lt 456', True),
-('test 123 -lt 123', False),
-('test 456 -lt 123', False),
-
-('test 123 -le 456', True),
-('test 123 -le 123', True),
-('test 456 -le 123', False),
-
-('test 456 -gt 123', True),
-('test 123 -gt 123', False),
-('test 123 -gt 456', False),
-
-('test 456 -ge 123', True),
-('test 123 -ge 123', True),
-('test 123 -ge 456', False),
-
-# Octal tests
-
-('test 010 -eq 010', True),
-('test 010 -eq 011', False),
-
-('test 010 -ne 011', True),
-('test 010 -ne 010', False),
-
-# Hexadecimal tests
-
-('test 0x200 -gt 0x201', False),
-('test 0x200 -gt 0x200', False),
-('test 0x200 -gt 0x1ff', True),
-
-# Mixed tests
-
-('test 010 -eq 10', False),
-('test 010 -ne 10', True),
-('test 0xa -eq 10', True),
-('test 0xa -eq 012', True),
-
-('test 200 -gt 0x1ff', False),
-('test 0x200 -gt 1ff', True),
-('test 0x200 -lt 1ff', False),
-('test 0x200 -eq 200', False),
-('test 0x200 -ne 200', True),
-
-('test -z ""', True),
-('test -z "aaa"', False),
-
-('test -n "aaa"', True),
-('test -n ""', False),
-
-# Inversion of simple tests.
-
-('test ! aaa = aaa', False),
-('test ! aaa = bbb', True),
-('test ! ! aaa = aaa', True),
-('test ! ! aaa = bbb', False),
-
-# Binary operators.
-
-('test aaa != aaa -o bbb != bbb', False),
-('test aaa != aaa -o bbb = bbb', True),
-('test aaa = aaa -o bbb != bbb', True),
-('test aaa = aaa -o bbb = bbb', True),
-
-('test aaa != aaa -a bbb != bbb', False),
-('test aaa != aaa -a bbb = bbb', False),
-('test aaa = aaa -a bbb != bbb', False),
-('test aaa = aaa -a bbb = bbb', True),
-
-# Inversion within binary operators.
-
-('test ! aaa != aaa -o ! bbb != bbb', True),
-('test ! aaa != aaa -o ! bbb = bbb', True),
-('test ! aaa = aaa -o ! bbb != bbb', True),
-('test ! aaa = aaa -o ! bbb = bbb', False),
-
-('test ! ! aaa != aaa -o ! ! bbb != bbb', False),
-('test ! ! aaa != aaa -o ! ! bbb = bbb', True),
-('test ! ! aaa = aaa -o ! ! bbb != bbb', True),
-('test ! ! aaa = aaa -o ! ! bbb = bbb', True),
-)
-
-def exec_hush_if(u_boot_console, expr, result):
-"""Execute a shell "if" command, and validate its result."""
-
-config = u_boot_console.config.buildconfig
-maxargs = int(config.get('config_sys_maxargs', '0'))
-args = len(expr.split(' ')) - 1
-if args > maxargs:
-u_boot_console.log.warning('CONFIG_SYS_MAXARGS too low; need ' +
-str(args))
-pytest.skip()
-
-cmd = 'if ' + expr + '; then echo true; else echo false; fi'
-response = u_boot_console.run_command(cmd)
-assert response.strip() == str(result).lower()
-
-@pytest.mark.buildconfigspec('cmd_echo')
-@pytest.mark.parametrize('expr,result', subtests)
-def test_hush_if_test(u_boot_console, expr, result):
-"""Test a single "if test" condition."""
-
-exec_hush_if(u_boot_console, expr, result)
-
-def test_hush_z(u_boot_console):
-"""Test the -z operator"""
-u_boot_console.run_command('setenv ut_var_nonexistent')
-u_boot_console.run_command('set

[PATCH v11 01/24] test: Add framework to test hush behavior

2023-11-07 Thread Francis Laniel
Introduce a new subcommand to ut: ut hush.
For the moment, this command does nothing, future commits will add tests which
will be run on command call.

Note that CONFIG_HUSH_PARSER must be defined to compile this new subcommand.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 include/test/hush.h | 15 +++
 include/test/suites.h   |  1 +
 test/Makefile   |  3 +++
 test/cmd_ut.c   |  6 ++
 test/hush/Makefile  |  6 ++
 test/hush/cmd_ut_hush.c | 20 
 6 files changed, 51 insertions(+)
 create mode 100644 include/test/hush.h
 create mode 100644 test/hush/Makefile
 create mode 100644 test/hush/cmd_ut_hush.c

diff --git a/include/test/hush.h b/include/test/hush.h
new file mode 100644
index 00..cca66544a0
--- /dev/null
+++ b/include/test/hush.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#ifndef __TEST_HUSH_H__
+#define __TEST_HUSH_H__
+
+#include 
+
+/* Declare a new environment test */
+#define HUSH_TEST(_name, _flags)   UNIT_TEST(_name, _flags, hush_test)
+
+#endif /* __TEST_HUSH_H__ */
diff --git a/include/test/suites.h b/include/test/suites.h
index ad4fc926f4..fc0f9334c0 100644
--- a/include/test/suites.h
+++ b/include/test/suites.h
@@ -42,6 +42,7 @@ int do_ut_env(struct cmd_tbl *cmdtp, int flag, int argc, char 
*const argv[]);
 int do_ut_exit(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_fdt(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_font(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
+int do_ut_hush(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_lib(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_loadm(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]);
 int do_ut_log(struct cmd_tbl *cmdtp, int flag, int argc, char * const argv[]);
diff --git a/test/Makefile b/test/Makefile
index 8e1fed2c28..10cf6a7976 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -17,6 +17,9 @@ obj-$(CONFIG_FUZZ) += fuzz/
 ifndef CONFIG_SANDBOX_VPL
 obj-$(CONFIG_UNIT_TEST) += lib/
 endif
+ifneq ($(CONFIG_HUSH_PARSER),)
+obj-$(CONFIG_$(SPL_)CMDLINE) += hush/
+endif
 obj-$(CONFIG_$(SPL_)CMDLINE) += print_ut.o
 obj-$(CONFIG_$(SPL_)CMDLINE) += str_ut.o
 obj-$(CONFIG_UT_TIME) += time_ut.o
diff --git a/test/cmd_ut.c b/test/cmd_ut.c
index 2d5b80f992..2f96dfa934 100644
--- a/test/cmd_ut.c
+++ b/test/cmd_ut.c
@@ -118,6 +118,9 @@ static struct cmd_tbl cmd_ut_sub[] = {
 #ifdef CONFIG_CMD_ADDRMAP
U_BOOT_CMD_MKENT(addrmap, CONFIG_SYS_MAXARGS, 1, do_ut_addrmap, "", ""),
 #endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+   U_BOOT_CMD_MKENT(hush, CONFIG_SYS_MAXARGS, 1, do_ut_hush, "", ""),
+#endif
 #ifdef CONFIG_CMD_LOADM
U_BOOT_CMD_MKENT(loadm, CONFIG_SYS_MAXARGS, 1, do_ut_loadm, "", ""),
 #endif
@@ -210,6 +213,9 @@ U_BOOT_LONGHELP(ut,
 #ifdef CONFIG_CONSOLE_TRUETYPE
"\nfont - font command"
 #endif
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+   "\nhush - Test hush behavior"
+#endif
 #ifdef CONFIG_CMD_LOADM
"\nloadm - loadm command parameters and loading memory blob"
 #endif
diff --git a/test/hush/Makefile b/test/hush/Makefile
new file mode 100644
index 00..dfa2a92615
--- /dev/null
+++ b/test/hush/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0+
+#
+# (C) Copyright 2021
+# Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+
+obj-y += cmd_ut_hush.o
diff --git a/test/hush/cmd_ut_hush.c b/test/hush/cmd_ut_hush.c
new file mode 100644
index 00..48a1adbf28
--- /dev/null
+++ b/test/hush/cmd_ut_hush.c
@@ -0,0 +1,20 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+int do_ut_hush(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
+{
+   struct unit_test *tests = UNIT_TEST_SUITE_START(hush_test);
+   const int n_ents = UNIT_TEST_SUITE_COUNT(hush_test);
+
+   return cmd_ut_category("hush", "hush_test_",
+  tests, n_ents, argc, argv);
+}
-- 
2.34.1



[PATCH v11 02/24] test: hush: Test hush if/else

2023-11-07 Thread Francis Laniel
As asked in commit 9c6bf1715f6a ("test/py: hush_if_test: Add tests to cover
octal/hex values"), this commit translates test_hush_if_test.py to a C test.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |   1 +
 test/hush/if.c | 316 +
 2 files changed, 317 insertions(+)
 create mode 100644 test/hush/if.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index dfa2a92615..a3c9ae5106 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -4,3 +4,4 @@
 # Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
 
 obj-y += cmd_ut_hush.o
+obj-y += if.o
diff --git a/test/hush/if.c b/test/hush/if.c
new file mode 100644
index 00..b7200e32ec
--- /dev/null
+++ b/test/hush/if.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+/*
+ * All tests will execute the following:
+ * if condition_to_test; then
+ *   true
+ * else
+ *   false
+ * fi
+ * If condition is true, command returns 1, 0 otherwise.
+ */
+const char *if_format = "if %s; then true; else false; fi";
+
+static int hush_test_if_base(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_format, "true");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "false");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_if_base, 0);
+
+static int hush_test_if_basic_operators(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_format, "test aaa = aaa");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa = bbb");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa != bbb");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa != aaa");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa < bbb");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test bbb < aaa");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test bbb > aaa");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test aaa > bbb");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -eq 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -eq 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ne 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ne 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -lt 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -lt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -lt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -le 456");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -le 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -le 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -gt 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -gt 123");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -gt 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 456 -ge 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ge 123");
+   ut_assertok(run_command(if_formatted, 0));
+
+   sprintf(if_formatted, if_format, "test 123 -ge 456");
+   ut_asserteq(1, run_command(if_formatted, 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_if_basic_operators, 0);
+
+static int hush_test_if_octal(struct unit_test_state *uts)
+{
+   char if_formatted[128];
+
+   sprintf(if_formatted, if_f

[PATCH v11 00/24] Modernize U-Boot shell

2023-11-07 Thread Francis Laniel
ts are handled by test command and not hush
  itself. This permitted to have the ut fdt to pass.
  * Fix a problem with how exit was handled. This was reported by the ut exit
  test.
 v6:
  * There was no v6 and I got mixed up with version.
 v7:
  * Bumped upstream busybox hush commits until 9th May 2023.
  * Renamed parser command to change parser at runtime to cli and added
  documentation.
  * Added better separation of patches.
  * Removed code about __gnu_thumb1_case_si as it was merged in another series.
  * Various cleaning.
 v8:
  * Bumped upstream busybox hush commits until 25th May 2023.
 v9:
  * Bumped upstream busybox hush commits until 2nd October 2023.
 v10:
  * Fixed a build error in commit adding cli command.
  * Add new CONFIG_HUSH_SELECTABLE to only build cli command if the two shell
  flavors are selected.

Francis Laniel (24):
  test: Add framework to test hush behavior
  test: hush: Test hush if/else
  test/py: hush_if_test: Remove the test file
  test: hush: Test hush variable expansion
  test: hush: Test hush commands list
  test: hush: Test hush loops
  cli: Add Busybox upstream hush.c file
  cli: Port Busybox 2021 hush to U-Boot
  cli: Add menu for hush parser
  global_data.h: add GD_FLG_HUSH_OLD_PARSER flag
  cmd: Add new cli command
  cli: Enables using hush 2021 parser as command line parser
  cli: hush_2021: Enable variables expansion for hush 2021
  cli: hush_2021: Add functions to be called from run_command()
  cli: add hush 2021 as parser for run_command*()
  test: hush: Fix instructions list tests for hush 2021
  test: hush: Fix variable expansion tests for hush 2021
  cli: hush_2021: Enable using < and > as string compare operators
  cli: hush_2021: Enable if keyword
  cli: hush_2021: Enable loops
  test: hush: Fix loop tests for hush 2021
  cli: hush_2021: Add upstream commits up to 2nd October 2023.
  DO NOT MERGE: only to make CI happy
  DO NOT MERGE: ci: Build the world in any case.

 .azure-pipelines.yml   | 1 +
 cmd/Kconfig|26 +
 cmd/Makefile   | 2 +
 cmd/cli.c  |   134 +
 common/Makefile| 3 +-
 common/cli.c   |82 +-
 common/cli_hush_2021.c |   324 +
 common/cli_hush_upstream.c | 13030 +++
 configs/sheevaplug_defconfig   | 1 +
 doc/usage/cmd/cli.rst  |74 +
 doc/usage/index.rst| 1 +
 include/asm-generic/global_data.h  | 8 +
 include/cli_hush.h |51 +-
 include/test/hush.h|15 +
 include/test/suites.h  | 1 +
 test/Makefile  | 3 +
 test/cmd_ut.c  | 6 +
 test/hush/Makefile |10 +
 test/hush/cmd_ut_hush.c|20 +
 test/hush/dollar.c |   226 +
 test/hush/if.c |   316 +
 test/hush/list.c   |   140 +
 test/hush/loop.c   |91 +
 test/py/tests/test_hush_if_test.py |   197 -
 test/py/tests/test_ut.py   | 8 +-
 tools/patman/series.py | 4 +
 26 files changed, 14563 insertions(+), 211 deletions(-)
 create mode 100644 cmd/cli.c
 create mode 100644 common/cli_hush_2021.c
 create mode 100644 common/cli_hush_upstream.c
 create mode 100644 doc/usage/cmd/cli.rst
 create mode 100644 include/test/hush.h
 create mode 100644 test/hush/Makefile
 create mode 100644 test/hush/cmd_ut_hush.c
 create mode 100644 test/hush/dollar.c
 create mode 100644 test/hush/if.c
 create mode 100644 test/hush/list.c
 create mode 100644 test/hush/loop.c
 delete mode 100644 test/py/tests/test_hush_if_test.py


Best regards and thank you in advance.
---
[1] https://lists.denx.de/pipermail/u-boot/2021-July/453347.html
[2] https://runtimeterror.com/tech/lil/
[3] https://lists.denx.de/pipermail/u-boot/2021-July/453790.html
[4] https://lists.denx.de/pipermail/u-boot/2021-July/453848.html
[5] https://lists.denx.de/pipermail/u-boot/2021-August/458569.html
--
2.34.1



[RFC PATCH v10 24/24] DO NOT MERGE: ci: Build the world in any case.

2023-10-04 Thread Francis Laniel
The CI can fails previously for reasons which are not tied to hush 2021.
Let's build the world in any case to check everything is OK there too.

Signed-off-by: Francis Laniel 
---
 .azure-pipelines.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/.azure-pipelines.yml b/.azure-pipelines.yml
index da701e42da..3417722b21 100644
--- a/.azure-pipelines.yml
+++ b/.azure-pipelines.yml
@@ -447,6 +447,7 @@ stages:
 retryCountOnTaskFailure: 2 # QEMU may be too slow, etc.
 
 - stage: world_build
+  condition: always()
   jobs:
   - job: build_the_world
 timeoutInMinutes: 0 # Use the maximum allowed
-- 
2.34.1



[RFC PATCH v10 23/24] DO NOT MERGE: only to make CI happy

2023-10-04 Thread Francis Laniel
This commit set CONFIG_HUSH_PARSER_2021 as the default to trigger the CI with
this parser.

Nonetheless, the keymile (i.e. VENDOR_KM) board family is not compatible with
new 2021 hush parser.
Indeed, This boards used set_local_var() to store some variables as local shell.
They then used get_local_var() to retrieve the variables values.
Sadly, this two functions do not exist with CONFIG_HUSH_PARSER_2021.
A patch was proposed to use environment variables rather than local variables
but it does not tackle the problem, so complementary work is needed to make
this boards use CONFIG_HUSH_PARSER_2021 [1].

We also remove a #undef of CONFIG_FEATURE_SH_STANDALONE as it does not exist in
U-Boot and causes troubles in the CI.

We also set CONFIG_LTO for kirkwoord sheevaplug and phytec bk4r1, otherwise it
hits its board size limit.

We also disable some check for pylint as it was not able to find future for
commit object.

Acked-by: Tony Dinh 
Signed-off-by: Francis Laniel 
[1] https://marc.info/?l=u-boot=165541917618725=2
---
 cmd/Kconfig  | 3 ++-
 common/cli_hush_upstream.c   | 1 -
 configs/sheevaplug_defconfig | 1 +
 tools/patman/series.py   | 4 
 4 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 36595638a2..e113ed49ac 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -28,7 +28,7 @@ menu "Hush flavor to use"
 
config HUSH_OLD_PARSER
bool "Use hush old parser"
-   default y
+   default y if VENDOR_KM
help
  This option enables the old flavor of hush based on hush 
Busybox from
  2005.
@@ -37,6 +37,7 @@ menu "Hush flavor to use"
 
config HUSH_2021_PARSER
bool "Use hush 2021 parser"
+   default y if !VENDOR_KM
help
  This option enables the new flavor of hush based on hush 
Busybox from
  2021.
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 709c055d2f..243710ab51 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -427,7 +427,6 @@
 #include "NUM_APPLETS.h"
 #if NUM_APPLETS == 1
 /* STANDALONE does not make sense, and won't compile */
-# undef CONFIG_FEATURE_SH_STANDALONE
 # undef ENABLE_FEATURE_SH_STANDALONE
 # undef IF_FEATURE_SH_STANDALONE
 # undef IF_NOT_FEATURE_SH_STANDALONE
diff --git a/configs/sheevaplug_defconfig b/configs/sheevaplug_defconfig
index 2e4901b840..365f779cc8 100644
--- a/configs/sheevaplug_defconfig
+++ b/configs/sheevaplug_defconfig
@@ -16,6 +16,7 @@ CONFIG_ENV_OFFSET=0x8
 CONFIG_DEFAULT_DEVICE_TREE="kirkwood-sheevaplug"
 CONFIG_IDENT_STRING="\nMarvell-Sheevaplug"
 CONFIG_SYS_LOAD_ADDR=0x80
+CONFIG_LTO=y
 CONFIG_HAS_BOARD_SIZE_LIMIT=y
 CONFIG_BOARD_SIZE_LIMIT=524288
 CONFIG_BOOTDELAY=3
diff --git a/tools/patman/series.py b/tools/patman/series.py
index 6866e1dbd0..f99818e33a 100644
--- a/tools/patman/series.py
+++ b/tools/patman/series.py
@@ -316,6 +316,8 @@ class Series(dict):
 # Show progress any commits that are taking forever
 lastlen = 0
 while True:
+# pylint does not find future which is set above.
+# pylint: disable=E1101
 left = [commit for commit in self.commits
 if not commit.future.done()]
 if not left:
@@ -333,6 +335,8 @@ class Series(dict):
 print('Cc processing complete')
 
 for commit in self.commits:
+# pylint does not find future which is set above.
+# pylint: disable=E1101
 cc = commit.future.result()
 all_ccs += cc
 print(commit.patch, '\0'.join(sorted(set(cc))), file=fd)
-- 
2.34.1



[RFC PATCH v10 22/24] cli: hush_2021: Add upstream commits up to 2nd October 2023.

2023-10-04 Thread Francis Laniel
This commit adds the following hush busybox upstream commits:
791b222dd55d ("sleep: fix "sleep -- ARGS"")
5353df91cba7 ("Update applet size estimates")
e41e481fd571 ("hush: fix a compile failure")
07a95cfcabb0 ("ash: disable check for "good" function name, bash does not check 
this")
e5692e2342c6 ("hush: quote values in "readonly" output")
96769486e20f ("shell: move varcmp() to shell_common.h and use it in hush")
bab8828b0dad ("hush: fix expansion of space in "a=${a:+$a }c" construct")
b5be8da350b5 ("hush: make "false" built-in")
6824298ab4d3 ("hush: fix ELIF cmd1;cmd2 THEN ... not executing cmd2, closes 
15571")
3a7f00eadcf4 ("hush: add comment about abort on syntax error %{^}")
acae889dd972 ("ash,hush: tab completion of functions and aliases")
90b607d79a13 ("hush: quote variable values printed by "set" (match ash 
behavior)")
6748e6494c22 ("hush (NOMMU): fix LINENO in execed children")
fd5fb2d2b596 ("hush: speed up "big heredoc" code")
1409432d072e ("hush: add TODO comment")
93ae7464e6e4 ("hush: restore SIGHUP handling, this time explain why we do what 
we do")
1fdb33bd07e5 ("hush: restore tty pgrp on SIGHUP")
6101b6d3eaa0 ("hush: remove special handling of SIGHUP")
93e0898c663a ("shell: fix SIGWINCH and SIGCHLD (in hush) interrupting line 
input, closes 15256")
969e00816835 ("hush: code shrink")
27be0e8cfeb6 ("shell: fix compile failures in some configs")
7d1c7d833785 ("ash,hush: use HOME for tab completion and prompts")
21afddefd258 ("hush: fix "error: invalid preprocessing directive ##"")
e53c7dbafc78 ("hush: fix set -n to act immediately, not just after run_list()")
574b9c446da1 ("hush: fix var_LINENO3.tests failure")
49bcf9f40cff ("hush: speed up ${x//\*/|} too")
53b2fdcdba4c ("*: add NOINLINEs where code noticeably shrinks")
7c3e96d4b3d4 ("shell: use more compact SHELL_ASH / HUSH config defines. no code 
changes")
62f1eed1e191 ("hush: in a comment, document what -i might be doing")
aaf3d5ba74c5 ("shell: tweak --help")
db5546ca1018 ("libbb: code shrink: introduce and use [_]exit_SUCCESS()")
931c55f9e2b4 ("libbb: invert the meaning of SETUP_ENV_NO_CHDIR -> 
SETUP_ENV_CHDIR")
12566e7f9b5e ("ash,hush: fix handling of SIGINT while waiting for interactive 
input")
987be932ed3c ("*: slap on a few ALIGN_PTR where appropriate")

Signed-off-by: Francis Laniel 
---
 common/cli_hush_2021.c |  20 +-
 common/cli_hush_upstream.c | 407 ++---
 2 files changed, 304 insertions(+), 123 deletions(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index 0a207d147b..15a5d3134e 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -26,7 +26,7 @@
 /*
  * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
  */
-#define BB_VER "1.34.0.git37460f5daff9"
+#define BB_VER "1.35.0.git7d1c7d833785"
 
 /*
  * Define hush features by the names used upstream.
@@ -236,6 +236,24 @@ static size_t list_size(char **list)
return size;
 }
 
+static int varcmp(const char *p, const char *q)
+{
+   int c, d;
+
+   while ((c = *p) == (d = *q)) {
+   if (c == '\0' || c == '=')
+   goto out;
+   p++;
+   q++;
+   }
+   if (c == '=')
+   c = '\0';
+   if (d == '=')
+   d = '\0';
+out:
+   return c - d;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 23392939e1..709c055d2f 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -91,7 +91,7 @@
  * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
  */
 //config:config HUSH
-//config:  bool "hush (68 kb)"
+//config:  bool "hush (70 kb)"
 //config:  default y
 //config:  select SHELL_HUSH
 //config:  help
@@ -339,7 +339,7 @@
  * therefore we don't show them either.
  */
 //usage:#define hush_trivial_usage
-//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
+//usage:   "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
 //usage:#define hush_full_usage "\n\n"
 //usage:   "Unix shell interpreter"
 
@@ -374,7 +374,7 @@
 # define F_DUPFD_CLOEXEC F_DUPFD
 #endif
 
-#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !(ENABLE_ASH || ENABLE_SH_IS_ASH || 
ENABLE_BASH_IS_ASH)
+#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !ENABLE_SHELL_ASH
 # include "embedded_scripts.h"

[RFC PATCH v10 21/24] test: hush: Fix loop tests for hush 2021

2023-10-04 Thread Francis Laniel
Modifies return code got from while loop as hush 2021 always returns 0 from
while loop.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/loop.c | 34 ++
 1 file changed, 30 insertions(+), 4 deletions(-)

diff --git a/test/hush/loop.c b/test/hush/loop.c
index ca777e38fe..add5402a32 100644
--- a/test/hush/loop.c
+++ b/test/hush/loop.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_for(struct unit_test_state *uts)
 {
@@ -21,7 +24,12 @@ static int hush_test_for(struct unit_test_state *uts)
ut_assert_nextline("quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_i=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_i and it cannot 
be unset!");
+   }
 
return 0;
 }
@@ -31,12 +39,30 @@ static int hush_test_while(struct unit_test_state *uts)
 {
console_record_reset_enable();
 
-   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
-   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* Hush 2021 always returns 0 from while loop...
+* You can see code snippet near this line to have a better
+* understanding:
+* debug_printf_exec(": while expr is false: breaking 
(exitcode:EXIT_SUCCESS)\n");
+*/
+   ut_assertok(run_command("while test -z \"$loop_foo\"; do echo 
bar; loop_foo=quux; done", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   /*
+* Exit status is that of test, so 1 since test is false to quit
+* the loop.
+*/
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do 
echo bar; loop_foo=quux; done", 0));
+   }
ut_assert_nextline("bar");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("loop_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable loop_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
-- 
2.34.1



[RFC PATCH v10 18/24] cli: hush_2021: Enable using < and > as string compare operators

2023-10-04 Thread Francis Laniel
In Busybox hush, '<' and '>' are used as redirection operators.
For example, cat foo > bar will write content of file foo inside file bar.
In U-Boot, we do not have file system, so we can hardly redirect command output
inside a file.

But, in actual U-Boot hush, these operators ('<' and '>') are used as string
compare operators.
For example, test aaa < bbb returns 0 as aaa is before bbb in the dictionary.
Busybox hush also permits this, but operators need to be escaped ('\<' and
'\>').
Indeed, if escaping is needed it permits the developer to think about its code,
as in a lot of case, we want to compare integers (using '-lt' or '-gt') rather
than strings.

As testing in U-Boot is handled by the test command, we will stick with the
original behaviour and not adapt to Busybox one.

Nonetheless, if one day we decide to implement test with '[[ ]]', we will then
stick to upstream Busybox behavior.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 23 ++-
 1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index dbf77ad503..68a9a4bb72 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -6160,7 +6160,28 @@ static struct pipe *parse_stream(char **pstring,
if (parse_redirect(, redir_fd, redir_style, input))
goto parse_error_exitcode1;
continue; /* get next char */
-#endif /* !__U_BOOT__ */
+#else /* __U_BOOT__ */
+   /*
+* In U-Boot, '<' and '>' can be used in test command 
to test if
+* a string is, alphabetically, before or after another.
+* In 2021 Busybox hush, we will keep the same behavior 
and so not treat
+* them as redirection operator.
+*
+* Indeed, in U-Boot, tests are handled by the test 
command and not by the
+* shell code.
+* So, better to give this character as input to test 
command.
+*
+* NOTE In my opinion, when you use '<' or '>' I am 
almost sure
+* you wanted to use "-gt" or "-lt" in place, so 
thinking to
+* escape these will make you should check your code 
(sh syntax
+* at this level is, for me, error prone).
+*/
+   case '>':
+   fallthrough;
+   case '<':
+   o_addQchr(, ch);
+   continue;
+#endif /* __U_BOOT__ */
case '#':
if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
/* skip "#comment" */
-- 
2.34.1



[RFC PATCH v10 17/24] test: hush: Fix variable expansion tests for hush 2021

2023-10-04 Thread Francis Laniel
Modifies the expected result for hush 2021.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As hush 2021 is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/dollar.c | 79 --
 1 file changed, 69 insertions(+), 10 deletions(-)

diff --git a/test/hush/dollar.c b/test/hush/dollar.c
index defb2c3fd0..f23392c72d 100644
--- a/test/hush/dollar.c
+++ b/test/hush/dollar.c
@@ -9,6 +9,9 @@
 #include 
 #include 
 #include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
 
 static int hush_test_simple_dollar(struct unit_test_state *uts)
 {
@@ -51,13 +54,29 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
ut_asserteq(1, run_command("dollar_foo='bar quux", 0));
/* Next line contains error message */
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* For some strange reasons, the console is not empty after
+* running above command.
+* So, we reset it to not have side effects for other tests.
+*/
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_asserteq(1, run_command("dollar_foo=bar quux\"", 0));
/* Two next lines contain error message */
ut_assert_skipline();
ut_assert_skipline();
-   ut_assert_console_end();
+
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* See above comments. */
+   console_record_reset_enable();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar \"quux'", 0));
 
@@ -71,17 +90,35 @@ static int hush_test_simple_dollar(struct unit_test_state 
*uts)
 */
console_record_reset_enable();
 
-   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
-   /* Next line contains error message */
-   ut_assert_skipline();
-   ut_assert_console_end();
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* Old parser returns an error because it waits for closing
+* '\'', but this behavior is wrong as the '\'' is surrounded by
+* '"', so no need to wait for a closing one.
+*/
+   ut_assertok(run_command("dollar_foo=\"bar 'quux\"", 0));
+
+   ut_assertok(run_command("echo $dollar_foo", 0));
+   ut_assert_nextline("bar 'quux");
+   ut_assert_console_end();
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("dollar_foo=\"bar 'quux\"", 0));
+   /* Next line contains error message */
+   ut_assert_skipline();
+   ut_assert_console_end();
+   }
 
ut_assertok(run_command("dollar_foo='bar quux'", 0));
ut_assertok(run_command("echo $dollar_foo", 0));
ut_assert_nextline("bar quux");
ut_assert_console_end();
 
-   puts("Beware: this test set local variable dollar_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("dollar_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable dollar_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -109,7 +146,12 @@ static int hush_test_env_dollar(struct unit_test_state 
*uts)
/* Clean up setting the variable */
env_set("env_foo", NULL);
 
-   puts("Beware: this test set local variable env_foo and it cannot be 
unset!");
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /* Reset local variable. */
+   ut_assertok(run_command("env_foo=", 0));
+   } else if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   puts("Beware: this test set local variable env_foo and it 
cannot be unset!");
+   }
 
return 0;
 }
@@ -144,7 +186,18 @@ static int hush_test_command_dollar(struct unit_test_state 
*uts)
ut_assertok(run_command("dollar_bar='echo bar\\n'", 0));
 
ut_assertok(run_command("$dollar_bar", 0));
-   ut_assert_nextline("barn");
+
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+  

[RFC PATCH v10 19/24] cli: hush_2021: Enable if keyword

2023-10-04 Thread Francis Laniel
Adds support for "if then else" construct both for command line interface and
through run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_2021.c | 11 +++
 common/cli_hush_upstream.c | 12 
 2 files changed, 15 insertions(+), 8 deletions(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index 7dd30ea0ef..aba5dcbbcd 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -33,6 +33,7 @@
  */
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
+#define ENABLE_HUSH_IF 1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
@@ -124,6 +125,11 @@ static void bb_error_msg(const char *s, ...)
va_end(p);
 }
 
+static void bb_simple_error_msg(const char *s)
+{
+   bb_error_msg("%s", s);
+}
+
 static void *xmalloc(size_t size)
 {
void *p = NULL;
@@ -147,6 +153,11 @@ static void *xrealloc(void *ptr, size_t size)
return p;
 }
 
+static void *xmemdup(const void *s, int n)
+{
+   return memcpy(xmalloc(n), s, n);
+}
+
 #define xstrdupstrdup
 #define xstrndup   strndup
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 68a9a4bb72..9b65dcbde1 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1488,7 +1488,6 @@ static void msg_and_die_if_script(unsigned lineno, const 
char *fmt, ...)
die_if_script();
 }
 
-#ifndef __U_BOOT__
 static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
 {
if (msg)
@@ -1497,7 +1496,6 @@ static void syntax_error(unsigned lineno UNUSED_PARAM, 
const char *msg)
bb_simple_error_msg("syntax error");
die_if_script();
 }
-#endif /* !__U_BOOT__ */
 
 static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
 {
@@ -3962,7 +3960,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[PIPE_OR ] = "OR" ,
[PIPE_BG ] = "BG" ,
};
-#ifndef __U_BOOT__
static const char *RES[] = {
[RES_NONE ] = "NONE" ,
 # if ENABLE_HUSH_IF
@@ -3992,7 +3989,6 @@ static void debug_print_tree(struct pipe *pi, int lvl)
[RES_ ] = "" ,
[RES_SNTX ] = "SNTX" ,
};
-#endif /* !__U_BOOT__ */
static const char *const CMDTYPE[] = {
"{}",
"()",
@@ -4010,10 +4006,8 @@ static void debug_print_tree(struct pipe *pi, int lvl)
lvl*2, "",
pin,
pi->num_cmds,
-#ifdef __U_BOOT__
(IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
RES[pi->res_word],
-#endif /* !__U_BOOT__ */
pi->followup, PIPE[pi->followup]
);
prn = 0;
@@ -9833,6 +9827,7 @@ static NOINLINE int run_pipe(struct pipe *pi)
rcode = 1; /* exitcode if redir failed */
 #ifndef __U_BOOT__
if (setup_redirects(command, ) == 0) {
+#endif /* !__U_BOOT__ */
debug_printf_exec(": run_list\n");
 //FIXME: we need to pass squirrel down into run_list()
 //for SH_STANDALONE case, or else this construct:
@@ -9841,10 +9836,11 @@ static NOINLINE int run_pipe(struct pipe *pi)
 //and in SH_STANDALONE mode, "find" is not execed,
 //therefore CLOEXEC on saved fd does not help.
rcode = run_list(command->group) & 0xff;
+#ifndef __U_BOOT__
}
restore_redirects(squirrel);
-   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
 #endif /* !__U_BOOT__ */
+   IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
debug_leave();
debug_printf_exec("run_pipe: return %d\n", rcode);
return rcode;
@@ -10363,12 +10359,12 @@ static int run_list(struct pipe *pi)
break;
if (G_flag_return_in_progress == 1)
break;
+#endif /* !__U_BOOT__ */
 
IF_HAS_KEYWORDS(rword = pi->res_word;)
debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
rword, cond_code, last_rword);
 
-#endif /* !__U_BOOT__ */
sv_errexit_depth = G.errexit_depth;
if (
 #if ENABLE_HUSH_IF
-- 
2.34.1



[RFC PATCH v10 20/24] cli: hush_2021: Enable loops

2023-10-04 Thread Francis Laniel
Enables the use of for, while and until loops for command line as
well as with run_command().

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_2021.c |  1 +
 common/cli_hush_upstream.c | 15 ++-
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index aba5dcbbcd..0a207d147b 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -34,6 +34,7 @@
 #define ENABLE_HUSH_INTERACTIVE1
 #define ENABLE_FEATURE_EDITING 1
 #define ENABLE_HUSH_IF 1
+#define ENABLE_HUSH_LOOPS  1
 /* No MMU in U-Boot */
 #define BB_MMU 0
 #define USE_FOR_NOMMU(...) __VA_ARGS__
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 9b65dcbde1..23392939e1 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -10349,7 +10349,7 @@ static int run_list(struct pipe *pi)
 #ifndef __U_BOOT__
for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) 
pi->next) {
 #else /* __U_BOOT__ */
-   for (; pi; pi = pi->next) {
+   for (; pi; pi = rword == RES_DONE ? loop_top : pi->next) {
 #endif /* __U_BOOT__ */
int r;
int sv_errexit_depth;
@@ -10451,7 +10451,20 @@ static int run_list(struct pipe *pi)
}
/* Insert next value from for_lcur */
/* note: *for_lcur already has quotes removed, $var 
expanded, etc */
+#ifndef __U_BOOT__
set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], 
*for_lcur++), /*flag:*/ 0);
+#else /* __U_BOOT__ */
+   /* We cannot use xasprintf, so we emulate it. */
+   char *full_var;
+   char *var = pi->cmds[0].argv[0];
+   char *val = *for_lcur++;
+
+   /* + 1 to take into account =. */
+   full_var = xmalloc(strlen(var) + strlen(val) + 1);
+   sprintf(full_var, "%s=%s", var, val);
+
+   set_local_var_2021(full_var, /*flag:*/ 0);
+#endif /* __U_BOOT__ */
continue;
}
if (rword == RES_IN) {
-- 
2.34.1



[RFC PATCH v10 16/24] test: hush: Fix instructions list tests for hush 2021

2023-10-04 Thread Francis Laniel
Modifies the expected result for hush 2021.
Indeed, there were bugs in actual U-Boot hush which were fixed in upstream
Busybox.
As hush 2021 is based on upstream Busybox, these bugs no longer exist.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 test/hush/list.c | 69 +---
 1 file changed, 65 insertions(+), 4 deletions(-)

diff --git a/test/hush/list.c b/test/hush/list.c
index 052cf2783c..6f8f10f15e 100644
--- a/test/hush/list.c
+++ b/test/hush/list.c
@@ -9,6 +9,7 @@
 #include 
 #include 
 #include 
+#include 
 
 static int hush_test_semicolon(struct unit_test_state *uts)
 {
@@ -46,12 +47,43 @@ static int hush_test_or(struct unit_test_state *uts)
 }
 HUSH_TEST(hush_test_or, 0);
 
+DECLARE_GLOBAL_DATA_PTR;
+
 static int hush_test_and_or(struct unit_test_state *uts)
 {
/* A && B || C truth table. */
ut_asserteq(1, run_command("false && false || false", 0));
-   ut_asserteq(1, run_command("false && false || true", 0));
-   ut_asserteq(1, run_command("false && true || true", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && false || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && false) || true
+* So, (false && false) returns 1, the second false is not
+* executed, and true is executed because of ||.
+*/
+   ut_assertok(run_command("false && false || true", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_asserteq(1, run_command("false && true || true", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (false && true) || true
+* So, (false && true) returns 1, the true is not executed, and
+* true is executed because of ||.
+*/
+   ut_assertok(run_command("false && true || true", 0));
+   }
+
ut_asserteq(1, run_command("false && true || false", 0));
ut_assertok(run_command("true && true || false", 0));
ut_asserteq(1, run_command("true && false || false", 0));
@@ -69,8 +101,37 @@ static int hush_test_or_and(struct unit_test_state *uts)
ut_asserteq(1, run_command("false || false && true", 0));
ut_assertok(run_command("false || true && true", 0));
ut_asserteq(1, run_command("false || true && false", 0));
-   ut_assertok(run_command("true || true && false", 0));
-   ut_assertok(run_command("true || false && false", 0));
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || true && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || true) && false
+* So, (true || true) returns 0, the second true is not
+* executed, and then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || true && false", 0));
+   }
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ut_assertok(run_command("true || false && false", 0));
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* This difference seems to come from a bug solved in Busybox
+* hush.
+*
+* Indeed, the following expression can be seen like this:
+* (true || false) && false
+* So, (true || false) returns 0, the false is not executed, and
+* then false is executed because of &&.
+*/
+   ut_asserteq(1, run_command("true || false && false", 0));
+   }
+
ut_assertok(run_command("true || false && true", 0));
ut_assertok(run_command("true || true && true", 0));
 
-- 
2.34.1



[RFC PATCH v10 15/24] cli: add hush 2021 as parser for run_command*()

2023-10-04 Thread Francis Laniel
Enables using, in code, hush 2021 as parser for run_command function family.
It also enables the command run to be used by CLI user of hush 2021.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c   | 63 --
 common/cli_hush_upstream.c |  2 +-
 2 files changed, 48 insertions(+), 17 deletions(-)

diff --git a/common/cli.c b/common/cli.c
index e3e2bc7fe1..899007da9d 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -43,14 +43,31 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#elif CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
-
-   if (flag & CMD_FLAG_ENV)
-   hush_flags |= FLAG_CONT_ON_NEWLINE;
-   return parse_string_outer(cmd, hush_flags);
-#else /* HUSH_2021_PARSER */
-   /* Not yet implemented. */
+#else
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
+
+   if (flag & CMD_FLAG_ENV)
+   hush_flags |= FLAG_CONT_ON_NEWLINE;
+   return parse_string_outer(cmd, hush_flags);
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   /*
+* Possible values for flags are the following:
+* FLAG_EXIT_FROM_LOOP: This flags ensures we exit from loop in
+* parse_and_run_stream() after first iteration while normal
+* behavior, * i.e. when called from cli_loop(), is to loop
+* infinitely.
+* FLAG_PARSE_SEMICOLON: Hush 2021 parses ';' and does not stop
+* first time it sees one. So, I think we do not need this flag.
+* FLAG_REPARSING: For the moment, I do not understand the goal
+* of this flag.
+* FLAG_CONT_ON_NEWLINE: This flag seems to be used to continue
+* parsing even when reading '\n' when coming from
+* run_command(). In this case, Hush 2021 reads until it finds
+* '\0'. So, I think we do not need this flag.
+*/
+   return parse_string_outer_2021(cmd, FLAG_EXIT_FROM_LOOP);
+   }
return 1;
 #endif
 }
@@ -67,12 +84,25 @@ int run_command_repeatable(const char *cmd, int flag)
 #ifndef CONFIG_HUSH_PARSER
return cli_simple_run_command(cmd, flag);
 #else
+   int ret;
+
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   ret = parse_string_outer(cmd,
+FLAG_PARSE_SEMICOLON
+| FLAG_EXIT_FROM_LOOP);
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   ret = parse_string_outer_2021(cmd,
+ FLAG_PARSE_SEMICOLON
+ | FLAG_EXIT_FROM_LOOP);
+   } else {
+   ret = 1;
+   }
+
/*
 * parse_string_outer() returns 1 for failure, so clean up
 * its result.
 */
-   if (parse_string_outer(cmd,
-  FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP))
+   if (ret)
return -1;
 
return 0;
@@ -111,12 +141,13 @@ int run_command_list(const char *cmd, int len, int flag)
buff[len] = '\0';
}
 #ifdef CONFIG_HUSH_PARSER
-#if CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
-   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
-#else /* HUSH_2021_PARSER */
-   /* Not yet implemented. */
-   rcode = 1;
-#endif
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER) {
+   rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
+   } else if (gd->flags & GD_FLG_HUSH_2021_PARSER) {
+   rcode = parse_string_outer_2021(buff, FLAG_PARSE_SEMICOLON);
+   } else {
+   rcode = 1;
+   }
 #else
/*
 * This function will overwrite any \n it sees with a \0, which
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index b11445c3ac..dbf77ad503 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -8023,7 +8023,7 @@ static int parse_and_run_string(const char *s)
 }
 
 #ifdef __U_BOOT__
-int parse_string_outer(const char *cmd, int flags)
+int parse_string_outer_2021(const char *cmd, int flags)
 {
int ret;
int old_flags;
-- 
2.34.1



[RFC PATCH v10 14/24] cli: hush_2021: Add functions to be called from run_command()

2023-10-04 Thread Francis Laniel
run_command() is called internally by the command run and it can also be called
directly from U-Boot code, e.g. to do unit tests.
This commit adds this path to go to hush 2021.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_upstream.c | 66 --
 1 file changed, 63 insertions(+), 3 deletions(-)

diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index b806c5c653..b11445c3ac 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -1012,6 +1012,7 @@ struct globals {
 #ifdef __U_BOOT__
int flag_repeat;
int do_repeat;
+   int run_command_flags;
 #endif /* __U_BOOT__ */
char *ifs_whitespace; /* = G.ifs or malloced */
 #ifndef __U_BOOT__
@@ -3005,7 +3006,24 @@ static int i_getch(struct in_str *i)
if (i->p && *i->p != '\0') {
ch = (unsigned char)*i->p++;
goto out;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* There are two ways for command to be called:
+* 1. The first one is when they are typed by the user.
+* 2. The second one is through run_command() (NOTE command run
+* internally calls run_command()).
+*
+* In the second case, we do not get input from the user, so once we
+* get a '\0', it means we need to stop.
+* NOTE G.run_command_flags is only set on run_command call stack, so
+* we use this to know if we come from user input or run_command().
+*/
+   } else if (i->p && *i->p == '\0' && G.run_command_flags){
+   return EOF;
+   }
+#endif /* __U_BOOT__ */
 #endif
 #ifndef __U_BOOT__
/* peek_buf[] is an int array, not char. Can contain EOF. */
@@ -3164,7 +3182,6 @@ static void setup_file_in_str(struct in_str *i)
 #endif /* !__U_BOOT__ */
 }
 
-#ifndef __U_BOOT__
 static void setup_string_in_str(struct in_str *i, const char *s)
 {
memset(i, 0, sizeof(*i));
@@ -3172,7 +3189,6 @@ static void setup_string_in_str(struct in_str *i, const 
char *s)
i->p = s;
 }
 
-#endif /* !__U_BOOT__ */
 
 /*
  * o_string support
@@ -7911,7 +7927,11 @@ static int run_and_free_list(struct pipe *pi);
  * NUL: parse all, execute, return
  * ';': parse till ';' or newline, execute, repeat till EOF
  */
+#ifndef __U_BOOT__
 static void parse_and_run_stream(struct in_str *inp, int end_trigger)
+#else /* __U_BOOT__ */
+static int parse_and_run_stream(struct in_str *inp, int end_trigger)
+#endif /* __U_BOOT__ */
 {
/* Why we need empty flag?
 * An obscure corner case "false; ``; echo $?":
@@ -7920,7 +7940,11 @@ static void parse_and_run_stream(struct in_str *inp, int 
end_trigger)
 * this breaks "false; echo `echo $?`" case.
 */
bool empty = 1;
+#ifndef __U_BOOT__
while (1) {
+#else /* __U_BOOT__ */
+   do {
+#endif /* __U_BOOT__ */
struct pipe *pipe_list;
 
 #if ENABLE_HUSH_INTERACTIVE
@@ -7967,21 +7991,57 @@ static void parse_and_run_stream(struct in_str *inp, 
int end_trigger)
empty = 0;
if (G_flag_return_in_progress == 1)
break;
+#ifndef __U_BOOT__
}
+#else /* __U_BOOT__ */
+   /*
+* This do/while is needed by run_command to avoid looping on a command
+* with syntax error.
+*/
+   } while (!(G.run_command_flags & FLAG_EXIT_FROM_LOOP));
+
+   return G.last_exitcode;
+#endif /* __U_BOOT__ */
 }
 
 #ifndef __U_BOOT__
 static void parse_and_run_string(const char *s)
+#else /* __U_BOOT__ */
+static int parse_and_run_string(const char *s)
+#endif /* __U_BOOT__ */
 {
struct in_str input;
//IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
 
setup_string_in_str(, s);
+#ifndef __U_BOOT__
parse_and_run_stream(, '\0');
+#else /* __U_BOOT__ */
+   return parse_and_run_stream(, '\0');
+#endif /* __U_BOOT__ */
//IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
 }
-#endif /* !__U_BOOT__ */
 
+#ifdef __U_BOOT__
+int parse_string_outer(const char *cmd, int flags)
+{
+   int ret;
+   int old_flags;
+
+   /*
+* Keep old values of run_command to be able to restore them once
+* command was executed.
+*/
+   old_flags = G.run_command_flags;
+   G.run_command_flags = flags;
+
+   ret = parse_and_run_string(cmd);
+
+   G.run_command_flags = old_flags;
+
+   return ret;
+}
+#endif /* __U_BOOT__ */
 #ifndef __U_BOOT__
 static void parse_and_run_file(HFILE *fp)
 #else /* __U_BOOT__ */
-- 
2.34.1



[RFC PATCH v10 13/24] cli: hush_2021: Enable variables expansion for hush 2021

2023-10-04 Thread Francis Laniel
Enables variables expansion for hush 2021, both for local and environment
variables.
So the following commands:
foo=bar
echo $foo
setenv bar foo
echo $bar
leads to "bar" and "foo" being printed on console output.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 common/cli_hush_2021.c | 17 +++
 common/cli_hush_upstream.c | 91 +++---
 2 files changed, 103 insertions(+), 5 deletions(-)

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
index 653ea52929..7dd30ea0ef 100644
--- a/common/cli_hush_2021.c
+++ b/common/cli_hush_2021.c
@@ -207,6 +207,23 @@ static const char* endofname(const char *name)
return name;
 }
 
+/**
+ * list_size() - returns the number of elements in char ** before NULL.
+ *
+ * Argument must contain NULL to signalize its end.
+ *
+ * @list The list to count the number of element.
+ * @return The number of element in list.
+ */
+static size_t list_size(char **list)
+{
+   size_t size;
+
+   for (size = 0; list[size] != NULL; size++);
+
+   return size;
+}
+
 struct in_str;
 static int u_boot_cli_readline(struct in_str *i);
 
diff --git a/common/cli_hush_upstream.c b/common/cli_hush_upstream.c
index 84227a248e..b806c5c653 100644
--- a/common/cli_hush_upstream.c
+++ b/common/cli_hush_upstream.c
@@ -3486,7 +3486,6 @@ static int o_get_last_ptr(o_string *o, int n)
return ((int)(uintptr_t)list[n-1]) + string_start;
 }
 
-#ifndef __U_BOOT__
 /*
  * Globbing routines.
  *
@@ -3741,8 +3740,10 @@ static int glob_needed(const char *s)
  */
 static int perform_glob(o_string *o, int n)
 {
+#ifndef __U_BOOT__
glob_t globdata;
int gr;
+#endif /* __U_BOOT__ */
char *pattern;
 
debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
@@ -3751,13 +3752,16 @@ static int perform_glob(o_string *o, int n)
pattern = o->data + o_get_last_ptr(o, n);
debug_printf_glob("glob pattern '%s'\n", pattern);
if (!glob_needed(pattern)) {
+#ifndef __U_BOOT__
  literal:
+#endif /* __U_BOOT__ */
/* unbackslash last string in o in place, fix length */
o->length = unbackslash(pattern) - o->data;
debug_printf_glob("glob pattern '%s' is literal\n", pattern);
return o_save_ptr_helper(o, n);
}
 
+#ifndef __U_BOOT__
memset(, 0, sizeof(globdata));
/* Can't use GLOB_NOCHECK: it does not unescape the string.
 * If we glob "*.\*" and don't find anything, we need
@@ -3793,16 +3797,22 @@ static int perform_glob(o_string *o, int n)
if (DEBUG_GLOB)
debug_print_list("perform_glob returning", o, n);
return n;
+#else /* __U_BOOT__ */
+   /*
+* NOTE We only use perform glob to call unbackslash to remove backslash
+* from string once expanded.
+* So, it seems OK to return this if no previous return was done.
+*/
+   return o_save_ptr_helper(o, n);
+#endif /* __U_BOOT__ */
 }
 
-#endif /* !__U_BOOT__ */
 #endif /* !HUSH_BRACE_EXPANSION */
 
 /* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
  * Otherwise, just finish current list[] and start new */
 static int o_save_ptr(o_string *o, int n)
 {
-#ifndef __U_BOOT__
if (o->o_expflags & EXP_FLAG_GLOB) {
/* If o->has_empty_slot, list[n] was already globbed
 * (if it was requested back then when it was filled)
@@ -3810,7 +3820,6 @@ static int o_save_ptr(o_string *o, int n)
if (!o->has_empty_slot)
return perform_glob(o, n); /* o_save_ptr_helper is 
inside */
}
-#endif /* !__U_BOOT__ */
return o_save_ptr_helper(o, n);
 }
 
@@ -5456,7 +5465,20 @@ static int parse_dollar(o_string *as_string,
nommu_addchr(as_string, ch);
if (ch == '}')
break;
+#ifndef __U_BOOT__
if (!isalnum(ch) && ch != '_') {
+#else /* __U_BOOT__ */
+   /*
+* In several places in U-Boot, we use variable like
+* foo# (e.g. serial#), particularly in env.
+* So, we need to authorize # to appear inside
+* variable name and then expand this variable.
+* NOTE Having # in variable name is not permitted in
+* upstream hush but expansion will be done (even though
+* the result will be empty).
+*/
+   if (!isalnum(ch) && ch != '_' && ch != '#') {
+#endif /* __U_BOOT__ */
unsigned end_ch;
 #ifndef __U_BOOT__
unsigned char last_ch;
@@ -7031,7 +7053,20 @@ static NOINLINE int 

[RFC PATCH v10 08/24] cli: Port Busybox 2021 hush to U-Boot

2023-10-04 Thread Francis Laniel
Adds new file cli_hush_2021.c, it is a copy of Busybox hush file as it was of
time to commit 37460f5da.
This commit modifies Busybox hush to not compile some part specific to Busybox
and adds some code needed by U-Boot.
The modifications consists mainly on adding code #if(n)def guards.

For the moment, this refurbished flavor of hush only permits running command
without any keywords (i.e., if and for are not recognized) or variable expansion
(i.e., echo $foo prints foo and not value stored in variable foo).

A new file was also added to define some functions specific to U-Boot.

Signed-off-by: Francis Laniel 
Signed-off-by: Harald Seiler 
---
 common/cli_hush_2021.c | 274 
 common/cli_hush_upstream.c | 501 -
 2 files changed, 774 insertions(+), 1 deletion(-)
 create mode 100644 common/cli_hush_2021.c

diff --git a/common/cli_hush_2021.c b/common/cli_hush_2021.c
new file mode 100644
index 00..6d109933b8
--- /dev/null
+++ b/common/cli_hush_2021.c
@@ -0,0 +1,274 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * This file defines the compilation unit for the new hush shell version.  The
+ * actual implementation from upstream BusyBox can be found in
+ * `cli_hush_2021_upstream.c` which is included at the end of this file.
+ *
+ * This "wrapper" technique is used to keep the changes to the upstream version
+ * as minmal as possible.  Instead, all defines and redefines necessary are 
done
+ * here, outside the upstream sources.  This will hopefully make upgrades to
+ * newer revisions much easier.
+ *
+ * Copyright (c) 2021, Harald Seiler, DENX Software Engineering, h...@denx.de
+ */
+
+#include  /* readline */
+#include 
+#include  /* malloc, free, realloc*/
+#include /* isalpha, isdigit */
+#include 
+#include 
+#include 
+#include 
+#include /* find_cmd */
+#include 
+
+/*
+ * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
+ */
+#define BB_VER "1.34.0.git37460f5daff9"
+
+/*
+ * Define hush features by the names used upstream.
+ */
+#define ENABLE_HUSH_INTERACTIVE1
+#define ENABLE_FEATURE_EDITING 1
+/* No MMU in U-Boot */
+#define BB_MMU 0
+#define USE_FOR_NOMMU(...) __VA_ARGS__
+#define USE_FOR_MMU(...)
+
+/*
+ * Size-saving "small" ints (arch-dependent)
+ */
+#if CONFIG_IS_ENABLED(X86) || CONFIG_IS_ENABLED(X86_64) || 
CONFIG_IS_ENABLED(MIPS)
+/* add other arches which benefit from this... */
+typedef signed char smallint;
+typedef unsigned char smalluint;
+#else
+/* for arches where byte accesses generate larger code: */
+typedef int smallint;
+typedef unsigned smalluint;
+#endif
+
+/*
+ * Alignment defines used by BusyBox.
+ */
+#define ALIGN1 __attribute__((aligned(1)))
+#define ALIGN2 __attribute__((aligned(2)))
+#define ALIGN4 __attribute__((aligned(4)))
+#define ALIGN8 __attribute__((aligned(8)))
+#define ALIGN_PTR  __attribute__((aligned(sizeof(void*
+
+/*
+ * Miscellaneous compiler/platform defines.
+ */
+#define FAST_FUNC /* not used in U-Boot */
+#define UNUSED_PARAM   __always_unused
+#define ALWAYS_INLINE  __always_inline
+#define NOINLINE   noinline
+
+/*
+ * Defines to provide equivalents to what libc/BusyBox defines.
+ */
+#define EOF(-1)
+#define EXIT_SUCCESS   0
+#define EXIT_FAILURE   1
+
+/*
+ * Stubs to provide libc/BusyBox functions based on U-Boot equivalents where it
+ * makes sense.
+ */
+#define utoa   simple_itoa
+
+static void __noreturn xfunc_die(void)
+{
+   panic("HUSH died!");
+}
+
+#define bb_error_msg_and_die(format, ...) do { \
+panic("HUSH: " format, __VA_ARGS__); \
+} while (0);
+
+#define bb_simple_error_msg_and_die(msg) do { \
+panic_str("HUSH: " msg); \
+} while (0);
+
+/* fdprintf() is used for debug output. */
+static int __maybe_unused fdprintf(int fd, const char *format, ...)
+{
+   va_list args;
+   uint i;
+
+   assert(fd == 2);
+
+   va_start(args, format);
+   i = vprintf(format, args);
+   va_end(args);
+
+   return i;
+}
+
+static void bb_verror_msg(const char *s, va_list p, const char* strerr)
+{
+   /* TODO: what to do with strerr arg? */
+   vprintf(s, p);
+}
+
+static void bb_error_msg(const char *s, ...)
+{
+   va_list p;
+
+   va_start(p, s);
+   bb_verror_msg(s, p, NULL);
+   va_end(p);
+}
+
+static void *xmalloc(size_t size)
+{
+   void *p = NULL;
+   if (!(p = malloc(size)))
+   panic("out of memory");
+   return p;
+}
+
+static void *xzalloc(size_t size)
+{
+   void *p = xmalloc(size);
+   memset(p, 0, size);
+   return p;
+}
+
+static void *xrealloc(void *ptr, size_t size)
+{
+   void *p = NULL;
+   if (!(p = realloc(ptr, size)))
+   panic("out of memor

[RFC PATCH v10 12/24] cli: Enables using hush 2021 parser as command line parser

2023-10-04 Thread Francis Laniel
If one defines HUSH_2021_PARSER, it is then possible to use 2021 parser with:
=> cli get
old
=> cli set 2021
=> cli get
2021

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig   |  8 +
 cmd/cli.c |  7 -
 common/Makefile   |  1 +
 common/cli.c  | 38 +++
 common/cli_hush_2021.c|  3 ++
 common/cli_hush_upstream.c| 46 +---
 doc/usage/cmd/cli.rst | 17 ++-
 include/asm-generic/global_data.h |  4 +++
 include/cli_hush.h| 51 +--
 9 files changed, 160 insertions(+), 15 deletions(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 5fb4cef54c..36595638a2 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -34,6 +34,14 @@ menu "Hush flavor to use"
  2005.
 
  It is actually the default U-Boot shell when decided to use 
hush as shell.
+
+   config HUSH_2021_PARSER
+   bool "Use hush 2021 parser"
+   help
+ This option enables the new flavor of hush based on hush 
Busybox from
+ 2021.
+
+ For the moment, it is highly experimental and should be used 
at own risks.
 endmenu
 
 config CMDLINE_EDITING
diff --git a/cmd/cli.c b/cmd/cli.c
index 7671785b83..d2b64da613 100644
--- a/cmd/cli.c
+++ b/cmd/cli.c
@@ -12,6 +12,8 @@ static const char *gd_flags_to_parser(void)
 {
if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
return "old";
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER)
+   return "2021";
return NULL;
 }
 
@@ -34,12 +36,15 @@ static int parser_string_to_gd_flags(const char *parser)
 {
if (!strcmp(parser, "old"))
return GD_FLG_HUSH_OLD_PARSER;
+   if (!strcmp(parser, "2021"))
+   return GD_FLG_HUSH_2021_PARSER;
return -1;
 }
 
 static void reset_parser_gd_flags(void)
 {
gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+   gd->flags &= ~GD_FLG_HUSH_2021_PARSER;
 }
 
 static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
@@ -108,7 +113,7 @@ static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
 #if CONFIG_IS_ENABLED(SYS_LONGHELP)
 static char cli_help_text[] =
"get - print current cli\n"
-   "set - set the current cli, possible value is: old"
+   "set - set the current cli, possible value are: old, 2021"
;
 #endif
 
diff --git a/common/Makefile b/common/Makefile
index 615eba8672..4b060bb565 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -9,6 +9,7 @@ obj-y += init/
 obj-y += main.o
 obj-y += exports.o
 obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_2021_PARSER) += cli_hush_2021.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 
 # # boards
diff --git a/common/cli.c b/common/cli.c
index d419671e8c..e3e2bc7fe1 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -43,12 +43,15 @@ int run_command(const char *cmd, int flag)
return 1;
 
return 0;
-#else
+#elif CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP;
 
if (flag & CMD_FLAG_ENV)
hush_flags |= FLAG_CONT_ON_NEWLINE;
return parse_string_outer(cmd, hush_flags);
+#else /* HUSH_2021_PARSER */
+   /* Not yet implemented. */
+   return 1;
 #endif
 }
 
@@ -108,7 +111,12 @@ int run_command_list(const char *cmd, int len, int flag)
buff[len] = '\0';
}
 #ifdef CONFIG_HUSH_PARSER
+#if CONFIG_IS_ENABLED(HUSH_OLD_PARSER)
rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON);
+#else /* HUSH_2021_PARSER */
+   /* Not yet implemented. */
+   rcode = 1;
+#endif
 #else
/*
 * This function will overwrite any \n it sees with a \0, which
@@ -254,8 +262,13 @@ err:
 void cli_loop(void)
 {
bootstage_mark(BOOTSTAGE_ID_ENTER_CLI_LOOP);
-#ifdef CONFIG_HUSH_PARSER
-   parse_file_outer();
+#if CONFIG_IS_ENABLED(HUSH_PARSER)
+   if (gd->flags & GD_FLG_HUSH_2021_PARSER)
+   parse_and_run_file();
+   else if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   parse_file_outer();
+
+   printf("Problem\n");
/* This point is never reached */
for (;;);
 #elif defined(CONFIG_CMDLINE)
@@ -268,10 +281,23 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
+   /* This if block is used to initialize hush parser gd flag. */
if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER)
-   && CONFIG_IS_ENABLED(HUSH_OLD_PARSER))
-   gd->flags |= GD_FLG_HUSH_OLD_PARSER;
-   u_boot_hush_start();
+   && !(gd->flags & GD_FLG_HUSH_2021_PARSER)) {
+   if (CONF

[RFC PATCH v10 09/24] cli: Add menu for hush parser

2023-10-04 Thread Francis Laniel
For the moment, the menu contains only entry: HUSH_OLD_PARSER which is the
default.
The goal is to prepare the field to add a new hush parser which guarantees
actual behavior is still correct.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 cmd/Kconfig | 13 +
 common/Makefile |  2 +-
 2 files changed, 14 insertions(+), 1 deletion(-)

diff --git a/cmd/Kconfig b/cmd/Kconfig
index 43ca10f69c..5fb4cef54c 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -23,6 +23,19 @@ config HUSH_PARSER
  If disabled, you get the old, much simpler behaviour with a somewhat
  smaller memory footprint.
 
+menu "Hush flavor to use"
+   depends on HUSH_PARSER
+
+   config HUSH_OLD_PARSER
+   bool "Use hush old parser"
+   default y
+   help
+ This option enables the old flavor of hush based on hush 
Busybox from
+ 2005.
+
+ It is actually the default U-Boot shell when decided to use 
hush as shell.
+endmenu
+
 config CMDLINE_EDITING
bool "Enable command line editing"
depends on CMDLINE
diff --git a/common/Makefile b/common/Makefile
index f5c3d90f06..615eba8672 100644
--- a/common/Makefile
+++ b/common/Makefile
@@ -8,7 +8,7 @@ ifndef CONFIG_SPL_BUILD
 obj-y += init/
 obj-y += main.o
 obj-y += exports.o
-obj-$(CONFIG_HUSH_PARSER) += cli_hush.o
+obj-$(CONFIG_HUSH_OLD_PARSER) += cli_hush.o
 obj-$(CONFIG_AUTOBOOT) += autoboot.o
 
 # # boards
-- 
2.34.1



[RFC PATCH v10 10/24] global_data.h: add GD_FLG_HUSH_OLD_PARSER flag

2023-10-04 Thread Francis Laniel
This flag is used to indicate we are using the hush parser.

Reviewed-by: Simon Glass 
Signed-off-by: Francis Laniel 
---
 common/cli.c  | 2 ++
 include/asm-generic/global_data.h | 4 
 2 files changed, 6 insertions(+)

diff --git a/common/cli.c b/common/cli.c
index 3916a7b10a..e5fe1060d0 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,6 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
 
diff --git a/include/asm-generic/global_data.h 
b/include/asm-generic/global_data.h
index 8fc205ded1..f21926aa23 100644
--- a/include/asm-generic/global_data.h
+++ b/include/asm-generic/global_data.h
@@ -667,6 +667,10 @@ enum gd_flags {
 * @GD_FLG_OF_TAG_MIGRATE: Device tree has old u-boot,dm- tags
 */
GD_FLG_OF_TAG_MIGRATE = 0x20,
+   /**
+* @GD_FLG_HUSH_OLD_PARSER: Use hush old parser.
+*/
+   GD_FLG_HUSH_OLD_PARSER = 0x40,
 };
 
 #endif /* __ASSEMBLY__ */
-- 
2.34.1



[RFC PATCH v10 11/24] cmd: Add new cli command

2023-10-04 Thread Francis Laniel
This command can be used to print the current parser with 'cli print'.
It can also be used to set the current parser with 'cli set'.
For the moment, only one value is valid for set: old.

Signed-off-by: Francis Laniel 
---
 cmd/Makefile  |   2 +
 cmd/cli.c | 120 ++
 common/cli.c  |   3 +-
 doc/usage/cmd/cli.rst |  59 +
 doc/usage/index.rst   |   1 +
 5 files changed, 184 insertions(+), 1 deletion(-)
 create mode 100644 cmd/cli.c
 create mode 100644 doc/usage/cmd/cli.rst

diff --git a/cmd/Makefile b/cmd/Makefile
index 9bebf321c3..d468cc5065 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -226,6 +226,8 @@ obj-$(CONFIG_CMD_AVB) += avb.o
 # Foundries.IO SCP03
 obj-$(CONFIG_CMD_SCP03) += scp03.o
 
+obj-$(CONFIG_HUSH_PARSER) += cli.o
+
 obj-$(CONFIG_ARM) += arm/
 obj-$(CONFIG_RISCV) += riscv/
 obj-$(CONFIG_SANDBOX) += sandbox/
diff --git a/cmd/cli.c b/cmd/cli.c
new file mode 100644
index 00..7671785b83
--- /dev/null
+++ b/cmd/cli.c
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const char *gd_flags_to_parser(void)
+{
+   if (gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   return "old";
+   return NULL;
+}
+
+static int do_cli_get(struct cmd_tbl *cmdtp, int flag, int argc,
+char *const argv[])
+{
+   const char *current = gd_flags_to_parser();
+
+   if (!current) {
+   printf("current cli value is not valid, this should not 
happen!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   printf("%s\n", current);
+
+   return CMD_RET_SUCCESS;
+}
+
+static int parser_string_to_gd_flags(const char *parser)
+{
+   if (!strcmp(parser, "old"))
+   return GD_FLG_HUSH_OLD_PARSER;
+   return -1;
+}
+
+static void reset_parser_gd_flags(void)
+{
+   gd->flags &= ~GD_FLG_HUSH_OLD_PARSER;
+}
+
+static int do_cli_set(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   char *parser_name;
+   int parser_flag;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   parser_name = argv[1];
+
+   parser_flag = parser_string_to_gd_flags(parser_name);
+   if (parser_flag == -1) {
+   printf("Bad value for parser name: %s\n", parser_name);
+   return CMD_RET_USAGE;
+   }
+
+   if (parser_flag == GD_FLG_HUSH_OLD_PARSER &&
+   !CONFIG_IS_ENABLED(HUSH_OLD_PARSER)) {
+   printf("Want to set current parser to old, but its code was not 
compiled!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   if (parser_flag == GD_FLG_HUSH_2021_PARSER &&
+   !CONFIG_IS_ENABLED(HUSH_2021_PARSER)) {
+   printf("Want to set current parser to 2021, but its code was 
not compiled!\n");
+   return CMD_RET_FAILURE;
+   }
+
+   reset_parser_gd_flags();
+   gd->flags |= parser_flag;
+
+   cli_init();
+   cli_loop();
+
+   /* cli_loop() should never return. */
+   return CMD_RET_FAILURE;
+}
+
+static struct cmd_tbl parser_sub[] = {
+   U_BOOT_CMD_MKENT(get, 1, 1, do_cli_get, "", ""),
+   U_BOOT_CMD_MKENT(set, 2, 1, do_cli_set, "", ""),
+};
+
+static int do_cli(struct cmd_tbl *cmdtp, int flag, int argc,
+ char *const argv[])
+{
+   struct cmd_tbl *cp;
+
+   if (argc < 2)
+   return CMD_RET_USAGE;
+
+   /* drop initial "parser" arg */
+   argc--;
+   argv++;
+
+   cp = find_cmd_tbl(argv[0], parser_sub, ARRAY_SIZE(parser_sub));
+   if (cp)
+   return cp->cmd(cmdtp, flag, argc, argv);
+
+   return CMD_RET_USAGE;
+}
+
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+static char cli_help_text[] =
+   "get - print current cli\n"
+   "set - set the current cli, possible value is: old"
+   ;
+#endif
+
+U_BOOT_CMD(cli, 3, 1, do_cli,
+  "cli",
+#if CONFIG_IS_ENABLED(SYS_LONGHELP)
+  cli_help_text
+#endif
+);
diff --git a/common/cli.c b/common/cli.c
index e5fe1060d0..d419671e8c 100644
--- a/common/cli.c
+++ b/common/cli.c
@@ -268,7 +268,8 @@ void cli_loop(void)
 void cli_init(void)
 {
 #ifdef CONFIG_HUSH_PARSER
-   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER))
+   if (!(gd->flags & GD_FLG_HUSH_OLD_PARSER)
+   && CONFIG_IS_ENABLED(HUSH_OLD_PARSER))
gd->flags |= GD_FLG_HUSH_OLD_PARSER;
u_boot_hush_start();
 #endif
diff --git a/doc/usage/cmd/cli.rst b/doc/usage/cmd/cli.rst
new file mode 100644
index 00..89ece3203d
--- /dev/null
+++ b/doc/usage/cmd/cli.rst
@@ -0,0 +1,59 @@
+.. SPDX-License-Identifier: GPL-2.0+
+
+cli command

[RFC PATCH v10 06/24] test: hush: Test hush loops

2023-10-04 Thread Francis Laniel
The added tests verifies correct behavior of for, while and until loops.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/loop.c   | 65 ++
 2 files changed, 66 insertions(+)
 create mode 100644 test/hush/loop.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index ff4fe7700b..a2d98815e5 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -7,3 +7,4 @@ obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
 obj-y += list.o
+obj-y += loop.o
diff --git a/test/hush/loop.c b/test/hush/loop.c
new file mode 100644
index 00..ca777e38fe
--- /dev/null
+++ b/test/hush/loop.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_for(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   ut_assertok(run_command("for loop_i in foo bar quux quux; do echo 
$loop_i; done", 0));
+   ut_assert_nextline("foo");
+   ut_assert_nextline("bar");
+   ut_assert_nextline("quux");
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_i and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_for, 0);
+
+static int hush_test_while(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+
+   /* Exit status is that of test, so 1 since test is false to quit the 
loop. */
+   ut_asserteq(1, run_command("while test -z \"$loop_foo\"; do echo bar; 
loop_foo=quux; done", 0));
+   ut_assert_nextline("bar");
+   ut_assert_console_end();
+
+   puts("Beware: this test set local variable loop_foo and it cannot be 
unset!");
+
+   return 0;
+}
+HUSH_TEST(hush_test_while, 0);
+
+static int hush_test_until(struct unit_test_state *uts)
+{
+   console_record_reset_enable();
+   env_set("loop_bar", "bar");
+
+   /*
+* WARNING We have to use environment variable because it is not 
possible
+* resetting local variable.
+*/
+   ut_assertok(run_command("until test -z \"$loop_bar\"; do echo quux; 
setenv loop_bar; done", 0));
+   ut_assert_nextline("quux");
+   ut_assert_console_end();
+
+   /*
+* Loop normally resets foo environment variable, but we reset it here 
in
+* case the test failed.
+*/
+   env_set("loop_bar", NULL);
+   return 0;
+}
+HUSH_TEST(hush_test_until, 0);
-- 
2.34.1



[RFC PATCH v10 05/24] test: hush: Test hush commands list

2023-10-04 Thread Francis Laniel
Verifies behavior of commands separated by ';', '&&' and '||'.

Signed-off-by: Francis Laniel 
Reviewed-by: Simon Glass 
---
 test/hush/Makefile |  1 +
 test/hush/list.c   | 79 ++
 2 files changed, 80 insertions(+)
 create mode 100644 test/hush/list.c

diff --git a/test/hush/Makefile b/test/hush/Makefile
index feb4f71956..ff4fe7700b 100644
--- a/test/hush/Makefile
+++ b/test/hush/Makefile
@@ -6,3 +6,4 @@
 obj-y += cmd_ut_hush.o
 obj-y += if.o
 obj-y += dollar.o
+obj-y += list.o
diff --git a/test/hush/list.c b/test/hush/list.c
new file mode 100644
index 00..052cf2783c
--- /dev/null
+++ b/test/hush/list.c
@@ -0,0 +1,79 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * (C) Copyright 2021
+ * Francis Laniel, Amarula Solutions, francis.lan...@amarulasolutions.com
+ */
+
+#include 
+#include 
+#include 
+#include 
+#include 
+
+static int hush_test_semicolon(struct unit_test_state *uts)
+{
+   /* A; B = B truth table. */
+   ut_asserteq(1, run_command("false; false", 0));
+   ut_assertok(run_command("false; true", 0));
+   ut_assertok(run_command("true; true", 0));
+   ut_asserteq(1, run_command("true; false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_semicolon, 0);
+
+static int hush_test_and(struct unit_test_state *uts)
+{
+   /* A && B truth table. */
+   ut_asserteq(1, run_command("false && false", 0));
+   ut_asserteq(1, run_command("false && true", 0));
+   ut_assertok(run_command("true && true", 0));
+   ut_asserteq(1, run_command("true && false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and, 0);
+
+static int hush_test_or(struct unit_test_state *uts)
+{
+   /* A || B truth table. */
+   ut_asserteq(1, run_command("false || false", 0));
+   ut_assertok(run_command("false || true", 0));
+   ut_assertok(run_command("true || true", 0));
+   ut_assertok(run_command("true || false", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or, 0);
+
+static int hush_test_and_or(struct unit_test_state *uts)
+{
+   /* A && B || C truth table. */
+   ut_asserteq(1, run_command("false && false || false", 0));
+   ut_asserteq(1, run_command("false && false || true", 0));
+   ut_asserteq(1, run_command("false && true || true", 0));
+   ut_asserteq(1, run_command("false && true || false", 0));
+   ut_assertok(run_command("true && true || false", 0));
+   ut_asserteq(1, run_command("true && false || false", 0));
+   ut_assertok(run_command("true && false || true", 0));
+   ut_assertok(run_command("true && true || true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_and_or, 0);
+
+static int hush_test_or_and(struct unit_test_state *uts)
+{
+   /* A || B && C truth table. */
+   ut_asserteq(1, run_command("false || false && false", 0));
+   ut_asserteq(1, run_command("false || false && true", 0));
+   ut_assertok(run_command("false || true && true", 0));
+   ut_asserteq(1, run_command("false || true && false", 0));
+   ut_assertok(run_command("true || true && false", 0));
+   ut_assertok(run_command("true || false && false", 0));
+   ut_assertok(run_command("true || false && true", 0));
+   ut_assertok(run_command("true || true && true", 0));
+
+   return 0;
+}
+HUSH_TEST(hush_test_or_and, 0);
-- 
2.34.1



  1   2   3   4   >