Failing shell code under busybox 1.36.1 that worked with 1.31.1

Harvey harv at gmx.de
Thu Feb 15 21:48:42 UTC 2024


David,

I can confirm that your patch solves my problem without any further need 
to change the shell code. Now V 1.36.1 acts in the same way as V 1.31.1 
did. Thank you so mutch! Maybe this is candidate to merge ;)

Greetings
Harvey

Am 15.02.24 um 09:45 schrieb David Leonard:
> 
>>         _result="$_result '${_p//'/'\"'\"'}'"
> 
> Busybox ash has a CONFIG_ASH_BASH_COMPAT config to get this ${var//...}
> replacement behaviour.  And, in bash, the ' in the pattern section is 
> treated
> as a quote start. To give the ' a literal meaning, use \'
> eg ${_p//\'/..repl..}
> 
> In current busybox ash you will run into a bug where quotes aren't
> handled in the pattern, and even loses the rest of the word, as you
> observed. That is, given this script:
> 
>      var="don't g/o"
>      echo "${var/'/'o}!"
>      echo "${var/\'/\'o}!"
> 
> bash outputs:
> 
>      don't g!            (pattern="/o" repl="")
>      don'ot g/o!            (pattern="'", repl="'o")
> 
> and busybox ash outputs:
> 
>      don't g/o            (pattern=?, repl=?, NOTE: lost !)
>      don'ot g/o!            (pattern="'", repl="'o")
> 
> Here's a patch to make ash a little bit closer to bash.
> 
> --- a/shell/ash.c
> +++ b/shell/ash.c
> @@ -7058,6 +7058,7 @@ subevalvar(char *start, char *str, int strloc,
>           */
>          repl = NULL;
>          if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
> +               int quoted = 0;
>                  /* Find '/' and replace with NUL */
>                  repl = start;
>                  /* The pattern can't be empty.
> @@ -7073,6 +7074,11 @@ subevalvar(char *start, char *str, int strloc,
>                                  repl = NULL;
>                                  break;
>                          }
> +                       /* Handle quoted patterns */
> +                       if ((unsigned char)*repl == CTLQUOTEMARK)
> +                               quoted ^= 1;
> +                       if (quoted)
> +                               goto literal;
>                          if (*repl == '/') {
>                                  *repl = '\0';
>                                  break;
> @@ -7084,6 +7090,7 @@ subevalvar(char *start, char *str, int strloc,
>                          /* Handle escaped slashes, e.g. "${v/\//_}" 
> (they are CTLESC'ed by this point) */
>                          if ((unsigned char)*repl == CTLESC && repl[1])
>                                  repl++;
> +               literal:
>                          repl++;
>                  }
>          }
> 
> new file mode 100644
> index 000000000..23aecb5ae
> --- /dev/null
> +++ b/shell/ash_test/ash-vars/var_bash_repl_quoted.right
> @@ -0,0 +1,3 @@
> +ab.
> +axb.
> +x/b.
> new file mode 100644
> index 000000000..d2ba39578
> --- /dev/null
> +++ b/shell/ash_test/ash-vars/var_bash_repl_quoted.tests
> @@ -0,0 +1,4 @@
> +v=a/b
> +echo "${v/'/'}."
> +echo "${v/'/'/x}."
> +echo "${v/'a'/x}."
> 
> 
> 
> On Wed, 14 Feb 2024, Harvey wrote:
> 
>> Sorry, posted the wrong version of the pack_args() funtion:
>> I have payed with possible solutions for so long that I forgot the
>> restore the original before posting  -blush-
>>
>> Here is the original version:
>> ------------------------------------------------------------------------
>>
>> # Packs arguments into a single string.
>> #
>> # Input:
>> #   $1 = name of variable receiving the arguments in such a way that
>> #        (re)evaluating them provides the original arguments
>> #   $2... = arguments
>> # Example:
>> #   func() {
>> #     local args
>> #     pack_args args "$@"
>> #     send_rpc func_impl "$args"
>> #   }
>> #   # (in another process) > #   receive_rpc() {
>> #     local func="$1"
>> #     local args="$2"
>> #     eval $func "$args"
>> #   }
>> pack_args()
>> {
>>     local _p _resvar=$1 _result= _value
>>     shift
>>     for _p
>>     do
>>         _result="$_result '${_p//'/'\"'\"'}'"
>>     done
>>     eval $_resvar=\${_result\# }
>> }
>> ------------------------------------------------------------------------
>>
>> Harvey
>>
>> Am 14.02.24 um 19:06 schrieb Harvey:
>>> Hello,
>>>
>>> I am part of the team of a small router distribution called fli4l:
>>> https://www.fli4l.de/doku.php?id=start.
>>>
>>> We use buildroot to generate our images. Due to a hardware crash and the
>>> leaving of our main developer we are facing the dead of our project. But
>>> there are still some people left and at least we want to try to keep it
>>> alive 😉
>>>
>>> That said - the first we have to try is the update of our buildroot
>>> base. A lot of work is already done and but we struggle with the busybox
>>> update, especially with the ash shell contained. When updating to
>>> Version 1.36.1 we are facing script failures in places that did work
>>> until busybox 1.31.1.
>>>
>>> I have tried to debug the code and it seems that the error is contained
>>> in a helper include for string handling.
>>>
>>> The code is:
>>> ------------------------------------------------------------------------
>>> # Packs arguments into a single string.
>>> #
>>> # Input:
>>> #   $1 = name of variable receiving the arguments in such a way that
>>> #        (re)evaluating them provides the original arguments
>>> #   $2... = arguments
>>> # Example:
>>> #   func() {
>>> #     local args
>>> #     pack_args args "$@"
>>> #     send_rpc func_impl "$args"
>>> #   }
>>> #   # (in another process)
>>> #   receive_rpc() {
>>> #     local func="$1"
>>> #     local args="$2"
>>> #     eval $func "$args"
>>> #   }
>>> pack_args()
>>> {
>>>      local _p _resvar=$1 _result= _value
>>>      shift
>>>      for _p
>>>      do
>>>          _result="$_result '${_p//'/'\"'\"'}''"
>>>      done
>>>      eval $_resvar=\${_result\# }
>>> }
>>> ------------------------------------------------------------------------
>>>
>>> The debug trace (using set -x) in V1.31.1 looks like this:
>>>
>>> + pack_args exit_trap_0 sync_unlock_all_resources
>>> + local _p '_resvar=exit_trap_0' '_result=' _value
>>> + shift
>>> + _result=' '"'"'sync_unlock_all_resources'"'"
>>> + eval 'exit_trap_0=${_result#' '}'
>>> + exit_trap_0=''"'"'sync_unlock_all_resources'"'"
>>> + exit_trap_num=1
>>> + return 0
>>>
>>> ------------------------------------------------------------------------
>>> while in V1.36.1 it looks like this:
>>>
>>> + pack_args exit_trap_0 sync_unlock_all_resources
>>> + local _p '_resvar=exit_trap_0' '_result=' _value
>>> + shift
>>> + _result=' '"'"'sync_unlock_all_resources'
>>>                                            ^^^^ missing quote
>>> + eval 'exit_trap_0=${_result#' '}'
>>> + exit_trap_0=''"'"'sync_unlock_all_resources'
>>>                                               ^^^^ missing quote
>>> + exit_trap_num=1
>>> + return 0
>>>
>>> The eval line then fails with 'Unterminated quoted string'
>>>
>>> Unfortunately I can't tell where the difference lies.
>>>
>>> I hope someone can help or at least point me in the right direction.
>>>
>>> Greetings
>>> Harvey
>>> _______________________________________________
>>> busybox mailing list
>>> busybox at busybox.net
>>> http://lists.busybox.net/mailman/listinfo/busybox
>> _______________________________________________
>> busybox mailing list
>> busybox at busybox.net
>> http://lists.busybox.net/mailman/listinfo/busybox
>>


More information about the busybox mailing list