diff mbox series

main: don't raise exception when executing dotcmd() on a non-existent file

Message ID 20181124162021.10486-1-ao2@ao2.it (mailing list archive)
State Rejected
Delegated to: Herbert Xu
Headers show
Series main: don't raise exception when executing dotcmd() on a non-existent file | expand

Commit Message

Antonio Ospite Nov. 24, 2018, 4:20 p.m. UTC
When sourcing a file with the dotcmd() builtin, dash raises an exception
when the input file cannot be opened.

For instance the following script fails prematurely and does not output
the message:

  #!/bin/sh

  . /non-existent-file
  echo "Survived!"

In this case, the behavior of dash is different from other shells (e.g.
bash, zsh) which are more forgiving and allow execution to carry on even
if the input file cannot be opened.

Fix this by passing the INPUT_NOFILE_OK flag when calling setinputfile()
in dotcmd().

As a bonus, this also fixes cases like the following:

  set -e
  . /non-existent-file || true
  echo "Survived! Let's do something else..."

This idiom is sometimes used in shell script to source a config file
with default values only to provide fallback values if the default
values were not available.

Signed-off-by: Antonio Ospite <ao2@ao2.it>
---

Hi Herbert,

I also thought about printing the warning in setinputfile() itself, maybe
guarding the printout with a new flag, like INPUT_NOFILE_OK_SILENT, which
could be used by readprofile() to keep the current silent behavior when
profile files are not found.

I though i'd send this simple version first to see if changing the behavior of
dotcmd() is acceptable in the first place. If it is we can then discuss about
how to do it.

Thank you,
   Antonio

 src/main.c | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Comments

Jilles Tjoelker Nov. 24, 2018, 5:56 p.m. UTC | #1
On Sat, Nov 24, 2018 at 05:20:21PM +0100, Antonio Ospite wrote:
> When sourcing a file with the dotcmd() builtin, dash raises an exception
> when the input file cannot be opened.

> For instance the following script fails prematurely and does not output
> the message:

>   #!/bin/sh

>   . /non-existent-file
>   echo "Survived!"

> In this case, the behavior of dash is different from other shells (e.g.
> bash, zsh) which are more forgiving and allow execution to carry on even
> if the input file cannot be opened.

POSIX is unambiguous (XCU 2.14 Special Built-In Utilities -> dot) that a
non-interactive shell shall abort when a dot script is not found, and
bash and zsh comply to this when standards compliance is requested (e.g.
by naming the shell "sh" in argv[0] or using "set -o posix" in bash or
"set -o posixbuiltins" in zsh). Most other shells (e.g. mksh, FreeBSD
sh, yash) comply to POSIX unconditionally here, like dash currently
does.

> Fix this by passing the INPUT_NOFILE_OK flag when calling setinputfile()
> in dotcmd().

> As a bonus, this also fixes cases like the following:

>   set -e
>   . /non-existent-file || true
>   echo "Survived! Let's do something else..."

> This idiom is sometimes used in shell script to source a config file
> with default values only to provide fallback values if the default
> values were not available.

The above code is specific to bash and zsh non-POSIX modes, and will not
work in a #!/bin/sh script unless specific steps are taken (such as "set
+o posix" or starting the script with "bash script1" rather than
"./script1").

The simple solution is
  [ ! -f /non-existent-file ] || . /non-existent-file
Time-of-check-time-of-use issues should not be an issue for config
files.

Alternatively, one could try
  command . ./non-existent-file || true
but this may ignore more errors than desired (such as syntax errors in
the sourced file) and does not work correctly in yash 2.30.
Antonio Ospite Nov. 24, 2018, 6:09 p.m. UTC | #2
On Sat, 24 Nov 2018 18:56:48 +0100
Jilles Tjoelker <jilles@stack.nl> wrote:

> On Sat, Nov 24, 2018 at 05:20:21PM +0100, Antonio Ospite wrote:
> > When sourcing a file with the dotcmd() builtin, dash raises an exception
> > when the input file cannot be opened.
> 
> > For instance the following script fails prematurely and does not output
> > the message:
> 
> >   #!/bin/sh
> 
> >   . /non-existent-file
> >   echo "Survived!"
> 
> > In this case, the behavior of dash is different from other shells (e.g.
> > bash, zsh) which are more forgiving and allow execution to carry on even
> > if the input file cannot be opened.
> 
> POSIX is unambiguous (XCU 2.14 Special Built-In Utilities -> dot) that a
> non-interactive shell shall abort when a dot script is not found, and
> bash and zsh comply to this when standards compliance is requested (e.g.
> by naming the shell "sh" in argv[0] or using "set -o posix" in bash or
> "set -o posixbuiltins" in zsh). Most other shells (e.g. mksh, FreeBSD
> sh, yash) comply to POSIX unconditionally here, like dash currently
> does.
>

OK, thanks for clarifying that.

I guess I was a little lazy for not checking before posting.

> > Fix this by passing the INPUT_NOFILE_OK flag when calling setinputfile()
> > in dotcmd().
> 
> > As a bonus, this also fixes cases like the following:
> 
> >   set -e
> >   . /non-existent-file || true
> >   echo "Survived! Let's do something else..."
> 
> > This idiom is sometimes used in shell script to source a config file
> > with default values only to provide fallback values if the default
> > values were not available.
> 
> The above code is specific to bash and zsh non-POSIX modes, and will not
> work in a #!/bin/sh script unless specific steps are taken (such as "set
> +o posix" or starting the script with "bash script1" rather than
> "./script1").
> 
> The simple solution is
>   [ ! -f /non-existent-file ] || . /non-existent-file
> Time-of-check-time-of-use issues should not be an issue for config
> files.
>

Indeed this is the most used form, thank you again.

Now I know that the other one is non-standard.

Sorry for the noise,
   Antonio

> Alternatively, one could try
>   command . ./non-existent-file || true
> but this may ignore more errors than desired (such as syntax errors in
> the sourced file) and does not work correctly in yash 2.30.
> 
> -- 
> Jilles Tjoelker
diff mbox series

Patch

diff --git a/src/main.c b/src/main.c
index 6d53e00..a229d68 100644
--- a/src/main.c
+++ b/src/main.c
@@ -331,12 +331,18 @@  dotcmd(int argc, char **argv)
 		char *fullname;
 
 		fullname = find_dot_file(*argv);
-		setinputfile(fullname, INPUT_PUSH_FILE);
+		status = setinputfile(fullname, INPUT_PUSH_FILE | INPUT_NOFILE_OK);
+		if (status < 0) {
+			sh_warnx("%s: %s", fullname, strerror(errno));
+			status = 1;
+			goto out;
+		}
 		commandname = fullname;
 		status = cmdloop(0);
 		popfile();
 	}
 
+out:
 	return status;
 }