"command" behaviour

2016-10-19 Thread Martijn Dekker
The "command" builtin is supposed to disable the "special" properties of
"special" builtins, which, as far as I can tell, simply means it should
stop them from exiting the shell on error as they normally do.

In practice this is a bit hit and miss, but it works for many builtins
on many shells. However, the following:

command exec /dev/null/nonexistent/file || echo oops

instantly exits all the shells I can find, never getting to the 'echo'.

So is it that no shell is compliant, or am I missing something and is
this actually compliant behaviour?

The thing is that

test -x /some/file && exec /some/file

has a race condition between the "test" and the "exec"; if the other
method worked, it would be atomic.

Thanks,

- M.



Re: "command" behaviour

2016-10-19 Thread Martijn Dekker
Op 19-10-16 om 16:39 schreef Joerg Schilling:
> Martijn Dekker  wrote:
> 
>> The "command" builtin is supposed to disable the "special" properties of
>> "special" builtins, which, as far as I can tell, simply means it should
>> stop them from exiting the shell on error as they normally do.
>>
>> In practice this is a bit hit and miss, but it works for many builtins
>> on many shells. However, the following:
>>
>> command exec /dev/null/nonexistent/file || echo oops
>>
>> instantly exits all the shells I can find, never getting to the 'echo'.
> 
> The exec*() syscall family always starts with destroing the previous address 
> space of the calling command. There is no other way...

OK, maybe I misspoke when I said shells "instantly" exit: in fact they
produce a diagnostic error message, then instantly exit. All the shells
seem to manage to stick around that long after the 'exec' command fails.
(Bash even manages to speak a little Dutch.)

By my logic, if the shell can stick around to produce an error message,
it could also decide not to exit.

$ cat >test.sh < command exec /dev/null/nonexistent/file || echo oops
> EOF
$ bash test.sh
test.sh: regel 1: /dev/null/nonexistent/file: Not a directory
test.sh: regel 1: exec: Kan /dev/null/nonexistent/file niet uitvoeren:
Not a directory
$ ksh test.sh
test.sh[1]: exec: /dev/null/nonexistent/file: cannot execute [Not a
directory]
$ mksh test.sh
test.sh[1]: /dev/null/nonexistent/file: not found
$ yash test.sh
exec: cannot execute command `/dev/null/nonexistent/file': Not a directory
$ zsh -o posixbuiltins test.sh
test.sh:1: not a directory: /dev/null/nonexistent/file
$ dash test.sh
test.sh: 1: exec: /dev/null/nonexistent/file: not found

- M.



Re: "command" behaviour

2016-10-19 Thread Martijn Dekker
Op 19-10-16 om 16:30 schreef Chet Ramey:
> On 10/19/16 10:15 AM, Martijn Dekker wrote:
>> The "command" builtin is supposed to disable the "special" properties of
>> "special" builtins, which, as far as I can tell, simply means it should
>> stop them from exiting the shell on error as they normally do.
>>
>> In practice this is a bit hit and miss, but it works for many builtins
>> on many shells. However, the following:
>>
>> command exec /dev/null/nonexistent/file || echo oops
>>
>> instantly exits all the shells I can find, never getting to the 'echo'.
> 
> It doesn't matter whether the `command' prefix is supplied; the description
> of `exec' says (here, command refers to the argument to exec):
> 
> "If command is specified, exec shall not return to the shell"

Maybe that needs changing then. I can see no good reason why this is
mandated, making atomic checking and error handling impossible.

In any case I think the full description is multi-interpretable at best:

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_20
| If /command/ is specified, exec shall not return to the shell; rather,
| the exit status of the process shall be the exit status of the
| program implementing /command/, which overlaid the shell.

But, in the situation under discussion, there is no such command,
because it was not found; therefore it cannot possibly have any exit
status, and the shell was not overlaid (as proven by the fact that it
produces a diagnostic error message before exiting); so, as far as I can
tell, it is logically impossible for this entire sentence to have any
relevance to this situation at all.

| If command is not found, the exit status shall be 127. If command is
| found, but it is not an executable utility, the exit status shall be
| 126. If a redirection error occurs (see Consequences of Shell
| Errors), the shell shall exit with a value in the range 1-125.
| Otherwise, exec shall return a zero exit status.

None of this, given the preceding, provides any reason to treat 'exec'
differently than any other special builtin.

Thanks,

- M.



Re: Should aliases expand after 'command'?

2016-10-23 Thread Martijn Dekker
Op 24-10-16 om 02:39 schreef David Korn:
> In ksh-88 on which the standard was based, the man page says that if an
> alias ends in a space then the next word is checked for an alias.  This
> seems to be omitted in the posix standard without any comment in the
> rationale as to why.

No, it's there.
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_03_01
| If the value of the alias replacing the word ends in a , the
| shell shall check the next command word for alias substitution; this
| process shall continue until a word is found that is not a valid
| alias or an alias value does not end in a .


> Anyway in ksh93, command is both a builtin command and an alias for
> 'command ' which causes the word after command to be expanded.

But why?

Thanks,

- Martijn



Re: "command" behaviour

2016-10-28 Thread Martijn Dekker
Op 28-10-16 om 14:58 schreef Vincent Lefevre:
> And after the
> exec is handled, the shell is no longer involved, so that 2.8.1
> cannot apply in any case.

But that is just the logic I was questioning in my replies to Jörg and Chet.

How can the exec be "handled" if the file to exec doesn't exist?

How does a shell that is "no longer involved" inform the user that the
file doesn't exist before exiting?

> This would be a change of the existing behavior and might be a
> security issue as code after the exec could be run

This is a valid argument.

- M.



Re: [1003.1(2013)/Issue7+TC1 0001016]: race condition with set -C

2016-11-01 Thread Martijn Dekker
Op 01-11-16 om 15:58 schreef Robert Elz:
> Depends what you believe "this function" really is - but it certainly is
> not avoiding race conditions when two processes are attempting to create
> the file at the same time.

Says who?

> What -C is (or should be) really for is avoiding accidentally writing
> on top of (long existing) stable files (particularly ones that are hard
> to re-create).

That's not what the spec says, though. It simply says:

| -C (Uppercase C.) Prevent existing files from being overwritten by
| the shell's '>' redirection operator (see Redirecting Output);

This says nothing about how long the files are supposed to have existed
or how stable they are supposed to be before -C prevents them from being
overwritten. As written, the spec inherently requires atomicity.

AFAIK, there also is no other POSIX shell mechanism that could create
files both atomically and non-destructively.

If 'mktemp' were a POSIX standard utility, this wouldn't be an issue,
but it's not, so it is.

- M.



Re: [1003.1(2013)/Issue7+TC1 0001016]: race condition with set -C

2016-11-02 Thread Martijn Dekker
Op 02-11-16 om 10:51 schreef Stephane Chazelas:
> At the moment, there's no way (that I know) to create a temp file
> reliably with POSIX utilities (though it may be possible to create a
> temp dir reliably, which is generally a better idea than creating a temp
> file in a world writable directory anyway).

Hmmm. Interesting.

(Just for clarity: below I'm still talking about creating temporary
files for any use as in 'mktemp', and *not* about locking.)

If both 'mkdir' and 'ln' operate atomically, there could be a safe
workaround for creating a regular file directly under /tmp. It would
involve creating a (very) temporary directory under /tmp using 'mkdir
-m700', then creating the file inside there, setting the mode, etc. with
no need for atomicity, then attempting to 'ln' that file back to /tmp
until we've got an available name. Do you think this could work?

The lack of a good POSIX shell way to create a temporary file has irked
me for years. So, as part of my cross-platform POSIX shell library
"modernish", I've created a shell implementation of 'mktemp' which is a
superset of currently available 'mktemp' implementations. To create
regular files, it currently uses the 'set -C'-plus-output-redirection
technique discussed earlier. At the time of writing it I was assuming it
must be safe, but it isn't, and (given the discussions here) it seems
uncertain if it will ever be safe in the future[*]. If the workaround
described above is good, I will have to implement it.

It also has an -F flag to create a temporary FIFO. Should 'mkfifo' be
considered atomic?

Thanks,

- Martijn

[*] I don't get all the objections. Is there any good reason for *not*
ensuring that noclobber is atomic? It's not as if modern operating
systems make this hard to implement. Why not err on the side of safety?



Re: [1003.1(2013)/Issue7+TC1 0001016]: race condition with set -C

2016-11-06 Thread Martijn Dekker
Op 02-11-16 om 13:32 schreef Martijn Dekker:
> If both 'mkdir' and 'ln' operate atomically, there could be a safe
> workaround for creating a regular file directly under /tmp. It would
> involve creating a (very) temporary directory under /tmp using 'mkdir
> -m700', then creating the file inside there, setting the mode, etc. with
> no need for atomicity, then attempting to 'ln' that file back to /tmp
> until we've got an available name. Do you think this could work?

No one replied to poke holes in this, so I went ahead and implemented
this workaround in the modernish shell library implementation of
'mktemp'. <https://github.com/modernish/modernish>

Just one thing still worries me a bit. Though 'ln' without the -f option
is never supposed to overwrite files, the spec also states:

| If the last operand specifies an existing file of a type not
| specified by the System Interfaces volume of POSIX.1-2008, the
| behavior is implementation-defined.
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ln.html

Does this mean I cannot actually rely on 'ln' not overwriting a file or
otherwise behaving unexpectedly?

Thanks,

- M.



Re: [1003.1(2013)/Issue7+TC1 0001016]: race condition with set -C

2016-11-07 Thread Martijn Dekker
Op 07-11-16 om 03:55 schreef Shware Systems:
> To last question, yes, but the effects are supposed to be documented so
> generic guard code that may invoke platform specific pre-ln attempt
> handling can be written. This is a compromise to disqualifying a system
> that defines additional file types from being considered conforming at
> all. In a script this might look like:
> if [ -e /app/$platform/linkhandler ] ;
> then { . .../linkhandler }
> else { do ln directly }; fi

Thanks for the reply. Where can I find more info about this? Is there a
standardised /app directory structure? I don't find it on any actual
system I've access to.

> To some extent it's also the operator's responsibility to sandbox use of
> non-standard file types outside directories the standard says portable
> applications need access to, such as $TMP, to avoid issues.

That makes sense. Many things would break if /tmp does not operate portably.

> A platform
> aware application might create such a file in a $TMP/appsubdir directory
> but shouldn't link it into /tmp after, iow, but to an ~/app/files type
> directory instead. That is more a training issue to me, not something
> the standard can reasonably address or make a requirement.

Unfortunately, by far the most common use case of 'mktemp' is to create
a temporary file in /tmp, so my cross-platform shell implementation of
it will have to be able to do that.

For the time being, unless anyone has concrete evidence or convincing
arguments to the contrary, I will assume that any issues with 'ln'
atomicity into /tmp are theoretical.

- M.



Re: [1003.1(2013)/Issue7+TC1 0001016]: race condition with set -C

2016-11-07 Thread Martijn Dekker
Op 07-11-16 om 13:45 schreef Stephane Chazelas:
> 2016-11-02 13:32:44 +0000, Martijn Dekker:
> [...]
>> > If both 'mkdir' and 'ln' operate atomically, there could be a safe
>> > workaround for creating a regular file directly under /tmp. It would
>> > involve creating a (very) temporary directory under /tmp using 'mkdir
>> > -m700', then creating the file inside there, setting the mode, etc. with
>> > no need for atomicity, then attempting to 'ln' that file back to /tmp
>> > until we've got an available name. Do you think this could work?
> [...]
> 
> I don't think you can use ln here.
> 
> ln "$tempdir/file" "$tempfile"
> 
> would create a "$tempfile/file" link if "$tempfile" existed and
> was of type directory or a symlink eventually resolving to a
> directory.

...and it's not as if I didn't know that either. Sorry for generating
useless noise here. I let the details blind me to the obvious.

> You could use "link" (Unix, not POSIX), or "ln -T" (GNU, not
> POSIX) or "mv -Tn" (GNU) instead.

"link" seems good. It's on AIX (according to ibm.com), on anything using
GNU coreutils, on the Mac, on FreeBSD, NetBSD, and Solaris 13. And I've
confirmed for all of these but AIX that it can be invoked by a regular user.

But it's not on OpenBSD. Drat. :/

GNU "ln -T" or GNU "mv -Tn" won't help me as "link" and "ln" are both
part of the same GNU coreutils package.

The current OpenBSD release version has m4 with mkstemp() (thanks for
pointing that out earlier, Geoff -- I hadn't discovered that yet), so
between those and "link" it should hopefully be covered. I could even
fall back on OS implementations of "mktemp" which are also fairly
ubiquitous.

This is going to need a feature test. Since m4 with mkstemp() is POSIX,
it should be preferred.

Well, back to work.

Thanks,

- M.



Re: [1003.1(2013)/Issue7+TC1 0001016]: race condition with set -C

2016-11-09 Thread Martijn Dekker
Op 07-11-16 om 18:08 schreef Martijn Dekker:
> Op 07-11-16 om 13:45 schreef Stephane Chazelas:
>> You could use "link" (Unix, not POSIX), or "ln -T" (GNU, not
>> POSIX) or "mv -Tn" (GNU) instead.
> 
> "link" seems good. It's on AIX (according to ibm.com), on anything using
> GNU coreutils, on the Mac, on FreeBSD, NetBSD, and Solaris 13. And I've
> confirmed for all of these but AIX that it can be invoked by a regular user.
> 
> But it's not on OpenBSD. Drat. :/

Also, on Mac OS X and FreeBSD, 'link' turns out to act like 'ln': if a
directory or a symlink to it exists, the file is created inside it. This
is contrary to what the man page says, by the way. But in any case it
means I can't use it.

- M.



Clarify ${var-$*}, ${1+$*}, etc. [was: [1003.1(2013)/Issue7+TC1 0000888]: Clarify expansion of '@' and '*' special parameters]

2017-03-18 Thread Martijn Dekker
On 05-Nov-2014 at 18:00, Geoff Clare wrote via bug 888 on Austin Group
Bug Tracker:
> to:
> 
> The following examples illustrate some of the ways in which '*' and '@'
> can be expanded:
> 
> set "abc" "def ghi" "jkl"
[...]
> IFS='' # null
[...]
> unset var
> printf '%s\n' ${var-$*}
> abcdef ghijkl

This example has made it in the published standard by now.
Unfortunately, following discussions on the bug-bash list[*], I believe
this particular example is incorrect. This refers to C165.pdf, section
C.2.5, page 3724, lines 127689-127690.

According to the specification (2.5.2 Special Parameters), given null
IFS and unset 'var', the output of
printf '%s\n' ${var-$*}
should be:
abc
def ghi
jkl
and not
abcdef ghijkl

Reasoning:

First, given an unset 'var', unquoted $* and unquoted ${var-$*} (and
unquoted ${1+$*}, etc) should act identically. If they don't, that's a
bug either way.

Second, the cited example is contrary to the specification, which says:
"Expands to the positional parameters, starting from one, initially
producing one field for each positional parameter that is set. When the
expansion occurs in a context where field splitting will be performed,
any empty fields may be discarded and each of the non-empty fields shall
be further split as described in Field Splitting. [...]"

Well, the expansion occurs "in a context where field splitting will be
performed" because it is unquoted (the fact that IFS happens to be null
is neither here nor there; its value or lack thereof has no bearing on
the lexical context). So the non-empty fields, having been generated,
"shall be further split as described in Field Splitting", which, given
that IFS is null, is a no-op.

In other words, quoted "$*" should join the fields into one, but (given
null IFS) unquoted $* should leave the fields alone altogether.

In other words again, unquoted $* should act identically to unquoted $@,
hence, unquoted ${var-$*}, ${1+$*}, etc. should act identically to
unquoted ${var-$@}, ${1+$@}, etc.

(Note that the case of a default assignment using a parameter
substitution, e.g. ${var=$*} given unset 'var', is different; 2.6.2
Parameter Expansion explicitly states for ${parameter:=[word]} that "in
all cases, the final value of /parameter/ shall be substituted". So the
example in lines 127695-127696 is correct.)

Situation in the wild:

For ${var-$*}, ${1+$*}, etc., every current POSIX shell except bash (as
of 3.x) acts like the specification. Bash 3.x (2007) and up act like the
buggy example (which makes me wonder if the buggy example was
accidentally inspired by bash's behaviour).

Given the test script:

IFS=
set "abc" "def ghi" "jkl"
printf '$*: '
printf '|%s| ' $*
printf '  ${1+$*}: '
printf '|%s| ' ${1+$*}

test results for some current shells are as follows:

 bash:  $*: |abc| |def ghi| |jkl|   ${1+$*}: |abcdef ghijkl|
 dash:  $*: |abc| |def ghi| |jkl|   ${1+$*}: |abc| |def ghi| |jkl|
 yash:  $*: |abc| |def ghi| |jkl|   ${1+$*}: |abc| |def ghi| |jkl|
  zsh:  $*: |abc| |def ghi| |jkl|   ${1+$*}: |abc| |def ghi| |jkl|
ksh93:  $*: |abc| |def ghi| |jkl|   ${1+$*}: |abc| |def ghi| |jkl|
 mksh:  $*: |abc| |def ghi| |jkl|   ${1+$*}: |abc| |def ghi| |jkl|
 oksh:  $*: |abc| |def ghi| |jkl|   ${1+$*}: |abc| |def ghi| |jkl|

Thanks to Chet Ramey for giving me a heads-up on this on the bug-bash
list. There, he also wrote (on 27-Feb-2017 at 21:03):

> You might also prepare a counter to the argument that at the time the
> $* on the right side of the parameter expansion is expanded, the rules
> in force are detailed in 2.6.2 ("word shall be subjected to tilde
> expansion, parameter expansion, command substitution, and arithmetic
> expansion"), and word splitting isn't listed. I think that came up before.

I've got three counterpoints to that:

1. This is irrelevant, because the bug does not concern splitting
fields, but instead failure to generate fields as specified.

2. Even if it were relevant, in the case under discussion, IFS is null,
so field splitting is a no-op anyway.

3. In the general case, field splitting not being listed there simply
means that field splitting is not performed for the word on the right
side /within/ an expansion such as ${var-$*} (in this case, that word
being $*). If neither the ${var-$*} expansion as a whole nor the /word/
within the expansion (in this case, $*) are quoted, then field splitting
is performed as normal for the expansion as a whole (as is pathname
expansion/globbing), as detailed in 2.6.

Thanks,

- M.



Rethinking pre/post-increment operators [was: [1003.1(2013)/Issue7+TC1 0001128]: Where is the ',' (comma) operator ?]

2017-03-18 Thread Martijn Dekker
(0003623) stephane (reporter) - 2017-03-17 10:32
http://austingroupbugs.net/view.php?id=1128#c3623
> That's for instance why the spec says it's unspecified (or implementation
> defined, I don't remember) whether ++ and -- are supported or not, to tell
> applications not  to assume $((--a)) is the same as $((- - a)) (as they
> could if they had left that bit out).

The exact wording is that these are "not required" to be supported.

Speaking of which, I don't understand why this is. The
{pre,post}-{in,de}crement operators are a very frequently used feature
of C-style arithmetics, so users expect them. Implementing these surely
is rather trivial.

So it seems like an unnecessary annoyance that you cannot rely on these
in a cross-platform shell script -- particularly since (as Stéphane
points out above) this creates an avoidable ambiguity: $((--a)) and
$((++a)) do not have the same meaning on every shell.

Are there any reasons for that decision, other than "ksh88 didn't
support them", that I may not be aware of?

If ksh88 is the reason, then maybe it's time to revisit this.

The only current POSIX shells I'm aware of that *don't* support them are
Almquist derivatives dash and FreeBSD sh[*]; even Busybox ash added them
(as did ksh88 clone pdksh, back in 1993).

(Dash silently accepts $((--a)) and $((++a)) as equivalent to $((a)), as
did ksh88; FreeBSD sh rejects them.)

- M.

[*] as well as another Almquist derivative, NetBSD sh, but it is far too
obsolete to be considered POSIX in 2017.



Re: Clarify ${var-$*}, ${1+$*}, etc. [was: [1003.1(2013)/Issue7+TC1 0000888]: Clarify expansion of '@' and '*' special parameters]

2017-03-21 Thread Martijn Dekker
Op 21-03-17 om 10:42 schreef Geoff Clare:
> Martijn Dekker  wrote, on 18 Mar 2017:
>> 1. This is irrelevant, because the bug does not concern splitting
>> fields, but instead failure to generate fields as specified.
>>
>> 2. Even if it were relevant, in the case under discussion, IFS is null,
>> so field splitting is a no-op anyway.
>>
>> 3. In the general case, field splitting not being listed there simply
>> means that field splitting is not performed for the word on the right
>> side /within/ an expansion such as ${var-$*} (in this case, that word
>> being $*). If neither the ${var-$*} expansion as a whole nor the /word/
>> within the expansion (in this case, $*) are quoted, then field splitting
>> is performed as normal for the expansion as a whole (as is pathname
>> expansion/globbing), as detailed in 2.6.
> 
> Points 1 and 2 are wrong, but 3 is right and that's the one that matters.
> 
> (The reason 1 and 2 are wrong is because of the part of the $* description
> you omitted above: "When the expansion occurs in a context where field
> splitting will not be performed, the initial fields shall be joined to
> form a single field ...")

Sorry to be awkward, but this makes no sense to me and I'd like to
understand the intention of the standard.

Since the expansion is unquoted in this example, how are we in a
"context where field splitting will not be performed"? Aren't we
lexically in a field splitting context, but field splitting happens to
be a no-op because IFS is null?

What am I missing? Am I perhaps misinterpreting the word "context" and
it means some other kind of context than lexical context?

Actually, if I'm wrong in 1 and 2 and you are right, then doesn't the
standard say that "the initial fields shall be joined to form a single
field", so then isn't my whole bug report wrong and the original example
correct?

(But if that's the case, no existing shells would be compliant... not
even bash, which would only be compliant for unquoted ${var-$*}, ${1+$*}
etc. and not for simple unquoted $*.)

> Please could you submit a new Mantis bug to request the change to the
> example output.

Done.

- M.



Re: [1003.1(2013)/Issue7+TC1 0001016]: race condition with set -C

2017-04-01 Thread Martijn Dekker
Op 29-03-17 om 18:30 schreef shwares...@aol.com:
> I agree something along these lines should be added. As worded now the
> assumption looks to be more the system is a single user one, or
> sandboxed to that effect, not a multi-user server.

Yes.

> It doesn't even have to be a different user; a separate process that
> happens to use the same tmpnam() template for a FIFO creation might
> cause a race condition.

This is true, but I figured a user who has nefarious processes running
under their own account has bigger problems to worry about than -o
noclobber.

> On some systems stdin and stdout may be implemented as FIFOs, so a
> shell could be expected to access them along with regular files in
> redirects. I'd have it be process private directories, not simply
> user private,

How does one go about making these in POSIX?

> therefore, or that non-regular files like FIFOs are required to be
> atomically unlinked before attempting the regular file creation.

I think "atomically" and "before ..." are mutually exclusive.

- M.



Capturing standard error using command substitution and redirection

2017-07-03 Thread Martijn Dekker
On every POSIX shell I can test[*] except bash, ksh93 and FreeBSD sh, it
is possible to capture a command's output to standard error in a
variable like so:

$ ls_err=$(set +x; ls -l /dev/null/nonexistent /bin/sh 2>&1 1>&3) 3>&1
/bin/sh
$ echo "$ls_err"
ls: /dev/null/nonexistent: Not a directory

On bash and FreeBSD sh, this results in an error:

$ ls_err=$(set +x; ls -l /dev/null/nonexistent /bin/sh 2>&1 1>&3) 3>&1
$ echo "$ls_err"
-bash: 3: Bad file descriptor

On ksh93, standard output is discarded but not standard error:

$ ls_err=$(set +x; ls -l /dev/null/nonexistent /bin/sh 2>&1 1>&3) 3>&1
$ echo "$ls_err"
ls: /dev/null/nonexistent: Not a directory

It does work on both bash, ksh93 and FreeBSD sh when you use curly
braces as a workaround:

$ { ls_err=$(set +x; ls -l /dev/null/nonexistent /bin/sh 2>&1 1>&3); } 3>&1
/bin/sh
$ echo "$ls_err"
ls: /dev/null/nonexistent: Not a directory

Should the necessity of these curly braces be considered a bug?

- M.

[*] Confirmed to work without the curly braces on dash, Busybox ash,
yash, pdksh, mksh/lksh, and zsh



Re: Capturing standard error using command substitution and redirection

2017-07-16 Thread Martijn Dekker
Op 03-07-17 om 15:18 schreef Joerg Schilling:
> In our case, it is doubtful whether something like:
> 
>   var=value
> 
> allows IO redirection at all, as there is no command. Note that we recently
> discovered that var=$(cmd) does not set $? because there is no regular 
> command.
> 
>   { var=value; }
> 
> converts the pure assignement into something tha can be seen as a construct 
> that
> forms a command and thus allows redirection.

That makes sense, thanks. I think I found the exact answer in 2.9.1
"Simple Commands"[*]:

[...]
| 3. Redirections shall be performed as described in Redirection.
|
| 4. Each variable assignment shall be expanded for tilde expansion,
|parameter expansion, command substitution, arithmetic expansion,
|and quote removal prior to assigning the value.
|
| In the preceding list, the order of steps 3 and 4 may be reversed if
| no command name results from step 2 or if the command name matches
| the name of a special built-in utility; see Special Built-In
| Utilities.

There is no command name, so apparently bash, ksh93 and FreeBSD sh
reverse the order of steps 3 and 4, as allowed by the last paragraph
quoted above. Using the braces forces them to evaluate the redirections
in the order I want, as per 2.9.4 Compound Commands.

- M.

[*]
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01



Should "exec" run a shell function?

2017-07-16 Thread Martijn Dekker
A test script:

#! /bin/sh
true() {
echo "exec runs function"
}
exec true

On zsh, pdksh and mksh, 'exec' runs the shell function. On all other
POSIX shells (at least bash, dash, yash, ksh93, FreeBSD sh), it runs the
external 'true' utility.

Which behaviour is correct?

On the one hand, exec "shall replace the shell with /command/ without
creating a new process". A shell function is included in the definition
of "command" (ref: 2.9.1 Simple Commands) and so this ought to work with
'exec', in which case every shell except zsh and pdksh/mksh is broken.

On the other hand, can 'exec' legitimately run a shell function from the
current shell (or a builtin, for that matter) when it is supposed to
*replace* the shell with the command in question? Logically, you can't
run something that's part of a shell process that you're supposed to
have replaced.

- M.



Re: Should "exec" run a shell function?

2017-07-17 Thread Martijn Dekker
Op 17-07-17 om 12:18 schreef Geoff Clare:
> Joerg Schilling  wrote, on 17 Jul 2017:
>>
>> Geoff Clare  wrote:
>>
>>> I thought initially that all uses of "command" should change to "utility",
>>> but I think it is common for exec to be used before a pipeline.  For this
>>> to work correctly, I suspect that exec needs to be a reserved word.
>>
>> Do you believe the parser should know about "exec"?
> 
> Having played a bit with how exec works in pipelines, it seems my
> suspicion was incorrect.  We'll need to be careful how we update the
> wording, to ensure it doesn't have incorrect implications for the
> pipeline case.

Looks like your initial thought to use "utility" works.

How about builtins, though?

Since the utility is expected to overlay the current shell process, can
it be said that

(exec true)

is a POSIXly correct way of guaranteeing the execution of the external
bin/true utility rather than the shell builtin?

- M.



Re: Should "exec" run a shell function?

2017-07-20 Thread Martijn Dekker
Op 20-07-17 om 10:30 schreef Geoff Clare:
> I just noticed something not touched on in the previous discussion.
> 
> The EXIT STATUS section for exec says:
> 
> If command is specified, exec shall not return to the shell;
> rather, the exit status of the process shall be the exit status of
> the program implementing command, which overlaid the shell.
> 
> The use of "program" and "overlaid the shell" here means that the
> standard clearly does not allow the execution of built-in utilities and
> functions.

I had noticed that and that's how I interpreted it as well, but the EXIT
STATUS section seems like a strange place to specify that builtins and
functions cannot be executed by 'exec'. It looks like an afterthought.
So I wasn't sure if that should be considered normative, particularly since
- two long-standing shells (pdksh/mksh and zsh) do not act accordingly,
even in their POSIX modes;
- there seems to be a widespread notion that POSIX does not provide for
a way to guarantee the execution of an external command (except by doing
a path search manually, as extern() in modernish does).

So it's good to have you clarify this; now I can use this thread as a
source when I report this as a bug. :)

> Of course, we could still choose to treat this as a defect in the
> standard.

The text does need clarifying, IMHO.

- M.



Should 'exec' export assignments to external commands?

2017-09-09 Thread Martijn Dekker
Consider something like

LC_ALL=C exec ls

According to POSIX, should LC_ALL be exported to 'ls'?

Relevant here is that 'exec' is a special builtin (unlike 'command').
2.9.1 Simple Commands seems to suggest[*1] that it is unspecified
whether LC_ALL will be exported, but all current POSIX shells do[*2],
and the opposite seems very counterintuitive.

- M.

[*1]
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01
| * If the command name is a special built-in utility, variable
|   assignments shall affect the current execution environment. Unless
|   the set -a option is on (see set), it is unspecified:
|   - Whether or not the variables gain the export attribute during
| the execution of the special built-in utility
|   - Whether or not export attributes gained as a result of the
| variable assignments persist after the completion of the special
| built-in utility

[*2] bosh (shilytools sh) didn't export assignments preceding 'exec' but
recently changed its behaviour to match the other POSIX shells.



FYI: ksh88 (/usr/xpg4/bin/sh) is not actually POSIX compliant

2017-09-29 Thread Martijn Dekker
I've been in correspondence with a few people who still seem to believe
that AT&T ksh88 is POSIX compliant because POSIX was originally based on
ksh88. On Solaris, to this day, /usr/xpg4/bin/sh is ksh88 and is
considered "the POSIX compliant shell".

While developing modernish (my cross-platform shell language extension
and feature testing library), I've not only found that POSIX
intentionally deviates from ksh88 in at least three important ways (see
FTL_NOFNOVER, FTL_PARONEARG and FTL_UPP below), but ksh88 also has many
serious bugs, some of which have fatal effects. It is also no longer
being developed, and closed source, so these bugs will never be fixed. I
believe the following shows that ksh88 should not actually be considered
compliant.

/usr/xpg4/bin/sh on Solaris 11.3 has at least the following bugs. The
list is not exhaustive; this is just what I've discovered while
developing modernish. The IDs here are those from modernish's shell
bug/quirk/feature testing framework. FTL_* are considered "fatal" shell
bugs that will normally cause modernish to refuse to initialise.

* FTL_ASGNBIERR: Variable assignments preceding regular builtin commands
should not persist after the command exits, but with this bug they do if
the builtin exits with an error. This may break scripts in obscure ways.
For example, 'LC_ALL=C test a -wrong b' causes the LC_ALL assignment to
persist, so your locale settings are gone. Even more havoc may be
wreaked with a similar temporary assignment to PATH...

* FTL_BRACSQBR: a bracket glob pattern containing a quoted closing
square bracket ']' is never matched, not even if that character is
escaped or passed from a quoted variable. For example,
case ] in [a\]b] ) echo ok;; * ) echo bug;; esac
case a in [a\]b] ) echo ok;; * ) echo bug;; esac
case b in [a\]b] ) echo ok;; * ) echo bug;; esac
all output 'bug'.

* FTL_COMMAND2P: double parsing of parameter expansion when using
'command' with an external command. For example,
a=\$x
x=bug
command printf '%s\n' "$a"
prints 'bug'. Clearly, this makes the 'command' builtin unusable.

* FTL_NOFNOVER: ksh88 does not allow you to override a regular shell
builtins with a shell function. POSIX specifies that only special
builtins can't be overridden with shell functions, and all other shells
allow overriding regular builtins.

* FTL_PARONEARG: When IFS is empty (i.e. field splitting is off), "$@"
is counted as a single argument instead of each positional parameter as
separate arguments. In other words, "$@" does not *generate* fields when
field *splitting* is disabled (which is of course illogical and POSIX
has rightly fixed this).

* FTL_UNSETFAIL: the 'unset' command sets a non-zero exit status if the
variable to unset was not already set, whereas POSIX says: "Unsetting a
variable or function that was not previously set shall not be considered
an error [...]"
This can cause spurious non-zero exit statuses for functions that end in
an 'unset' command to clean up internal variables. (In itself this is
easy to work around; modernish only considers this bug fatal because all
existing shells that have this bug have other fatal errors as well.)

* FTL_UPP: Cannot access "$@" or "$*" if set -u (-o nounset) is active
and there are no positional parameters. If that option is set, ksh88
errors out on accessing "$@" and "$*". This makes 'set -u' impractical
to use, so POSIX changed this from ksh88, rendering "$@" and "$*" exempt
from 'set -u'.

* BUG_ARITHINIT: Using unset or empty variables in arithmetic
expressions causes the shell to error out with a "bad number" error.
Instead, according to POSIX, it should take them as a value of zero.

* BUG_CMDPV: More 'command' builtin breakage: the -p and -v options
cannot be combined, so you can't query the location of the command
within the default system PATH.

* BUG_HDPARQUOT: Quotes within parameter substitutions in Here-Documents
aren't removed. For instance, if 'var' is set, ${var+"x"} in a here-
document erroneously yields "x", not x.

* BUG_PP_08: When IFS is null, unquoted $* within a substitution (e.g.
${1+$*} or ${var-$*}) does not generate one field for each positional
parameter as expected, but instead joins them into a single field.

* BUG_PSUBBKSL: A backslash-escaped character within a quoted parameter
substitution is not unescaped, e.g.
echo "${foo-\x}"
outputs '\x' instead of 'x'.

That's all I have. Maybe some of you know some other ksh88 bugs...

- Martijn


P.S. Not that it's very relevant anymore, but for completeness's sake:
/usr/bin/ksh on Solaris 10.1 (non-sh ksh88, which was replaced by ksh93
on Solaris 11) has the following bugs as well:

* FTL_NOARITH: incomplete and buggy support for POSIX shell arithmetic.
Particularly, octal numbers by leading zero such as $((010 == 8)) are
not supported. There are other bugs as well, like:
x=42
echo $(( (y=x)==0x2A ))
results in "0x2A: bad number", which is clearly wrong, becaus

Re: FYI: ksh88 (/usr/xpg4/bin/sh) is not actually POSIX compliant

2017-09-29 Thread Martijn Dekker
Op 29-09-17 om 14:16 schreef Stephane Chazelas:
> 2017-09-29 11:59:52 +0200, Martijn Dekker:
> [...]
> 
> Ouch, quite of lot of nasty bugs. Have you reported them to
> Solaris?

No, because I thought ksh88 is no longer being developed. Is that incorrect?

Where/how would I report Solaris bugs?

>> * FTL_BRACSQBR: a bracket glob pattern containing a quoted closing
>> square bracket ']' is never matched, not even if that character is
>> escaped or passed from a quoted variable. For example,
>>  case ] in [a\]b] ) echo ok;; * ) echo bug;; esac
>>  case a in [a\]b] ) echo ok;; * ) echo bug;; esac
>>  case b in [a\]b] ) echo ok;; * ) echo bug;; esac
>> all output 'bug'.
> 
> Is that specified by POSIX? I have that notion that "]" can only
> be included in a set if used as the first character ([]ab]).
> 
> I understand that quotes remove the special meaning of glob
> operators, but that's not clear to me what should happen if
> you're quoting only part of that [...] operator.

My understanding is that a backslash should remove the special meaning
of the following character, in this case the first ']'. But note that
quoting all of it doesn't work either on ksh88:

case ] in ['a]b'] ) echo ok;; * ) echo bug;; esac
var=a]b; case ] in ["$var"] ) echo ok ;; * ) echo bug ;; esac

both still output "bug".

In fact, so does

var=]ab; case ] in ["$var"] ) echo ok ;; * ) echo bug ;; esac

Even moving ']' to the start of a variable will not make it work.

2010 versions of ksh93 have this bug as well, and they are still seeing
some considerable use. But it's fixed in 2012 versions.

> ksh88's [a\]b] matches on [a]b].

I'm not sure what you mean there.

> older versions of zsh don't match "-" with [a\-b]

Older versions of zsh (< 5.0.7) have lots of other fatal bugs as well.

> ksh93 matches "\" with ["^"c]

Remember the negator in POSIX glob bracket patterns is !, not ^. But
yes, that ksh93 bug has modernish ID BUG_BRACQUOT. Even if a '!' or '^'
is passed in a quoted variable it's still interpreted as a negator. (I
discovered that bug myself some time ago because it requires a
workaround for the trim() function in the var/string modernish module.)

> ksh93 and zsh match "b" with [[:alpha\:]] (mksh matches neither
> "a]" nor "b" nor "[[:alpha:]]")

mksh does not have character classes at all (BUG_NOCHCLASS); this is a
design decision on Thorsten's part.

> [...]
>> * FTL_PARONEARG: When IFS is empty (i.e. field splitting is off), "$@"
>> is counted as a single argument instead of each positional parameter as
>> separate arguments. In other words, "$@" does not *generate* fields when
>> field *splitting* is disabled (which is of course illogical and POSIX
>> has rightly fixed this).
> 
> Same bug as in the Bourne shell. I hadn't realised it was also
> in ksh88.

It's in early ksh93 versions as well.

> [...]
>> * BUG_HDPARQUOT: Quotes within parameter substitutions in Here-Documents
>> aren't removed. For instance, if 'var' is set, ${var+"x"} in a here-
>> document erroneously yields "x", not x.
> 
> Is that specified?

As far as I know, is not specified that they should act any differently
just because they're in a here-document, so I think by default that
should be considered a bug. (The only other shell I've found that acts
like this is FreeBSD sh, which is otherwise nearly bug-free.)

>> * BUG_PP_08: When IFS is null, unquoted $* within a substitution (e.g.
>> ${1+$*} or ${var-$*}) does not generate one field for each positional
>> parameter as expected, but instead joins them into a single field.
> 
> IIRC that was clarified as "unspecified" recently.

I see. Do you (or does anyone) have a link?

>> * BUG_PSUBBKSL: A backslash-escaped character within a quoted parameter
>> substitution is not unescaped, e.g.
>>  echo "${foo-\x}"
>> outputs '\x' instead of 'x'.
> [...]
> 
> Not sure about that one, there have been extensive discussions
> on similar matter in the past few years.

Hmm. Yeah, this one is probably wrong. Most shells treat \} differently
from other backslash-quoted characters.

unset -v foo; printf '%s ' "${foo-\}}" "${foo-\x}" "${foo-\\}"

bash 3.2, dash:
\} \x

bash 4.4, ksh93, mksh, zsh, FreeBSD sh, dash (Debian patched):
} \x

yash, oksh:
} x

As far as I can tell, POSIX only says this about it:

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
| Any '}' escaped by a  or within a quoted string, and
| characters in embedded arithmetic expansions, command substitutions,
| and variable expansions, shall not be examined in determining the
| matching '}'."

I will have to do a search on those discussions. Thanks.

- M.



Re: FYI: ksh88 (/usr/xpg4/bin/sh) is not actually POSIX compliant

2017-09-30 Thread Martijn Dekker
Op 30-09-17 om 15:01 schreef Joerg Schilling:
> Stephane Chazelas  wrote:
> 
>> I knew that was the case for ksh93's functions defined with the
>> function f {...;} syntax (though using static scoping), but
>> hadn't realised ksh88 also did local (dynamic) scoping for
>> options and traps even with the f(){...;} syntax.
>>
>> So it looks like that's another non-compliance.
>>
>> If I read the spec correctly, one should expect
>>
>> noglob() { set -f; }
>> glob() { set +f; }
>>
>> noglob; echo *; glob
>>
>> to output *, but that doesn't work with /usr/xpg4/bin/sh on
>> Solaris.
[...]
> This is an interesting behavior, but it is not related to local/dynamic 
> scoping, since this is a test whether a function is run in a sub-shell.

That's easy to disprove. I just did the the following on my Solaris 11.3
VM using /usr/xpg4/bin/sh:

$ noglob() { set -f; g=no; }
$ glob() { set +f; g=yes; }
$ noglob; echo "$- $g"
ism no
$ glob; echo "$- $g"
ism yes

So the shell options don't change outside of the function, but the
variable does. Which means that the function is not run in a subshell,
and that shell options have a mandatory local scope, so /usr/xpg4/bin/sh
functions are not POSIX compliant.

> I am not sure whether this is covered by POSIX at all.

It is. See:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_05

This says that part of the function definition is a Compound Command:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_04

which says that a compound command like ( some commands here ) is
executed in a subshell whereas one like { some commands here; } is not.

Hence, according to POSIX, a function defined as

fn() ( some commands here )

is executed in a subshell, whereas a function defined as

fn() { some commands here; }

is not.

- Martijn



Re: FYI: ksh88 (/usr/xpg4/bin/sh) is not actually POSIX compliant

2017-10-01 Thread Martijn Dekker
Op 01-10-17 om 12:58 schreef Joerg Schilling:
> Martijn Dekker  wrote:
>> So the shell options don't change outside of the function, but the
>> variable does. Which means that the function is not run in a subshell,
>> and that shell options have a mandatory local scope, so /usr/xpg4/bin/sh
>> functions are not POSIX compliant.
>>
>>> I am not sure whether this is covered by POSIX at all.
>>
>> It is. See:
>> http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_05
> 
> My impression is that POSIX would need to define that a function has to be 
> executed completely inside the environment of the calling shell process.
> 
> Such a text is however missing in POSIX.

It is not missing. As previously shown (and you removed from the quoted
text), the POSIX definition of a "function" incorporates the POSIX
definition of a "Compound Command". And that says:

{ compound-list ; }
Execute compound-list in the current process environment.

(see link given previously).

"In the current process environment" seems pretty clear to me.

- M.



Re: FYI: ksh88 (/usr/xpg4/bin/sh) is not actually POSIX compliant

2017-10-01 Thread Martijn Dekker
Op 01-10-17 om 14:43 schreef Joerg Schilling:
> The standard does not claim that a function has to be executed the same way 
> as  
> a compound ist would have been executed, or do you see such a claim in the 
> standard?
> 
> The way I read the standard just mentions that the function body in a 
> function 
> definition is parsed like a compund list.

2.9.5 Function Definition Command
[...]
fname ( ) compound-command [io-redirect ...]
[...]
"The argument /compound-command/ represents a compound command, as
described in Compound Commands."

It "represents" a compound command. This does not mean it is only parsed
as a compound command; it means it is to be treated as a compound
command in every way described under "Compound Commands".

2.9.4 Compound Commands
[...]
{ compound-list ; }
Execute compound-list in the current process environment.

This means any compound-command in that form (which therefore includes
any such compound-command that is the body of a function definition), is
executed in the current process environment.

I honestly don't see how your reading of it makes sense.

- Martijn



Re: FYI: ksh88 (/usr/xpg4/bin/sh) is not actually POSIX compliant

2017-10-01 Thread Martijn Dekker
Op 01-10-17 om 15:26 schreef Jilles Tjoelker:
> This text clearly defines things like
> 
> cat < $(ls -ld -- "$v") `ls -ld -- "$v"` ${w#"*"}
> EOF
> 
> the same way
> 
> a="$(ls -ld -- "$v") `ls -ld -- "$v"` ${w#"*"}"
> 
> are defined.
> 
> However, things like "${a-"*"}" are unspecified (in practice, the
> asterisk may either be literal or generate pathnames, depending on the
> shell).

Wow. Indeed.

According to a quick test I just did, the currently maintained shells
that generate pathnames on "${a-"*"}" are FreeBSD sh, AT&T ksh93,
/usr/xpg4/bin/sh (the ksh88 fork on Solaris), and bosh (schily's Bourne
shell fork).

> With
> 
> cat < ${a-"*"}
> EOF
> 
> the same problem occurs. It is unspecified what the interpretation of
> the characters between the double-quotes is. For example, ksh93 will do
> tilde expansion within a here-document when ${a-"${a-~}"} is used, the
> same way it will do when "${a-"${a-~}"}" is used normally. In other
> cases, special characters within the unquoted part lead to strange
> syntax errors.

This now all makes sense to me.

Here are some more test results.

Test script:

s=1234; unset -v u
cat < Given that and the behaviour of the Heirloom Bourne shell (which also
> keeps the quotes), I think it is valid to keep the quotes.

Yes. It is now clear to me that the modernish BUG_HDPARQUOT bug test is
wrong. My error was considering the quotes in ${foo-"bar"} as being the
same as the quotes in ${foo#"bar"} or ${foo%"bar"}. There is no bug here
(except in Solaris /usr/xpg4/bin/sh).

However, in currently maintained shells, the FreeBSD sh behaviour with
keeping the quotes in ${foo-"bar"} in here-documents appears to be in a
minority shared only with bosh, so it's very easily missed, and it may
bite people writing cross-platform scripts (as it did me).

So I'll rename it to a quirk (QRK_HDPARQUOT) instead of removing it, and
will update the modernish documentation accordingly. A quirk is not to
denote that the behaviour is wrong, just that it's an uncommon behaviour
that cross-shell script authors should be aware of.

- Martijn



ksh93 compliance [was: FYI: ksh88 (/usr/xpg4/bin/sh) ...]

2017-10-05 Thread Martijn Dekker
Op 05-10-17 om 18:35 schreef stephane.chaze...@gmail.com:
> Note that I'm not under the impression that ksh93 aimed for
> POSIX compliant. For instance, it did explicitely break POSIX
> compliance (and backward compatibility) when the <> redirection
> operator changed from being short for 0<> to  short for 1<> (I
> suppose to be consistent with the new <>; operator (and that one
> more generally wants to use it on stdout than stdin))

Interesting. I wasn't aware of that one -- probably because I've not
ever had a reason to use <>.

Funny how ksh93 deviates on at least this aspect, apparently
intentionally, and seems hyper-compliant in other aspects, like
disabling local variables with 'typeset' in POSIX functions.

This is another thing cross-platform script authors may need to be aware
of, so this is now going into modernish as BUG_REDIRIOO. (Deliberate or
not, it's clearly a bug in terms of POSIX compliance.)

Do you (or does anyone) know of any other aspects where ksh93
deliberately deviates from POSIX? I'm aware of the bugs below, but they
are clearly bugs and not deliberate deviations.



FYI, on the current ksh93 release version, Version AJM 93u+ 2012-08-01,
modernish as of this moment detects (and where necessary works around)
the following bugs:

BUG_BRACQUOT: shell quoting within bracket patterns has no effect (zsh <
5.3; ksh93) This bug means the - retains it special meaning of
'character range', and an initial ! (or ^) retains the meaning of
negation, even in quoted strings within bracket patterns, including
quoted variables.
for var in 'a-c' '!a'; do
case b in
( ["$var"] )echo 'bug' ;;
( [$var] )  echo 'no bug' ;;
esac
done
Output: bug (twice)
[Is this actually a bug, or just a quirk/minority behaviour? ksh93 and
older zsh are the only ones to act this way, but I can't seem to find
the relevant POSIX text now. But in any case, quoting '*' and '?' in
'case' glob patterns always works to disable their behaviour as
wildcards, so to me it smells like a bug for shell quoting to be ignored
in glob bracket patterns but not other glob patterns.]

BUG_CMDSPASGN: preceding a special builtin by 'command' does not stop
preceding invocation-local variable assignments from becoming global.
For instance:
foo=abc command eval "some command using \"\$foo\""
'eval' is a special builtin, and 'command' apparently fails to
completely disable its 'special' properties, so the 'foo' assignment
wrongly persists beyond the command.
This bug appears to be fixed in the unreleased beta.

BUG_FNSUBSH: This is a bad bug and it's been in ksh93 since 1993!
Function definitions within non-forked subshells (including command
substitutions) are silently ignored if a function by the same name
exists in the main shell, so the *wrong* code is executed. unset -f is
also silently ignored. Test script:
foo() { echo WRONG; }
(foo() { echo ok; } && foo && unset -f foo && foo)
Output:
WRONG
WRONG

BUG_IFSISSET: ${IFS+s} always yields 's' even if IFS is unset. This
applies to IFS only. [[ -v IFS ]] does not work either; it always
returns true. So is not possible to test in any normal way if IFS is set
or unset. The workaround I've found is to test field splitting behaviour:
case ${IFS:+n} in  # non-empty: it is set
( '' )  set -- 'a b'   # empty: test for default field splitting
set -- $1
[ "$#" -eq 1 ] # no field splitting: it is empty and set
esac
(This workaround is automatically incorporated into the modernish
isset() function if it detects this bug, so modernish 'isset IFS' will
always work correctly.)
This bug appears to be fixed in the unreleased beta.

BUG_ISSETLOOP: Expansions like ${var+set} and ${var+:nonempty) remain
static when used within a for, while or until loop; the expansions don't
change along with the state of the variable, so they cannot be used to
check whether a variable is set and/or empty within a loop if the state
of that variable may change in the course of the loop.
unset -v foo
r=
for i in 1 2 3 4; do
case ${foo+s} in
( s )   r=${r}s; unset -v foo ;;
( '' )  r=${r}u; foo= ;;
esac
done
printf '%s\n' "$r"
Output:  (expected: usus)
It is an effective workaround to use a shell function (such as modernish
isset()) that tests if the variable is set, even if that function uses
the same ${foo+s} sort of parameter expansion.

BUG_KUNSETIFS: Can't unset IFS under very specific circumstances. "unset
-v IFS" is a known POSIX shell idiom to activate default field
splitting. With this bug, the unset builtin silently fails to unset IFS
(i.e. fails to activate field splitting) if we're executing an eval or a
trap and a number of specific conditions are met. See BUG_KUNSETIFS.t
for more information.
https://github.com/modernish/modernish/blob/maste

Re: ksh93 compliance [was: FYI: ksh88 (/usr/xpg4/bin/sh) ...]

2017-10-06 Thread Martijn Dekker
Op 06-10-17 om 15:26 schreef Joerg Schilling:
> Martijn Dekker  wrote:
> 
>> Funny how ksh93 deviates on at least this aspect, apparently
>> intentionally, and seems hyper-compliant in other aspects, like
>> disabling local variables with 'typeset' in POSIX functions.
> 
> The fact that typeset does not work in POSIX function to create local 
> variables 
> does not look like something that causes POSIX compliance.

Agreed.

>> Do you (or does anyone) know of any other aspects where ksh93
>> deliberately deviates from POSIX? I'm aware of the bugs below, but they
>> are clearly bugs and not deliberate deviations.
> 
> I discovered aprox. 10 deviations, but I am sorry that I did not write them 
> down, so I currently only remember this one:
> 
>   the builtin command "times" does not create POSIX compliant output.

Ah yes, I had forgotten about that. In fact, ksh93 does not have a
'times' builtin at all -- it defaults to an alias:

$ type times
times is an alias for '{ { time;} 2>&1;}'

Using the 'time' command without an argument is an extension to POSIX so
of course the format can be whatever. But aliasing 'times' to this by
default does create a non-compliant situation.

I'm not sure if this actually affects any real-world scripts. Has anyone
ever heard of a script that parses the output of 'times'?

>> BUG_FNSUBSH: This is a bad bug and it's been in ksh93 since 1993!
>> Function definitions within non-forked subshells (including command
>> substitutions) are silently ignored if a function by the same name
>> exists in the main shell, so the *wrong* code is executed. unset -f is
>> also silently ignored. Test script:
>>  foo() { echo WRONG; }
>>  (foo() { echo ok; } && foo && unset -f foo && foo)
>> Output:
>> WRONG
>> WRONG
> 
> This also works as expected in ksh88.

That's not surprising because ksh88 forks its subshells the classical
way; this is ostensibly a bug introduced along with non-forking
subshells in ksh93 (as evidenced by the fact that forcing the subshell
to be forked is an effective workaround).

By the way, I currently know of two ways to force a subshell to be
forked in ksh93: one is pretty obvious, turn it into a backgroud job:

([[ $(ksh -c 'echo $PPID') != $$ ]] && echo forked) & wait "$!"

The other one only works with a command substitution subshell. If you
add a dummy command that redirects (or even closes) standard output, the
command substitution subshell will be forked.

echo $(: 1>&-; [[ $(ksh -c 'echo $PPID') != $$ ]] && echo forked)

I discovered this accidentally and I am not sure if the behaviour is
intentional, but I suppose it sort of makes sense: ksh needs to be sure
it can capture stdout from the command substitution and I figure the
problem was found too hard to solve for a non-forking subshell.

Hmm. Playing with this some more, I'm finding it's actually smart enough
to fork a subshell mid-execution:

echo $([[ $(ksh -c 'echo $PPID') != $$ ]] && echo forked1; \
  : 1>&-; [[ $(ksh -c 'echo $PPID') != $$ ]] && echo forked2)

outputs just "forked2". This certainly looks intentional.

>> BUG_TESTERR1A: test/[ exits with a non-error 'false' status (1) if an
>> invalid argument is given to an operator, instead of a status > 1 as
>> required by POSIX.
> 
> Do you have an example?
> 
> "test 1 -gt aa" results in $? == 0 with ksh93

That's because 'test' arguments in ksh93 are not integers but arithmetic
expressions, and 'aa' is a valid arithmetic expression which defaults to
0 if the variable 'aa' is unset or empty. Try:

test 1 -gt 1gg

Result is a 'ksh: test: 1gg: arithmetic syntax error' and $? == 1.

- M.



Re: FYI: ksh88 (/usr/xpg4/bin/sh) is not actually POSIX compliant

2017-10-06 Thread Martijn Dekker
Op 30-09-17 om 17:35 schreef Alan Coopersmith:
> It is true that ksh88 is no longer being developed, but the Solaris
> /usr/xpg4/bin/sh is a fork of ksh88, modified for standards compliance,
> that is still being maintained, and bugs fixed as necessary.

That's good to know!

>> Where/how would I report Solaris bugs?
> 
> Customers with support contracts can report bugs via Oracle Support.

I was afraid it might be something like that.

I'm no corporate customer, I'm just a slightly obsessed guy with a weird
love for that quirky shell language, trying to prove the idea that a
good solid shell library could (a) really enhance the language, as well
as get rid of some of its main annoyances, and (b) make cross-platform
scripting downright feasible. https://github.com/modernish/modernish

Shell bugs are a major obstacle to that goal. As part of that, I've been
on a bit of a crusade to get all the POSIX-related bugs fixed in all the
shells. My library experiments with things in shell that I don't believe
have really been tried before, so I keep running into all sorts of bugs
in various shells. Some of those bugs went unreported for decades.

All of which is to say, I'm not about to take out a support contract
just to report bugs, but I think it might still be in your interest to
get my bug reports for /usr/xpg4/bin/sh.

> The people responsible for running the standards conformance test suites
> on Solaris are also on this mailing list, and can file bugs if they see
> issues we need to fix.

I hope one of them can help by verifying and filing the bugs that I
posted to this list.

Is there anyone in particular I could cc in hopes of getting it noticed?

Thanks,

- Martijn



Re: ksh93 compliance [was: FYI: ksh88 (/usr/xpg4/bin/sh) ...]

2017-10-06 Thread Martijn Dekker
Op 06-10-17 om 18:05 schreef Joerg Schilling:
> Martijn Dekker  wrote:
[...]
>> That's not surprising because ksh88 forks its subshells the classical
>> way; this is ostensibly a bug introduced along with non-forking
>> subshells in ksh93 (as evidenced by the fact that forcing the subshell
>> to be forked is an effective workaround).
> 
> I cannot see such a correlation.

My tests show it, though. If you force a subshell to fork, suddenly you
can unset or change a function set in the main shell.

With a non-forking subshell:
foo() { echo WRONG; }
(foo() { echo ok; } && foo && unset -f foo && foo)
Output:
WRONG
WRONG

With a forking subshell:
foo() { echo WRONG; }
(foo() { echo ok; } && foo && unset -f foo && foo) & wait
Output:
ok
ksh: foo: not found

or, to use the trick with the output redirection in a command
substitution to force the subshell to fork:
foo() { echo WRONG; }
: $(: 1>&-; foo() { echo ok 1>&2; } && foo && unset -f foo && foo)
Output:
ok
ksh: foo: not found

> Non-forking subshells are a feature mainly introduced to permit a native port 
> to Win-DOS, where fork() is unknown. Note that David created "uwin" which is 
> much faster than Cygwin because of this trick.

Interesting. I've always seen it described as primarily a performance
enhancement.

uwin is good to know about, thanks.

- M.



Testing if two files are on the same file system

2017-10-18 Thread Martijn Dekker
Is there a way, using POSIX shell and utilities, to reliably test if two
files are on the same file system? It seems like a basic feature that
the shell should have access to, so I'd like to add it to the modernish
shell library.

The only POSIX command I've found so far that identifies the file system
that a file is on, is 'df -P', so I've experimented with parsing its
output. Given two file arguments, it normally outputs their file system
names in the first field of the second and third lines. The fields are
separated by one or more spaces.

Unfortunately, on the Mac, some of the file system names contain spaces
(on my system: "map -hosts" and "map auto_home"). POSIX doesn't seem to
prohibit this: file system names are considered implementation-defined[*].

Given that there is a fixed number of fields, it would be possible to
cope with this if no other fields can contain spaces, but the last field
is a directory name that may also contain spaces -- or even newlines.

So the output of 'df -P' is unparseable.

GNU stat (or gstat) makes this trivial: 'stat --file-system --format=%i
/file/one /file/two' provides two hexadecimal file system identifiers to
compare. But GNU stat is not standard and nowhere near ubiquitous. BSD
'stat' does not have any such feature.

Is there anything else POSIX or de-facto standard that could be (ab)used
to determine if two files are on the same file system?

Thanks,

- M.

[*]
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/df.html#tag_20_33_10



Re: Testing if two files are on the same file system

2017-10-19 Thread Martijn Dekker
Op 18-10-17 om 16:11 schreef Geoff Clare:
> After the filesystem name there are four numeric fields with a trailing '%'
> on the fourth.  Treating this as the terminator for the filesystem name
> ought to be good enough in practice.  It would only not work in the
> extremely unlikely event that a filesystem name has within it four numeric
> fields with a trailing '%' on the fourth, or if two different filesystem
> names are the same when trailing blanks are removed.
> 
> I tried it with the following command and it seems to work:
> 
> df -P file1 file2 |
> sed '1d;s/\([[:blank:]]\{1,\}[[:digit:]]\{1,\}\)\{4\}%[[:blank:]].*//'

That's very clever, and does seem to be fairly bullet-proof. Thanks!

There is another, less unlikely problem, though: a file system could be
mounted on a directory with a name containing a newline, which would
break line-based parsing. These days, with things like FUSE, you don't
even need to be root to perform a mount. So a cleverly crafted mount
could conceivably be abused to manipulate the behaviour of a script
using this technique.

But modernish can at least detect this condition and reduce it to a
simple denial of service: if the parsed 'df -P' output doesn't contain
exactly two lines, things have gone pear-shaped and the program should
halt immediately[*] to avoid potential disaster.

I'll also have to make it detect GNU 'stat' and use 'gstat --file-system
--format=%i' instead where possible, because that's the only properly
robust way I know of and it's fairly widespread.

- Martijn

[*]
https://github.com/modernish/modernish#user-content-reliable-emergency-halt



Re: Testing if two files are on the same file system

2017-10-19 Thread Martijn Dekker
Op 19-10-17 om 13:12 schreef Joerg Schilling:
> No, the first field usually is the background storage, it may be the filesytem
> name in some cases.

Whatever it technically may be, the POSIX spec calls it a file system
name, so I'm using that term to be consistent with it.
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/df.html#tag_20_33_10

> You need to look at the 2nd line and you may use the '%' sign to synchronize
> fields and you may even do this:
> 
> /usr/xpg4/bin/df -P /etc/passwd /etc/hosts
> Dateisystem   512-Blöcke  Belegt   Verfügbar Kapazität Eingehängt 
> auf
> /dev/dsk/c1t0d0s3   2420396023115442  84648097%/
> /dev/dsk/c1t0d0s3   2420396023115442  84648097%/
> 
> and just compare the last two lines.

What if this is done while something else is writing to a file? As far
as I can tell, there's nothing to say the number of free blocks can't
change between printing the first and second lines.

As for the '%' sign method, see Geoff's message and my reply to it.

- M.



Re: Testing if two files are on the same file system

2017-10-19 Thread Martijn Dekker
Op 19-10-17 om 15:14 schreef Joerg Wunsch:
> As Martijn Dekker wrote:
> 
>> There is another, less unlikely problem, though: a file system could be
>> mounted on a directory with a name containing a newline, which would
>> break line-based parsing.
> 
> Pipe the output of df -P through "tail -n +2 | head -1". :-)
> 
> That way, you don't have to care whether the mountpoint adds rubbish
> after a newline.  As I understand it, you are going to apply df -P to
> a single file at a time only, so it's sure its filesystem name will
> always at the beginning of line 2.

Well no, the idea was to apply 'df -P' to two files at once. Once you've
got two lines in a variable, it's fast and easy to use POSIX parameter
substitutions to split and compare them.

You're right, I could avoid the whole problem by using two separate 'df
-P' invocations and parsing them separately. But that would double the
overhead. This feature is especially useful in directory traversal
loops, so performance is important.

An actual case of a file system being mounted on a directory with a name
containing a newline is a sure sign of something dodgy going on, so I
don't really mind killing a modernish program outright if that condition
is detected.

To give people a more concerete idea of what I'm talking about, below is
my function that now performs this feature on systems without GNU
'stat'. It's an internal function, meant to be invoked indirectly via
'is onsamefs file1 file2' (don't follow symlinks) or 'is -L onsamefs
file1 file2' (follow symlinks).

The bulk of the overhead (i.e.: the subshell forks) is in the command
substitution at the beginning. This function performs the feature using
just two or three forks, depending on the shell.

Notes:
- $CCn is a newline;
- DEFPATH=$(getconf PATH);
- die() performs a reliable emergency program halt on scripts (the '||
return' is only ever reached on an interactive shell that traps and
ignores SIGINT).

_Msh_testOnSameFs() {
  _Msh_is=$(export LC_ALL=C POSIXLY_CORRECT=Y "PATH=$DEFPATH"
 unset -f df sed  # QRK_EXECFNBI compat
 exec df -P -- "$1" "$2" | exec sed "1d;
 s/\([[:blank:]]\{1,\}[[:digit:]]\{1,\}\)\{4\}%[[:blank:]].*//")
  # Sanity check: verify that exactly 2 lines were produced.
  case ${_Msh_is} in
  (*"$CCn"*"$CCn"*) die "is onsamefs: internal error 1" || return ;;
  (*"$CCn"*)  ;;
  (*) die "is onsamefs: internal error 2" || return ;;
  esac
  # If the two lines are identical, the files are on the same filesystem.
  case ${_Msh_is#*"$CCn"} in
  ("${_Msh_is%%"$CCn"*}") unset -v _Msh_is ;;
  (*) ! unset -v _Msh_is ;;
  esac
}

The more robust and faster version using GNU 'stat' (just one fork) is
identical, except for the command substitution which looks like:

  _Msh_is=$(export LC_ALL=C POSIXLY_CORRECT=Y
exec /opt/local/bin/gstat --file-system --format=%i,%t -- "$1" "$2")

(The presence of GNU 'stat' and its specific path are of course detected
at init time.)

I'll push the new feature to github tonight after some more testing, so
people can try to break it. :)

- Martijn



Re: Testing if two files are on the same file system

2017-10-20 Thread Martijn Dekker
Op 19-10-17 om 15:06 schreef Martijn Dekker:
> Op 18-10-17 om 16:11 schreef Geoff Clare:
>> After the filesystem name there are four numeric fields with a trailing '%'
>> on the fourth.  Treating this as the terminator for the filesystem name
>> ought to be good enough in practice.  It would only not work in the
>> extremely unlikely event that a filesystem name has within it four numeric
>> fields with a trailing '%' on the fourth, or if two different filesystem
>> names are the same when trailing blanks are removed.
>>
>> I tried it with the following command and it seems to work:
>>
>> df -P file1 file2 |
>> sed '1d;s/\([[:blank:]]\{1,\}[[:digit:]]\{1,\}\)\{4\}%[[:blank:]].*//'
> 
> That's very clever, and does seem to be fairly bullet-proof. Thanks!

Hmm... on Linux, there can be several tmpfs mounts and they all have the
same file system name in the first column. Example:

tmpfs 33039212   0 33039212   0% /dev/shm
tmpfs 33039212 3277180 29762032  10% /run
tmpfs 5120   0 5120   0% /run/lock
tmpfs 33039212   0 33039212   0% /sys/fs/cgroup

Looks like we also need the mount point to uniquely identify a file
system. A little tweak to the sed incantation accomplishes that:

df -P /dev/shm /run |
sed '1d;
  s/\([[:blank:]]\{1,\}[[:digit:]]\{1,\}\)\{4\}%[[:blank:]]\{1,\}/,/'

gives

tmpfs,/dev/shm
tmpfs,/run

But then, wouldn't it be sufficient simply to use the mount point only?
Can two file systems ever be on the same mount point?

- Martijn



Re: Testing if two files are on the same file system

2017-10-25 Thread Martijn Dekker
Op 23-10-17 om 17:36 schreef Vincent Lefevre:
> The initial question was actually not clear. First, you should define
> what a file system is. If this is not what is identified by st_dev[*],
> what is it?

That's a good point. What I mean is an instance of a file system on a
particular device or partition, mounted on a particular mount point.

A concrete working definition is: if I can create a hard link from a
file on one directory into another directory, those directories are on
the same file system. I'd like to be able to do something like:

if is onsamefs "$file1" "$file2"; then
ln "$file2" "$file1"# hard links are possible
else
ln -s "$file2" "$file1"
fi

I found that, on Linux, when a file system is mounted twice on different
mount points using mount --bind, 'ln' will refuse to create a hard link
between the different mount points, even though they are on the same
device (with the same device ID). So including the mount point in the
definition turns out to be essential. It looks like neither GNU 'stat'
nor BSD 'stat' will allow this.

(I know in the concrete example above I could just do
ln "$file2" "$file1" 2>/dev/null || ln -s "$file2" "$file1"
but there are other use cases. Besides, doing a proper test first is
actually better, as you can distinguish between failure modes.)

Thanks,

- M.



Re: Testing if two files are on the same file system

2017-10-25 Thread Martijn Dekker
Op 24-10-17 om 11:22 schreef Stephane Chazelas:
> using sed on the output of df
> #
> 
> the output "df -P" is only specified in the POSIX locale.

That's now how I read the spec at

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/df.html#tag_20_33_10

It says the first line (the header) is only specified in the POSIX
loicale, but it makes no such statement about any subsequent lines.
We're ignoring the first line, so no problem.

> I'm not sure what the output of "LC_ALL=C df -P" should be for a
> mount point like a UTF-8 /home/stéphane

That's a good point. I had actually added LC_ALL=C to my 'df'
invocation, more out of habit than anything. I should remove it.

> symlinks and device files
> #
> 
> Depending on the stat implementation, for symlinks, you'll get
> the st_dev of the symlink or the file it points to, while
> AFAICT, df gives you the information for the target of the
> symlink

In modernish I deal with that:

- for 'is onsamefs', by stripping the last element from each given path
if it's not a directory;

- for 'is -L onsamefs' (-L = follow symlinks), by stripping the last
element from each given path if it's not a directory or a non-broken
symlink.

An extra complication for 'is -L onsamefs' is that 'df -P' on Mac and
BSD does not like device files, e.g. 'df -P /dev/null' gives an error:
"Raw devices not supported". Is that situation POSIX compliant? I
suspect not, but in any case I have to deal with it.

It's fine for 'is onsamefs /dev/null /foo/bar' as the final /null is
stripped anyway, but for 'is -L on samefs symlink_to_dev_null /foo/bar',
a valid symlink to a device still gives the error. The only way to deal
with that is to read the the symlink's target, then strip the last
element off the target. Reading the target of a symlink using POSIX
shell and utilities is worth an entirely new thread (unfortunately
'readlink' is not universal and implementations differ in behaviour),
but I am pretty sure I found a robust way...

- Martijn



Symlink behaviour [was; Testing if two files are on the same file system]

2017-10-25 Thread Martijn Dekker
Op 25-10-17 om 14:40 schreef Stephane Chazelas:
> The behaviour of df on symlinks is unspecified AFAICT

Hmmm...

Base Definitions, 3.381 Symbolic Link:
| A type of file with the property that when the file is encountered
| during pathname resolution, a string stored by the file is used to
| modify the pathname resolution.

Which references Base Definitions, 4.13 Pathname Resolution:
| If a symbolic link is encountered during pathname resolution, the
| behavior shall depend on whether the pathname component is at the end
| of the pathname and on the function being performed. If all of the
| following are true, then pathname resolution is complete:
| 1. This is the last pathname component of the pathname.
| 2. The pathname has no trailing .
| 3. The function is required to act on the symbolic link itself, or
|certain arguments direct that the function act on the symbolic
|link itself.
| In all other cases, the system shall prefix the remaining pathname,
| if any, with the contents of the symbolic link, except that if the
| contents of the symbolic link is the empty string, then either
| pathname resolution shall fail with functions reporting an [ENOENT]
| error and utilities writing an equivalent diagnostic message, or
| the pathname of the directory containing the symbolic link shall be
| used in place of the contents of the symbolic link. [...]

I think this means 'df' and all other utilities (and even basic libc
functions like fopen()) are required to follow symlinks given as
arguments, unless explicitly specified otherwise either by the spec or
(if a relevant option exists) by the user/programmer.

- Martijn



Preserving standard input for background commands in scripts

2017-10-29 Thread Martijn Dekker
2.9.3 List states under "Asynchronous Lists":
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_03_02
| If job control is disabled (see set, -m), the standard input for an
| asynchronous list, before any explicit redirections are performed,
| shall be considered to be assigned to a file that has the same
| properties as /dev/null. This shall not happen if job control is
| enabled. In all cases, explicit redirection of standard input shall
| override this activity.

Question: should the last sentence quoted above also apply if standard
input is explicitly redirected to itself? E.g., when running the
following with job control disabled (e.g. as a script) and standard
input on a terminal:

if [ -t 0 ]; then echo yes; else echo no; fi <&0 & wait

then bash and yash outout "yes", whereas bosh, (d)ash, ksh93,
pdksh/mksh/lksh, zsh, and Solaris /usr/xpg4/bin/sh all output "no".

Which is right? The way I read the text above, it seems like bash and
yash are, but they are in the minority. Does this need clarification?

If this worked more widely, it would have been a convenient way to
preserve standard input for a background job, which is useful for
commands like stty.

It does work on all shells to redirect standard input to a copy of
itself, like

{ if [ -t 0 ]; then echo yes; else echo no; fi <&3 & } 3<&0; wait

but that comes at the cost of using another file descriptor for the copy.

- M.



Re: UTF-8 locale & POSIX text model

2017-11-22 Thread Martijn Dekker
Op 22-11-17 om 13:58 schreef Danny Niu:
> Q1: What is the rationale for not making POSIX an application of ASCII? 

Actually, it mostly is. POSIX mandates that all supported locales
include the "portable character set", which is ASCII minus some control
characters.

http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap06.html

- M.



Re: UTF-8 locale & POSIX text model

2017-11-22 Thread Martijn Dekker
Op 22-11-17 om 16:02 schreef Geoff Clare:
> Danny Niu  wrote, on 22 Nov 2017:
>>
>> Q1: What is the rationale for not making POSIX an application of ASCII? 
> 
> So that systems which use other encodings (specifically EBCDIC) can
> be POSIX-conforming.  IBM z/OS is certified UNIX 95 and uses EBCDIC.

Well, it looks like my previous reply was completely clueless, sorry
about that.

But then how should I interpret the table in 6.1 Portable Character Set,
particularly the UCS column?

- Martijn



Should shell quoting within glob bracket patterns be effective?

2018-04-10 Thread Martijn Dekker

Re: https://github.com/att/ast/issues/71

Consider this test script:

(set -o posix) 2>/dev/null && set -o posix
emulate sh 2>/dev/null  # for zsh
for var in 'a-c' '!a'; do
case b in
( ["$var"] )  echo 'quirk' ;;
( [$var] )  echo 'no quirk' ;;
esac
done

Most shells output 'no quirk' for both values of 'var', but AT&T ksh93 
outputs 'quirk' for both, as does zsh 5.2 and earlier (zsh-as-sh changed 
to match the majority in 5.3). Now one of the current ksh93 lead 
developers says this does not look like a bug.


Does POSIX specify anything, either way, regarding the effect of shell 
quoting within glob bracket patterns? I can't find any relevant text 
under "2.13 Pattern Matching Notation" or anything it references, so 
clarification would be appreciated.


Thanks,

- Martijn



Re: Should shell quoting within glob bracket patterns be effective?

2018-04-10 Thread Martijn Dekker

Op 10-04-18 om 21:06 schreef Robert Elz:

 Date:Tue, 10 Apr 2018 13:41:25 +0100
 From:Martijn Dekker 
 Message-ID:  

   | Does POSIX specify anything, either way, regarding the effect of shell
   | quoting within glob bracket patterns?

I would say it is unclear - in general, quoting inside [] does not work
(XCU 2.13 char classes are derived from XBD 9.3.5 char classes, and
in the latter, quote characters are just characters ["] is a char class
containing just a double quote character.


However:

$ ksh93 -c 'case \" in ["a-z"]) echo match;; *) echo no match;; esac'
no match

The quotes are not considered part of the bracket expression, but 
removed by the shell, even on ksh93.



Also, 2.13.1 does say:

When pattern matching is used where shell quote removal is not performed
[...]

[this includes case patterns as quote removal is not performed on them]


But it is. POSIX explicitly specifies quote removal for 'case' patterns:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_04_05
| The conditional construct case shall execute the /compound-list/
| corresponding to the first one of several /patterns/ (see Pattern
| Matching Notation) that is matched by the string resulting from the
| tilde expansion, parameter expansion, command substitution,
| arithmetic expansion, and quote removal of the given word.




The NetBSD sh however produces "no quirk" for both -


I hope you won't change it to ksh93's counterintuitive behaviour. Your 
current behaviour is certainly consistent with POSIX (as well as every 
other current shell except ksh93).


- M.



Re: Should shell quoting within glob bracket patterns be effective?

2018-04-10 Thread Martijn Dekker

Op 10-04-18 om 21:52 schreef Robert Elz:

No, it doesn't.

Read that again, with the emphasis I am adding ...

   
|http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_04_05
   | | The conditional construct case shall execute the/compound-list/
   | | corresponding to the first one of several/patterns/  (see Pattern
   | | Matching Notation) that is matched by the string resulting from the
   | | tilde expansion, parameter expansion, command substitution,
   | | arithmetic expansion, and quote removal **of the given word**.

That part is talking about the "case $x in" $x is the "given word",
that is certainly subject to quote removal.

Quite right, I stand corrected.

Thanks,

- M.



Re: Should shell quoting within glob bracket patterns be effective?

2018-04-10 Thread Martijn Dekker

Op 10-04-18 om 15:59 schreef Joerg Schilling:

Whom do you call "current ksh93 lead developers"?


As far as I can tell from what's going on at the github repo, Siteshwar 
Vashisht and Kurtis Rader currently appear to be in charge of its 
development.


- M.



Re: Should shell quoting within glob bracket patterns be effective?

2018-04-10 Thread Martijn Dekker

Op 10-04-18 om 22:50 schreef Jilles Tjoelker:

I prefer "no quirk" twice as output but it is indeed not fully
specified.


I agree with your preference.

Ignoring shell quoting in glob bracket patterns means removing a useful 
feature: the ability to pass an arbitrary string of characters in a 
parameter, one of which is to be matched.


OTOH, honouring shell quoting in glob bracket patterns does not remove 
any functionality, as you can simply not quote the expansion (which is 
safe, as it is not subject to split or glob in that context).


So if this is indeed not specified, I think the standard ought to be 
amended to specify the current majority behaviour (everything but ksh93).


- Martijn



Re: Fall-back string matching in "case"

2018-04-17 Thread Martijn Dekker

Op 17-04-18 om 16:02 schreef Geoff Clare:

(0003970) joerg (reporter) - 2018-04-17 13:46

In the Bourne Shell and in ksh88, the case statement first tries
to match the case argument against the pattern using gmatch() and
in case this does not result in a match, the quoting on the pattern
is removed and a plain string compare is done.

[...]

Personally I am very strongly opposed to allowing this behaviour in
the standard.  It breaks applications which perform input validation
such as:

case $input in
[0-9]) ;;
*) echo "Input must be a single digit"; exit ;;
esac

This code is supposed only to allow the script to proceed when $input
is a single digit, but with fall-back string matching it would
proceed with input='[0-9]'.

This behaviour is, frankly, utterly broken and I'm astonished that
any shell author would think it was a good idea.


Wow...

$ case "[0-9]" in [0-9]) echo BUG;; *) echo ok;; esac

bash: ok
bosh: BUG  (including bosh -o posix)
Busybox ash: ok
dash: ok
FreeBSD sh: ok
ksh93: BUG
NetBSD sh: ok
mksh: ok
pdksh: ok
yash: ok
zsh: ok

Even ksh93 exhibits this breakage to this day, up to including current 
git. That is certainly something to be aware of. I'll file a bug report 
at github.com/att/ast.


As for bosh, I understand Jörg intends to preserve historical Bourne 
shell behaviour by default, but this should certainly be fixed for the 
POSIX mode.


- M.



Re: Fall-back string matching in "case"

2018-04-17 Thread Martijn Dekker

Op 17-04-18 om 17:16 schreef Joerg Schilling:

Martijn Dekker  wrote:


Wow...

$ case "[0-9]" in [0-9]) echo BUG;; *) echo ok;; esac

bash: ok
bosh: BUG  (including bosh -o posix)


/usr/xpg4/bin/sh
$ case "[0-9]" in [0-9]) echo BUG;; *) echo ok;; esac
BUG

So bosh behaves just like the POSIX shell.


Yes, I also used to think that ksh88 is "the POSIX shell" on Solaris, 
until I found a load of bugs in it, and was corrected here on my 
misconception that ksh88 is still considered a reference implementation. 
It is not, at least not any more. And as I've shown here before, it 
still has enough bugs in it that it should really not be considered 
POSIX compliant at all.


The behaviour in question:
1. is undocumented;
2. is not specified by the text of the standard, in fact directly 
contravenes it;

3. breaks legit and important use cases (as demonstrated by Geoff);
4. requires an ugly workaround that nobody would expect to have to use 
(see 1 and 2);
5. has 0 real uses that wouldn't be better served by simply quoting the 
pattern;
6. of current shells, is only exhibited by a minority of bosh (how many 
regular users?), ksh88 (basically dead and buried) and ksh93.


These all seem like good reasons for fixing it to me.

- M.



What exit status for an executable that is found but cannot be invoked?

2018-04-19 Thread Martijn Dekker

2.8.2 "Exit Status for Commands" specifies:


If a command is not found, the exit status shall be 127. If the
command name is found, but it is not an executable utility, the exit
status shall be 126.


This does not say what should happen if the command is found and is an 
executable utility, but the utility cannot be invoked because of some 
error or other.


Most shells return 126 in such cases, as I would expect, but zsh returns 
126 only for EACCES (permission denied) and ENOEXEC (exec format error), 
returning 127 for other error conditions such as E2BIG (argument list 
too long).


I was going to report this to the zsh developers as a bug, but then 
found that POSIX doesn't actually specify the exit status in such cases.


Seems like an oversight to me. Agreed?

- Martijn



Re: What exit status for an executable that is found but cannot be invoked?

2018-04-19 Thread Martijn Dekker

Op 20-04-18 om 00:34 schreef Shware Systems:

Whatever the reason for the load failure, at that point in time it is
"not an executable utility", in other words.


That is an interesting interpretation of the words "executable utility". 
I do not believe that most people would read it this way.


Also consider error conditions such as E2BIG. If the utility is found 
and the argument list is too long, that does not make the utility 
non-executable even by your definition. There is no load failure; 
loading the file is never attempted to begin with, because there is not 
enough space to pass it the list of arguments.


POSIX does not define the term "executable utility" exactly but it does 
define "executable file" under 3.154:

| A regular file acceptable as a new process image file by the
| equivalent of the exec family of functions, and thus usable as one
| form of a utility.

This defines "executable" in terms of the file's acceptability to 'exec' 
and friends, which I take to mean properties such as its format and 
permissions. It says nothing about errors invoking the file that have 
nothing to do with the nature of the file, e.g. I/O error, argument list 
too long, etc. -- so it seems to me that those error conditions do not 
influence the meaning of "executable".


- M.



Re: What exit status for an executable that is found but cannot be invoked?

2018-04-19 Thread Martijn Dekker

Op 20-04-18 om 01:29 schreef Don Cragun:



On Apr 19, 2018, at 4:04 PM, Martijn Dekker  wrote:

Op 20-04-18 om 00:34 schreef Shware Systems:

Whatever the reason for the load failure, at that point in time it is
"not an executable utility", in other words.


That is an interesting interpretation of the words "executable
utility". I do not believe that most people would read it this
way.


Interpret them as specified in section 2.9.1.1 of the standard
(titled Command Search and Execution in the Shell Command Language
part of the Shell and Utilities volume of the standard).  It goes
into explicit detail about when 126 and 127 are used as exit code
when the shell tries to run a utility.  If you have the PDF version
of the 2016 or 2017 version of the standard, this section can be
found on pages 2367-2368 lines 75546-75627.


Thanks for the pointer.

That section actually seems to take great pains to avoid specifying the 
status that should be returned in case of error conditions such as 
"argument list too long" or "I/O error".


It merely specifies that the shell *may* return 126 if the file format 
is neither valid executable nor text (lines 75586-75594, 75613-75617) 
and *shall* return 127 if the utility cannot be found (75606-75607).



Also consider error conditions such as E2BIG. If the utility is
found and the argument list is too long, that does not make the
utility non-executable even by your definition. There is no load
failure; loading the file is never attempted to begin with, because
there is not enough space to pass it the list of arguments.


It is strange that you believe that.  If an attempt to execute the
utility fails, that seems to me to be a clear indication that it is
not executable (at least with the environment and arguments that were
passed to it on this invocation).


The environment is not a property of the file. It is illogical to say 
that a file is non-executable when the attempt to execute it fails due 
to an error condition that is unrelated to the file.



  Why would you believe that the
exit status should be 127 indicating that the file not found if the
file was found, but the exec failed?


To be clear, I do not believe that at all. In fact, if the standard says 
anything clearly about this, it's that 127 is only to be used if the 
utility was not found.


My point is that, though it seems quite obvious that 126 should be used 
instead, the text of the standard does not actually specify this.


It merely specifies 126 if the file is not an executable utility. It 
does not specify the exit status to use if the file *is* an executable 
utility but the attempt to exec it fails for some reason unrelated to 
the nature of the file.


In my view, that is a defect in the standard.


One meaning of executable is that the file is in a format that can be
executed by the underlying system.  Another meaning is that the file
and the needed resources to run it are available at the time an
attempt was made to execute it.


As with the environment, the resources needed to run a file are also not 
a property of the file, so it is equally illogical to say that a file is 
non-executable if there are insufficient resources to execute it.


If such an illogical definition of "executable file" is to be used, at 
the very least that should be stated very clearly in a section such as 
XBD 3.154, but it is not. In fact, the definition there is quite logical.



  According to the standard, both of
these meanings are supposed to result in exit code 126.


Unfortunately, neither XCU 2.8.2 nor XCU 2.9.1.1 actually say this. Not 
only that, XCU 2.9.1.1 seems to bend over backwards to avoid saying it.


Thanks,

- M.



Re: What exit status for an executable that is found but cannot be invoked?

2018-04-20 Thread Martijn Dekker

Op 20-04-18 om 07:11 schreef Robert Elz:

I think this has been discussed before.


If anyone has a pointer I'd appreciate it.


At this point, all you are going to get (at best) added to the standard is
that it is unspecified whether 126 or 127 (or I think perhaps even 1 or 2)
is the error that is generated when execution fails for a "less common"
reason - because not all shells handle them the same way.


OK, so I did some testing. You're right that not all do, but I would say 
the ones that don't clearly have a bug. Let's trigger E2BIG:


v=foo_bar_baz_quux_lorem_ipsum_dolor_sit_amet
while :; do
v=$v$v$v$v
/usr/bin/env true $v || { echo $?; exit; }
done

Results:

- pdksh and mksh return 1. Clearly, that is awfully broken. For example, 
a script has no way to distinguish between a non-match in 'grep' and a 
too-long argument list for 'grep'.


- NetBSD sh and dash return 2, which is nearly as broken. Exit status 2 
is commonly used for many other things. Depending on the command, a 
script may well have no way to distinguish.


- zsh returns 127, which is wrong in any case as it means "not found".

- bash returns 126.

- AT&T ksh93 returns 126.

- FreeBSD sh returns 126.

- schily's bosh returns 126.

- yash returns 126.

Discounting zsh whose behaviour is unambiguously a bug even by the 
current norm, there seems to be a clear majority for 126.


I also found that the historical ksh88 and Bourne shells returned 1. 
That may be the real reason why this is unspecified. But, frankly, that 
behaviour is as broken as the fall-back string matching for 'case' 
discussed recently: it breaks command validation based on exit status.


(I'm aware a program or script *could* decide to return 126 or 127 of 
its own volition. That doesn't really change anything about the above 
though. If you intentionally break things of course you're going to get 
the wrong behaviour.)



There is of course also the possibility that execution fails for a non-posix
specified reason - clearly there posix cannot specify what the error
code must be.

[...]

Sure it can. It doesn't need to specify the nature of the error 
condition at all. For example:


"If a command is not found, the exit status shall be 127. If the command 
name is found, but the command cannot be invoked, the exit status shall 
be 126."


This would go with the current majority behaviour while removing clearly 
broken behaviour from the standard.


- M.



Re: sh(1): 2.9.5 function: clarification on compound command?

2018-04-20 Thread Martijn Dekker

Op 20-04-18 om 14:35 schreef Joerg Schilling:
[...]> $ f() echo > /dev/null

ksh93: syntax error: `>' unexpected

It looks as of all Bourne derived shells behave the same.


My testing says that most ash derivatives (dash, NetBSD sh, Busybox ash, 
but not FreeBSD sh) accept a simple command there, as does zsh, and 
pdksh and derivatives (including mksh).


- Martijn



Re: sh(1): 2.9.5 function: clarification on compound command?

2018-04-20 Thread Martijn Dekker

Op 20-04-18 om 15:10 schreef Joerg Schilling:

Martijn Dekker  wrote:


Op 20-04-18 om 14:35 schreef Joerg Schilling:
[...]> $ f() echo > /dev/null

ksh93: syntax error: `>' unexpected

It looks as of all Bourne derived shells behave the same.


My testing says that most ash derivatives (dash, NetBSD sh, Busybox ash,
but not FreeBSD sh) accept a simple command there, as does zsh, and
pdksh and derivatives (including mksh).


Note: by "accept a simple command there" I meant, including that 
redirection.



The problem here is not the simple command but the redirection that cannot be
assigned to either the function body or the function definition command.


Again, my testing says otherwise. Try it yourself if you don't believe me.

$ dash -c 'f() echo hi >&2; f' >/dev/null
hi
$ mksh -c 'f() echo hi >&2; f' >/dev/null
hi
$ zsh -c 'f() echo hi >&2; f' >/dev/null
hi

- M.



Re: What exit status for an executable that is found but cannot be invoked?

2018-04-20 Thread Martijn Dekker

Op 20-04-18 om 14:34 schreef Robert Elz:
[...]

This will be fixed in NetBSD-8 (when it is released, soon, we all hope).
Testing sh in NetBSD in earlier versions (7.* and before) is just a good
way to waste lots of time - there are zillions of (generally unnoticeable, so
they don't get fixed and create churn) bugs in those versions.


Good news! Thanks for the info.


The problem with ...

"If a command is not found, the exit status shall be 127. If the command
name is found, but the command cannot be invoked, the exit status shall
be 126."

is that in general, the way the shell finds out if a command name is found,
is by attempting to exec it.   Then it needs to map the errno value onto
one of the 2 error codes.   The spec (kind of) says which error codes map
to which value but (obviously) cannot say anything about error codes the
spec doesn't know about.   It could say "all others" but then you'd end up
with some "command not found" giving 126 exit status, because the reason
the command was not found was because of some error that is non-posix.


I just don't see that. POSIX says quite simply that the status is 127 if 
the command was not found. It does *not* say that the status is 127 only 
for commands that were not found for reasons recognised by POSIX. As 
such, the reason the command was not found is irrelevant. If it's not 
found, it's 127, period. How is that complicated in any way?


That's why I think the standard should be simple in a similar way for 
126. If the command was found but could not be invoked, the status 
should be 126. Reason irrelevant. Not complicated.


Sometimes it seems like some of you actively look for ways to make 
things as complicated as possible... :-)


- Martijn



Re: What exit status for an executable that is found but cannot be invoked?

2018-04-20 Thread Martijn Dekker

Op 20-04-18 om 17:26 schreef Robert Elz:

 Date:Fri, 20 Apr 2018 16:16:12 +0200
 From:Martijn Dekker 
 Message-ID:  <4f30a2d7-d99d-e04d-b78e-94295999e...@inlv.org>

   | That's why I think the standard should be simple in a similar way for
   | 126. If the command was found but could not be invoked, the status
   | should be 126. Reason irrelevant. Not complicated.

Your shell does

execve("/path/to/something",  argv, environ);

The system returns -1, with errno set to EDEADFROGS

Which error status do you set, 126 or 127?


Well, a shell written for that system would know what EDEADFROGS means 
on that system, so would know which exit status to set.


A cross-platform shell, on the other hand... OK, I see your point now.

I still think erring on the side of 126 would be best though. Certainly 
much better than returning 1 or 2.


I'm also not convinced this scenario is actually valid. Doesn't POSIX 
specify ENOENT for "not found"? How would it be valid for a system to 
return anything else if a file is not found?


- Martijn



Laundry list

2018-04-25 Thread Martijn Dekker
The central POSIX promise of "write once, run anywhere" has never held 
quite true for the POSIX shell. There are numerous differences, quirks 
and bugs between both POSIX shells and POSIX utilities. What's more, 
certain essential utilities (e.g. mktemp) are missing from the standard 
altogether, so they differ even more or may be absent. As a result, 
outside of the standards inner circle, the mere suggestion that creating 
a cross-shell or cross-platform shell script is a good idea usually 
elicits hollow laughter.


Modernish is my ongoing attempt to properly realise "write once, run 
anywhere" for the shell. Every programming language has a good standard 
library or two, except shell. So I'm trying to create a cross-platform 
shell library that knows and works around all the shell bugs and quirks 
in currently used shell release versions, while implementing important 
functionality missing from the standard and providing ways to fix the 
most common shell language annoyances.


I've been working intensively at this since late 2015 and while I keep 
getting closer to my goal, modernish is still not quite robust enough 
for a first beta release. I keep finding (and reporting) new shell 
quirks and bugs in current release versions. I think this illustrates 
the difficulty of the problem I'm trying to solve.


In addition, I've found myself forced to abandon my initial goal of 
POSIX purism and rely on certain features that, while not standardised 
by POSIX, appear to be universal on all current UNIX-like operating 
systems and shells, because they are generally seen to be indispensable. 
These are listed below.


And that's the point of this message. I think that (A) certain 
universally implemented but unstandardised features should be 
standardised, and (B) standardised features that nobody implements 
should be removed, especially if they are inherently broken. Before 
cluttering the Austin group bug system with each of the points below, 
I'd be interested in your opinions on these.



A. Universally implemented but unstandardised features

A1. File names longer than 14 bytes. (_POSIX_NAME_MAX == 14)

What system still limits file names to 14 bytes? I think that system 
would not be compatible with much of anything in 2018, so it seems to me 
that supporting longer file names should not be considered optional.


A2. /usr/bin/env

This is very, very commonly used in hashbang paths to deal with varying 
locations of the shell, e.g.:


#! /usr/bin/env bash

I don't know a system that has 'env' at any other location, so I think 
/usr/bin/env should join /dev/tty and a few others as a standard path.


A3. /dev/urandom

Access to cryptographically strong randomness is essential in 2018. 
Standard access would be even better. Does any POSIX system from the 
last two decades *not* have /dev/urandom? I don't know of any. Even 
Microsoft Interix 3.5 on Windows 2000 had it.


So I think /dev/urandom should be added to POSIX, possibly along with 
getentropy() (see bug 1134).


Maybe /dev/random as well -- on Linux, this is the blocking variant, but 
there doesn't seem to be as much point, as most or all non-Linux systems 
provide this as synonymous to /dev/urandom.


A4. Option flag 'i' in $- to see if shell is interactive.

For my shell library it is essential to be able to determine if a shell 
is interactive or not. Thankfully all current shells support the 'i' 
flag. I think it should be added to the standard that $- shall contain 
'i' if the shell is interactive, with it being unspecified whether that 
option can be changed or not.


A5. The -nt, -ot, -ef operators in test/[

All current shells support these. This is the only built-in way shells 
have to determine if a file is newer, older or the same file as another 
file. As far as I know, other POSIX utilities do not provide for this 
possibility. So I think these should be standardised.


A6. For $((x)), accepts leading whitespace in the value of x.

There is no current shell that considers "  1" to be an invalid number, 
as C functions like strtol() are specified to accept and skip initial 
whitespace. So I think that should be specified for valid shell integer 
values as well.


(Most shells accept trailing whitespace as well but that is not 
universal. Also note that $(($x)) is different: it always accepts 
leading and trailing whitespace, because $x is expanded before the 
arithmetic context is entered.)


A7. 'var=foo exec somecommand ...' exports var to somecommand

This is unspecified in POSIX because 'exec' is a special builtin; the 
historical Bourne and ksh88 shells didn't export, but all current POSIX 
shells do. Schily's bosh was the last to change to the common behaviour, 
sometime last year IIRC.


(Note that I'm not counting /usr/xpg4/bin/sh on Solaris which is 
basically ksh88 and, as I've shown here before, breaks POSIX compliance 
in several fatal ways, with little prospect of a fix.)



B. Standardised features that (almost) no

Re: Laundry list

2018-04-26 Thread Martijn Dekker

Op 26-04-18 om 13:32 schreef Robert Elz:

It really is fairly easy - the NetBSD sh used not to allow trailing whitespace,
but does now (since Martijn brought it to my attention.)   The only slight
extra hitch is to not allow vars that contain only whitespace (and are not
empty).   ie:
var='   '; :  $((var))
is not sane, and should continue to be rejected.


I agree, but it might be worth pointing out that ksh88 (which invented 
that syntax) accepted an all-whitespace string by defaulting that to 
zero, and so currently do bash, AT&T ksh93, pdksh/mksh, and zsh. They 
also accept a literal $(( )).


ksh88, ksh93 and bash 4.4+ even accept $(('  ')).


  | > B3. Elements of a pipeline (except the last) run in current environment.
  | >
  | This was intentionally not part of the standard.
I think you are misunderstanding the request - it was not to require pipelines
to run in the same shell env, but to forbid it, except for the final element.

[...]
He doesn't believe any are, that's the point, it is only the std that is the 
problem.


Thanks, yes, I think that should be prohibited in the standard (except 
for the final element) to prevent future incompatibilities.


Huh?   What shell, anywhere, exits on command not found ? 

[...]

Basically - every command must be run in a sub-shell, just in case
it does not exist - if the script is to continue after.

No-one writes code like that, and no actual shells require it.


Indeed. I'm very, very much for hardening the shell, but this would be 
inherently broken given historical and current practice. Instead, 
scripts should check for dependencies on initialisation and refuse to 
run if any are not found.


However, I think some kind of usable 'command not found' handler would 
be very good to have for scripts.


Bash supports command_not_found_handle() and zsh supports 
command_not_found_handler(), but both are run in the subshell forked to 
exec the command if the command is not found, so they are not useful for 
exiting the program (unless you use modernish, in which case die() will 
work quite nicely) or otherwise handling the error in the current 
environment. Instead, these are designed to enhance 'not found' error 
messages on interactive shells.


So here's an entirely new idea. I think an EXECFAIL pseudosignal trap 
executed in the current shell environment would be very useful for 
scripts to handle 'not found' and other failures to exec commands.


It wouldn't work for 'foo | bar' though, as traps are reset in 
subshells. Maybe that could be worked around: if a subshell, including 
any element of a pipeline, exits with a status indicating exec failure 
(126 or 127), run that trap in the current environment.


Which would only work for the last-executed command in multiple-command 
subshells. But then again, you could set 'trap "exit \$?" EXECFAIL' in 
that subshell to have the effect propagate back to the current 
environment. Maybe that should even be done automatically, although that 
would technically violate POSIX...


Do you think such a pseudosignal trap would be useful to have? If so, I 
could put in a feature request to all the shell authors in hopes to make 
it widespread...


- M.



Re: Laundry list

2018-04-26 Thread Martijn Dekker

Op 26-04-18 om 13:48 schreef Joerg Schilling:

Robert Elz  wrote:


   | For some commands you need to do that (e.g. if you like to check whether
   | "set -o posix" is usable.

Huh?   What shell, anywhere, exits on command not found ?   (that is, not
counting when the command that was not found was the last, and not when
it was run via "exec").


all conforming shells exit with "set -o bla" in a script.


This has nothing to do with command not found. The 'set' special 
built-in command is, in fact, found.


This is the rule that special built-ins shall exit on encountering an 
error, which is a completely different thing, also defined in XCU 2.8.1 
Consequences of Shell Errors, second row in the table: "Special built-in 
utility error": "shall exit".


What we are talking about is the last row in that same table: "Command 
not found": "may exit".


Link to that table:
http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_08_01

- M.



Re: Laundry list

2018-04-26 Thread Martijn Dekker

Op 26-04-18 om 13:52 schreef Robert Elz:

 Date:Thu, 26 Apr 2018 11:23:49 +0100
 From:Geoff Clare 
   | > B3. Elements of a pipeline (except the last) run in current environment.

   | I'm having a hard time imagining what "many existing scripts" could do
   | that would be broken by having a pipeline element other than the last
   | be executed in the current environment instead of a subshell.

Anything that changes the execution environment.   Since shells don't
actually run commands in pipelines in the current env, the side effects
are hidden.   While not very common, such scripts are far more likely


I would argue that

   foo | while read bar; do baz "$bar"; done | quux

is likely to be actually quite common indeed. But maybe that reflects my 
own bias, in that I've done things like this many times.


There is a current widespread expectation that the changes to the 
variable "bar" will not survive that pipeline, so if it does survive, 
the script may break. It doesn't survive on any current shell, though.


That's why I think the standard should specify that only the last 
element of a pipe may be executed in the current environment -- to 
prevent future breakage of that type.


- Martijn



z/OS shell access? [was: Laundry list]

2018-04-26 Thread Martijn Dekker

Op 26-04-18 om 10:35 schreef Alexander Terekhov:

Martijn Dekker  wrote:
[...]
 > Does anyone care about POSIX shell scripts on z/OS? (Serious question,
 > not rhetorical.) Do they even run, ...

Well, try

https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.1.0/com.ibm.zos.v2r1.bpxa400/ish.htm
https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.e0za100/ch1unix.htm


Thanks for the links.

I would really love to try it. Unfortunately I do not have access to a 
mainframe with z/OS.


You post from ibm.com -- are you involved in z/OS shell development?

Would it be possible to obtain ssh access to a z/OS POSIX shell with 
POSIX utilities for testing purposes? My interest would be in supporting 
z/OS on modernish before the first release. In return I'll email reports 
of all the bugs or quirks I find to the appropriate party.


Just to toot my own horn a bit: while I'm no Stéphane Chazelas, I do 
have a little bit of a track record by now, with bash, dash, yash, zsh, 
mksh, NetBSD sh, Schily's bosh, and a few other shells having fixed a 
total of dozens of bugs I reported since 2015.


Thanks,

- M.



Re: Laundry list

2018-04-27 Thread Martijn Dekker

[Resending to list. I'd accidentally replied to Geoff only.]

Op 26-04-18 om 16:19 schreef Geoff Clare:

Robert Elz  wrote, on 26 Apr 2018:


   | > B4. Shell may exit if command not found. (XCU 2.8.1, last row in table)
   | >
   | > I was only just made aware by kre that POSIX allows this. It's another 
thing
   | > that no shell actually does.
   |
   | Are you sure?  If no shell does it, then it seems odd that it was
   | allowed in the resolution of bug 882 which was relatively recent (2015).


Hmm. OK, time for some testing.

For: someshell -c 'foo; echo does not exit'

Historical Bourne:
- Xenix sh (1988): exits!
- Solaris 10.3 sh (2010): does *not* exit.

Current Bourne:
- schily's bosh: does not exit, including in POSIX mode.

AT&T ksh: none of the following exit:
- Solaris 10.3 ksh88 (2010)
- ksh93, 1993
- ksh93, 2012

pdksh: none of the following exit:
- original pdksh 5.2.14
- NetBSD ksh
- OpenBSD ksh
- mksh

bash: does not exit, including in POSIX mode (tested 2.05b [2002] 
through current git).


zsh: does not exit, including in 'emulate sh' mode (tested 4.3.11 
through current 5.5).


yash: does not exit, including in POSIX mode (tested 2.17 through 
current 2.47).


Almquist: none of the following exit:
- NetBSD sh
- FreeBSD sh
- dash
- Busybox ash
- Slackware Linux ash (an older ash, not up to POSIX scratch)

osh (from oil 0.4.0, http://www.oilshell.org/): does not exit. (This is 
a new shell written in Python that (cl)aims to have a bash-compatible 
mode, but a very quick initial test says it parses builtins at the 
parsing stage instead of the execution stage, so invalid arguments to 
builtins cause the program to refuse to even start running.)


Anyway, so I had to go back to 1988 to find a shell that exits. I think 
the fact this was fixed in the Solaris Bourne shell is probably evidence 
that this behaviour was considered broken early on.


The original inspiration for POSIX, ksh88, does not exit, and neither 
does anything since -- unless there is still a shell I haven't 
discovered. Do you know of any others?


kre wrote:

Since it is just a "may" it is very hard to argue against - one needs to
examine every possible sh and see what it does, and almost no-one
has the resources to do that.   Nothing is required to exit, so shell
authors just ignore this.



Script writers (who do have to deal with this)
are not well represented,


I agree. That's why I'm here.


 and also tend not to read the std anyway,
they just do what works, and "not exit" is what works.


One good reason to standardise it, IMHO.

Geoff wrote:

While working on bug 882 we did try to examine the behaviour of as
many shells as possible for the various cases in the table.  What's
particularly odd is that there are two "may exit" entries that have
a superscript 3 to indicate that a future version of this standard may
require the shell to not exit, but for some reason we decided not to
add a superscript 3 to the command not found case.  This makes me
wonder whether someone found a shell that does exit.  (The alternative
would be that we left it alone because it way already that way in the
old table - the superscript 3's are on new additions.)


I think maybe someone remembered an ancient historical practice. My 
testing says it's time to consign it to history where it belongs.


Thanks,

- M.



Re: Laundry list

2018-04-27 Thread Martijn Dekker

Op 27-04-18 om 15:11 schreef Martijn Dekker:

Op 26-04-18 om 16:19 schreef Geoff Clare:

[...]

| > B4. Shell may exit if command not found. (XCU 2.8.1, last row in table)
| >
| > I was only just made aware by kre that POSIX allows this. It's another thing
| > that no shell actually does.
|
| Are you sure?  If no shell does it, then it seems odd that it was
| allowed in the resolution of bug 882 which was relatively recent (2015).


Hmm. OK, time for some testing.

[...]
The original inspiration for POSIX, ksh88, does not exit, and neither 
does anything since -- unless there is still a shell I haven't 
discovered. Do you know of any others?


Yes, that would be the z/OS POSIX shell then. Does anyone know what it does?

Also, does anyone know whether this shell is an entirely separate entity 
or a port/derivative of another shell incarnation?


Thanks,

- M.



Pushing/restoring a file descriptor for a compound command

2018-04-27 Thread Martijn Dekker

I need references and opinions about the following, please.

Consider:

{
exec 8Should the effect of the 'exec' persist past the compound command (the 
curly braces block)?


My expectation is that the '8<&-' should push file descriptor 8 onto the 
shell's internal stack in a closed state, so that it is restored at the 
end of the block.


I think this should allow the effect of 'exec' to be local to that 
compound command, and I think this construct should be nestable.


According to my testing, nearly all shells do this. However, two very 
widely used shells, dash and bash, leave the file descriptor open beyond 
the block.


The author of dash, Herbert Xu, said in response to my bug report that 
dash is not obligated to push and restore that file descriptor if it is 
already closed when entering the compound command -- implying that a 
file descriptor should not be pushed if the local state would be 
different from the parent state.


He asked me for a POSIX reference proving otherwise. I can't find any.

Without this behaviour, an awkward workaround is required. To guarantee 
that the FD will be restored at the end of the block, you'd need to 
attempt to push it twice in two different states in two nested compound 
command blocks, for instance:


{
{
exec 8/dev/null

Does anyone have pointers to the POSIX text, or other strong evidence, 
that this workaround should not be necessary?


Thanks,

- M.



Re: Laundry list

2018-04-27 Thread Martijn Dekker

Op 27-04-18 om 18:52 schreef Garrett Wollman:

< 
said:


The "#!" should be standardized - at this point, if your system doesn't support 
it,
everyone will consider your system broken.



I realize that there's some concern about standardizing pathnames, but
standardizing "/usr/bin/env" seems extremely reasonable, as well as bare names like 
"bash".


There's no need to standardize the actual pathname; a script can
include installation instructions to add the shebang (with an
appropriate, system-specified path), or indeed can come with an
installation script that does so automatically by invoking "command
-v" to find the path to the desired interpreter.


That installation script would need a way to run without editing that 
path (or explicitly specifying the shell on the command line) in the 
first place, or it would be a bit pointless.


I don't know of any way to accomplish that except by the de-facto 
standard mechanism of "#! /usr/bin/env sh". There is a long-time and 
highly widespread expectation that this will work.



  In addition to shell
scripts, the shebang hack is also commonly used with awk and sed
scripts (just to name two other POSIX-specified languages).


IMO, that's another good reason to standardise the hashbang path plus 
the location of /usr/bin/env.


Thanks for the feedback,

- M.



Re: Pushing/restoring a file descriptor for a compound command

2018-04-27 Thread Martijn Dekker

Op 27-04-18 om 18:49 schreef Martijn Dekker:
The author of dash, Herbert Xu, said in response to my bug report that 
dash is not obligated to push and restore that file descriptor if it is 
already closed when entering the compound command -- implying that a 
file descriptor should not be pushed if the local state would be 
different from the parent state.


Actually, that implication that I understood is not even correct. No 
shell, not even bash and dash, outputs "oops, closed" for the following:


exec 8To rephrase my request: I would welcome opinions on whether the 
dash/bash behaviour shown in the original message should reasonably 
considered a bug in standards terms.


Thanks,

- M.



Re: Pushing/restoring a file descriptor for a compound command

2018-04-27 Thread Martijn Dekker

Op 27-04-18 om 23:38 schreef Stephane Chazelas:

2018-04-27 20:28:57 +0200, Martijn Dekker:
[...]

: <&8 || echo "oops, closed"

[...]

Remember ":" is a special builtin, so its failure causes the
shell to exit. Another one of those accidents of implementation
of the Bourne shell that ended up being specified by POSIX, but
which makes little sense (but that other shells ended up
implementing for conformance).


Good point. Ignore the '|| echo "oops, closed"', it's pointless because 
a failed direction prints an error message anyway. I should have said 
that no shell produces an error there.


That said, do you have any opinion on whether something like
   { ... ; } 3>&-
should push/restore a closed file descriptor if it's already closed, so 
that the effect of exec-ing that descriptor within the compound command 
is local to that compound command?


- M.



Re: Pushing/restoring a file descriptor for a compound command

2018-04-27 Thread Martijn Dekker

Op 28-04-18 om 01:55 schreef Robert Elz:

 Date:Sat, 28 Apr 2018 00:23:51 +0200
 From:Martijn Dekker 
 Message-ID:  <8800d6d5-67ea-fad4-19c3-dac4bbfd8...@inlv.org>

   | That said, do you have any opinion on whether something like
   | { ... ; } 3>&-
   | should push/restore a closed file descriptor if it's already closed,

I suspect that it is just a bug, the usual way to do this is

saved_fd = fcntl(3, F_DUPFD, BIG_NUM);
close(3);

and later

dup2(saved_fd, 3);
close(saved_fd);

Which works fine, provided fd 3 is open at the beginning, otherwise
all of those fail with EBADF, which tends to just be ignored (and the
dup2() failing leaves fd 3 with whatever it was set to in between.)


It works fine on NetBSD sh though, and on every other ash derivative I 
know of except dash -- even for a file descriptor 3 that is closed at 
the beginning. How does NetBSD sh handle this?



Fixing it means adding code - as I understand it, in dash, that is
something they avoid unless it is absolutely essential


If it's a bug, surely it would meet that requirement.


 - and if no-one
can show where POSIX requires it, probably just won't happen.


The closest thing I've found is the first sentence of POSIX 2.7 
Redirection: "Redirection is used to open and close files for the 
current shell execution environment (see Shell Execution Environment) or 
for any command."


The first part of that sentence refers to exec'ing it, so is irrelevant 
here. The relevant bit is "or for any command". This includes compound 
commands.


I claim that this implies that a redirection added to a compound command 
should always cause the specified descriptor to be saved and restored, 
no matter the initial state of it, or the state initialised by the 
redirection.


The alternative seems inherently broken: the effect of an 'exec 3within that compound command will leak out of it, though attaching a 
3<&- or 3>&- redirection for the same FD creates a reasonable 
expectation that it should restore that FD's state when leaving the 
compound command.


Moreover, every current POSIX-compliant shell I know of works as I would 
expect, except bash and dash. The behaviour of dash appears to be unique 
for current ash derivatives, as FreeBSD sh, NetBSD sh, and Busybox ash 
also work as I would expect. I think that's further evidence that the 
behaviour of bash and dash should be considered a bug.


That's what I've got. Is that a sane interpretation?

It would be nice if there were something more unequivocal in the 
standard, but it seems there isn't...



You might have more luck with bash (perhaps.)


Chet, what do you think?

Thanks,

- M.



In defence of compound aliases

2019-01-13 Thread Martijn Dekker
I wish I had had the time to respond to the alias comment thread
earlier. Now, I'd like to make my point more coherently by starting anew
instead.

Let me start with a simple real-life example. In the regression test
suite of my shell library under development, modernish, until recently I
had numbered functions in each test script, doTest1(), doTest2(), etc.
This was a hassle. Inserting or removing test functions required awkward
renumbering. A simple pair of aliases solved my problem:

alias TEST='{ testFn() {'
alias ENDT='}; doTest; }'

Now, each regression test function is delimited by TEST ... ENDT instead
of the normal function definition. The ENDT alias calls a handler
function that runs each test function and automatically numbers the
result as the test script is sourced ('.').

I'm frankly astonished that there seems to be a growing consensus to
de-specify this long-standing type of alias usage. Please, reconsider.

The only objections I can see are of a dogmatic nature. There seems to
be a taboo against "redefining shell syntax", similar to the taboo on
using 'eval', even in safe ways. There is no real technical reason that
it's a "BAD IDEA". The above is perfectly handy, productive and robust.

It is also a highly established and portable usage: it works down to the
first Bourne-derived shell that ever implemented aliases -- because
defining new shell blocks was an original use case proposed for aliases
by David Korn for ksh86. See Korn's 1987 memo at:

- roff source:
https://github.com/weiss/original-bsd/blob/master/local/toolchest/ksh/sh.memo
- PDF: https://www.inlv.org/shellstuff/sh.memo.pdf -- note the date on
it is wrong: it's the date I generated it from the roff source. This is
from 1987.

As Korn wrote there: "The combination of aliases and functions can be
used to do things that can’t be done with either of these separately."

Indeed, it's not as if aliases are some sort of strange anomaly in the
programming world. In other languages, similar features are usually
called 'macros'. C library sources are loaded with them. Nobody calls
that a bad idea, or tells library programmers that they should use
functions instead -- because macros/aliases and functions are good at
different things.

Creative use of aliases may be a Bad Idea for casual shell scripters,
and maybe even many advanced ones -- but only in the sort of way that
using 'eval' is a Bad Idea. It should be done by people who know what
they're doing, who are properly familiar with the shell language and its
internals, in a way that can be re-used by others so they don't have to
reinvent the wheel badly. Hence my belief there is a need for a good
library. But in the long run this won't be possible to do portably if
this functionality is removed or de-sanctioned in the standard.

Please, let's not throw out the baby with the bathwater. Fix the spec if
necessary, but keep aliases working as they are.

- Martijn
  https://github.com/modernish/modernish



Re: In defence of compound aliases

2019-01-14 Thread Martijn Dekker
Op 14-01-19 om 09:02 schreef Ingo Schwarze:
> Hi Martijn,
> 
> Martijn Dekker wrote on Mon, Jan 14, 2019 at 06:59:14AM +0100:
> 
>> Indeed, it's not as if aliases are some sort of strange anomaly in the
>> programming world. In other languages, similar features are usually
>> called 'macros'. C library sources are loaded with them.
> 
> It depends.  Some software is indeed riddled with macros.
> Other software does actively avoid them where possible.
> 
>> Nobody calls that a bad idea, or tells library programmers that they
>> should use functions instead --
> 
> We do exactly that in OpenBSD, and we say exactly that, even with
> a special emphasis, and we have been saying so for a long time.
> Weeding out as much usage of macros as possible has been among the
> most important refactoring techniques employed in LibreSSL, but not
> only there; it is a general consensus diligently applied throughout
> the system.

On my OpenBSD-current VM...

openbsdbox# find /usr/src -path /usr/src/gnu -prune \
-o -name '*.h' -exec grep '^# *define ' {} + | wc -l
132272

That looks like roughly 132272 macros to me in OpenBSD header files,
with GNU sources excluded.

Ah, but my source directory on this VM is about a year old. Let's do a
'cvs up -d' and repeat that command...

...and now they've grown to 144542. (For comparison, /usr/src/gnu has
another 100061.)

What does this consensus you're speaking of entail exactly? Because "we
will not use macros" clearly isn't it.

> Also note that we generally advise against using the shell for any
> kind of serious programming since there a few languages making safe
> programming practices as hard as the shell, even when used in very
> conventional ways without any special creativity.

That is completely true, and it's actually the main thing I intend to
fix with modernish. Safety and robustness should be made the default.
Functionality causing pitfalls, such as split and glob, should be
disabled by default (safe mode) and fixed where at all possible
(--split/--glob operators for safe mode). Argument, bounds and exit
status checking must be paranoid at all times. A program should *never*
be allowed to continue to run if an inconsistent state is detected (this
is built in to modernish and can easily be added to external commands
with harden()). Emergency halt should be reliable, even when triggered
from a subshell.

That's my design philosophy and it's pretty much the polar opposite of
conventional shell scripting design. And it's amazing how many bugs
became very easy to catch simply by doing these things and adopting this
attitude. In general, modernish scripts do *not* go haywire, they die
(including all their processes) the moment a fatal error occurs.

In particular, see:

https://github.com/modernish/modernish#user-content-reliable-emergency-halt

https://github.com/modernish/modernish#user-content-use-safe

https://github.com/modernish/modernish#user-content-use-syscmdharden

In the event you're curious, unfortunately you'll have to try this on
some other shell because OpenBSD ksh is fatally broken for these
purposes. Among other things, disabling global field splitting (IFS='')
breaks "$@" completely, making the positional parameters unusable for
scripts. So much for eliminating split/glob from scripts that don't need
it. This is an ancient, ancient bug that all other shells fixed many
years ago. I reported it to you guys in early 2016 and even backported a
fix for it from mksh with Thorsten Glaser's blessing. This fix was
included in Theo's private binaries for a time, then quietly
disappeared. When I inquired, I got an email reply months later, saying
that it was not possible to audit the fix because the OpenBSD team has
insufficient people who understand the ksh internals.

- Martijn



Re: In defence of compound aliases

2019-01-14 Thread Martijn Dekker
Op 14-01-19 om 12:20 schreef Joerg Schilling:
> Martijn Dekker  wrote:
[...]
>> alias TEST='{ testFn() {'
>> alias ENDT='}; doTest; }'
>>
>> Now, each regression test function is delimited by TEST ... ENDT instead
>> of the normal function definition. The ENDT alias calls a handler
>> function that runs each test function and automatically numbers the
>> result as the test script is sourced ('.').
>>
>> I'm frankly astonished that there seems to be a growing consensus to
>> de-specify this long-standing type of alias usage. Please, reconsider.
> 
> I am not willing to make those things inspecified as long as there is an 
> example of an otherwise POSIX compliant shell that is incompatible to that.

Did you mean 'unless there is an example ...'?

I've tried all the shells I can get my hands on and I've found zero
examples of such a shell (except posh, which has removed alias suppport
altogether, but that doesn't count).

> I am also a bit confused as I remember the teleconference where we discussed 
> the current text and decided that it was required to call the lexer again 
> with 
> the repladcement after an alias replacement has been done.
> 
> This way, many things are possible and it is up to the user to define and use 
> related alias in a useful way.

Yes.

- Martijn



Re: In defence of compound aliases

2019-01-14 Thread Martijn Dekker
Op 14-01-19 om 11:59 schreef Robert Elz:
> Date:Mon, 14 Jan 2019 06:59:14 +0100
> From:    Martijn Dekker 
> Message-ID:  <90a5f4e6-64ae-6d08-fd20-9b9addd6d...@inlv.org>
> 
>   | Let me start with a simple real-life example.
> 
> I have a similar issue in tests I have written, and deal with it in
> an automated way without a single alias in sight.   That you have
> chosen to invent mangled syntax to deal with this doesn't mean
> by any means that it is the only way, or even a good one.

This "mangled syntax" qualification is gratuitous disapproval and
nothing more. You have still not come up with any actual argument
against it.

And, I repeat, it didn't require any "inventing". As I just showed, it's
David Korn himself who came up with the basic idea sometime in 1987 or
before.

It is also trivial. It's just a pair of shorthand macros for a simple
shell block with a function definition and a function call in it.
Nothing is being "mangled" whatsoever.

And it is useful and productive. It was an easy, straightforward,
functional and efficient way of solving an actual problem.

Where did I claim it's the *only* way? Of course it isn't. But that
doesn't make it bad.

I am willing to bet, though, that using that little pair of aliases is
the most efficient way, both in terms of maintenance and in terms of
execution. In fact it executes the tests even more efficiently than the
old alias-less way, because it eliminates the need for a loop that
checks if each function exists. And converting the regression tests to
this new method was about five minutes of work.

>   | I'm frankly astonished that there seems to be a growing consensus to
>   | de-specify this long-standing type of alias usage. Please, reconsider.
> 
> Ths issue is that the standard documents what actually works in the
> wild - as much of aliases as seems to work everywhere (and is reasonable
> to specify) is being retained.   That is not much - if you have time to read
> everything (going back years to when this was first discussed - before my
> time here) you'll see examples of different implementation choices in all
> kinds of areas.

Yes, there are implementation differences. Many other shell
functionality has implementation differences as well.

But defining a shell block using two aliases, e.g. one starting with a
'{' and the other ending with a '}', is very straightforward, and works
consistently on *all* the current shells that support aliases. Fact.

By now, you know better than many that my library regression tests tend
to test the shell (as well as the library) to its limits. So I think I
have a fairly solid basis for claiming this.

(I'm sure you'll agree that triggering segfaults in your NetBSD sh
development version doesn't count as my method not working, and I
suspect aliases have nothing to do with that anyway -- the problem
appeared when my new loop construct started using FIFOs intensively; I
was making use of aliases long before that.)

So, any outright claim that the new draft text "documents what actually
works in the wild" would be false. I think you know this, hence your
added qualifier, "(and is reasonable to specify)".

Somehow I get the feeling you're not going to consider anything that
sanctions an alias usage you disapprove of "reasonable".

The historical practice with defining shell blocks using aliases is
clearly useful, clearly portable, and would be perfectly reasonable to
specify. The standard developers just need to find the accurate wording
for it.

If it is not the scope of the POSIX standard to deprecate a historically
established usage that works consistently on all shells in the wild,
then please don't.

>   | The only objections I can see are of a dogmatic nature.
> 
> Then you haven't really looked - there are some of us (perhaps
> just me) who believe that aliases are a terrible idea for everything
[...]

This is literally just another way of saying that your objections are of
a dogmatic nature.

> The actual objections that have caused changes (such as it is, the
> definition in the current published standand is gibberish), or perhaps
> that have limited what is proposed to be specified, are all based upon
> real practical problems.

Then, please, show me the problems.

Particularly the real, concrete, practical problems with this:

alias TEST='{ testFn() {'
alias ENDT='}; doTest; }'

doTest() {
function calling testFn and handling result goes here
}

TEST
test code goes here
ENDT# and many more of these

What problems does this cause for the regression test scripts?

How does this not work well, on any POSIXy 

Re: Alias implementations being invalidated by proposed new wording?

2019-01-14 Thread Martijn Dekker
Op 03-01-19 om 13:39 schreef Robert Elz:
> ps: in ...
> 
> alias forever='while :; do' # the kind of thing for which
> # aliases are "useful" in that the
>   # same thing can't be done with
>   # functions
> 
> aside from this being explicitly unspecified in the proposed new
> wording, I dispute that aliases like this are "useful" - they're just "cute".

In principle I don't disagree there; this is a 'cute' one that I have in
modernish. It has two other cute things: 'not' and 'so', with 'not'
being '! ' and 'so' being an equivalent of '[ "$?" -eq 0 ]', so you can
do 'if so' and 'if not so'.

I would not do or recommend this in standard POSIX scripts. This is a
little something I've added exactly to emphasise that modernish
effectively creates a new shell dialect on top of POSIX sh -- and these
make the feel just slightly distinct in a way that I like. So I like a
little cuteness -- sue me. I'm not that strongly invested though, and if
too many other users dislike them during the upcoming alpha testing
(anyone like to host me a mailing list?), I'll probably get rid.
However, for a publicly documented library, the argument that aliases
make the script harder to understand doesn't really apply.

But all of this is not very interesting. Instead, I'd like to invite
readers to take look at some interesting functionality that depends on
compound aliases: an extensible shell loop construct including, for
example, a loop that integrates the POSIX 'find' utility into a shell
loop, with arbitrary file names (even with newlines) processed correctly
by default. This is effectively a recursive 'for' that supports all
'find' expression primaries plus -iterate (akin to -print except it
makes the loop iterate). And there's more:

https://github.com/modernish/modernish#user-content-use-varloop
https://github.com/modernish/modernish/blob/master/lib/modernish/mod/var/loop.mm
https://github.com/modernish/modernish/tree/master/lib/modernish/mod/var/loop

I believe this to be the first actually robust shell loop construct
implemented in shell. Here's what I learned, after throwing away old
experiments and starting over.

To avoid repeated evaluation of loop arguments causing unwanted side
effects, you need three aliases, not one or two. Of course 'do'...'done'
cannot be overloaded.  I settled on: LOOP args; DO commands; DONE

To make loop nesting, 'continue' and 'break' work as expected, a
block-local state is indispensable. Keeping the state in variables is
not possible to make work correctly. The one form of block-local state
provided by the POSIX shell is file descriptors, declared local by
adding redirections. So a loop can gain a local state by reserving just
one file descriptor for the purpose, let's say 8. This is added as a
8<&- redirection in the DONE alias, which should create a local state
for FD 8 (some shells have BUG_SCLOSEDFD which means they need
8https://github.com/modernish/modernish/blob/master/lib/modernish/mod/var/loop/repeat.mm

It gets more interesting with 'LOOP for' and 'LOOP select'. These
provide operators for safe splitting and globbing of their arguments for
use when these are globally disabled (safe mode). Split and glob are
processed by the iteration generator process; the main shell never gets
anywhere near it. I take the opportunity to make some things even safer,
e.g. by automatically prepending './' to expanded pathnames that start
with a dash, and removing non-matching patterns (as keeping them in
unexpanded form makes no sense in this context).

Interestingly, this all performs rather well -- as fast as the shell can
run 'read' and 'eval'. Iterations are generated in parallel, so don't
slow down the main shell process on a modern multiprocessing system.

The general idea of the LOOP...DO...DONE aliases is just as simple and
robust as the TEST...ENDT aliases in my regression test suite that I
wrote about in my other recent message: everything is all wrapped in one
enveloping { ... } block, so it's grammatically robust. There's slightly
more code in them, that's all.

None of this would be possible without compound aliases. I do believe
that this is a useful, non-frivolous use case for them. It doesn't even
do anything particularly new or unusual in and of itself -- all it does
is combine some old things (aliases, shell functions, local file
descriptors, FIFOs, background jobs) in a new way.

- Martijn

[*] Actually, the other week, my loop construct did manage to expose a
24 year old race condition with FIFOs in SunOS kernels (Solaris,
illumos)! An Oracle employee saw my thread on that in comp.unix.shell,
filed a bug, and fixed it, about a week ago. He was kind enough to let
me pass significant details on to the illumos bug tracker as well:
https://www.illumos.org/issues/10179



Re: In defence of compound aliases

2019-01-14 Thread Martijn Dekker
Op 14-01-19 om 18:54 schreef Joerg Schilling:
> Stephane Chazelas  wrote:
> 
>> The "alias" and "unalias" builtins were already removed in the
>> very first version of posh (0.01 in 2002), I don't know why.
>> Maybe the Debian policy at the time was forbidding the use of
>> aliases.
> 
> Shure?
> 
> posh
> $ type alias
> alias is a shell builtin

Yes:

$ ./posh -c 'alias'
posh: alias: not found
$ ./posh -c 'type alias'
./posh: type: not found

You're on Solaris, right? posh doesn't have 'type' either so you must be
running your /usr/xpg4/bin/type external command which reports on your
ksh 'alias' command.

A good example of how providing builtins as external commands is NOT a
good idea if those commands are only relevant to the shell's internal
state, no matter what POSIX says.

- Martijn



Re: getopts, OPTIND, and grouped options

2019-06-14 Thread Martijn Dekker

Op 14-06-19 om 14:41 schreef Michael Greenberg:

My question is: are both of these behaviors (the early-incrementing
OPTIND=2 at first and the late-incrementing OPTIND=1 at first) allowed?
One might read "the `getopts` utility shall place ... the index of the
next argument to be processed in the shell variable `OPTIND`" and assume
that late-incrementing is the right choice (since we're still processing
the first argument after processing `-a`), but it's not 100% clear to me
that's the right interpretation.


I don't see the text specify anything in particular about "the index of 
the next argument to be processed" (either value or format), so as far 
as I can tell, the OPTIND value is only specified at initialisation and 
at the end of options processing, and shells can do what they want in 
between.


- M.



Re: getopts, OPTIND, and grouped options

2019-06-14 Thread Martijn Dekker

Op 14-06-19 om 17:00 schreef Geoff Clare:

Michael Greenberg  wrote, on 14 Jun 2019:


Martijn Dekker  writes:


Op 14-06-19 om 14:41 schreef Michael Greenberg:

My question is: are both of these behaviors (the early-incrementing
OPTIND=2 at first and the late-incrementing OPTIND=1 at first) allowed?
One might read "the `getopts` utility shall place ... the index of the
next argument to be processed in the shell variable `OPTIND`" and assume
that late-incrementing is the right choice (since we're still processing
the first argument after processing `-a`), but it's not 100% clear to me
that's the right interpretation.


I don't see the text specify anything in particular about "the index of
the next argument to be processed" (either value or format), so as far
as I can tell, the OPTIND value is only specified at initialisation and
at the end of options processing, and shells can do what they want in
between.


Fair enough! :)


I don't think Martijn's interpretation is valid.


Of course it may well not be valid. But Yuki Watanabe makes a big deal 
of yash being strictly POSIX compliant, yet it uses non-numeric indexes 
for combined options, so to me it looks like I'm not the first one who 
interpreted it that way.



He seems to be claiming that when the standard says "the index of the
next argument to be processed" it is not specifying a value or format,
but when it says "the index of the first operand" it is.  Given the
similarity of those two pieces of text, I don't see how he can reach
that conclusion.


OK, so I'll explain how I reached that conclusion, without necessarily 
claiming it's valid.


The text only explicitly specifies the value 1 of OPTIND at 
initialisation time, and it only explicitly requires it to be numeric at 
the end of option processing.


Yes, it would intuitively make sense to assume the value is required to 
be numeric in between init and end of processing as well, but the text 
does not state this explicitly.


And implementers run into a bit of problem: a purely numeric OPTIND is 
insufficient to track the state of short options as several options may 
be combined into a single argument.


So that leaves shells with two options: adhere to the intuitive 
interpretation of the text and maintain an invisible extra state, which 
is a botch, but that's what the majority does. Or store the extra state 
in OPTIND as well, rendering it potentially non-numeric until the end of 
option processing, but otherwise resulting in a cleaner and more 
transparent implementation. I can't find anything in the text that 
actually prohibits this.



The only ambiguity is how the word "next" should be interpreted.


If the only valid interpretation is for OPTIND to be numeric at every 
stage of option processing (and thus an invisible extra state must 
necessarily be maintained), then the text is ambiguous, because (unless 
I'm wrong) it does not actually state this requirement.


- M.

--
modernish -- harness the shell
https://github.com/modernish/modernish



Exporting an unset variable

2019-07-07 Thread Martijn Dekker
On FreeBSD sh, when you set the export attribute on an otherwise unset 
variable, an empty environment variable is exported, though the variable 
continues to be considered unset within the current shell. This is the 
only shell I know of that does this.


$ unset -v foo
$ export foo
$ echo ${foo+set}
[empty line]
$ sh -c 'echo ${foo+set}'
set
$ set | grep ^foo=
$ env | grep ^foo=
foo=

Question: is this behaviour a bug in POSIX terms?

As far as I can tell, the POSIX spec doesn't explicitly say what to do 
in this case:

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_22
| The shell shall give the export attribute to the variables
| corresponding to the specified names, which shall cause them to be in
| the environment of subsequently executed commands.

I could see how this phrasing technically requires the FreeBSD sh 
behaviour, but given that no other shell does this, it seems unlikely 
this requirement is intended. Could the text do with clarifying this?


Thanks,

- M.

--
modernish -- harness the shell
https://github.com/modernish/modernish



Re: Exporting an unset variable

2019-07-07 Thread Martijn Dekker

Op 08-07-19 om 00:59 schreef Robert Elz:
[...]

  No-one is doubting the effect of the
export command upon the shell itself.

What is in question is what should actually be placed in the environment
when an unset, but exported, variable exists in the shell, and some other
command is invoked.


Exactly right, thank you.

After Jilles' reply, it looks like there's a strong consensus for 
"nothing" among shell implementers.



What needs to change is to perhaps add "if set" into the "shall cause them
to be exported of" words, making

which shall cause them to be, if set, in the environment of
subsequently executed commands.


IMHO, that's still not clear enough; this could be interpreted to say 
that it should be added to the environment if it is set at the time the 
'export' command is run. What shell implementations actually do is add a 
variable whose name was previously given the export flag to the 
environment the moment it acquires a value, whenever that may be.


I think the only way to make that really clear is to leave the phrase 
you quoted as is and add another one, something like


If a /name/ is unset and not followed by =/word/, then its
/export/ attribute shall be remembered without otherwise
affecting its state, and shall take effect if and when the
corresponding variable is set.

Thanks,

- M.

--
modernish -- harness the shell
https://github.com/modernish/modernish



Re: Bug 1254 gets worse: "Job" definition is wrong

2019-08-07 Thread Martijn Dekker

Op 07-08-19 om 09:40 schreef Geoff Clare:

It seems that bash has a rather nasty bug here, as can be seen in the
following:

$ sleep 5 || true && echo foo
^Z
[1]+  Stopped(SIGTSTP)sleep 5
foo
$ fg
sleep 5

When the sleep 5 was stopped, bash treated this as completion of
that pipeline (with non-zero "exit" status) and it went ahead and
executed the remainder of the AND-OR list at that point, resulting in
the "foo" output.

Obviously what it should have done is delay processing the remainder
of the AND-OR list until after the sleep had terminated (which was a
couple of seconds after I typed "fg").

This was tested in bash 4.4.12 - perhaps someone who has bash 5 can
check whether it still does the same.


Bash 5.0 release does act the same. (Current git doesn't compile for me.)

Other test results:

AT&T ksh93 acts like you say is correct.

bosh (Schily Tools sh) acts like you say is correct.

mksh acts like bash, if you use the path to an external 'sleep' -- its 
'sleep' builtin cannot be stopped at all.


pdksh (/bin/ksh on NetBSD and OpenBSD) acts like bash.

zsh acts like bash.

yash acts like bash.

dash acts like bash.

gwsh acts like bash.

FreeBSD sh acts like bash.

NetBSD sh acts like bash.

Busybox ash acts like bash.

So it appears that every current shell is broken in the same way, except 
the two that can claim a direct lineage to the original Bourne shell.


Which is interesting because the obsolete Bourne shell on Solaris 10 
(/bin/sh) does not seem to support job control at all.


- Martijn



Re: Bug 1254 gets worse: "Job" definition is wrong

2019-08-07 Thread Martijn Dekker

Op 07-08-19 om 11:09 schreef Joerg Schilling:

Martijn Dekker  wrote:


So it appears that every current shell is broken in the same way, except
the two that can claim a direct lineage to the original Bourne shell.

Which is interesting because the obsolete Bourne shell on Solaris 10
(/bin/sh) does not seem to support job control at all.


This is not correct.

The Bourne Shell supports jobcontrol since at least 1988. I guess the code has
been written at a similar time as ksh88 got jobcontrol.

If you like to use jobcontrol in the Bourne Shell, you however need to either
call "set -m" or call "jsh" (a link to sh) in order to get jobcontrol enabled
by default - see man page...


Thanks for the correction. Sorry, didn't have time to RTFM.

After 'set -m' I cannot get /bin/sh on Solaris 10.1 to interrupt the 
'sleep' command with ^Z, so I can't easily test whether Geoff's test 
case works correctly there.



In general, I believe that we should not make inventions but rather start a
discussion that ends in fixed to various shells and later standardize something
that has been verified to be working.


FWIW, I agree.

- M.

--
modernish -- harness the shell
https://github.com/modernish/modernish



Exit status 128 [was: exit status for false should be 1-125]

2020-01-30 Thread Martijn Dekker

Op 29-01-20 om 16:36 schreef Austin Group Bug Tracker:

The exit status of the false utility is simply required to be non-zero.
This means applications have no way of distinguishing between a normal
exit of this utility and the special exit statuses, greater than or
equal to 126, used for "not found", "can't be executed" or "terminated
by a signal" conditions.


A status that signifies termination by a signal is > 128, isn't it? 
Doesn't that mean that 128 isn't special itself?


- M.

--
modernish -- harness the shell
https://github.com/modernish/modernish



Re: Exit status 128 [was: exit status for false should be 1-125]

2020-01-31 Thread Martijn Dekker

Op 31-01-20 om 10:11 schreef Geoff Clare:

Martijn Dekker  wrote, on 30 Jan 2020:

A status that signifies termination by a signal is > 128, isn't it? Doesn't
that mean that 128 isn't special itself?


Correct, but see https://austingroupbugs.net/view.php?id=1229#c4269
(specifically the RATIONALE addition, last bullet point).


Thanks for the pointer.

Does this mean exiting with status 128 is going to be disallowed for 
conforming shell scripts as well?


- Martijn

--
modernish -- harness the shell
https://github.com/modernish/modernish



awk: FS matching 0 or more characters

2020-02-03 Thread Martijn Dekker

Consider:

echo 'one!two!!three!!!end' | awk -v 'FS=!*' \
'{ for (i=NF; i>0; i--) print $i; }'

Onetrueawk, mawk, GNU awk, and Solaris awk all print:


end
three
two
one


However, Busybox awk prints:



d
n
e

e
e
r
h
t

o
w
t

e
n
o


In a way, the Busybox awk behaviour makes more sense. The "!*" ERE 
means: match zero or more "!", and that's exactly what it did.


Changing the ERE to '!+' makes all awks behave consistently, so that's 
the obvious fix.


But what, if anything, does POSIX have to say about an FS ERE matching 
zero or more characters?


https://pubs.opengroup.org/onlinepubs/9699919799/utilities/awk.html#tag_20_06_13_04

I can only find:


1. If FS is a null string, the behavior is unspecified.


That doesn't really apply; FS is a non-null ERE, though one that may 
match the null string.



3. [...] Each occurrence of a sequence matching the extended regular
expression shall delimit fields.


Is a null string matching the ERE a "sequence" that matches it?

So at this point I'm not sure whether to report a bug in Busybox awk, or 
an area in the standard that needs further specification or 
clarification, or neither...


- Martijn

--
modernish -- harness the shell
https://github.com/modernish/modernish



Re: XCU: 'return' from subshell

2020-03-13 Thread Martijn Dekker

Op 13-03-20 om 15:17 schreef Chet Ramey:

On 3/13/20 10:14 AM, Harald van Dijk wrote:

[...]

I don't see how you can allow that without also allowing

   f() { (return 7; echo no); echo $?; }; f

If that also works in all shells (meaning it doesn't print no, and does
print 7), then by all means standardise it.


I can't find one that doesn't in my quick initial testing, but I don't
have binaries for every shell under the sun.


My testing says it acts identically (outputs '7') on the following 
shells: ash variants (Busybox ash, dash, FreeBSD sh, gwsh, NetBSD sh), 
bash, bosh, ksh88, ksh93, mksh, pdksh, yash, and zsh.


- M.

--
modernish -- harness the shell
https://github.com/modernish/modernish



Is ksh93's default alias command='command ' POSIX compliant?

2020-06-14 Thread Martijn Dekker
I am now the maintainer of what is currently, to the best of my 
knowledge, the only actively developed fork of AT&T ksh93. It is based 
on the last stable AST version, 93u+ 2012-08-01. Along with a few others 
I have been fixing a bunch of bugs. See https://github.com/ksh93/ksh for 
the current activity and some history/rationale in the README.md.


One issue is ksh39's default alias
command='command '
which continues alias substitution after 'command', defeating its 
ability to bypass aliases.


I think that doing this by default violates POSIX, because 'command' is 
specified as a regular builtin, not as an alias. So I have removed it:


https://github.com/ksh93/ksh/commit/61d9bca5

There is some disagreement about that, however -- as you can see in the 
comment under that commit (scroll all the way down). And of course it is 
possible that I am wrong. So I would like the ask the Austin Group's 
opinions. Does this alias, by virtue of being default, violate POSIX?


One issue in particular I would note: on 93u+, 'times' is defined as a 
default alias

times='{ { time;} 2>&1;}'
which not only does not produce POSIX compliant output, but also does 
not combine with the default 'command' alias: 'command times' is a 
syntax error. And that is a perfectly valid POSIX idiom, so at the very 
least, that seems like a straight-up standards violation. However, I 
have already replaced the 'times' alias by a proper POSIX compliant 
builtin on my version. This does demonstrate a problem, however; since 
aliases can contain arbitrary shell grammar, continuing alias expansion 
after 'command' seems problematic even if it doesn't violate POSIX.


   [Possibly, this alias was added to make 'command' work with some
POSIX commands that are also defined as default aliases, i.e. 'fc',
'hash', 'times', and 'type' -- as well as some ksh-specific
scripting-relevant commands: 'autoload', 'compound', 'float',
'functions', 'integer', 'nameref', 'redirect'. I think defining
any of these as aliases is a bug in itself, as 'unalias -a' removes
all of them, so it is impossible for a script to start with a clean
alias slate without losing essential commands. That's why I'm in the
process of converting all of these to proper builtin commands.]

- Martijn

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: Is ksh93's default alias command='command ' POSIX compliant?

2020-06-14 Thread Martijn Dekker

Op 14-06-20 om 18:03 schreef shwaresyst:
The command alias is nominally conforming, I believe, in that recursive 
alias expansion isn't permitted so looking for a utility named command 
still occurs.


So are you saying that, in order to be POSIXly sure of running a 
standard command 'foo' given the default shell alias configuration, you 
have to do "command command foo" or "\command foo"? Surely that can't be 
the intention...?


 However, the implementation of various utilities as 
aliases changes the reporting of 'command -v', or '-V',  to that they 
are aliases and not actual utilities as a user might expect. Such a 
change may be allowed, but I don't see it as intended for the utilities 
the standard requires.


The definition of times only as an alias, in the manner used, is not 
conforming; the use of curly braces, being keywords, turns it into a 
compound command the command utility is not expected to see as an 
argument list, and defeats the recognition of times as a special 
built-in per XCU 2.9.1, as time is a regular utility.


In fact, that's what all such aliases do, whether they have extra shell 
grammar or not. If 'unalias -a' deletes it, it's not a regular utility.


 There still needs 
to be an implementation that is accessed when "\times", to disable 
expansion as a possibility, is specified as well, that I see.


Yes, there is that too. I believe that logically means that all standard 
commands should be regular or special builtins, because having a default 
alias overriding a default builtin only to exhibit the same behaviour is 
100% pointless.


Thanks for the feedback.

- M.

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: Is ksh93's default alias command='command ' POSIX compliant?

2020-06-16 Thread Martijn Dekker

Op 16-06-20 om 17:37 schreef Alan Coopersmith:

We are maintaining ksh93 packages, but not doing active development work on
them.  Our packages are still based on the last stable release from AT&T -
2012-08-01 with an unfortunately high number of local patches applied:
https://github.com/oracle/solaris-userland/tree/master/components/ksh93

We really don't want our ksh93 to be a separate fork, but got stuck here 
due to past decisions it's a bit too late to undo now.


Maybe not, because 2012-08-01 is precisely the version that we are 
basing our rebooted development on at the new ksh93 repository. This new 
project is really picking up steam as we speak. Many bugs have been 
fixed already and many more fixes are coming. Stability, correctness and 
POSIX compliance are the immediate goals. Feature development can wait.


I would like to invite you (as I do everyone) to submit any of your 
fixes that are suitable for incorporating upstream to 
https://github.com/ksh93/ksh as pull requests, documenting in each 
commit message what it fixes and how it fixes it.


And it would be even better if you could periodically check for open 
issues labelled "help wanted", as we're currently a bit stuck on those 
and they are bugs in the Solaris version of ksh93 as well. Especially 
this one: https://github.com/ksh93/ksh/issues/2


Please, have a look at our commit history and let me know if you like 
what you see. We'd be delighted to have any fixes you are willing to 
share, and hope we will prove worthy to become the new ksh93 upstream.


- Martijn

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: Is ksh93's default alias command='command ' POSIX compliant?

2020-06-18 Thread Martijn Dekker

Op 18-06-20 om 14:11 schreef Joerg Schilling:

Is my impression correct that you did not use the modifications from Redhat
people that introduced non-portability, many bugs and a slowdown?


Yes.


Be careful when looking at changes from these people, since they e.g. did
throw away the ksh93 specific malloc() that is much faster than the system
malloc() since it does not need to take care of multi-threading. Also note
that ksh93 depends on being able to use a freed()d piece of memory even
after the next malloc (if that returned a different start address) as it has
been in AT&T malloc() in the time before multi threaded environments.


I don't know much about malloc, but what you describe sounds rather 
dodgy. It evidently works though, so I'm leaving well enough alone.


It may turn out to be an advantage for this bugfix project that my 
primary expertise is in the shell language itself, whereas my C skills 
are at an intermediate level. I am simply not well-versed enough in C to 
even think about touching things like malloc. On the other hand, if some 
change breaks the shell language, I am likely to notice it quickly.


I not only have the ksh regression test suite (which I fixed, as it was 
in a deplorable state), but also the modernish regression tests which 
have been exceptionally good at breaking shells for a few years now. 
Even kre has had to admit that much. ;)


There's also a policy of documenting in every commit message not only 
what it fixes, but also how it fixes it. This helps improve everyone's 
understanding of this very poorly commented codebase, while preserving 
this documentation for future generations without depending on GitHub in 
perpetuity. The idea is that 'git blame' will lead you straight to the 
rationale for every change.


But more importantly, this also enables me to know what's going on 
myself, while forcing contributors to double-check that they understand 
what they are doing. No pull request is merged until I understand 
exactly the ins and outs of it.



I would like to invite you (as I do everyone) to submit any of your
fixes that are suitable for incorporating upstream to
https://github.com/ksh93/ksh as pull requests, documenting in each
commit message what it fixes and how it fixes it.


I pulled the snapshot, compiled and had a quick check for speed.

++  It still uses the mature and highly portable ksh93
build environment.


I have no intention of changing the build system, but I would question 
your praise for it. In my experience, it is currently very brittle. It 
failed to build on modern Macs before I fixed it for that. It currently 
fails to build on at least Solaris (see below), NetBSD, as well as AIX 
and QNX (those latter two as hosted by polarhome.com).


And if I restore nmake (which got removed along with the other 
superfluous non-ksh AST utilities), it fails to build on even more 
systems, including my Mac. Currently it only works if the build system 
is forced to fall back to mamake, which uses the Mamfiles 
machine-generated by nmake and mostly ignores the Makefiles. Of course, 
the long-term goal is to restore nmake and make the build system 
actually work with that, so that the Makefiles become editable/usable again.


In any case, I don't even know where to begin thinking about changing 
build systems, and there is community support for keeping the current 
one. So it is just going to have to be fixed instead... somehow.


Anyone who cares about and understands this build sytem, PLEASE send 
patches or pull requests that make it build on systems it currently 
fails on.



++  It still compiles out of the box on Solaris


That's amazing to me, because for me it consistently fails to compile on 
the pristine Solaris 11.3 and 11.4 evaluation VMs as downloaded directly 
from Oracle. The problem is that the 'dll' iffe feature test fails to 
write the Dllscan_t structure and a few others to a generated header 
file. The build then fails because Dllscan_t is an unknown identifier.


Your build environment must be somehow different from the default 
Solaris configuration as provided in their VMs. I would be very 
interested to know how it is different, so that I can build and test it 
on Solaris myself.


BTW, the same type of build failure happens when compiling on NetBSD.


-   It does not compile/use libshell.


Well, it could have fooled me:

$ find arch -name libshell\*
arch/darwin.i386-64/lib/libshell.a.old
arch/darwin.i386-64/lib/libshell.a
arch/darwin.i386-64/src/cmd/ksh93/libshell.a

I'm not sure how you think ksh could possibly have worked or compiled 
without libshell...



-   Using the default compile setup, it is 5.5% slower than the
OpenSolaris ksh93. Mesured with the "configure" script from
Schilytools.

-   Using the additional compile options "-O -fast -xspace", it
is still 2.5% slower than the OpenSolaris ksh93.


It would be great if you could help isolate the commit that caused 

Re: LC_CTYPE=UTF-8

2020-06-25 Thread Martijn Dekker

Op 25-06-20 om 16:59 schreef Alan Coopersmith:

On 6/25/20 6:33 AM, Hans Åberg wrote:
Perhaps there should be a default UTF-8 locale: It seems that the 
current construct does not apply so well to it.


If the goal is to standardize existing behavior the standard could define
the C.UTF-8 locale (or perhaps a POSIX.UTF-8 locale) that a number of
systems already have, which is the standard C/POSIX locale with just the
character set changed to UTF-8 instead.


Datapoint: AT&T ksh93 has a C.UTF-8 locale built in which it handles 
internally. I suppose that could be considered a precedent of sorts.


- M.

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: LC_CTYPE=UTF-8

2020-06-25 Thread Martijn Dekker

Op 25-06-20 om 21:13 schreef Alan Coopersmith:
The only thought I had along those lines was that I thought the "C" 
locale came from the C standard, and might be best left to the C 
committee to standardize, while this group controls the "POSIX" 
locale definition.


Actually, as far as POSIX is concerned, the two are synonymous.

XBD 7.2 "POSIX Locale":
https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap07.html#tag_07_02
| Conforming systems shall provide a POSIX locale, also known as the C
| locale. In POSIX.1 the requirements for the POSIX locale are more
| extensive than the requirements for the C locale as specified in the ISO
| C standard. However, in a conforming POSIX implementation, the POSIX
| locale and the C locale are identical.


--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Draft suggestion: Job control and subshells

2020-07-23 Thread Martijn Dekker
The current POSIX definition (including the current draft) for a 
subshell environment is:



A subshell environment shall be created as a duplicate of the shell
environment, except that signal traps that are not being ignored shall
be set to the default action. Changes made to the subshell environment
shall not affect the shell environment.


This does not mention job control. However, I think job control should 
be disabled in subshells as it makes no sense to do otherwise, because 
subshells are not interactive and you cannot access a subshell job table 
from your interactive main shell.


Nearly all shells disable job control for all subshells. But for 
virtual/non-forked subshells, ksh93 currently only disables job control 
for command substitutions, causing buggy and inconsistent behaviour.


So maybe the first sentence should be:

"A subshell environment shall be created as a duplicate of the shell 
environment, except that job control shall be disabled and signal traps 
that are not being ignored shall be set to the default action."


Thoughts?

- Martijn

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: Draft suggestion: Job control and subshells

2020-07-23 Thread Martijn Dekker

Op 23-07-20 om 14:47 schreef Geoff Clare:

Not according to a quick test I just did.  The only shell that I tried
which won't do job control in a subshell is zsh in its default
environment; with "emulate sh" it does.

$ for shell in bash dash ksh mksh zsh; do echo $shell ...; $shell -c
'set -m; (sleep 2 & jobs; wait %%)'; done
On the vast majority of shells, 'wait %%' works without job control, so 
that is not a correct test. The only shells I've found where that does 
*not* work is (1) bash in posix mode, where it complains of no job 
control and (2) zsh in sh emulation mode, where it is simply ignored.


Note also the behaviour of interactive shells changes when launching a 
background job in a subshell: most do not print job numbers and none 
print 'Done' notices. Only FreeBSD sh and ksh93 print a job number, but 
I'm pretty sure that's a bug. The ksh93 code for printing that job 
number tests for SH_INTERACTIVE (-i) whereas it should be testing for 
SH_MONITOR (-m).


ksh93 job control is also broken in other ways, as you can see from your 
own "" output. I'm currently trying to fix it. It 
disables job control in command substitutions but not in non-forked 
non-comsub subshells, causing several different kinds of misbehaviour.


A proper test for job control is whether you can use the 'fg' command; 
it will generally tell you if job control is disabled. There is no 
current shell where you can use 'fg' in a subshell:


bash:

$ (sleep 1 & fg)
bash: fg: no job control

dash:

$ (sleep 1 & fg)
dash: 1: fg: job (null) not created under job control

FreeBSD sh:

$ (sleep 1 & fg)
[1] 29348
sh: fg: No job control

ksh88:

$ (sleep 1 & fg)
fg: job not created under job control

ksh93:

$ (sleep 1 & fg)
[1] 17088
/bin/ksh: fg: No job control

NetBSD sh:

$ (sleep 1 & fg)
fg: job not created under job control

mksh:

$ (sleep 1 & fg)
mksh: fg: job control not enabled

yash, including yash -o posix:

$ (sleep 1 & fg)
fg: job control is disabled

zsh:

% (sleep 1 & fg)
fg: no job control in this shell.

'zsh --emulate sh' seems as bit broken but is essentially the same:

$ (sleep 1 & fg)
fg: no current job

So, to me it looks like 100% of shells currently disable job control in 
subshells, and to me this also still looks like the only sensible thing 
to do. Unless I'm missing something, my suggestion stands...


- Martijn

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



ksh93 job control behaviour [was: Draft suggestion: Job control and subshells]

2020-07-24 Thread Martijn Dekker

Op 24-07-20 om 10:47 schreef Geoff Clare:

Ksh93 doesn't turn off -m in a subshell, so testing SH_MONITOR wouldn't
prevent the job number output.  I think it should have a separate flag
to indicate when the shell is behaving non-interactively despite the -i
flag being set.


It depends on how you test it. ksh93 internally keeps track of 
(different aspects of) job control in three different ways:


sh_isoption(SH_MONITOR) // the actual shell option
sh_isstate(SH_MONITOR)  // internal state flag
job.jobcontrol  // global state variable

I'm not sure what the difference is between the latter two. When ksh 
tests for job control, sometimes it uses sh_isstate(), sometimes the 
global variable, and there seems to be no rhyme or reason re which is 
used when. I do know that ksh93 job control behaviour is broken in a few 
ways, although it remains to be clarified how exactly.


Currently, ksh tests for sh_isstate(SH_INTERACTIVE) to decide whether to 
output the job number. Adding '&& sh_isstate(SH_MONITOR)' prevents it 
from outputting the job number in non-forked subshells, even if the -m 
option is active in them.



Bash and dash also don't turn off -m, as reported by set +o, so your
claim that most shells "disable" job control is not correct in that regard.


At least on ksh93, the state of the -m shell option is not all that 
relevant for subshells. Most shells (mksh being the exception) don't 
turn off the -i option in subshells either, yet subshells act 
non-interactively.



ksh93 job control is also broken in other ways, as you can see from your own
"" output. I'm currently trying to fix it. It disables job
control in command substitutions but not in non-forked non-comsub subshells,
causing several different kinds of misbehaviour.


It doesn't turn off -m in command substitutions:

$ echo $(set +o) | grep monitor
set --default --bgnice --braceexpand --monitor --multiline --vi --viraw

Obviously that's a good thing since the point of set +o is to be
able to obtain the option settings to restore them later.


True. However, ksh93 has explicit code for disabling job control in 
command substitutions. It changes the SH_MONITOR state flag and the 
job.jobcontrol variable, but not the shell option.



A proper test for job control is whether you can use the 'fg' command


The fg command is a feature of interactive job control. There is no
reason why anyone would want to use it in an (effectively or actually)
non-interactive shell environment.


Does POSIX specify that job control behaves differently for interactive 
shells vs. (non-interactive shells or subshells)? If so, where?


[...]

It is only the interactive aspects of job control that are not available
in a subshell.  The parts that still work (in particular process group
ID assignment and the ability to send a signal to that process group
using kill %%) are still available and useful.


But ksh 93u+ 2012-08-01 does not do this either (note the extra '; :' is 
needed to avoid optimising the subshell away when using '-c'):


$ /bin/ksh -c 'set -m; (/bin/sleep 2 & ps -o pid,pgid,command); :'
  PID  PGID COMMAND
24570 24570 ksh -c set -m; (/bin/sleep 2 & ps -o pid,pgid,command); :
24571 24570 ksh -c set -m; (/bin/sleep 2 & ps -o pid,pgid,command); :
24572 24570 /bin/sleep 2

However, an "interactive" shell (-i flag, which implies -m) does create 
a process group:


$ /bin/ksh -i -c '(/bin/sleep 2 & ps -o pid,pgid,command); :'
[1] 24636
  PID  PGID COMMAND
24634 24634 /bin/ksh -i -c set -m; (/bin/sleep 2 & ps -o 
pid,pgid,command); exit
24635 24635 /bin/ksh -i -c set -m; (/bin/sleep 2 & ps -o 
pid,pgid,command); exit

24636 24635 /bin/sleep 2

So, would you say that ksh93's behaviour with just -m is broken (as in 
POSIX non-compliant) and needs fixing?


At least, ksh93 is the odd one out here. By my testing, other shells 
(bash, dash, mksh, yash, zsh) do act as you suggest, even when adding 
the '; :'.


- Martijn

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: Bug 1393 ("command" declaration utility) possible solution

2021-01-09 Thread Martijn Dekker via austin-group-l at The Open Group
Op 23-09-20 om 15:11 schreef Geoff Clare via austin-group-l at The Open 
Group:

Playing around with export and readonly in shells that support array
assignments, it's clear that they are treating them as separate tokens
in the grammar in order to accept array assignment syntax:

 $ bash -c 'export a=(1 2 3); echo ${a[0]}'
 1
 $ bash -c 'e=export; $e a=(1 2 3); echo ${a[0]}'
 bash: -c: line 0: syntax error near unexpected token `('
 $  bash -c 'readonly a=(1 2 3); echo ${a[0]}'
 1
 $  bash -c 'r=readonly; $r a=(1 2 3); echo ${a[0]}'
 bash: -c: line 0: syntax error near unexpected token `('

 $ ksh -c 'export a=(1 2 3); echo ${a[0]}'
 1
 $ ksh -c 'e=export; $e a=(1 2 3); echo ${a[0]}'
 ksh: syntax error at line 1: `(' unexpected
 $ ksh -c 'readonly a=(1 2 3); echo ${a[0]}'
 1
 $ ksh -c 'r=readonly; $r a=(1 2 3); echo ${a[0]}'
 ksh: syntax error at line 1: `(' unexpected

In order to make "command export ..." and "command readonly ..."
work the same, all that's necessary is for those shells to treat
command as a separate token too.

The standard allows this special treatment of export and readonly
(and command) because it allows shells to extend the syntax such
that they accept input that would, according to the standard, be a
syntax error.


I would question that the currently published standard allows any 
regular builtin to override the regular shell grammar with special 
syntactic properties. What exactly do you base this on?


Aren't regular builtins supposed to be exactly equivalent to external 
commands, with every regular builtin being mandated to have a 
corresponding external version? How would you expect an external command 
to have special grammatical properties in the shell?


This is also not a majority shell behaviour. For instance, bash does not 
currently do this. I wonder if Chet has any plans of changing that now.


The resolution of this bug is particularly problematic for ksh93. The 
way the ksh93 parser implements this behaviour for 'command' is by 
internally parsing something like


command export foo=bar

as

foo=bar command export

i.e. a preceding assignment. The parser similarly reorders 'command 
readonly foo=bar' and 'command typeset foo=bar'. You can check this 
using set -o xtrace. And yes, this works even if you literally type the 
second variant, and even then the assignment will persist as exported 
(which is a clear violation of the standard, but this is how Korn et al 
have chosen to do it -- and fundamentally reworking the parser is not on 
the cards).


One side effect of this bizarre parser behaviour is that 'command' is 
unable to prevent the shell from exiting. Something like 'readonly foo; 
command export foo=bar' will exit the shell because 'foo=bar' is 
actually executed as an assignment preceding 'command', so the shell 
exits before the 'command' builtin is even run.


To fix this for the new POSIX mode of ksh 93u+m, I've disabled this 
parsing behaviour so that 'command' is treated as a regular builtin if 
the -o posix option is on, and 'typeset' falls back to parsing the 
assignment-arguments itself without any special grammatical properties 
(the code to do this was still present). This then allows 'command' to 
stop the shell from exiting on trying to modify a readonly variable.


But that does mean that 'command' behaves as a regular builtin with 
regular shell grammar as I believe the currently published POSIX 
standard specifies it, i.e., it disables both "special" and 
"declaration" properties of commands it runs.



The bug description claims that the situation for command is different
from export and readonly (it says "A similar argument could be made
for 'export' and 'readonly' themselves, but at least they are already
special built-in utilities").  This is a red herring.  Whether or not
a utility is a special built-in does not affect the grammar.

Note that I was careful to say "separate tokens", not "reserved words".
They are not being treated as reserved words, because they are subject
to alias expansion whereas reserved words are not.


(Note that both 'export' and 'readonly' are reserved words on zsh 5.1 
and later, including in its POSIX mode. The 'disable -r' command can be 
used to disable the reserved words, reverting zsh to the original 
builtin commands.)



So I think that the normative text in Issue 8 draft 1 is fine as-is.
It meets the goal of requiring "command export args..." to expand its
arguments the same way as "export args...", and likewise for readonly,
and that remains a worthwhile goal.  All that is needed is to add some
explanatory rationale (to command, export, and readonly).


I question that this goal is worthwhile. I think it makes the shell 
grammar even more inconsistent than it already is and increases 
confusion. It also means that the standard now will not provide for any 
way to ensure regular shell grammar parsing for arbitrary commands,

execve(2) optimisation for last command

2021-04-15 Thread Martijn Dekker via austin-group-l at The Open Group

Most shells 'exec' the last command in -c scripts, e.g.:

$ bash -c 'printf "$$ "; sh -c "echo \$\$"'
66303 66303
$ dash -c 'printf "$$ "; sh -c "echo \$\$"'
66304 66304
$ ksh93 -c 'printf "$$ "; sh -c "echo \$\$"'
66305 66305
$ yash -c 'printf "$$ "; sh -c "echo \$\$"'
66309 66309
$ zsh -c 'printf "$$ "; sh -c "echo \$\$"'
66310 66310

However, no shell seems to do this for scripts loaded from a file:

$ echo 'printf "$$ "; sh -c "echo \$\$"' >/tmp/test.sh
$ chmod +x /tmp/test.sh
$ bash /tmp/test.sh
66325 66326
$ dash /tmp/test.sh
66327 66328
$ /bin/ksh /tmp/test.sh
66329 66330
$ yash /tmp/test.sh
66331 66332
$ zsh /tmp/test.sh
66333 66334

My question: why is this? I would have thought that a script is a script 
and that it should make no essential difference whether a script is 
taken from a -c argument or loaded from a file. What makes the 
optimisation appropriate for one but not the other?


ksh93 used to use this exec-the-last-command optimisation also for 
scripts loaded from a regular file, but disabled this (keeping it only 
for -c scripts) in version 2011-12-24. The corresponding changelog entry 
is characteristically uninformative, so doesn't help answer my question. 
Now that a couple of redirection-related bugs have been fixed with that 
optimisation, we've re-enabled it for regular scripts in ksh 93u+m ahead 
of releasing the first beta of this reboot (Very Soon Now(tm)), but if 
there is a good reason that shouldn't be done, we should revert this.


--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: execve(2) optimisation for last command

2021-04-16 Thread Martijn Dekker via austin-group-l at The Open Group

Op 15-04-21 om 22:43 schreef Harald van Dijk:
[...]
In specific cases, the shell may be able to detect that these issues 
cannot be a problem. In those specific cases, the optimisation should be 
perfectly valid and any reasons why it should or should not be performed 
are not of a technical nature.


It makes sense now: instead of stdio, ksh93 uses libast's sfio, which on 
most systems is capable of peeking ahead on arbitrary files without 
consuming data. And it doesn't care if the input is a file or a string 
because both are turned into sfio input streams. Thanks to Chet and 
Harald for confirming that I've not overlooked some reason why the 
optimisation shouldn't be re-enabled.


--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: what variables are affected by the "allexport" shell option?

2022-01-16 Thread Martijn Dekker via austin-group-l at The Open Group

Op 14-08-18 om 17:21 schreef Stephane Chazelas:

Hi, the spec for the "allexport" (set -a) shell option says:

POSIX2018> When this option is on, the export attribute shall
POSIX2018> be set for each variable to which an assignment is
POSIX2018> performed; see XBD Variable Assignment. If the
POSIX2018> assignment precedes a utility name in a command,
POSIX2018> the export attribute shall not persist in the
POSIX2018> current execution environment after the utility
POSIX2018> completes, with the exception that preceding one of
POSIX2018> the special built-in utilities causes the export
POSIX2018> attribute to persist after the built-in has
POSIX2018> completed. If the assignment does not precede a
POSIX2018> utility name in the command, or if the assignment
POSIX2018> is a result of the operation of the getopts or read
POSIX2018> utilities, the export attribute shall persist until
POSIX2018> the variable is unset.

the reference to "XBD Variable Assignment" would suggest it's
only meant to be done for variables assigned with an assignment.

But then it goes on about "getopts" and "read", but not "for",
and doesn't say if that applies to variables the shell sets by
itself like $IFS, $LINENO, $PPID, $PWD

[...]

I'd like to rekindle this old discussion. Thread archived at: 
https://www.mail-archive.com/austin-group-l@opengroup.org/msg02754.html


Today I received a pull request to make allexport work for ${var:=value} 
and ${var=value} on ksh 93u+m. This currently works on bash, dash, *BSD 
sh, and zsh, but not on ksh, pdksh/mksh, or yash.


Expansions of that form weren't mentioned in the 2018 discussion.

I need some clarity. Should ${var:=value} and ${var=value} honour 
allexport? Is this unspecified in POSIX terms or is support for this 
implied/intended by the current text? I think it might be, but shells 
differ too much.


--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



Re: what variables are affected by the "allexport" shell option?

2022-01-18 Thread Martijn Dekker via austin-group-l at The Open Group

Op 17-01-22 om 22:54 schreef Robert Elz:

 Date:Sun, 16 Jan 2022 20:26:00 +
 From:"Martijn Dekker via austin-group-l at The Open Group" 

 Message-ID:  <409b0a89-6fe1-d688-23f9-8c918ef89...@inlv.org>


   | I need some clarity. Should ${var:=value} and ${var=value} honour
   | allexport?

Yes.

Any assignment (in the usual sense of that word) to a variable while -a is
in effect should result in the variable being exported, no matter how
it is accomplished.   Further in draft 2.1 it actually says:

   When this option is on, a variable that is assigned a value
  (see XBD Section 4.23),

[that xref is prehaps unfortunate, it really is not needed here as:]


Yes, that xref is the whole cause of the issue. On ksh, the bug appears 
to be historic behaviour inherited from the Bourne shell, but the xref 
appears to have mislead the author of the newer shell yash, which 
replicated the bug.



But the fact that they're included makes it clear that any assignment
to a variable counts (even the absurd _ variable, which fortunately
is not posix, but is supported by ksh versions, and to varying extents
by most other shells).   Assignments performed as a side effect of
word expansions are certainly included.


Then the standard should say that.


Another case worth checking is unset var; set -a; echo $(( var=3 ));
and verify that after that, var is exported ($SHELL -c 'echo $var').


Good point.

The situation with arithmetic assignment seems to be the same: ksh93, 
pdksh/mksh, and yash do not export the variable.



It might be worth appending to the "including variables..." clause
above:
 and also including side effects of parameter and arithmetic expansions,


Yes.


What follows has nothing to do with -a, but another aspect of ${var=value}

What is less clear about ${var=value} (and the form with the : as
well of course) is whether the assignemnt, when it is performed, ought
to be considered as a XBD 4.23 type variable assignment, with everything
that comes with that (including the rules for how tilde expansion happens).


As far as I can tell, there is currently nothing to indicate that it should.

--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



$? behaviour after comsub in same command

2023-04-05 Thread Martijn Dekker via austin-group-l at The Open Group

Consider:

false || echo $(true) $?

dash, mksh and yash print 1.
bash, ksh93 and zsh print 0.
Which is right?

My hunch says dash, mksh and yash are right, as 'true' is executed in a 
command substitution (a subshell) within the same command, so 'false' 
should still be the "most recent pipeline" for the purpose of the 
subsequent $? expansion.


Thoughts?

My apologies if this has been discussed before; terms like $? are not 
easily searchable.


--
||  modernish -- harness the shell
||  https://github.com/modernish/modernish
||
||  KornShell lives!
||  https://github.com/ksh93/ksh



  1   2   >