1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-02-13 11:42:21 +00:00
Commit graph

284 commits

Author SHA1 Message Date
Johnothan King
cd562b16e2 Port more shell lint improvements from illumos and ksh93v- (#374)
This commit adds onto <https://github.com/ksh93/ksh/pull/353> by porting
over two additional improvements to the shell linter:

1) The changes in the aforementioned pull request were merged into
   illumos-gate with an additional change.[*] The illumos revision of
   the patch improved the warning for (( $foo = $? )) to specify '$foo'
   causes the warning.[**] Example:
      $ ksh -n -c '(( $? != $bar ))'
      ksh: warning: line 1: in '(( $? != $bar ))', using '$' as in '$bar' is slower and can introduce rounding errors
   While I was porting the illumos patch I did notice one problem. The
   string it uses from paramsub() skips over the initial '{' in
   '${var}', resulting in the warning printing '$var}' instead:
      $ ksh -n -c '(( ${.sh.pid} != $$ ))'
      ...  in '(( ${.sh.pid} != $$ ))', using '$' as in '$.sh.pid}' is slower  ...
   This was fixed by including the missing '{' in the string returned by
   paramsub for ${var} variables.

2) In ksh93v-, parsing x=$((expr)) with the shell linter will cause ksh
   to warn the user x=$((expr)) is slower than ((x=expr)). This
   improvement has been backported with a modified warning:
      # Result from this commit
      $ ksh -n -c 'x=$((1 + 2))'
      ksh: warning: line 1: x=$((1 + 2)) is slower than ((x=1 + 2))
      # Result from ksh93v-
      $ ksh93v -n -c 'x=$((1 + 2))'
      ksh93v: warning: line 1: ((x=1 + 2)) is more efficient than x=$((1 + 2))
   Minor note: the ksh93v- patch had an invalid use of memcmp; this
   version of the patch uses strncmp instead.

References:
be548e87bc
https://code.illumos.org/c/illumos-gate/+/1834/comment/65722363_22fdf8e7/
2021-12-13 01:49:37 +01:00
Martijn Dekker
65feb9641a Fix two more PS2/SIGINT crashing bugs (re: 3023d53b)
*** Crash 1: ***

ksh crashed if the PS1 prompt contains one or more command
substitutions and you enter a multi-line command substitution
on the command line, then interrupt while on the PS2 prompt.

    $ ENV=/./dev/null /usr/local/bin/ksh -o emacs
    $ PS1='$(echo foo) $(echo bar) $(echo baz) ! % '
    foo bar baz 16999 % echo $(
    > true		<-- here, press Ctrl+C instead of Return

    Memory fault

The crash occurred due to a corrupted lexer state while trying to
display the PS1 prompt.

Analysis: My fix for the crashing bug with Ctrl+C in commit
3023d53b is incorrect and only worked accidentally. sh_fault() is
not the right place to reset the lexer state because, when we press
Ctrl+C on a PS2 prompt, ksh had been waiting for input to finish
lexing a multi-line command, so sh_lex() and other lexer functions
are on the function call stack and will be returned to.

src/cmd/ksh93/sh/fault.c: sh_fault():
- Remove incorrect SIGINT fix.

src/cmd/ksh93/sh/io.c: io_prompt():
- Reset the lexer state immediately before printing every PS1
  prompt. Even in situations where this is redundant it should be
  perfectly safe, the overhead is negligible, and it resolves this
  crash. It may pre-empt other problems as well.

*** Crash 2: ***

If an INT trap is set, and you start entering a multi-line command
substitution, then press Ctrl+C on the PS2 prompt to trigger the
crash, the lexer state is corrupted because the lexer is invoked to
eval the trap action. A crash then occurs on entering the final ')'
of the command substitution.

    $ trap 'echo TRAPPED' INT
    $ echo $(
    > trueTRAPPED	<-- press Ctrl+C to output "TRAPPED"
    > )
    Memory fault

Technically, as SIGINT is trapped, it should not interrupt, so ksh
should execute the trap, then continue with the PS2 prompt to let
the user finish inputting the command. But I have been unsuccessful
in many different attempts to make this work properly. I managed to
get multi-line command substitutions to lex correctly by saving and
restoring the lexer state, but command substitutions were still
corrupted at the parser and/or execution level and I have not
managed to trace the cause of that.

My testing showed that all other shells interrupt the PS2 prompt
and return to PS1 when the user presses Ctrl+C, even if SIGINT is
trapped. I think that is a reasonable alternative, and it is
something I managed to make work.

src/cmd/ksh93/sh/fault.c: sh_chktrap():
- Immediately after invoking sh_trap() to run a trap action, check
  if we're in a PS2 prompt (sh.nextprompt == 2). If so, assume the
  lexer state is now overwritten. Closing the fcin stream with
  fcclose() seems to reliably force the lexer to stop doing
  anything else. Then we can just reset the prompt to PS1 and
  invoke sh_exit() to start new command line, which will now reset
  the lexer state as per above.
2021-12-11 04:29:53 +01:00
Martijn Dekker
feedc05037 Reset lexer state on syntax error and on SIGINT (Ctrl+C)
ksh crashed if you pressed Ctrl+C or Ctrl+D on a PS2 prompt while
you haven't finished entering a $(command substitution). It
corrupts subsequent command substitutions. Sometimes the situation
recovers, sometimes the shell crashes.

Simple crash reproducer:

  $ PS1="\$(echo foo) \$(echo bar) \$(echo baz) > "
  foo bar baz > echo $(                        <-- now press Ctrl+D
  > ksh: syntax error: `(' unmatched
  Memory fault

The same happens with Ctrl+C, minus the syntax error message.

The problem is that the lexer state becomes inconsistent when the
lexer is interrupted in the middle of reading a command
substitution of the form $( ... ). This is tracked in the
'lexd.dolparen' variable in the lexer state struct.

Resetting that variable is sufficient to fix this issue. However,
in this commit I prefer to just reinitialise the lexer state
completely to pre-empt any other possible issues. Whether there was
a syntax error or the user pressed Ctrl+C, we just interrupted all
lexing and parsing, so the lexer *should* restart from scratch.

src/cmd/ksh93/sh/fault.c: sh_fault():
- If the shell is in an interactive state (e.g. not a subshell) and
  SIGINT was received, reinitialise the lexer state. This fixes the
  crash with Ctrl+C.

src/cmd/ksh93/sh/lex.c: sh_syntax():
- When handling a syntax error, reset the lexer state. This fixes
  the crash with Ctrl+D.

NEWS:
- Also add the forgotten item for the previous fix (re: 2322f939).
2021-12-09 07:35:12 +01:00
Martijn Dekker
350e52877b Revert "[1.0 release prep] Remove tilde expansion discipline"
This reverts c0334e32, thereby restoring 936a1939.

After the fixes in 0a343244 and a2bc49be, the tilde expansion
disciplines work nicely, so they can come back to the 1.0 branch.
2021-12-09 07:31:37 +01:00
Johnothan King
2b8eaa6609 Fix handling of files without newlines in the head and tail builtins (#365)
The head and tail builtins don't correctly handle files that lack
newlines[*]:
    $ print -n foo > /tmp/bar
    $ /opt/ast/bin/head -1 /tmp/bar  # No output
    $ print -n 'foo\nbar' > /tmp/bar
    $ /opt/ast/bin/tail -1 /tmp/bar
    foo
    bar$
This commit backports the required changes from ksh93v- to handle files
without a newline in the head and tail builtins. (Also note that the
required fix to sfmove was already backported in commit 1bd06207.)

src/lib/libcmd/{head,tail}.c:
- Backport the relevant ksh93v- code for handling files
  without newlines.

src/cmd/ksh93/tests/builtins.sh:
- Add a few regression tests for using 'head -1' and 'tail -1' on a file
  missing and ending newline.

[*]: https://www.illumos.org/issues/4149
2021-12-09 06:43:10 +01:00
Martijn Dekker
b3050769ea Fix 'return' emitting signals; allow arbitrary return values
When a global EXIT trap is set, and a ksh-style function exits with
a status > 256 that could have been the result of a signal, then
the shell incorrectly issues that signal to itself. Depending on
the signal, this causes ksh to terminate itself ungracefully:

    $ cat /tmp/exit267
    trap 'echo OK' EXIT  # This trap triggers the crash
    function foo { return 267; }
    foo
    $ bash /tmp/exit267
    OK
    $ ksh-3aee10d7 /tmp/exit267
    OK
    $ ksh /tmp/exit267
    Memory fault(coredump)

On most systems, status 267 corresponds to SIGSEGV. The reported
memory fault is not real; it results from ksh incorrectly killing
itself with that signal.

The problem is caused by two factors:

1. As of 93u+ 2012-08-01, ksh explicitly allows 'return' to use an
   exit status corresponding to a signal (from 257 to end of signal
   range). The rest of the integer range is trunctated to 8 bits.
   This is contrary to both 'man ksh' and 'return --man' which both
   say it's always truncated to 8 bits. Plus, combined with point 2
   below, this new behaviour is nonsensical, as 'return' has no
   business actually generating signals. However, a couple of
   regression tests now depend on this, as may some scripts.

2. When a ksh-style function does not handle a signal, the signal
   is passed down to the parent environment and ksh does this by
   reissuing the signal to its own process after leaving the
   function scope. However, it does this by checking the exit
   status, which is very bad practice as there is no guarantee
   that an exit status corresponding to a signal was in fact
   produced by a signal, particularly after they changed the
   behaviour of 'return' per 1 above.

This commit fixes both issues. It also takes a proper decision on
allowable 'return' exit status arguments. Since 93u+ was released
nearly a decade ago and some scripts may now rely on being able to
pass certain exit statuses out of the 8-bit range, we should not
disallow this now. But neither should we be half-hearted in
allowing only some arbitrary selection of 9-bit statuses; 'return'
values categorically should have nothing to do with signals, so
this is no basis for limiting them. We're now allowing the full
unsigned integer range, which is usually 32 bits. This is like zsh,
and may create some interesting possibilities for scripts.
Just don't forget that $? will still lose all but its 8 least
significant bits when leaving the current (sub)shell environment.

src/cmd/ksh93/sh/xec.c: sh_funscope():
- Fix passing down unhandled signals from interrupted ksh functions
  (jumpval==SH_JMPFUN) to the parent environment. Do not pay any
  attention to the exit status. Instead, use sh.lastsig (a.k.a.
  shp->lastsig). It is set by sh_fault() in fault.c for just this
  purpose and contains the last signal handled for the current
  command. It is reset in sh_exec() before running any new command.
  So if it contains a signal, that is the one that interrupted the
  ksh function, so it's the correct one to pass down. (Further
  evidence: sh_subshell() was already using this in the same way.)

src/cmd/ksh93/bltins/cflow.c: b_return():
- Allow any signed int return value when invoked as and behaving
  like 'return'.
- Add warning if a passed value is out of int range. Set the exit
  status to 128 in that case; int overflow is undefined behaviour
  in C and we want consistent behaviour across platforms. It should
  be safe enough to check if the long and int values are equal.
- Refactor for clarity.

src/cmd/ksh93/sh/subshell.c: sh_subshell():
- If a function returns with a status out of the 8 bit range in a
  virtual subshell, this status could be passed down to the parent
  shell in full. However, if the subshell forks, then the kernel
  will enforce an 8-bit exit status. That is inconsistent. Scripts
  should not be able to tell the difference between forked and
  non-forked subshells, so artificially enforce that limit here.

Other changed files:
- Documentation updates and copy-edits.
- Update an AT&T functions.sh regress test to allow arbitrary
  integer return values for functions.
- Add regression tests based in part on @JohnoKing's reproducers.
- Rework some vaguely related regression tests to fail gracefully.

Thanks to Johnothan King for the report and the testing.

Fixes: https://github.com/ksh93/ksh/issues/364
2021-12-09 06:41:39 +01:00
Johnothan King
cd8c48cc5a Add the '-e' flag to the 'cd' builtin (#358)
This change adds the -e flag to the cd builtin, as specified in
<https://www.austingroupbugs.net/view.php?id=253>. The -e flag is
used to verify if the the current working directory after 'cd -P'
successfully changes the directory, and returns with exit status 1
if the cwd couldn't be determined. Additionally, it causes all
other errors to return with exit status >1 (i.e., status 2 unless
ENOMEM occurs) if -e and -P are both active.

src/cmd/ksh93/bltins/cd_pwd.c:
- Add -e option to the cd builtin command. It verifies $PWD by
  using test_inode() to execute the equivalent of [[ . -ef $PWD ]].
- The check for restricted mode has been moved after optget to
  allow 'cd -eP' to return with exit status 2 when in restricted
  mode. To avoid changing the previous behavior of cd when -e isn't
  passed, extra checks have been added to prevent cd from printing
  usage information in restricted mode.

src/cmd/ksh93/tests/builtins.sh:
- Add regression tests for the exit status when using the cd -P
  flag with and without -e.

src/cmd/ksh93/data/builtins.c,
src/cmd/ksh93/sh.1:
- Document the addition of -e to the cd builtin.
2021-12-06 06:58:11 +01:00
Johnothan King
f6a6d236f1 Port over illumos fixes for conf.sh (#361)
This commit ports over two of Andy Fiddaman's bugfixes to conf.sh
on illumos:

- The compiler isn't passed on to an invocation of iffe. The bugfix is
  from this commit: <https://github.com/citrus-it/ast/commit/63563232>
- The getconf builtin is missing several parameters on illumos.
  Reproducer:
     $ /opt/ast/bin/getconf ADDRESS_WIDTH
     getconf: Invalid argument (ADDRESS_WIDTH)  # Should output '64'
  This bug occurs because conf.sh expects GNU sed and fails to work
  properly with other sed implementations. The bugfix and original bug
  report can be found here:
  https://www.illumos.org/issues/14044
  https://github.com/citrus-it/ast/commit/ba443cfd
2021-12-05 19:57:15 +01:00
Johnothan King
0a343244c1 nvdisc.c: Fix crash after an error or signal in discipline function (#356)
This patch fixes the crashes experienced when a discipline function
exited because of a signal or an error from a special builtin. The
crashes were caused by ksh entering an inconsistent state after
performing a longjmp away from the assign() and lookup() functions in
nvdisc.c. Fixing the crash requires entering a new context, then setting
a nonlocal goto with sigsetjmp(3). Any longjmps that happen while
running the discipline function will go back to assign/lookup, allowing
ksh to do a proper cleanup afterwards.

Resolves: https://github.com/ksh93/ksh/issues/346
2021-12-05 19:28:09 +01:00
Johnothan King
6904585f49 Port illumos' shell linter improvements (#353)
This commit ports over two improvements to the shell linter from
illumos (original patch written by Andy Fiddaman). Links to the
relevant bug reports and the original patch:
https://www.illumos.org/issues/13601
https://www.illumos.org/issues/13631
c7b656fc71

The first improvement is to the lint warning for arithmetic
operators in [[ ... ]]. The ksh linter now suggests the correct
equivalent operator to use in ((...)). Example:
  $ ksh -nc '[[ 30 -gt 25 ]]'
  # Original warning
  warning: line 1: -gt within [[ ... ]] obsolete, use ((...))
  # New warning
  warning: line 1: [[ ... -gt ... ]] obsolete, use ((... > ...))

The second improvement pertains to variable expansion in arithmetic
expressions. The ksh linter now suggests referencing variable names
directly:
  $ ksh -nc 'integer foo=40; (($foo < 50 ))'
  # Old warning
  warning: line 1: variable expansion makes arithmetic evaluation less efficient
  # New warning
  warning: line 1: in '(($foo < 50))', using '$' is slower and can introduce rounding errors

src/cmd/ksh93/{data/lexstates,sh/lex,sh/parse}.c:
- Port the improved shell lint warnings from illumos to ksh93u+m.
- The original checks for arithmetic operators involved a bunch of
  if statements with inefficient calls to strcmp(3). These were
  replaced with a more efficient switch statement that avoids
  strcmp.
2021-12-05 19:27:16 +01:00
Johnothan King
370440473e Fix KEYBD trap crash when inputting a command substitution (#355)
This change fixes a crash that can occur after setting a KEYBD trap
then inputting a multi-line command substitution. The crash is
similar to issue #347, but it's easier to reproduce since it
doesn't require you to setup a kshrc file. Reproducer for the
crash:

  $ ENV=/./dev/null ksh
  $ trap : KEYBD
  $ : $(
  > true)
  Memory fault(coredump)

The bugfix was backported (with considerable changes) from ksh93v-
2013-10-08. The crash was first reported on the old mailing list:
https://www.mail-archive.com/ast-users@lists.research.att.com/msg00313.html

src/cmd/ksh93/{include/shlex.h,sh/lex.c}:
- To fix this properly, we need sizeof(Lex_t) to work as expected
  in edit.c, but that is thwarted by the _SHLEX_PRIVATE macro in
  lex.c which shlex.h uses to add private structs to the Lex_t type
  in lex.c only. So get rid of that _SHLEX_PRIVATE macro and make
  those members part of the centrally defined struct, renaming them
  to make it clear they're considered private to lex.c.

src/cmd/ksh93/edit/edit.c:
- Now that we can get its size, save and restore the shell lexing
  context when a KEYBD trap is present.

src/cmd/ksh93/tests/pty.sh:
- Add a regression test for the KEYBD trap crash.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2021-11-30 04:27:31 +01:00
Johnothan King
bfad44e56d Fix memory fault on ARM-based Macs (#354)
Ksh segfaults on M1 Macs, apparently because Apple's compiler
optimizer can't cope with overriding bzero(3) with libast's
implementation (though it's nothing more than "memset(b, 0, n);").

Apple has disabled libast's bzero function since 2018-12-04:
https://opensource.apple.com/source/ksh/ksh-27/patches/src__lib__libast__comp__omitted.c.diff.auto.html

src/lib/libast/comp/omitted.c:
- Only fall back to the libast bzero function if the OS lacks an
  implementation of bzero.
- Remove the bzero undef since this fallback is only reached if the
  OS doesn't have bzero.

NEWS:
- Add compat info for macOS on ARM64. This notes that macOS
  Monterey can now compile and run ksh, although there is one
  regression test failure:
        builtins.sh[345]: printf %H: invalid UTF-8 characters
        (expected %3F%C2%86%3F%3F%3F; got %3F%C2%86%3F%3Fv%3F%3F)

Thanks to @DesantBucie for the report and the testing.

Resolves: https://github.com/ksh93/ksh/issues/329
2021-11-29 20:12:57 +01:00
Johnothan King
a0eeb14787 Stop the time keyword overriding errexit (#351)
This bug was first reported in <https://www.illumos.org/issues/7694>.
The time keyword currently overrides the errexit shell option,
allowing failing scripts to continue after an error:

  $ cat 1.sh
  #!/bin/sh
  time false   # This should cause the script to exit
  echo FAILURE
  true
  $ ksh -o errexit 1.sh

  real    0m0.00s
  user    0m0.00s
  sys     0m0.00s
  FAILURE

src/cmd/ksh93/sh/xec.c:
- When the time keyword runs a command, pass the errexit state flag
  to the sh_exec call. This state flag is required for ksh to exit
  when a command fails while the errexit option is on.

src/cmd/ksh93/tests/basic.sh:
- Add a regression test based on the reproducer.
2021-11-29 20:12:15 +01:00
Martijn Dekker
f508660ddf Revert "Fix defining types conditionally and/or in subshells (re: 8ced1daa)"
This reverts commit 2b9cbbbc8e.

This is not ready for prime time. Crashses when running a $PS2
discipline function. This needs fixing and more testing in
development before making it into the 1.0 branch. In the meantime,
that terrible problem with types is back, sorry about that.
2021-11-29 20:08:53 +01:00
Martijn Dekker
2b9cbbbc8e Fix defining types conditionally and/or in subshells (re: 8ced1daa)
This commit mitigates the effects of the hack explained in the
referenced commit so that dummy built-in command nodes added by the
parser for declaration/assignment purposes do not leak out into the
execution level, except in a relatively harmless corner case.

Something like

	if false; then
		typeset -T Foo_t=(integer -i bar)
	fi

will no longer leave a broken dummy Foo_t declaration command. The
same applies to declaration commands created with enum.

The corner case remaining is:

$ ksh -c 'false && enum E_t=(a b c); E_t -a x=(b b a c)'
ksh: E_t: not found

Since the 'enum' command is not executed, this should have thrown
a syntax error on the 'E_t -a' declaration:
ksh: syntax error at line 1: `(' unexpected

This is because the -c script is parsed entirely before being
executed, so E_t is recognised as a declaration built-in at parse
time. However, the 'not found' error shows that it was successfully
eliminated at execution time, so the inconsistent state will no
longer persist.

This fix now allows another fix to be effective as well: since
built-ins do not know about virtual subshells, fork a virtual
subshell into a real subshell before adding any built-ins.

src/cmd/ksh93/sh/parse.c:

- Add a pair of functions, dcl_hactivate() and dcl_dehacktivate(),
  that (de)activate an internal declaration built-ins tree into
  which check_typedef() can pre-add dummy type declaration command
  nodes. A viewpath from the main built-ins tree to this internal
  tree is added, unifying the two for search purposes and causing
  new nodes to be added to the internal tree. When parsing is done,
  we close that viewpath. This hides those pre-added nodes at
  execution time. Since the parser is sometimes called recursively
  (e.g. for command substitutions), keep track of this and only
  activate and deactivate at the first level.

- We also need to catch errors. This is done by setting libast's
  error_info.exit variable to a dcl_exit() function that tidies up
  and then passes control to the original (usually sh_exit()).

- sh_cmd(): This is the most central function in the parser. You'd
  think it was sh_parse(), but $(modern)-form command substitutions
  use sh_dolparen() instead. Both call sh_cmd(). So let's simply
  add a dcl_hacktivate() call at the beginning and a
  dcl_deactivate() call at the end.

- assign(): This function calls path_search(), which among many
  other things executes an FATH search, which may execute arbitrary
  code at parse time (!!!). So, regardless of recursion level,
  forcibly dehacktivate() to avoid those ugly parser side effects
  returning in that context.

src/cmd/ksh93/bltins/enum.c: b_enum():

- Fork a virtual subshell before adding a built-in.

src/cmd/ksh93/sh/xec.c: sh_exec():

- Fork a virtual subshell when detecting typeset's -T option.

Improves fix to https://github.com/ksh93/ksh/issues/256
2021-11-29 09:02:07 +01:00
Johnothan King
84ded2d0c4 Backport the ksh93v- rm builtin to fix 'rm -d' (#348)
The -d flag implemented in the rm builtin is completely broken. No
matter what you do it refuses to remove directories, even if -r is
also passed. Reproducer:

  $ mkdir /tmp/empty
  $ PATH=/opt/ast/bin rm -d /tmp/empty
  rm: /tmp/empty: directory
  $ PATH=/opt/ast/bin rm -dr /tmp/empty
  rm: /tmp/empty: directory not removed [Is a directory]

Additionally, the description of 'rm -d' in the man page contradicts
how it's specified in <https://www.austingroupbugs.net/view.php?id=802>.

The ksh93v- rm builtin fixed nearly all of these issues, so I've
backported it to 93u+m and applied one additional fix for 'rm -rd'.

src/lib/libcmd/rm.c:
- Backported the fixes from the ksh93v- rm builtin's -d flag when
  used on empty directories.
- Backported the man page update for rm(1) from ksh93v-.
- The ksh93v- rm builtin had one additional bug that caused the -r
  option to fail when combined with -d. This was fixed by
  overriding -d if -r is also passed.

src/cmd/ksh93/tests/builtins.sh:
- Add regression tests for the rm builtin's -d option.
2021-11-25 03:52:05 +01:00
Martijn Dekker
214308f81e '.': disable ksh function lookup in POSIX mode
POSIXly, '.' loads only files, not functions.

This only applies to '.', not 'source' (which is not in POSIX).

src/cmd/ksh93/bltins/misc.c: b_source():
- For ksh function lookup, add an additional check that we're not
  in POSIX mode and running the '.' (SYSDOT) builtin.
2021-11-24 09:12:39 +01:00
Martijn Dekker
c0334e32a1 [1.0 release prep] Remove tilde expansion discipline
Defining a .sh.tilde.get or .sh.tilde.set discipline function to
extend tilde expansion works well as long as the discipline
function doesn't get interrupted (e.g. with Crtl+C) or produce an
error message. Either of those will cause the shell to become
unstable and crash.

This feature is now removed from the 1.0 branch as it is not ready
for prime time. It can return to a release branch if/when we manage
to fix it on the master branch.

Related: https://github.com/ksh93/ksh/issues/346
2021-11-24 07:46:58 +01:00
Martijn Dekker
a66cd72f7d arith: implement range checking for enum types
Within arithmetic expressions, enumeration values of variables of a
type created with the 'enum' command translate to index numbers
from 0 to the number of elements minus 1. However, there was no
range checking on this in the arithmetic subsystem, allowing the
assignment of out-of-range values that did not correspond to any
enumeration value.

Variables of an enum type are internally unsigned short integers
(NV_UINT16), like those created with 'integer -su', except with an
additional discipline function (ENUM_disc).

src/cmd/ksh93/bltins/enum.c,
src/cmd/ksh93/include/builtins.h:
- To implement range checking, the arithmetic system needs access
  to the 'nelem' (number of elements) member of 'struct Enum'. This
  is only defined locally in enum.c. We could move that to name.h
  so arith.c can access it, but enum.c has code that supports
  compiling as standalone. So, instead, define a quick extern
  function, b_enum_elem(), that does the necessary type conversion
  and returns a type's number of elements.
- Add --man documentation for the arithmetic subsystem behaviour
  for enum types. Tell the enuminfo() function, which dynamically
  inserts values into the documentation, how to process new \f tags
  'lastv' (the last-defined value) and 'lastn' (the number of the
  last element).

src/cmd/ksh93/sh/arith.c: arith():
- For NV_UINT16 variables with an ENUM_disc discipline, check the
  range using b_enum_elem() and error out if necessary.

Resolves: https://github.com/ksh93/ksh/issues/335
2021-11-23 22:10:40 +01:00
Johnothan King
e26937b36a Add support for 'stty size' to the libcmd 'stty' builtin (#342)
This commit adds support for 'stty size' to the stty builtin, as
defined in <https://austingroupbugs.net/view.php?id=1053>. The size
mode is used to display the terminal's number of rows and columns.
Note that stty isn't included in the default list of builtin
commands; testing this addition requires adding CMDLIST(stty) to
the table of builtins in src/cmd/ksh93/data/builtins.c.

src/lib/libcmd/stty.c:
- Add support for the size mode to the stty builtin. This mode is
  only used to display the terminal's number of rows and columns,
  so error out if any arguments are given that attempt to set the
  terminal size.
2021-11-23 15:38:14 +01:00
Martijn Dekker
8ced1daadf Fix enum type definition pre-parsing for shcomp and dot/source
Parser limitations prevent shcomp or source from handling enum
types correctly:

    $ cat /tmp/colors.sh
    enum Color_t=(red green blue orange yellow)
    Color_t -A Colors=([foo]=red)

    $ shcomp /tmp/colors.sh > /dev/null
    /tmp/colors.sh: syntax error at line 2: `(' unexpected
    $ source /tmp/colors.sh
    /bin/ksh: source: syntax error: `(' unexpected

Yet, for types created using 'typeset -T', this works. This is done
via a check_typedef() function that preliminarily adds the special
declaration builtin at parse time, with details to be filled in
later at execution time.

This hack will produce ugly undefined behaviour if the definition
command creating that type built-in is then not actually run at
execution time before the type built-in is accessed.

But the hack is necessary because we're dealing with a fundamental
design flaw in the ksh language. Dynamically addable built-ins that
change the syntactic parsing of the shell language on the fly are
an absurdity that violates the separation between parsing and
execution, which muddies the waters and creates the need for some
kind of ugly hack to keep things like shcomp more or less working.

This commit extends that hack to support enum.

src/cmd/ksh93/sh/parse.c:
- check_typedef():
  - Add 'intypeset' parameter that should be set to 1 for typeset
    and friends, 2 for enum.
  - When processing enum arguments, use AST getopt(3) to skip over
    enum's options to find the name of the type to be defined.
    (getopt failed if we were running a -c script; deal with this
    by zeroing opt_info.index first.)
- item(): Update check_typedef() call, passing lexp->intypeset.
- simple(): Set lexp->intypeset to 2 when processing enum.

The rest of the changes are all to support the above and should be
fairly obvious, except:

src/cmd/ksh93/bltins/enum.c:
- enuminfo(): Return on null pointer, avoiding a crash upon
  executing 'Type_t --man' if Type_t has not been fully defined due
  to the definition being pre-added at parse time but not executed.
  It's all still wrong, but a crash is worse.

Resolves: https://github.com/ksh93/ksh/issues/256
2021-11-21 17:43:55 +01:00
Johnothan King
e554a07c56 typeset -T shouldn't list types created with enum (#340)
Listing types with 'typeset -T' will list not only types created with
typeset, but also types created with enum. However, the types created
by enum are not displayed correctly in the resulting output:

$ enum Foo_t=(foo bar)
$ typeset -T
typeset -T Foo_t
typeset -T Foo_t=fo)

The fix for this bug was backported from ksh93v- 2013-10-08.

src/cmd/ksh93/sh/nvtype.c:
- sh_outtype(): Skip over enums when listing types with 'typeset -T'.
2021-11-20 09:48:48 +01:00
Johnothan King
396b388e1f Fix a few issues with $RANDOM seeding in subshells (#339)
This commit fixes an issue I found in the subshell $RANDOM
reseeding code.

The main issue is a performance regression in the shbench fibonacci
benchmark, introduced in commit af6a32d1. Performance dropped in
this benchmark because $RANDOM is always reseeded and restored,
even when it's never used in a subshell. Performance results from
before and after this performance fix (results are on Linux with
CC=gcc and CCFLAGS='-O2 -D_std_malloc'):

  $ ./shbench -b bench/fibonacci.ksh -l 100 ./ksh-0f06a2e ./ksh-af6a32d ./ksh-f31e368 ./ksh-randfix

  benchmarking ./ksh-0f06a2e, ./ksh-af6a32d, ./ksh-f31e368, ./ksh-randfix ...
  *** fibonacci.ksh ***
  # ./ksh-0f06a2e  # Recent version of ksh93u+m
  # ./ksh-af6a32d  # Commit that introduced the regression
  # ./ksh-f31e368  # Commit without the regression
  # ./ksh-randfix  # Ksh93u+m with this patch applied

  -------------------------------------------------------------------------------------------------
  name           ./ksh-0f06a2e        ./ksh-af6a32d        ./ksh-f31e368        ./ksh-randfix
  -------------------------------------------------------------------------------------------------
  fibonacci.ksh  0.481 [0.459-0.515]  0.472 [0.455-0.504]  0.396 [0.380-0.442]  0.407 [0.385-0.439]
  -------------------------------------------------------------------------------------------------

src/cmd/ksh93/include/variables.h,
src/cmd/ksh93/sh/{init,subshell}.c:
- Rather than reseed $RANDOM every time a subshell is created, add
  a sh_save_rand_seed() function that does this only when the
  $RANDOM variable is used in a subshell. This function is called
  by the $RANDOM discipline functions nget_rand() and put_rand().
  As a minor optimization, sh_save_rand_seed doesn't reseed if it's
  called from put_rand().
- Because $RANDOM may have a seed of zero (i.e., RANDOM=0),
  sp->rand_seed isn't enough to tell if $RANDOM has been reseeded.
  Add sp->rand_state for this purpose.
- sh_subshell(): Only restore the former $RANDOM seed and state if
  it is necessary to prevent a subshell leak.

src/cmd/ksh93/tests/variables.sh:
- Add two regression tests for bugs I ran into while making this
  patch.
2021-11-19 08:18:44 +01:00
Martijn Dekker
bd9752e43c Backport 'printf -v' from ksh 93v-
'printf' on bash and zsh has a popular -v option that allows
assigning formatted output directly to variables without using a
command substitution. This is much faster and avoids snags with
stripping final linefeeds. AT&T had replicated this feature in the
abandoned 93v- beta version. This backports it with a few tweaks
and one user-visible improvement.

The 93v- version prohibited specifying a variable name with an
array subscript, such as printf -v var\[3\] foo. This works fine on
bash and zsh, so I see no reason why this should not work on ksh,
as nv_putval() deals with array subscripts just fine.

src/cmd/ksh93/bltins/print.c: b_print():
- While processing the -v option when called as printf, get a
  pointer to the variable, creating it if necessary. Pass only the
  NV_VARNAME flag to enforce a valid variable name, and not (as
  93v- does) the NV_NOARRAY flag to prohibit array subscripts.
- If a variable was given, set the output file to an internal
  string buffer and jump straight to processing the format.
- After processing the format, assign the contents to the string
  buffer to the variable.

src/cmd/ksh93/data/builtins.c:
- Document the new option, adding a warning that unquoted square
  brackets may trigger pathname expansion.
2021-11-19 03:54:33 +01:00
Martijn Dekker
c734568b02 arithmetic: Fix the octal leading zero mess (#337)
In C/POSIX arithmetic, a leading 0 denotes an octal number, e.g.
010 == 8. But this is not a desirable feature as it can cause
problems with processing things like dates with a leading zero.
In ksh, you should use 8#10 instead ("10" with base 8).

It would be tolerable if ksh at least implemented it consistently.
But AT&T made an incredible mess of it. For anyone who is not
intimately familiar with ksh internals, it is inscrutable where
arithmetic evaluation special-cases a leading 0 and where it
doesn't. Here are just some of the surprises/inconsistencies:

1. The AT&T maintainers tried to honour a leading 0 inside of
   ((...)) and $((...)) and not for arithmetic contexts outside it,
   but even that inconsistency was never quite consistent.

2. Since 2010-12-12, $((x)) and $(($x)) are different:
      $ /bin/ksh -c 'x=010; echo $((x)) $(($x))'
      10 8
   That's a clear violation of both POSIX and the principle of
   least astonishment. $((x)) and $(($x)) should be the same in
   all cases.

3. 'let' with '-o letoctal' acts in this bizarre way:
      $ set -o letoctal; x=010; let "y1=$x" "y2=010"; echo $y1 $y2
      10 8
   That's right, 'let y=$x' is different from 'let y=010' even
   when $x contains the same string value '010'! This violates
   established shell grammar on the most basic level.

This commit introduces consistency. By default, ksh now acts like
mksh and zsh: the octal leading zero is disabled in all arithmetic
contexts equally. In POSIX mode, it is enabled equally.

The one exception is the 'let' built-in, where this can still be
controlled independently with the letoctal option as before (but,
because letoctal is synched with posix when switching that on/off,
it's consistent by default).

We're also removing the hackery that causes variable expansions for
the 'let' builtin to be quietly altered, so that 'x=010; let y=$x'
now does the same as 'let y=010' even with letoctal on.

Various files:
- Get rid of now-redundant sh.inarith (shp->inarith) flag, as we're
  no longer distinguishing between being inside or outside ((...)).

src/cmd/ksh93/sh/arith.c:
- arith(): Let disabling POSIX octal constants by skipping leading
  zeros depend on either the letoctal option being off (if we're
  running the "let" built-in") or the posix option being off.
- sh_strnum(): Preset a base of 10 for strtonll(3) depending on the
  posix or letoctal option being off, not on the sh.inarith flag.

src/cmd/ksh93/include/argnod.h,
src/cmd/ksh93/sh/args.c,
src/cmd/ksh93/sh/macro.c:
- Remove astonishing hackery that violated shell grammar for 'let'.

src/cmd/ksh93/sh/name.c (nv_getnum()),
src/cmd/ksh93/sh/nvdisc.c (nv_getn()):
- Remove loops for skipping leading zeroes that included a broken
  check for justify/zerofill attributes, thereby fixing this bug:
	$ typeset -Z x=0x15; echo $((x))
	-ksh: x15: parameter not set
  Even if this code wasn't redundant before, it is now: sh_arith()
  is called immediately after the removed code and it ignores
  leading zeroes via sh_strnum() and strtonll(3).

Resolves: https://github.com/ksh93/ksh/issues/334
2021-11-17 04:28:08 +01:00
Johnothan King
b40155fae8 Fix file descriptor leaks in the hist builtin (#336)
This commit fixes two file descriptor leaks in the hist built-in.
The bugfix for the first file descriptor leak was backported from
ksh2020. See:
https://github.com/att/ast/issues/872
https://github.com/att/ast/commit/73bd61b5

Reproducer:
  $ echo no
  $ hist -s no=yes

The second file descriptor leak occurs after a substitution error
in the hist built-in (this leak wasn't fixed in ksh2020).
Reproducer:
  $ echo no
  $ ls /proc/$$/fd
  $ hist -s no=yes
  $ hist -s no=yes
  $ ls /proc/$$/fd

src/cmd/ksh93/bltins/hist.c:
- Close leftover file descriptors when an error occurs and after
  'hist -s' runs a command.

src/cmd/ksh93/tests/builtins.sh:
- Add two regression tests for both of the file descriptor leaks.
2021-11-16 23:34:46 +01:00
Martijn Dekker
56c2e13e92 arith: Fix variables 'nan' and 'inf' in arithmetic for POSIX mode
The --posix compliance option now disables the case-insensitive
special floating point constants Inf and NaN so that all case
variants of $((inf)) and $((nan)) refer to the variables by those
names as the standard requires. (BUG_ARITHNAN)

src/cmd/ksh93/sh/arith.c: arith():
- Only do case-insensitive checks for "Inf" and "NaN" if the POSIX
  option is off.
2021-11-15 21:16:23 +01:00
Martijn Dekker
a4375f3090 Fix crash on unsetting .sh.match
ksh crashed after unsetting .sh.match and then matching a pattern:

$ unset .sh.match
$ [[ bar == ba* ]]
Memory fault

src/cmd/ksh93/sh/init.c: sh_setmatch():
- Do nothing if we cannot get an array pointer to SH_MATCHNOD.
2021-11-15 21:15:08 +01:00
Martijn Dekker
d9f1fdaa41 Fix [ \( str -a str \) ], [ \( str -o str \) ]
Symptoms:

$ test \( string1 -a string2 \)
/usr/local/bin/ksh: test: argument expected
$ test \( string1 -o string2 \)
/usr/local/bin/ksh: test: argument expected

The parentheses should be irrelevant and this should be a test for
the non-emptiness of string1 and/or string2.

src/cmd/ksh93/bltins/test.c:

- b_test(): There is a block where the case of 'test' with five or
  less arguments, the first and last one being parentheses, is
  special-cased. The parentheses are removed as a workaround: argv
  is increased to skip the opening parenthesis and argc is
  decreased by 2. However, there is no corresponding increase of
  tdata.av which is a copy of this function's argv. This renders
  the workaround ineffective. The fix is to add that increase.

- e3(): Do not handle '!' as a negator if not followed by an
  argument. This allows a right-hand expression that is equal to
  '!' (i.e. a test for the non-emptiness of the string '!').
2021-11-15 02:44:56 +01:00
Martijn Dekker
c81473061a test/[: binary operators: fix '<' and add '=~'; some more cleanups
In ksh88, the test/[ built-in supported both the '<' and '>'
lexical sorting comparison operators, same as in [[. However, in
every version of ksh93, '<' does not work though '>' still does!

Still, the code for both is present in test_binop():

src/cmd/ksh93/bltins/test.c
548:		case TEST_SGT:
549:			return(strcoll(left, right)>0);
550:		case TEST_SLT:
551:			return(strcoll(left, right)<0);

Analysis: The binary operators are looked up in shtab_testops[] in
data/testops.c using a macro called sh_lookup, which expands to a
sh_locate() call. If we examine that function in sh/string.c, it's
easy to see that on systems using ASCII (i.e. all except IBM
mainframes), it assumes the table is sorted in ASCII order.

src/cmd/ksh93/sh/string.c
64:	while((c= *tp->sh_name) && (CC_NATIVE!=CC_ASCII || c <= first))

The problem was that the '<' operator was not correctly sorted in
shtab_testops[]; it was sorted immediately before '>', but after
'='. The ASCII order is: < (60), = (61), > (62). This caused '<' to
never be found in the table.

The test_binop() function is also used by [[, yet '<' always worked
in that. This is because the parser has code that directly checks
for '<' and '>' within [[ (in sh/parse.c, lines 1949-1952).

This commit also adds '=~' to 'test', which took three lines of
code and allowed eliminating error handling in test_binop() as
test/[ and [[ now support the same binary ops. (re: fc2d5a60)

src/cmd/ksh93/*/*.[ch]:
- Rename a couple of very misleadingly named macros in test.h:
  . For == and !=, the TEST_PATTERN bit is off for pattern compares
    and on for literal string compares! Rename to TEST_STRCMP.
  . The TEST_BINOP bit does not denote all binary operators, but
    only the logical -a/-o ops in test/[. Rename to TEST_ANDOR.

src/cmd/ksh93/bltins/test.c: test_binop():
- Add support for =~. This is only used by test/[. The method is
  implemented in two lines that convert the ERE to a shell pattern
  by prefixing it with ~(E), then call test_strmatch with that
  temporary string to match the ERE and update ${.sh.match}.
- Since all binary ops from shtab_testops[] are now accounted for,
  remove unknown op error handling from this function.

src/cmd/ksh93/data/testops.c:
- shtab_testops[]:
  . Correctly sort the '<' (TEST_SLT) entry.
  . Remove ']]' (TEST_END). It's not an op and doesn't belong here.
- Update sh_opttest[] documentation with =~, \<, \>.
- Remove now-unused e_unsupported_op[] error message.

src/cmd/ksh93/sh/lex.c: sh_lex():
- Check for ']]' directly instead of relying on the removed
  TEST_END entry from shtab_testops[].

src/cmd/ksh93/tests/bracket.sh:
- Add relevant tests.

src/cmd/ksh93/tests/builtins.sh:
- Fix an old test that globally deleted the 'test' builtin. Delete
  it within the command substitution subshell only.
- Remove the test for non-support of =~ in test/[.
- Update the test for invalid test/[ op to use test directly.
2021-11-14 02:46:34 +01:00
Martijn Dekker
6f5c9fea93 test/[: Fix binary -a/-o operators in POSIX mode
POSIX requires
	test "$a" -a "$b"
to return true if both $a and $b are non-empty, and
	test "$a" -o "$b"
to return true if either $a or $b is non-empty.

In ksh, this fails if "$a" is '!' or '(' as this causes ksh to
interpret the -a and -o as unary operators (-a being a file
existence test like -e, and -o being a shell option test).

$ test ! -a ""; echo "$?"
0		(expected: 1/false)
$ set -o trackall; test ! -o trackall; echo "$?"
1		(expected: 0/true)
$ test \( -a \); echo "$?"
ksh: test: argument expected
2		(expected: 0/true)
$ test \( -o \)
ksh: test: argument expected
2		(expected: 0/true)

Unfortunately this problem cannot be fixed without risking breakage
in legacy scripts. For instance, a script may well use
	test ! -a filename
to check that a filename is nonexistent. POSIX specifies that this
always return true as it is a test for the non-emptiness of both
strings '!' and 'filename'.

So this commit fixes it for POSIX mode only.

src/cmd/ksh93/bltins/test.c: e3():
- If the posix option is active, specially handle the case of
  having at least three arguments with the second being -a or -o,
  overriding their handling as unary operators.

src/cmd/ksh93/data/testops.c:
- Update 'test --man --' date and say that unary -a is deprecated.

src/cmd/ksh93/sh.1:
- Document the fix under the -o posix option.
- For test/[, explain that binary -a/-o are deprecated.

src/cmd/ksh93/tests/bracket.sh:
- Add tests based on reproducers in bug report.

Resolves: https://github.com/ksh93/ksh/issues/330
2021-11-13 03:43:29 +01:00
Martijn Dekker
09a8a279f2 Fix bug on closed stdout; improve BUG_PUTIOERR fix (re: 93e15a30)
Stéphane Chazelas reported:

> As noted in this austin-group-l discussion[*] (relevant to this
> issue):
>
>   $ ksh93u+m -c 'pwd; echo "$?" >&2; echo test; echo "$?" >&2' >&-
>   0
>   1
>   /home/chazelas
>
> when stdout is closed, pwd does claim it succeeds (by returning a
> 0 exit status), while echo doesn't (not really relevant to the
> problem here, only to show it doesn't affect all builtins), and
> the output that pwd failed to write earlier ends up being written
> on stderr here instead of stdout upon exit (presumably) because
> of that >&2 redirection.
>
> strace shows ksh93 attempting write(1, "/home/chazelas\n", 15) 6
> times (1, the last one, successful).
>
> It gets even weirder when redirecting to a file:
>
>   $ ksh93u+m -c 'pwd; echo "$?" >&2; echo test; echo "$?" > file' >&-
>   0
>   $ cat file
>   1
>   1
>   ome/chazelas

In my testing, the problem does not occur when closing stdout at
the start of the -c script itself (using redirect >&- or exec >&-);
it only occurs if stdout was closed before initialising the shell.

That made me suspect that the problem had to do with an
inconsistent file descriptor state in the shell. ksh uses internal
sh_open() and sh_close() functions, among others, to maintain that
state.

src/cmd/ksh93/sh/main.c: sh_main():
- If the shell is initialised with stdin, stdout or stderr closed,
  then make the shell's file descriptor state tables reflect that
  fact by calling sh_close() for the closed file descriptors.

This commit also improves the BUG_PUTIOERR fix from 93e15a30. Error
checking after sfsync() is not sufficient. For instance, on
FreeBSD, the following did not produce a non-zero exit status:
  ksh -c 'echo hi' >/dev/full
even though this did:
  ksh -c 'echo hi >/dev/full'
Reliable error checking requires not only checking the result of
every SFIO command that writes output, but also synching the buffer
at the end of the operation and checking the result of that.

src/cmd/ksh93/bltins/print.c:
- Make exitval variable global to allow functions called by
  b_print() to set a nonzero exit status.
- Check the result of all SFIO output commands that write output.
- b_print(): Always sfsync() at the end, except if the s (history)
  flag was given. This allows getting rid of the sfsync() call that
  required the workaround introduced in 846ad932.

[*] https://www.mail-archive.com/austin-group-l@opengroup.org/msg08056.html

Resolves: https://github.com/ksh93/ksh/issues/314
2021-11-07 15:44:06 +00:00
Martijn Dekker
7b5b0a5d54 Fix octal number arguments in printf integer arithmetic
Bug 1: POSIX requires numbers used as arguments for all the %d,
%u... in printf to be interpreted as in the C language, so
	printf '%d\n' 010
should output 8 when the posix option is on. However, it outputs 10.

This bug was introduced as a side effect of a change introduced in
the 2012-02-07 version of ksh 93u+m, which caused the recognition
of leading-zero numbers as octal in arithmetic expressions to be
disabled outside ((...)) and $((...)). However, POSIX requires
leading-zero octal numbers to be recognised for printf, too.

The change in question introduced a sh.arith flag that is set while
we're processing a POSIX arithmetic expression, i.e., one that
recognises leading-zero octal numbers.
Bug 2: Said flag is not reset in a command substitution used within
an arithmetic expression. A command substitution should be a
completely new context, so the following should both output 10:

$ ksh -c 'integer x; x=010; echo $x'
10            # ok; it's outside ((…)) so octals are not recognised
$ ksh -c 'echo $(( $(integer x; x=010; echo $x) ))'
8             # bad; $(comsub) should create new non-((…)) context

src/cmd/ksh93/bltins/print.c: extend():
- For the u, d, i, o, x, and X conversion modifiers, set the POSIX
  arithmetic context flag before calling sh_strnum() to convert the
  argument. This fixes bug 1.

src/cmd/ksh93/sh/subshell.c: sh_subshell():
- When invoking a command substitution, save and unset the POSIX
  arithmetic context flag. Restore it at the end. This fixes bug 2.

Reported-by: @stephane-chazelas
Resolves: https://github.com/ksh93/ksh/issues/326
2021-09-13 04:57:37 +02:00
Martijn Dekker
bdc3069bfd Fix 'ps' output for hashbangless scripts on Linux/macOS
When invoking a script without an interpreter (#!hashbang) path,
ksh forks, but there is no exec syscall in the child. The existing
command line is overwritten in fixargs() with the name of the new
script and associated arguments. In the generic/fallback version of
fixargs() which is used on Linux and macOS, if the new command line
is longer than the existing one, it is truncated. This works well
when calling a script with a shorter name.

However, it generates a misleading name in the common scenario
where a script is invoked from an interactive shell, which
typically has a short command line. For instance, if "/tmp/script"
is invoked, "ksh" gets replaced with "/tm" in "ps" output.

A solution is found in the fact that, on these systems, the
environment is stored immediately after the command line arguments.
This space can be made available for use by a longer command line
by moving the environment strings out of the way.

src/cmd/ksh93/sh/main.c: fixargs():
- Refactor BSD setproctitle(3) version to be more self-contained.
- In the generic (Linux/macOS) version, on init (i.e. mode==0), if
  the command line is smaller than 128 bytes and the environment
  strings have not yet been moved (i.e. if they still immediately
  follow the command line arguments in memory), then strdup the
  environment strings, pointing the *environment[] members to the
  new strings and adding the length of the strings to the maximum
  command line buffer size.

Reported-by: @gkamat
Resolves: https://github.com/ksh93/ksh/pull/300
2021-09-12 05:34:52 +02:00
Martijn Dekker
a2196f9434 Fix backtick comsubs by making them act like $(modern) ones
ksh93 currently has three command substitution mechanisms:
- type 1: old-style backtick comsubs that use a pipe;
- type 3: $(modern) comsubs that use a temp file, currently with
  fallback to a pipe if a temp file cannot be created;
- type 2: ${ shared-state; } comsubs; same as type 3, but shares
  state with parent environment.

Type 1 is buggy. There are at least two reproducers that make it
hang. The Red Hat patch applied in 4ce486a7 fixed a hang in
backtick comsubs but reintroduced another hang that was fixed in
ksh 93v-. So far, no one has succeeded in making pipe-based comsubs
work properly.

But, modern (type 3) comsubs use temp files. How does it make any
sense to have two different command substitution mechanisms at the
execution level? The specified functionality between backtick and
modern command substitutions is exactly the same; the difference
*should* be purely syntactic.

So this commit removes the type 1 comsub code at the execution
level, treating them all like type 3 (or 2). As a result, the
related bugs vanish while the regression tests all pass.

The only side effect that I can find is that the behaviour of bug
https://github.com/ksh93/ksh/issues/124 changes for backtick
comsubs. But it's broken either way, so that's neutral.

So this commit can now be added to my growing list of ksh93 issues
fixed by simply removing code.

src/cmd/ksh93/sh/xec.c:
- Remove special code for type 1 comsubs from iousepipe(),
  sh_iounpipe(), sh_exec() and _sh_fork().

src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/sh/subshell.c:
- Remove pipe support from sh_subtmpfile(). This also removes the
  use of a pipe as a fallback for $(modern) comsubs. Instead, panic
  and error out if temp file creation fails. If the shell cannot
  create a temporary file, there are fatal system problems anyway
  and a script should not continue.
- No longer pass comsub type to sh_subtmpfile().

All other changes:
- Update sh_subtmpfile() calls.

src/cmd/ksh93/tests/subshell.sh:
- Add two regression tests based on reproducers from bug reports.

Resolves: https://github.com/ksh93/ksh/issues/305
Resolves: https://github.com/ksh93/ksh/issues/316
2021-08-13 09:14:11 +02:00
Martijn Dekker
d25dbcc1ef [[ ... ]]: fix '!' to negate another '!'
Bug: [[ ! ! 1 -eq 1 ]] returns false, but should return true.

This bug was reported for bash, but ksh has it too:
https://lists.gnu.org/archive/html/bug-bash/2021-06/msg00006.html

Op 24-05-21 om 17:47 schreef Chet Ramey:
> On 5/22/21 2:45 PM, Vincent Menegaux wrote:
>> Previously, these commands:
>>
>>    [[ ! 1 -eq 1 ]]; echo $?
>>    [[ ! ! 1 -eq 1 ]]; echo $?
>>
>> would both result in `1', since parsing `!' set CMD_INVERT_RETURN
>> instead of toggling it.
>
> Interestingly, ksh93 produces the same result as bash. I agree
> that it's more intuitive to toggle it.

Also interesting is that '!' as an argument to the simple
'test'/'[' command does work as expected (on both bash and ksh93):
'test ! ! 1 -eq 1' and '[ ! ! 1 -eq 1 ]' return 0/true.

Even the man page for [[ is identical for bash and ksh93:

|               ! expression
|                      True if expression is false.

This suggests it's supposed to be a logical negation operator, i.e.
'!' is implicitly documented to negate another '!'. Bolsky & Korn's
1995 ksh book, p. 167, is slightly more explicit about it:
"! test-expression. Logical negation of test-expression."

I also note that multiple '!' negators in '[[' work as expected on
mksh, yash and zsh.

src/cmd/ksh93/sh/parse.c: test_primary():
- Fix bitwise logic for '!': xor the TNEGATE bit into tretyp
  instead of or'ing it, which has the effect of toggling it.
2021-06-03 15:57:16 +02:00
Martijn Dekker
0dd115e4b4 Fix shell exit on function call redirection error (re: 23f2e23)
This regression also exists on ksh 93v- and ksh2020, from which it
was backported.

Reproducer:

$ (fn() { true; }; fn >/dev/null/ne; true) 2>/dev/null; echo $?
1

Expected output: 0 (as on ksh 93u+).

FreeBSD sh and NetBSD sh are the only other known shells that share
this behaviour. POSIX currently allows both behaviours, but may
require the ksh 93u+ behaviour in future. In any case, this causes
an incompatibility with established ksh behaviour that could easily
break existing ksh scripts.

src/cmd/ksh93/sh/xec.c: sh_exec():
- Commit 23f2e23 introduced a check for jmpval > SH_JMPIO (5).
  When a function call pushes context for a redirection, this is
  done with the jmpval exit value of SH_JMPCMD (6). Change that to
  SH_JMPIO to avoid triggering that check.

src/cmd/ksh93/tests/exit.sh:
- Add regression tests for exit behaviour on various kinds of
  shell errors as listed in the POSIX standard, including an error
  in a redirection of a function call.

Fixes: https://github.com/ksh93/ksh/issues/310
2021-05-19 06:59:18 +02:00
Martijn Dekker
e5e1d4b53e Decrease SHLVL before doing 'exec' from main shell
Problem:

$ exec ksh
$ echo $SHLVL
2
$ exec ksh
$ echo $SHLVL
3
$ exec ksh
$ echo $SHLVL
4

...etc. SHLVL is supposed to acount the number of shell processes
that you need to exit before you get logged out. Since ksh was
replacing itself with a new shell in the same process using 'exec',
SHLVL should not increase.

src/cmd/ksh93/bltins/misc.c: b_exec():
- When about to replace the shell and we're not in a subshell,
  decrease SHLVL to cancel out a subsequent increase by the
  replacing shell. Bash and zsh also do this.
2021-05-19 00:08:12 +02:00
Martijn Dekker
53f4bc6a53 Re-fix 'test -t 1' in command substitutions (re: 090b65e7)
Since a command substitution no longer forks on non-permanently
redirecting standard output within it for a specific command,
test -t 1, [ -t 1 ], and [[ -t 1 ]] broke as follows:
v=$(test -t 1 >/dev/tty && echo ok) did not assign 'ok' to v.
This is because the assumption in tty_check() that standard output
is never on a terminal in a non-forked command substitution, added
in 55f0f8ce, was made invalid by 090b65e7.

src/cmd/ksh93/edit/edit.c: tty_check():
- Implement a new method. Return false if the file descriptor
  stream is of type SF_STRING, which is the case for non-forked
  command substitutions -- it means the sfio stream writes directly
  into a memory area. This can be checked with the sfset(3)
  function (see src/lib/libast/man/sfio.3). To avoid a segfault
  when accessing sh.sftable, we need to validate the FD first.

src/cmd/ksh93/tests/pty.sh:
- Add the above reproducer.
2021-05-14 04:52:18 +02:00
Martijn Dekker
246062ff0b Release 1.0.0-beta.1
In May 2020, when every KornShell (ksh93) development project was
abandoned, development was rebooted in a new fork based on the last
stable AT&T version: ksh 93u+. Now, one year and hundreds of bug
fixes later, the first beta version is ready, and KornShell lives
again. This new fork is called ksh 93u+m as a permanent nod to its
origin; a standard semantic version number is added starting at
1.0.0-beta.1. Please test the beta and report any bugs you find,
or help us fix known bugs.
2021-05-10 18:42:42 +02:00
hyenias
92f7ca5423
Back port ksh93v- float, int, and exp10 changes from math.tab (#299)
src/cmd/ksh93/data/math.tab:
- Added exp10().
- Remove int() as being an alias to floor().
- Created entries for local float() and local int() which are
  defined in features/math.sh.

src/cmd/ksh93/features/math.sh:
- Backport floor() and int() related code from ksh93v-.

src/cmd/ksh93/sh.1:
- Sync man page to math.tab's potential functions.
2021-05-08 04:43:37 +01:00
Martijn Dekker
a197b0427a Fix two more 'command' bugs
BUG 1: Though 'command' is specified/documented as a regular
builtin, preceding assignments survive the invocation (as with
special or declaration builtins) if 'command' has no command
arguments in these cases:

$ foo=wrong1 command; echo $foo
wrong1
$ foo=wrong2 command -p; echo $foo
wrong2
$ foo=wrong3 command -x; echo $foo
wrong3

Analysis: sh_exec(), case TCOM (simple command), contains the
following loop that skips over 'command' prefixes, preparsing any
options and remembering the offset in the 'command' variable:

src/cmd/ksh93/sh/xec.c
1059 while(np==SYSCOMMAND || !np && com0
     && nv_search(com0,shp->fun_tree,0)==SYSCOMMAND)
1060 {
1061         register int n = b_command(0,com,&shp->bltindata);
1062         if(n==0)
1063                 break;
1064         command += n;
1065         np = 0;
1066         if(!(com0= *(com+=n)))
1067                 break;
1068         np = nv_bfsearch(com0, shp->bltin_tree, &nq, &cp);
1069 }

This skipping is not done if the preliminary b_command() call on
line 1061 (with argc==0) returns zero. This is currently the case
for command -v/-V, so that 'command' is treated as a plain and
regular builtin for those options.

The cause of the bug is that this skipping is even done if
'command' has no arguments. So something like 'foo=bar command' is
treated as simply 'foo=bar', which of course survives.

So the fix is for b_command() to return zero if there are no
arguments. Then b_command() itself needs changing to not error out
on the second/main b_command() call if there are no arguments.

src/cmd/ksh93/bltins/whence.c: b_command():
- When called with argc==0, return a zero offset not just for -v
  (X_FLAG) or -V (V_FLAG), but also if there are no arguments left
  (!*argv) after parsing options.
- When called with argc>0, do not issue a usage error if there are
  no arguments, but instead return status 0 (or, if -v/-V was given,
  status 2 which was the status of the previous usage message).
  This way, 'command -v $emptyvar' now also works as you'd expect.

BUG 2: 'command -p' sometimes failed after executing certain loops.

src/cmd/ksh93/sh/path.c: defpath_init():
- astconf() returns a pointer to memory that may be overwritten
  later, so duplicate the string returned. Backported from ksh2020.
  (re: f485fe0f, aa4669ad, <https://github.com/att/ast/issues/959>)

src/cmd/ksh93/tests/builtins.sh:
- Update the test for BUG_CMDSPASGN to check every variant of
  'command' (all options and none; invoking/querying all kinds of
  command and none) with a preceding assignment. (re: fae8862c)
  This also covers bug 2 as 'command -p' was failing on macOS prior
  to the fix due to a loop executed earlier in another test.
2021-05-05 02:43:18 +01:00
hyenias
642a105351
Fix arithmetic assignment operations for multidimensional indexed arrays (#296)
This PR corrects #168 for indexed arrays having more than one
level. Turns out ksh was only keeping track of the subscript number
for assignment in lvalue's nosub variable. By saving the actual
subscript reference, the result can be assigned to its proper
destination instead of putting the result into the last looked
value or subscript location.

src/cmd/ksh93/include/streval.h: struct lval:
- Create a new pointer named sub to hold the reference that nosub
  describes.

src/cmd/ksh93/sh/arith.c: arith():
- Adjust LOOKUP: for lvalue ARITH_ASSIGNOP operations on indexed
  arrays to save the np of the destination subscript for later use.
- Adjust ASSIGN: to act when lvalue's nosub > 0 which happens as
  the last step in the arithmetic parsing loop for assignment
  operations. Only indexed arrays will have a nosub value > 0. All
  others have a nosub of 0 unless they are involved in a unary
  operation (++, --) which sets nosub to -1. All said in the
  context of assignment operations like (( arr[0][1] += 1 )).

src/cmd/ksh93/sh/streval.c:
- Initialize the new sub pointer to 0.

src/cmd/ksh93/tests/arrays2.sh:
- Created a few multidimensional indexed array tests for assignment
  operations like += as an example.

Resolves: https://github.com/ksh93/ksh/issues/168
2021-05-04 03:13:14 +01:00
Martijn Dekker
d309d604e7 POSIX: 'command': don't disable declaration proprts (re: b9d10c5a)
Following the resolution of Austin Group bug 1393[*] that is set to
be included in the next version of the POSIX standard, the
'command' prefix in POSIX mode (set -o posix) no longer disables
the declaration properties of declaration built-ins.
[*] https://austingroupbugs.net/view.php?id=1393

src/cmd/ksh93/sh/parse.c: lex():
- Skip the 'command' prefix even in POSIX mode so that any
  declaration commands prefixed by it are treated as such in xec.c
  (sh_exec()).

src/cmd/ksh93/sh/xec.c: sh_exec():
- The foregoing change reintroduced a variant of BUG_CMDSPEXIT: the
  shell exits on something like 'command export readonlyvar=foo'.
  This now fixes that bug for both POSIX and non-POSIX mode. When
  calling nv_setlist() to process true shell assignments, and there
  is a 'command' prefix, push a shell context and use sigsetjmp to
  intercept any errors in assignments and stop the shell exiting.

src/cmd/ksh93/tests/builtins.sh:
- Borrow the BUG_CMDSPEXIT regression test from modernish and adapt
  it for ksh. (I'm the author so yes, I can do this.) Original:
  https://github.com/modernish/modernish/blob/ae8fe9c3/lib/modernish/tst/builtin.t#L80-L109
2021-05-04 00:52:10 +01:00
Martijn Dekker
af6a32d14f
Fix $RANDOM to act consistently in subshells (#294)
This fixes the following:
1. Using $RANDOM in a virtual/non-forked subshell no longer
   influences the reproducible $RANDOM sequence in the parent
   environment.
2. When invoking a subshell $RANDOM is now re-seeded (as mksh and
   bash do) so that invocations in repeated subshells (including
   forked subshells) longer produce identical sequences by default.
3. Program flow corruption that occurred in scripts on executing
   ( ( simple_command & ) ).

src/cmd/ksh93/include/variables.h:
- Move 'struct rand' here as it will be needed in subshell.c. Add
  rand_seed member to save the pseudorandom generator seed. Remove
  the pointer to the shell state as it's redundant.

src/cmd/ksh93/sh/init.c:
- put_rand(): Store given seed in rand_seed while calling srand().
  No longer pointlessly limit the number of possible seeds with the
  RANDMASK bitmask (that mask is to limit the values to 0-32767,
  it should not limit the number of possible sequences to 32768).
- nget_rand(): Instead of using rand(), use rand_r() to update the
  random_seed value. This makes it possible to save/restore the
  current seed of the pseudorandom generator.
- Add sh_reseed_rand() function that reseeds the pseudorandom
  generator by calling srand() with a bitwise-xor combination of
  the current PID, the current time with a granularity of 1/10000
  seconds, and a sequence number that is increased on each
  invocation.
- nv_init(): Set the initial seed using sh_reseed_rand() here
  instead of in sh_main(), as this is where the other struct rand
  members are initialised.

src/cmd/ksh93/sh/main.c: sh_main():
- Remove the srand() call that was replaced by the sh_reseed_rand()
  call in init.c.

src/cmd/ksh93/sh/subshell.c: sh_subshell():
- Upon entering a virtual subshell, save the current $RANDOM seed
  and state, then reseed $RANDOM for the subshell.
- Upon exiting a virtual subshell, restore $RANDOM seed and state
  and reseed the generator using srand() with the restored seed.

src/cmd/ksh93/sh/xec.c: sh_exec():
- When optimizing out a subshell that is the last command, still
  act like a subshell: reseed $RANDOM and increase ${.sh.subshell}.
- Fix a separate bug discovered while implementing this. Do not
  optimize '( simple_command & )' when in a virtual subshell; doing
  this causes program flow corruption.
- When optimizing '( simple_command & )', also reseed $RANDOM and
  increment ${.sh.subshell}.

src/cmd/ksh93/tests/subshell.sh,
src/cmd/ksh93/tests/variables.sh:
- Add various tests for all of the above.

Co-authored-by: Johnothan King <johnothanking@protonmail.com>
Resolves: https://github.com/ksh93/ksh/issues/285
2021-05-03 04:03:46 +01:00
Martijn Dekker
f31e368795 Fix remaining bug in ${var:-'{}'} (re: d087b031)
The following problems remained:

$ var=x; echo ${var:-'{}'}
x}
$ var=; echo ${var:+'{}'}
}

src/cmd/ksh93/sh/macro.c: varsub():
- Use the new ST_MOD1 state table to skip over ${var-'foo'}, etc.
  instead of ST_QUOTE. In ST_MOD1 the ' is categorised as S_LIT
  which causes the single quotes to be skipped over correctly.
  See d087b031 for more info.

src/cmd/ksh93/tests/quoting2.sh:
- Add tests for this remaining bug.
- Make the new test xtrace-proof.

Resolves: https://github.com/ksh93/ksh/issues/290 (again)
2021-05-03 03:14:30 +01:00
Martijn Dekker
88a1f3d661 Fork before entering shared-state command substitution
The code contains various checks to see if a subshell needs to
fork, like this one in the ulimit builtin:

	if(shp->subshell && !shp->subshare)
		sh_subfork();

All checks of this form are fatally broken, as each one of them
causes shared-state command substitutions to ignore parent virtual
subshells.

Currently the only feasible way to fix this is to fork a virtual
subshell before executing a shared-state command substitution in
it. In the long term I think shared-state command substitutions
should probably be redesigned to disassociate them completely from
the virtual subshell mechanism.

src/cmd/ksh93/sh/macro.c: comsubst():
- If we're in a non-subshare virtual subshell, fork it before
  entering a type 2 (subshare) command substitution.

src/cmd/ksh93/sh/subshell.c:
- sh_assignok(): Remove subshare fix from 911d6b06 as it's
  redundant now that the parent of a subshare is never a virtual
  subshell. Go back to not doing anything if the current "subshell"
  is a subshare.
- sh_subtracktree(), sh_subfuntree(): Similarly, remove the
  now-redundant subshare fixes from 13c57e4b.

src/cmd/ksh93/sh/xec.c: sh_exec():
- Fix a separate bug: only fork a virtual subshell before running a
  background job if that "subshell" is not a subshare.

src/cmd/ksh93/tests/subshell.sh:
- Add test for bug fixed in xec.c.
- Add tests for 'ulimit', 'builtin' and 'exec' run in subshare
  within subshell -- all commands that use checks of the form
  'if(sh.subshell && !sh.subshare) sh_subfork();'.

Resolves: https://github.com/ksh93/ksh/issues/289
2021-05-01 00:47:39 +01:00
Govind Kamat
7439e3dffe Parse quotes when extracting words from command history (#291)
This avoids splitting on quoted whitespace when extracting words
from the command history using the emacs M-. or vi _ command.

Example: if the prior command is

$ ls Stairway\ To\ Heaven.mp3

then, M-. in Emacs editing mode (and _ in vi mode) now inserts
Stairway\ To\ Heaven.mp3 instead of Heaven.mp3. The behavior is
similar for 'Stairway To Heaven.mp3' and "Stairway To Heaven.mp3".

src/cmd/ksh93/edit/history.c: hist_word():
- Skip over single-quoted and double-quoted strings and
  backslash-escaped characters.

src/cmd/ksh93/tests/pty.sh:
- Add regression test for this feature in vi mode. Since emacs and
  vi both use the same code for this, that should be good enough.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2021-04-30 20:18:07 +01:00
Martijn Dekker
d087b031f0 Fix single quotes in expansion operator string (re: 5ed9ffd6)
The referenced commit introduced the following bug:

> The closing quote does not appear to be registering during the
> parse of the following:
>
>	echo ${var:+'{}'}
>
> Within a script, this will result in:
>
>	syntax error at line 1: `'' unmatched

src/cmd/ksh93/data/lexstates.c,
src/cmd/ksh93/include/lexstates.h:
- Add new ST_MOD1 state table that is a copy of ST_QUOTE, but adds
  a special meaning (ST_LIT) for the single quote (position 39).

src/cmd/ksh93/sh/lex.c: sh_lex():
- For parameter expansion operators with old-style quoting
  (S_MOD1), use the new ST_MOD1 state table instead of ST_QUOTE.
  This causes single quotes within them to be processed properly.

src/cmd/ksh93/tests/quoting2.sh:
- Add tests.

Thanks to @gkamat for the bug report.
Resolves: https://github.com/ksh93/ksh/issues/290
2021-04-30 05:28:21 +01:00
Martijn Dekker
090b65e79b Fix fork after redirecting stdout in subshare (re: 500757d7)
Previously, command substitutions executed as virtual subshells
were always forked if any command was run within them that
redireceted standard output, even if the redirection was local to
that command.

Commit 500757d7 removed the check for a shared-state command
substitution (subshare), so introduced a bug where even that would
fork, causing it to stop sharing its state.

We can further improve on that fix by only forking if the
redirection is permanent as with `exec` or `redirect`. There should
be no need to do that if the redirection is local to a command run
within the command substitution, as the file descriptor is restored
when that command finishes, which is still within the command
substitution.

src/cmd/ksh93/sh/io.c: sh_redirect():
- Only fork upon redirecting stdout if the virtual subshell is a
  command substitution, and if the redirection is permanent
  (flag==1 or flag==2).
2021-04-26 18:22:17 +01:00