mbox series

[0/3] add "call" command

Message ID 20200925111942.4629-1-rasmus.villemoes@prevas.dk
Headers show
Series add "call" command | expand

Message

Rasmus Villemoes Sept. 25, 2020, 11:19 a.m. UTC
This adds a way to call a "function" defined in the environment with
arguments. I.e., whereas

  run foo

requires one to set the (shell or environment) variables referenced
from foo beforehand, with this one can instead do

  call foo arg1 arg2 arg3

and use $1... up to $9 in the definition of foo. $# is set so foo can
make decisions based on that, and ${3:-default} works as expected.

As I write in patch 2, it should be possible to get rid of the "call"
and simply allow

  foo arg1 arg2 arg3

i.e. if the search for a command named foo fails, try an environment
variable by that name and do it as "call". But that change of
behaviour, I think, requires a separate opt-in config knob, and can be
done later if someone actually wants that.

Rasmus Villemoes (3):
  cli_hush.c: refactor handle_dollar() to prepare for cmd_call
  cli_hush.c: add "call" command
  ut: add small hush tests

 cmd/Kconfig                 |  8 ++++
 common/cli_hush.c           | 93 +++++++++++++++++++++++++++++++++----
 configs/sandbox64_defconfig |  1 +
 configs/sandbox_defconfig   |  1 +
 include/test/suites.h       |  1 +
 test/cmd/Makefile           |  1 +
 test/cmd/hush.c             | 90 +++++++++++++++++++++++++++++++++++
 test/cmd_ut.c               |  6 +++
 8 files changed, 191 insertions(+), 10 deletions(-)
 create mode 100644 test/cmd/hush.c

Comments

Heinrich Schuchardt Sept. 25, 2020, 11:52 a.m. UTC | #1
On 25.09.20 13:19, Rasmus Villemoes wrote:
> This adds a way to call a "function" defined in the environment with
> arguments. I.e., whereas
>
>   run foo
>
> requires one to set the (shell or environment) variables referenced
> from foo beforehand, with this one can instead do
>
>   call foo arg1 arg2 arg3
>
> and use $1... up to $9 in the definition of foo. $# is set so foo can
> make decisions based on that, and ${3:-default} works as expected.
>
> As I write in patch 2, it should be possible to get rid of the "call"
> and simply allow
>
>   foo arg1 arg2 arg3
>
> i.e. if the search for a command named foo fails, try an environment
> variable by that name and do it as "call". But that change of
> behaviour, I think, requires a separate opt-in config knob, and can be
> done later if someone actually wants that.

If the behavior is configurable, this will result in users complaining
that a script which they copied does not run on another machine. Please,
do not introduce any configurability here.

Further we cannot first introduce a command call and then eliminate it
due to backward compatibility. We should decide on the final version
beforehand.

In the Linux world you can override a command using an alias. So I am
not sure if a built in command should take precedence over a variable of
the same name or the other way round.

Consider that we already have two types of variables in the shell. Those
that are in the environment and those that are not. Here the environment
variables take precedence.

Try

    setenv a;a=4;echo $a;setenv a 5;echo $a;setenv a;echo $a

Best regards

Heinrich

>
> Rasmus Villemoes (3):
>   cli_hush.c: refactor handle_dollar() to prepare for cmd_call
>   cli_hush.c: add "call" command
>   ut: add small hush tests
>
>  cmd/Kconfig                 |  8 ++++
>  common/cli_hush.c           | 93 +++++++++++++++++++++++++++++++++----
>  configs/sandbox64_defconfig |  1 +
>  configs/sandbox_defconfig   |  1 +
>  include/test/suites.h       |  1 +
>  test/cmd/Makefile           |  1 +
>  test/cmd/hush.c             | 90 +++++++++++++++++++++++++++++++++++
>  test/cmd_ut.c               |  6 +++
>  8 files changed, 191 insertions(+), 10 deletions(-)
>  create mode 100644 test/cmd/hush.c
>
Rasmus Villemoes Sept. 25, 2020, 12:36 p.m. UTC | #2
On 25/09/2020 13.52, Heinrich Schuchardt wrote:
> On 25.09.20 13:19, Rasmus Villemoes wrote:
>> This adds a way to call a "function" defined in the environment with
>> arguments. I.e., whereas
>>
>>   run foo
>>
>> requires one to set the (shell or environment) variables referenced
>> from foo beforehand, with this one can instead do
>>
>>   call foo arg1 arg2 arg3
>>
>> and use $1... up to $9 in the definition of foo. $# is set so foo can
>> make decisions based on that, and ${3:-default} works as expected.
>>
>> As I write in patch 2, it should be possible to get rid of the "call"
>> and simply allow
>>
>>   foo arg1 arg2 arg3
>>
>> i.e. if the search for a command named foo fails, try an environment
>> variable by that name and do it as "call". But that change of
>> behaviour, I think, requires a separate opt-in config knob, and can be
>> done later if someone actually wants that.
> 
> If the behavior is configurable, this will result in users complaining
> that a script which they copied does not run on another machine. Please,
> do not introduce any configurability here.

OK, but I'm actually not intending to add that functionality at all, I
was merely mentioning it if somebody would like it. But note that the
same argument can be used for any script that uses any command (or other
functionality) which is config-dependent - if I copy some snippet that
uses setexpr, that won't work on another machine that doesn't have
CONFIG_CMD_SETEXPR.

> Further we cannot first introduce a command call and then eliminate it
> due to backward compatibility. We should decide on the final version
> beforehand.
> 
> In the Linux world you can override a command using an alias. So I am
> not sure if a built in command should take precedence over a variable of
> the same name or the other way round.

POSIX(-like) shells have "command":

  The command utility shall cause the shell to treat the arguments as a
simple command, suppressing the shell function lookup

So I'd be inclined to make the normal commands have precedence, given
that there's a "call " that can be used to invoke the "function" variant
rather than the "builtin command" variant. But it's all moot if nobody
actually wants to be able to avoid the "call ".

> Consider that we already have two types of variables in the shell. Those
> that are in the environment and those that are not. Here the environment
> variables take precedence.
> 
> Try
> 
>     setenv a;a=4;echo $a;setenv a 5;echo $a;setenv a;echo $a

I know. So if you do 'env set 1 haha"', ${1} will not work (though I
think bare $1 will) in called functions. But nobody sets such
environment variables.

While I was trying to figure out how to implement this, I stumbled on
some code that tries to prevent the above (or rather, the converse:
setting a shell variable when an env variable of that name exists). But
that code is buggy and hence dead because it does the lookup on the
whole "a=4" string, before splitting on =. So currently we get

=> env set foo abc
=> foo=x
=>

moving the check:

--- a/common/cli_hush.c
+++ b/common/cli_hush.c
@@ -2185,14 +2185,6 @@ int set_local_var(const char *s, int flg_export)

        name=strdup(s);

-#ifdef __U_BOOT__
-       if (env_get(name) != NULL) {
-               printf ("ERROR: "
-                               "There is a global environment variable
with the same name.\n");
-               free(name);
-               return -1;
-       }
-#endif
        /* Assume when we enter this function that we are already in
         * NAME=VALUE format.  So the first order of business is to
         * split 's' on the '=' into 'name' and 'value' */
@@ -2203,6 +2195,15 @@ int set_local_var(const char *s, int flg_export)
        }
        *value++ = 0;

+#ifdef __U_BOOT__
+       if (env_get(name) != NULL) {
+               printf ("ERROR: "
+                               "There is a global environment variable
with the same name.\n");
+               free(name);
+               return -1;
+       }
+#endif
+

we get

=> env set foo abc
=> foo=x
ERROR: There is a global environment variable with the same name.
=>

But that code is ancient, so I don't know if it should be fixed to work
as clearly intended, or one should just delete it to remove a few bytes
of dead .text. In any case it does nothing to prevent setting an env
variable with the same name as an existing shell variable.

Rasmus
Wolfgang Denk Sept. 25, 2020, 12:59 p.m. UTC | #3
Dear Rasmus,

In message <20200925111942.4629-1-rasmus.villemoes@prevas.dk> you wrote:
> This adds a way to call a "function" defined in the environment with
> arguments. I.e., whereas
>
>   run foo
>
> requires one to set the (shell or environment) variables referenced
> from foo beforehand, with this one can instead do
>
>   call foo arg1 arg2 arg3
>
> and use $1... up to $9 in the definition of foo. $# is set so foo can
> make decisions based on that, and ${3:-default} works as expected.

This is definitely a useful idea.

But...

...the current version of hush in U-Boot is old, has a number of
known bugs and shortcomings, and I really recommend not to adding
any new features to it, because that would makie an update to a more
recent version even less likely.

So the first step before such extensions should be to update hush.
In that process (which might be more of a new port) one should
consider the possibility of keeping a little more of the
functionality - memory restrictins today are not so strict any more
as they were when hush was originally added.  One feature that would
definitely be useful is command substitution.

All this needs a bit of a long term maintainable concept.  Quick
hacking of the ancient code is not a good idea.

Best regards,

Wolfgang Denk
Wolfgang Denk Sept. 25, 2020, 1:09 p.m. UTC | #4
Dear Heinrich Schuchardt,

In message <4b00225d-d960-4a14-9aec-110ddddf7f30@gmx.de> you wrote:
>
> Further we cannot first introduce a command call and then eliminate it
> due to backward compatibility. We should decide on the final version
> beforehand.

Full agreement.  we need a concept of what is needed / wanted first.
And then we should look how current vrsions of hush fit into this.

> In the Linux world you can override a command using an alias. So I am
> not sure if a built in command should take precedence over a variable of
> the same name or the other way round.

This is simple. The PoLA (Principle of Least Astonishment) applies
here.  Behaviour must be the same as in other (to some extent POSIX
compatible) shells.  A shell should parse it's input, not adhoculate
it.

Best regards,

Wolfgang Denk
Rasmus Villemoes Sept. 25, 2020, 1:38 p.m. UTC | #5
On 25/09/2020 15.09, Wolfgang Denk wrote:
> Dear Heinrich Schuchardt,
> 
> In message <4b00225d-d960-4a14-9aec-110ddddf7f30@gmx.de> you wrote:
>>
>> Further we cannot first introduce a command call and then eliminate it
>> due to backward compatibility. We should decide on the final version
>> beforehand.

Please note that I never meant for

  f a b c

to be an eventual replacement for

  call f a b c

the latter syntax would continue to be accepted. And I'm personally
completely fine with that being the _only_ way to call a
function-defined-in-the-environment-with-positional-args, which also makes

>> I am
>> not sure if a built in command should take precedence over a variable of
>> the same name or the other way round.

a moot point.

So can we instead discuss whether the "call" command is worth having at
all. I notice that Wolfgang calls this a nice idea (thanks), but
soft-NAKs it because he'd rather see all of hush updated before adding
extra features. The thing is, I can't take that monumental task on me
(especially with all the backwards-compatibility concerns there'd be,
with various scripts in the wild that may have come to rely on U-Boot's
hush parser's behaviour in corner cases), but doing cmd_call is about
100 lines of mostly stand-alone code.

Rasmus
Heinrich Schuchardt Sept. 25, 2020, 1:38 p.m. UTC | #6
On 25.09.20 15:09, Wolfgang Denk wrote:
> Dear Heinrich Schuchardt,
>
> In message <4b00225d-d960-4a14-9aec-110ddddf7f30@gmx.de> you wrote:
>>
>> Further we cannot first introduce a command call and then eliminate it
>> due to backward compatibility. We should decide on the final version
>> beforehand.
>
> Full agreement.  we need a concept of what is needed / wanted first.
> And then we should look how current vrsions of hush fit into this.
>
>> In the Linux world you can override a command using an alias. So I am
>> not sure if a built in command should take precedence over a variable of
>> the same name or the other way round.
>
> This is simple. The PoLA (Principle of Least Astonishment) applies
> here.  Behaviour must be the same as in other (to some extent POSIX
> compatible) shells.  A shell should parse it's input, not adhoculate
> it.

For me this could be realized by enhancing the run command to allow:

run varname1 varname2 ... varnameN --args argv1 argv2 argv3

Arguments argv1, argv2, ... are passed to the script identified by the
last variable (varnameN).

No new command to learn. Just a new option.

Best regards

Heinrich
Rasmus Villemoes Sept. 25, 2020, 1:51 p.m. UTC | #7
On 25/09/2020 15.38, Heinrich Schuchardt wrote:
> On 25.09.20 15:09, Wolfgang Denk wrote:
>> Dear Heinrich Schuchardt,
>>
>> In message <4b00225d-d960-4a14-9aec-110ddddf7f30@gmx.de> you wrote:
>>>
>>> Further we cannot first introduce a command call and then eliminate it
>>> due to backward compatibility. We should decide on the final version
>>> beforehand.
>>
>> Full agreement.  we need a concept of what is needed / wanted first.
>> And then we should look how current vrsions of hush fit into this.
>>
>>> In the Linux world you can override a command using an alias. So I am
>>> not sure if a built in command should take precedence over a variable of
>>> the same name or the other way round.
>>
>> This is simple. The PoLA (Principle of Least Astonishment) applies
>> here.  Behaviour must be the same as in other (to some extent POSIX
>> compatible) shells.  A shell should parse it's input, not adhoculate
>> it.
> 
> For me this could be realized by enhancing the run command to allow:
> 
> run varname1 varname2 ... varnameN --args argv1 argv2 argv3
> 
> Arguments argv1, argv2, ... are passed to the script identified by the
> last variable (varnameN).
> 
> No new command to learn. Just a new option.

Yes, this is really more to be thought of as a "run_with_args" command
than an extension of hush (though the $1 treatment does need to hook
into the hush code, which is both why I made it dependent on HUSH_PARSER
and made it live in cli_hush.c). I'm certainly open to extending the
existing run command instead of creating a new "toplevel" command.
Though I'd probably make it

  run varname -- arg1 arg2 arg3

instead: Just use -- as a separator [that has precedent as "stop doing
X, use the rest as argv", though X is normally "interpret options" and
now it would be "read function names to run"], and only allow a single
"function" to be called. Otherwise, I don't there's any natural answer
to whether all the varnameX or only the last should be called with the
positional arguments. It's pretty simple to do "for x in v1 v2 v3; do
run $x -- arg1 arg2 arg3 ; done if one has a bunch of functions that
should be called in turn, and it's even more simple to do

  run varname1 varname2 varname{N-1}
  run varnameN -- arg1 arg2 arg3

if one has a bunch of parameter-less functions to call before varnameN.

Rasmus

Rasmus
Simon Glass Sept. 25, 2020, 2:40 p.m. UTC | #8
Hi,

On Fri, 25 Sep 2020 at 06:59, Wolfgang Denk <wd@denx.de> wrote:
>
> Dear Rasmus,
>
> In message <20200925111942.4629-1-rasmus.villemoes@prevas.dk> you wrote:
> > This adds a way to call a "function" defined in the environment with
> > arguments. I.e., whereas
> >
> >   run foo
> >
> > requires one to set the (shell or environment) variables referenced
> > from foo beforehand, with this one can instead do
> >
> >   call foo arg1 arg2 arg3
> >
> > and use $1... up to $9 in the definition of foo. $# is set so foo can
> > make decisions based on that, and ${3:-default} works as expected.
>
> This is definitely a useful idea.
>
> But...
>
> ...the current version of hush in U-Boot is old, has a number of
> known bugs and shortcomings, and I really recommend not to adding
> any new features to it, because that would makie an update to a more
> recent version even less likely.

I would like to see this also. Is the busybox version the latest?
There might even be some tests although I can't see any.

One concern is that it seems to be 3x the line count. Hopefully that
doesn't result in 3x the code size. It also seems to have developed an
even more severe case of #ifdef disease.

>
> So the first step before such extensions should be to update hush.
> In that process (which might be more of a new port) one should
> consider the possibility of keeping a little more of the
> functionality - memory restrictins today are not so strict any more
> as they were when hush was originally added.  One feature that would
> definitely be useful is command substitution.
>
> All this needs a bit of a long term maintainable concept.  Quick
> hacking of the ancient code is not a good idea.
>

Regards,
Simon
Wolfgang Denk Sept. 26, 2020, 8:51 a.m. UTC | #9
Dear Heinrich,

In message <99821acf-b921-2c06-05b8-dd32058f28f2@gmx.de> you wrote:
>
> For me this could be realized by enhancing the run command to allow:
>
> run varname1 varname2 ... varnameN --args argv1 argv2 argv3
>
> Arguments argv1, argv2, ... are passed to the script identified by the
> last variable (varnameN).

Nice idea!  Only we should do a better syntax (options preceeding
argument), i. e.

	run [ --args argv ] varname1 varname2 ... 

where argv would be the name of a variale to hold the arguments (as
a comma (?) separated list) ?

Do you have an idea how the "script" would pull out the arguments
from that variable?

Best regards,

Wolfgang Denk
Wolfgang Denk Sept. 26, 2020, 8:55 a.m. UTC | #10
Dear Rasmus,

In message <823e5d31-7022-346e-b3a3-e36a4a295764@prevas.dk> you wrote:
>
> Though I'd probably make it
>
>   run varname -- arg1 arg2 arg3

Or rather

	run arg1 arg2 ... -- varname1 varname2 ...


> instead: Just use -- as a separator [that has precedent as "stop doing
> X, use the rest as argv", though X is normally "interpret options" and
> now it would be "read function names to run"], and only allow a single
> "function" to be called. Otherwise, I don't there's any natural answer
> to whether all the varnameX or only the last should be called with the
> positional arguments.

"run" has always taken any number of vaiable names to run, and this
behaviour should be kept.  If there are arguments, these are the
same for all of these.  If you need indivisual argument settings,
you must break the sommand in parts combined with &&.


Best regards,

Wolfgang Denk
Heinrich Schuchardt Sept. 26, 2020, 10:39 a.m. UTC | #11
On 9/26/20 10:51 AM, Wolfgang Denk wrote:
> Dear Heinrich,
>
> In message <99821acf-b921-2c06-05b8-dd32058f28f2@gmx.de> you wrote:
>>
>> For me this could be realized by enhancing the run command to allow:
>>
>> run varname1 varname2 ... varnameN --args argv1 argv2 argv3
>>
>> Arguments argv1, argv2, ... are passed to the script identified by the
>> last variable (varnameN).
>
> Nice idea!  Only we should do a better syntax (options preceeding
> argument), i. e.
>
> 	run [ --args argv ] varname1 varname2 ...
>
> where argv would be the name of a variale to hold the arguments (as
> a comma (?) separated list) ?
In another mail you suggested "run arg1 arg2 ... -- varname1 varname2".

Whether arguments or script names are first or last does not make much
of a difference for the implementation effort. Any way you have to loop
over all arguments to find the separator "--".

"better syntax" does not apply here as the two alternatives have the
same expressivity, and need the same amount of typing and learning. It
is a matter of taste.

>
> Do you have an idea how the "script" would pull out the arguments
> from that variable?

Rasmus already suggested $1 .. $9 for the positional arguments (where
counting should not stop at 9).

Best regards

Heinrich

>
> Best regards,
>
> Wolfgang Denk
>
Wolfgang Denk Sept. 26, 2020, 2:02 p.m. UTC | #12
Dear Simon,

In message <CAPnjgZ27owL439SkG3_V38KQnBkUDL5u2Ay7vmPeJ=mCmvHnzA@mail.gmail.com> you wrote:
>
> I would like to see this also. Is the busybox version the latest?
> There might even be some tests although I can't see any.

I have never been able to locate any other origin of the housh shell
source code, so - exept for other ports - this is the only current
souce I know of.

> One concern is that it seems to be 3x the line count. Hopefully that
> doesn't result in 3x the code size. It also seems to have developed an
> even more severe case of #ifdef disease.

ouch...

Best regards,

Wolfgang Denk
Wolfgang Denk Sept. 26, 2020, 2:13 p.m. UTC | #13
Dear Heinrich,

In message <b2395001-191c-31f3-2682-ac3e9fcff236@gmx.de> you wrote:
>
> > Nice idea!  Only we should do a better syntax (options preceeding
> > argument), i. e.
> >
> > 	run [ --args argv ] varname1 varname2 ...
> >
> > where argv would be the name of a variale to hold the arguments (as
> > a comma (?) separated list) ?
> In another mail you suggested "run arg1 arg2 ... -- varname1 varname2".

I was commenting on another suggestion.

> Whether arguments or script names are first or last does not make much
> of a difference for the implementation effort. Any way you have to loop
> over all arguments to find the separator "--".

It does not make a differenc in terms of implementation, but
tradition in UNIX systems is to have

	command_name [ -options ... ] arguments

i. e. options always came first.

It is only later tools that ignored how a proper program (TM) should
behave ;-)

> "better syntax" does not apply here as the two alternatives have the
> same expressivity, and need the same amount of typing and learning. It
> is a matter of taste.

Indeed. One is more pretty, and the other one is more ugly ;-)

> > Do you have an idea how the "script" would pull out the arguments
> > from that variable?
>
> Rasmus already suggested $1 .. $9 for the positional arguments (where
> counting should not stop at 9).

Fine with me.

Best regards,

Wolfgang Denk
Tom Rini Sept. 29, 2020, 5:45 p.m. UTC | #14
On Sat, Sep 26, 2020 at 04:02:08PM +0200, Wolfgang Denk wrote:
> Dear Simon,
> 
> In message <CAPnjgZ27owL439SkG3_V38KQnBkUDL5u2Ay7vmPeJ=mCmvHnzA@mail.gmail.com> you wrote:
> >
> > I would like to see this also. Is the busybox version the latest?
> > There might even be some tests although I can't see any.
> 
> I have never been able to locate any other origin of the housh shell
> source code, so - exept for other ports - this is the only current
> souce I know of.

One of my worries about updating our hush code would be to maintain
compatibility with all of the scripts out there that rely on whatever
odd behavior we have.

If one goes down the path of "give U-Boot a better shell", an opt-in
"hush v2" or whatever naming makes sense for re-porting something from
busybox and having a plan to keep it in sync would be the way I would
like to see it go.
Wolfgang Denk Sept. 30, 2020, 11:46 a.m. UTC | #15
Dear Tom,

In message <20200929174506.GJ14816@bill-the-cat> you wrote:
> 
> One of my worries about updating our hush code would be to maintain
> compatibility with all of the scripts out there that rely on whatever
> odd behavior we have.

Agreed.  Unfortunately we never collected any list of warts and bugs
and possible or recommended workarounds - we really should have done
that.

But then, IIRC, all problems of hush are of the type the specific
things are not working correctly, and the work around was to use
other ways or a combination of other commands / operators.  All
these workarounds should work in a bugfree version of hush still
tehe same, they would just be not necessary any more.

Of course we can't know the zillion of scripts out in the wild, and
where they might rely on broken behaviour.  If there are such, then
for them the upgrade to a newver version would indeed break the
system.

> If one goes down the path of "give U-Boot a better shell", an opt-in
> "hush v2" or whatever naming makes sense for re-porting something from
> busybox and having a plan to keep it in sync would be the way I would
> like to see it go.

I don't understand what you mean?  We probably cannot keep both
versions the hush shell (the current one and a more recent, less
broken one) in parallel in the source tree.  and I don't think we
should.  Fixing bugs is perfectly ok, and we never made any claim
that future versions of U-Boot must be bug-compatible to older ones.

Best regards,

Wolfgang Denk