1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
Commit graph

307 commits

Author SHA1 Message Date
atheik
40a5c45b48 Allow double quotes within backtick comsub within double quotes
The following reproducer causes a spurious syntax error:

    foo="`: "("`"

The nested double quotes are not recognised correctly, causing a
syntax error at the '('. Removing the outer double quotes (which
are unnecessary) is a workaround, but it's still a bug as every
other shell accepts this. This bug has been present since the
original Bourne shell.

src/cmd/ksh93/sh/lex.c: sh_lex(): case S_QUOTE:
- If the current character is '"' and we're in a `...` command
  substitution (ingrave is true), then do not switch to the old
  mode but keep using the ST_QUOTE state table.

Thanks to @JohnoKing for the report and to @atheik for the fix.

Co-authored by: Martijn Dekker <martijn@inlv.org>
Resolves: https://github.com/ksh93/ksh/issues/352
2022-05-20 22:48:47 +01:00
Martijn Dekker
fd28da31da Fix another test/[ corner case bug; add --posix test script
This fixes another corner case bug in the horror show that is the
test/[ comand.

Reproducer:

   $ ksh --posix -c 'test X -a -n'
   ksh: test: argument expected

Every other shell returns 0 (success) as, POSIXly, this is a test
for the strings 'X' and '-n' both being non-empty, combined with
the binary -a (logical and) operator. Instead, '-n' was taken as a
unary primary operator with a missing argument, which is incorrect.

POSIX reference:
https://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
> 3 arguments:
> * If $2 is a binary primary, perform the binary test of $1 and $3.

src/cmd/ksh93/bltins/test.c:
- e3(): If the final argument begins with a dash, always treat it
  as a test for a non-empty string, therefore return true. Do not
  limit this to "new flags" only.

src/cmd/ksh93/tests/posix.sh:
- Added. These are tests for every aspect of the POSIX mode.
2022-03-11 21:23:45 +01:00
Martijn Dekker
9e2a8c6925 posix mode: disable effect of repeating whitespace char in $IFS
ksh has a little-known field splitting feature that conflicts with
POSIX: if a single-byte whitespace character (cf. isspace(3)) is
repated in $IFS, then field splitting is done as if that character
wasn't a whitespace character. An exmaple with the tab character:

  $ (IFS=$'\t'; val=$'\tone\t\ttwo\t'; set -- $val; echo $#)
  2
  $ (IFS=$'\t\t'; val=$'\tone\t\ttwo\t'; set -- $val; echo $#)
  4
The latter being the same as, for example
  $ (IFS=':'; val='1️⃣2️⃣'; set -- $val; echo $#)
  4

However, this is incompatible with the POSIX spec and with every
other shell except zsh, in which repeating a character in IFS does
not have any effect. So the POSIX mode must disable this.

src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/sh/init.c:
- Add sh_invalidate_ifs() function that invalidates the IFS state
  table by setting the ifsnp discipline struct member to NULL,
  which will cause the next get_ifs() call to regenerate it.
- get_ifs(): Treat a repeated char as S_DELIM even if whitespace,
  unless --posix is on.

src/cmd/ksh93/sh/args.c:
- sh_argopts(): Call sh_invalidate_ifs() when enabling or disabling
  the POSIX option. This is needed to make the change in field
  splitting behaviour take immediate effect instead of taking
  effect at the next assignment to IFS.
2022-03-11 21:22:22 +01:00
Martijn Dekker
fae1932e62 enum: remove arbitrary one-argument limitation
b_enum() contains a check that exactly one argument is given:

237:	if (error_info.errors || !*argv || *(argv + 1))

But the subsequent argument handling loop will happily deal with
multiple arguments:

246:	while(cp = *argv++)

Every other declaration command supports multiple arguments and I
see no reason why enum shouldn't. Simply removing the '*(argv + 1)'
check allows 'enum' to create more than one type per invocation.

src/cmd/ksh93/bltins/enum.c:
- b_enum(): Remove check for >1 args as described above.
- Update documentation to describe the behaviour of enumeration
  types in arithmetic expressions and to add an example: a bool
  type with two enumeration values 'false' (0) and 'true' (1).
  That type is predefined in ksh 93v- and 2020. We're not going
  to do that in 93u+m but it's good to document the possibility.

src/cmd/ksh93/sh.1:
- Make changes parallel to the enum.c self-doc update.
2022-03-11 21:21:23 +01:00
Martijn Dekker
b398f33c49 Another round of accumulated minor fixes and cleanups
Only notable changes listed below.

**/Mamfile:
- Do not bother redirecting standard error for 'cmp -s' to
  /dev/null. Normally, 'cmp -s' on Linux, macOS, *BSD, or Solaris
  do not not print any message. If it does, something unusual is
  going on and I would want to see the message.
- Since we now require a POSIX shell, we can use '!'.

src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/sh/init.c:
- Remove SH_TYPE_PROFILE symbol, unused after the removal of the
  SHOPT_PFSH code. (re: eabd6453)

src/cmd/ksh93/sh/io.c:
- piperead(), slowread(): Replace redundant sffileno() calls by
  the variables already containing their results. (re: 50d342e4)

src/cmd/ksh93/bltins/mkservice.c,
rc/lib/libcmd/vmstate.c:
- If these aren't compiled, define a stub function to silence the
  ranlib(1) warning that the .o file does not contain symbols.
2022-03-11 21:20:32 +01:00
atheik
2e5fd4d4c1 slowread(): Turn off O_NONBLOCK for stdin if it is on (#471)
This change turns off O_NONBLOCK for stdin if a previously ran
program left it on so that interactive programs that expect it
to be off work properly.

src/cmd/ksh93/sh/io.c: slowread():
- Turn off O_NONBLOCK for stdin if it is on.

Fixes: https://github.com/ksh93/ksh/issues/469
2022-03-11 21:10:59 +01:00
Johnothan King
e87dbebebd Fix use after free bug when using += (re: 75796a9c) (#466)
The previous fix for the += operator introduced a use-after-free
bug that could result in a variable pointing to random garbage:
   $ foo=bar
   $ foo+=_foo true
   $ typeset -p foo
   foo=V V
The use after free issue occurs because when nv_clone creates a
copy of $foo in the true command's invocation-local scope, it does
not duplicate the string $foo points to. As a result, the $foo
variable in the parent scope points to the same string as $foo in
the invocation-local scope, which causes the use after free bug
when cloned $foo variable is freed from memory.

src/cmd/ksh93/sh/nvdisc.c:
- To fix the use after free bug, allow nv_clone to duplicate the
  string with memdup or strdup when no flags are passed.

src/cmd/ksh93/tests/variables.sh:
- Add a regression test for using the += operator with regular
  commands.

src/cmd/ksh93/tests/leaks.sh:
- Add a regression test to ensure the bugfix doesn't introduce any
  memory leaks.
2022-03-11 21:08:57 +01:00
Martijn Dekker
11177d448d Fix crash on cd in subshell with PWD unset (re: 5ee290c)
Reproducer:

$ ksh -c 'unset PWD; (cd /); :'
Memory fault

The shell crashes because b_cd() is testing the value of the PWD
variable without checking if there is one.

src/cmd/ksh93/sh/path.c: path_pwd():
- Never return an unfreeable pointer to e_dot; always return a
  freeable pointer. This fixes another corner-case crashing bug.
- Make sure the PWD variable gets assigned a value if it doesn't
  have one, even if it's the "." fallback. However, if the PWD is
  inaccessible but we did inherit a $PWD value that starts with a
  /, then use the existing $PWD value as this will help the shell
  fail gracefully.

src/cmd/ksh93/bltins/cd_pwd.c:
- b_cd(): When checking if the PWD is valid, use the sh.pwd copy
  instead of the PWD variable. This fixes the crash above.
- b_cd(): Since path_pwd() now always returns a freeable value,
  free sh.pwd unconditionally before setting the new value.
- b_pwd(): Not only check that path_pwd() returns a value starting
  with a slash, but also verify it with test_inode() and error out
  if it's wrong. This makes the 'pwd' command useful for checking
  that the PWD is currently accessible.

src/cmd/ksh93/data/msg.c:
- Change e_pwd error message for accuracy and clarity.
2022-02-17 19:45:37 +00:00
Martijn Dekker
d55e9686d7 Backport 'read -a' and 'read -p' from ksh 93v-/2020
This backports two minor additions to the 'read' built-in from ksh
93v-: '-a' is now the same as '-A' and '-u p' is the same as '-p'.
This is for compatibility with some 93v- or ksh2020 scripts.

Note that their change to the '-p' option to support both prompts
and reading from the coprocess was *not* backported because we
found it to be broken and unfixable. Discussoin at:
https://github.com/ksh93/ksh/issues/463

src/cmd/ksh93/bltins/read.c: b_read():
- Backport as described above.
- Rename the misleadingly named 'name' variable to 'prompt'.
  It points to the prompt string, not to a variable name.

src/cmd/ksh93/data/builtins.c: sh_optpwd[]:
- Add -a as an alterative to -A. All that is needed is adding '|a'
  and optget(3) will automatically convert it to 'A'.
- Change -u from a '#' (numeric) to ':' option to support 'p'. Note
  that b_read() now needs a corresponding strtol() to convert file
  descriptor strings to numbers where applicable.
- Tweaks.

src/cmd/ksh93/sh.1:
- Update accordingly.
- Tidy up the unreadable mess that was the 'read' documentation.
  The options are now shown in a list.
2022-02-17 19:44:54 +00:00
Martijn Dekker
95d695cb5a Improve and document fast filescan loops (SHOPT_FILESCAN)
From README:

FILESCAN on  Experimental option that allows fast reading of files
             using while < file;do ...; done and allowing fields in
             each line to be accessed as positional parameters.

As SHOPT_FILESCAN has been enabled by default since ksh 93l
2001-06-01, the filescan loop is now documented in the manual page
and the compile-time option is no longer considered experimental.

We must disable this at runtime if --posix is active because it
breaks a portable use case: POSIXly, 'while <file; do stuff; done'
repeatedly excutes 'stuff' while 'file' can successfully be opened
for reading, without actually reading from 'file'.

This also backports a bugfix from the 93v- beta. Reproducer:

$ echo 'one two three' >foo
$ while <foo; do printf '[%s] ' "$@"; echo; done
[one two three]

Expected output:
[one] [two] [three]

The bug is that "$@" acts like "$*", joining all the positional
parameters into one word though it should be generating one word
for each.

src/cmd/ksh93/sh/macro.c: varsub():
- Backport fix for the bug described above. I do not understand the
  opaque macro.c code well enough yet to usefully describe the fix.

src/cmd/ksh93/sh/xec.c: sh_exec():
- Improved sanity check for filescan loop: do not recognise it if
  the simple command includes variable assignments, more than one
  redirection, or an output or append redirection.
- Disable filescan loops if --posix is active.
- Another 93v- fix: handle interrupts (errno==EINTR) when closing
  the input file.
2022-02-17 19:43:36 +00:00
Martijn Dekker
82ff91e9d9 Remove SHOPT_ENV dead code and sh.env variable (re: 8d7f616e)
As so often with SHOPT_* compile-time options, not all the relevant
code was actually conditional on the option macro. After removing
SHOPT_ENV, the arguments to sh_putenv() and env_delete() macro
calls are dead code and, after removing that, the sh.env variable
is unused.
2022-02-17 19:42:27 +00:00
Johnothan King
5bd18322e0 Remove unnecessary sh.defpathlist variable (re: e3a1dda9) (#461)
src/cmd/ksh93/{sh/path.c,include/shell.h}:
- The sh.defpathlist variable is never set once, which makes every
  use of this variable unnecessary (as it's always null). This
  commit removes sh.defpathlist while also fixing a possible memory
  leak (there was another location where defpathinit() was invoked
  without saving the returned pointer, causing a memory leak).
2022-02-17 19:40:53 +00:00
Martijn Dekker
4886463bb6 Disable broken KEYBD trap for multibyte characters
In UTF-8 locales, ksh breaks when a KEYBD trap is active, even a
dummy no-op one like 'trap : KEYBD'. Entering multi-byte characters
fails (the input is interrupted and a new prompt is displayed) and
pasting content with multi-byte characters produces corrupted
results.

The cause is that the KEYBD trap code is not multibyte-ready.
Unfortunately nobody yet understands the edit.c code well enough
to implement a proper fix. Pending that, this commit implements
a workaround that at least avoids breaking the shell.

src/cmd/ksh93/edit/edit.c: ed_getchar():
- When a multi-byte locale is active, do not trigger the the KEYBD
  trap except for ASCII characters (1-127).

Resolves: https://github.com/ksh93/ksh/issues/307
2022-02-17 19:39:42 +00:00
Martijn Dekker
6304dfce41 Fix corner-case >&- redirection leak out of subshell
Reproducer:

    exec 9>&1
    ( { exec 9>&1; } 9>&- )
    echo "test" >&9 # => 9: cannot open [Bad file descriptor]

The 9>&- incorrectly persists beyond the { } block that it
was attached to *and* beyond the ( ) subshell. This is yet another
bug with non-forking subshells; forking it with something like
'ulimit -t unlimited' works around the bug.

In over a year we have not been able to find a real fix, but I came
up with a workaround that forks a virtual subshell whenever it
executes a code block with a >&- or <&- redirection attached. That
use case is obscure enough that it should not cause any performance
regression except in very rare corner cases.

src/cmd/ksh93/sh/xec.c: sh_exec(): TSETIO:
- This is where redirections attached to code blocks are handled.
  Check for a >&- or <&- redirection using bit flaggery from
  shnodes.h and fork if we're executing such in a virtual subshell.

Resolves: https://github.com/ksh93/ksh/issues/161
Thanks to @ko1nksm for the bug report.
2022-02-17 19:35:47 +00:00
Martijn Dekker
14a43a0a88 Yet more misc. cleanups; rm SHOPT_PFSH, SHOPT_TYPEDEF
Notable changes:
- Remove SHOPT_PFSH compile-time option and associated code.
  This was meant to work with Solaris rights profiles, see:
  https://docs.oracle.com/cd/E23824_01/html/821-1461/profiles-1.html#REFMAN1profiles-1
  But it has been obsolete for years as Solaris stopped using
  it in its shipped ksh several OS versions ago, preferring a
  library-based wrapper around ksh and other shells.
  Nonetheless I experimented with the option on Solaris 11.4.
  Result: no external command will run; output of unitialised
  memory in error message. So it's already fallen victim to bit
  rot. There's nothing interesting here, so just get rid.
- Remove SHOPT_TYPEDEF compile-time option (but keep the code!).
  Turning it off caused the build to fail. It may be possible to
  fix it, but the type definition code is integral to ksh now (e.g.
  'enum' depends on much of it) so it makes no sense to disable it.
  This was removed in the ksh 93v- beta version as well.
- Remove nv_close() calls and remove nv_close() documentation from
  the nval.3 man page. This function is a dummy, present without
  any changes since the beginning of the ast-open-archive repo in
  1995. The comment was: "Currently this is a dummy, but someday
  will be needed for reference counting". 27 or more years later,
  it's time to admit it's never going to happen. (And of course,
  nv_close() calls were not being used with anything resembling
  consistency.)
- Add a null nv_close() macro to nval.h for compatibility with
  third party code that follows the old documentation.
- Add a few missing regression tests.
2022-02-10 21:04:56 +00:00
Johnothan King
f38494ea1d Fix multiple bugs in .sh.match (#455)
This commit backports all of the relevant .sh.match bugfixes from
ksh93v-. Most of the .sh.match rewrite is from versions 2012-08-24
and 2012-10-04, with patches from later releases of 93v- and
ksh2020 also applied. Note that there are still some remaining bugs
in .sh.match, although now the total count of .sh.match bugs should
be less that before.

These are the relevant changes in the ksh93v- changelog that were
backported:
12-08-07  .sh.match no longer gets set for patterns in PS4 during
          set -x.
12-08-10  Rewrote .sh.match expansions fixing several bugs and
          improving performance.
12-08-22  .sh.match now handles subpatterns that had no matches with
          ${var//pattern} correctly.
12-08-21  A bug in setting .sh.match after ${var//pattern/string}
          when string is empty has been fixed.
12-08-21  A bug in setting .sh.match after [[ string == pattern ]]
          has been fixed.
12-08-31  A bug that could cause a core dump after
          typeset -m var=.sh.match has been fixed.
12-09-10  Fixed a bug in typeset -m the .sh.match is being renamed.
12-09-07  Fixed a bug in .sh.match code that coud cause the shell
          to quitely
13-02-21  The 12-01-16 bug fix prevented .sh.match from being used
          in the replacement string. The previous code was restored
          and a different fix which prevented .sh.match from being
          computed for nested replacement has been used instead.
13-05-28  Fixed two bug for typeset -c and typeset -m for variable
          .sh.match.

Changes:
- The SHOPT_2DMATCH option has been removed. This was already the
  default behavior previously, and now it's documented in the man
  page.
- init.c: Backported the sh_setmatch() rewrite from 93v- 2012-08-24
  and 2012-10-04.
- Backported the libast 93v- strngrpmatch() function, as the
  .sh.match rewrite requires this API.
- Backported the sh_match regression tests from ksh93v-, with many
  other sh_match tests backported from ksh2020. Much of the sh_match
  script is based on code from Roland Mainz:
  https://marc.info/?l=ast-developers&m=134606574109162&w=2
  https://marc.info/?l=ast-developers&m=134490505607093
- tests/{substring,treemove}.sh: Backported other relevant .sh.match
  fixes, with tests added to the substring and treemove test scripts.
- tests/types.sh: One of the (now reverted) memory leak bugfixes
  introduced a CI test failure in this script, so for that test the
  error message has been improved.
- string/strmatch.c: The original ksh93v- code for the strngrpmatch()
  changes introduced a crash that could occur because strlen would
  be used on a null pointer. This has been fixed by avoiding strlen
  if the string is null.

One nice side effect of these changes is a considerable performance
improvement in the shbench[1] gsub benchmark (results from 20
iterations with CCFLAGS=-Os):
--------------------------------------------------
name      /tmp/ksh-current     /tmp/ksh-matchfixes
--------------------------------------------------
gsub.ksh  0.883 [0.822-0.959]  0.457 [0.442-0.505]
--------------------------------------------------

Despite all of the many fixes and improvements in the backported
93v- .sh.match code, there are a few remaining bugs:

- .sh.match is printed with a default [0] subscript (see also
  https://github.com/ksh93/ksh/issues/308#issuecomment-1025016088):
     $ arch/*/bin/ksh -c 'echo ${!.sh.match}'
       .sh.match[0]
  This bug appears to have been introduced by the changes from
  ksh93v- 2012-08-24.
- The wrong variable name is given for 'parameter not set' errors
  (from https://marc.info/?l=ast-developers&m=134489094602596):
     $ arch/*/bin/ksh -u
     $ x=1234
     $ true "${x//~(X)([012])|([345])/}"
     $ compound co
     $ typeset -m co.array=.sh.match
     $ printf "%q\n" "${co.array[2][0]}"
     arch/linux.i386-64/bin/ksh: co.array[2][(null)]: parameter not set
- .sh.match leaks out of subshells. Further information and a
  reproducer can be found here:
  https://marc.info/?l=ast-developers&m=136292897330187

[1]: https://github.com/ksh-community/shbench
2022-02-10 21:04:23 +00:00
Johnothan King
787058bdbf Fix the output of typeset -p for two dimensional indexed arrays (#454)
In ksh93v- 2012-10-04 the following bugfix is noted in the changelog
(this fix was most likely part of ksh93v- 2012-09-27, although that
version is not archived anywhere):
12-09-21  A bug in which the output of a two dimensional sparse
          indexed array would cause the second subscript be treated
          as an associative array when read back in has been fixed.
          Elements that are sparse indexed arrays now are prefixed
          type "typeset -a".

Below is a before and after of this change:

   # Before
   $ typeset -a foo[1][2]=bar
   $ typeset -p foo
   typeset -a foo=([1]=([2]=bar) )

   # After
   $ typeset -a foo[1][2]=bar
   $ typeset -p foo
   typeset -a foo=(typeset -a [1]=([2]=bar) )

src/cmd/ksh93/sh/*.c:
- Backport changes from ksh93v- to print 'typeset -a' before sparse
  indexed arrays and properly handle 'typeset -a' in reinput
  commands from 'typeset -p'.

src/cmd/ksh93/tests:
- Add two regression tests to arrays.sh for this change.
- Update the existing regression tests for compatibility with the
  new printed typeset output.
2022-02-10 21:01:40 +00:00
Martijn Dekker
e6d0187dd8 Don't allow 'enum' and 'typeset -T' to override special built-ins
Special builtins are undeleteable for a reason. But 'enum' and
'typeset -T' allow overriding them, causing an inconsistent state.

@JohnoKing writes:
| The behavior is rather buggy, as it appears to successfully
| override normal builtins but fails to delete the special
| builtins, leading to scenarios where both the original builtin
| and type are run:
|
| $ typeset -T eval=(typeset BAD; typeset TYPE)  # This should have failed
| $ eval foo=BAD
| /usr/bin/ksh: eval: line 1: foo: not found
| $ enum trap=(BAD TYPE)   # This also should have failed
| $ trap foo=BAD
| /usr/bin/ksh: trap: condition(s) required
| $ enum umask=(BAD TYPE)
| $ umask foo=BAD
| $ echo $foo
| BAD
|
| # Examples of general bugginess
| $ trap bar=TYPE
| /usr/bin/ksh: trap: condition(s) required
| $ echo $bar
| TYPE
| $ eval var=TYPE
| /usr/bin/ksh: eval: line 1: var: not found
| $ echo $var
| TYPE

This commit fixes the following:

The 'enum' and 'typeset -T' commands are no longer allowed to
override and replace special built-in commands, except for type
definition commands previously created by these commands; these
are already (dis)allowed elsewhere.

A command like 'typeset -T foo_t' without any assignments no longer
creates an incompletely defined 'foo_t' built-in comamnd. Instead,
it is now silently ignored for backwards compatibility. This did
have a regression test checking for it, but I'm changing it because
that's just not a valid use case. An incomplete type definition
command does nothing useful and only crashes the shell when run.

src/cmd/ksh93/bltins/enum.c: b_enum():
- Do not allow overriding non-type special built-ins.

src/cmd/ksh93/sh/name.c: nv_setlist():
- Do not allow 'typeset -T' to override non-type special built-ins.
  To avoid an inconsistent state, this must be checked for while
  processing the assignments list before typeset is really invoked.

src/cmd/ksh93/bltins_typeset.c: b_typeset():
- Only create a type command if sh.envlist is set, i.e., if some
  shell assignment(s) were passed to the 'typeset -T' command.

Progresses: https://github.com/ksh93/ksh/issues/350
2022-02-10 21:01:00 +00:00
Martijn Dekker
65aff0befb Fix conditional expansions ${array[i]=value}, ${array[i]?error}
$ unset foo
    $ echo ${foo[42]=bar}
    (empty line)

Instead of the empty line, 'bar' was expected. As foo[42] was
unset, the conditional assignment should have worked.

    $ unset foo
    $ : ${foo[42]?error: unset}
    (no output)

The expansion should have thrown an error with the given message.

This bug was introduced in ksh 93t 2008-10-01. Thanks to @JohnoKing
for finding the breaking change.

Analysis: The problem was experimenally determined to be in in the
following lines of nv_putsub(). If the array member is unset (i.e.
null), the value is set to the empty string instead:

src/cmd/ksh93/sh/array.c
1250: 		else
1251:			ap->val[size].cp = Empty;

It makes some sense: if there is a value (even an empty one), the
variable is set and these expansions should behave accordingly.
Sure enough, deleting these lines fixes the bug, but at the expense
of introducing a lot of other array-related regressions. So we need
a way to special-case the affected expansions.

Where to do this? If we replace line 1251 with an abort(3) call, we
get this stack trace:

0   libsystem_kernel.dylib    __pthread_kill + 10
1   libsystem_pthread.dylib   pthread_kill + 284
2   libsystem_c.dylib         abort + 127
3   ksh                       nv_putsub + 1411 (array.c:1255)
4   ksh                       nv_endsubscript + 940 (array.c:1547)
5   ksh                       nv_create + 4732 (name.c:1066)
6   ksh                       nv_open + 1951 (name.c:1425)
7   ksh                       varsub + 4934 (macro.c:1322)
[rest omitted]

The special-casing needs to be done on line 1250 of array.c, but
flagged in varsub() which processes these expansions. So, varsub()
calls nv_open() calls nv_create() calls nv_endsubscript() calls
nv_putsub(). That's a fairly deep call stack, so passing an extra
flag argument does not seem doable. I did try an approach using a
couple of new bit flags passed via these functions' flags and mode
parameters, but the way this code base uses bit flags is so
intricate, it does not seem to be possible to add or change
anything without unwanted side effects in all sorts of places.

So the only fix I can think of adds yet another global flag
variable for a very special case. It's ugly, but it works.
An elegant fix would probably involve a fairly comprehensive
redesign, which is simply not going to happen.

src/cmd/ksh93/include/shell.h:
- Add global sh.cond_expan flag.

src/cmd/ksh93/sh/array.c: nv_putsub():
- Do not set value to empty string if sh.cond_expan is set.

src/cmd/ksh93/sh/macro.c: varsub():
- Set sh.cond_expan flag while calling nv_open() for one of the
  affected expansions.
- Minor refactoring for legibility and to make the fix fit better.
- SSOT: Instead of repeating string "REPLY", use the node's nvname.
- Do not pointlessly add an extra 0 byte when saving id for error
  message; sfstruse() already adds this.

Thanks to @oguz-ismail for the bug report.

Resolves: https://github.com/ksh93/ksh/issues/383
2022-02-05 23:39:16 +00:00
Martijn Dekker
493a31053e Do not export variables with dot names (re: 8e72608c)
Variables with a dot in their name, such as those declared in
namespace { ... } blocks, are usually stored in a separate tree
with their actual names not containing any dots. But under some
circumstances, including at least direct assignment of a
non-preexisting dot variable, dot variables are stored in the main
sh.var_tree with names actually containing dots. With allexport
active, those could end up exported to the environment. This bug
was also present in previous release versions of ksh.

src/cmd/ksh93/sh/name.c: pushnam():
- Check for a dot in the name before pushing a variable to export.
2022-02-05 15:08:50 +00:00
Johnothan King
a8dd1bbd9d typeset -p: fix output of nonexistent [0]= array element (#451)
This fix was backported from ksh 93v- 2012-10-04.

src/cmd/ksh93/sh/nvtree.c: nv_outnode():
- If the array is supposed to be empty, do not continue. This
  avoids outputting a nonexistent [0]= element for empty arrays.

Resolves: https://github.com/ksh93/ksh/issues/420
Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-02-05 13:53:51 +00:00
Johnothan King
fb696ecfae trap: fix use after free (#446)
This commit adds a fix for the trap command, backported from a fork
of ksh2020: 2033375f

src/cmd/ksh93/sh/jobs.c: job_chldtrap():
- Fixed a use after free bug in the for loop. The string pointed to
  by sh.st.trapcom[SIGCHLD] may be freed from memory after
  sh_trap(), so it must be reobtained each time sh_trap() is called
  from within the for loop.
2022-02-05 13:53:11 +00:00
Johnothan King
8e72608c1c Export all variables assigned to while allexport is on (#431)
All variables that are assigned a value should be exported while
the allexport shell option is enabled. This works in most cases,
but variables assigned to with ${var:=foo} or $((var=123)) aren't
exported while allexport is on.

src/cmd/ksh93/sh/name.c:
- nv_putval(): This is the central assignment function; all forms
  of variable assignment end up here. So this is the best place
  to check for SH_ALLEXPORT and turn on the export attribute.
- nv_setlist(): Remove allexport handling, now redundant.

src/cmd/ksh93/bltins/read.c: sh_readline():
- Remove allexport handling, now redundant.

src/cmd/ksh93/sh/main.c: sh_main():
- nv_putval() is used to initialize PS4 and IFS using nv_putval();
  this is after an -a/--allexport specified on the ksh command
  line has been processed, so temporarily turn that off.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-02-05 13:52:28 +00:00
Johnothan King
0863a8eb29 Support glibc 2.35's posix_spawn_file_actions_addtcsetpgrp_np(3)
This commit implements support for the glibc 2.35
posix_spawn_file_actions_addtcsetpgrp_np(3) extension[2][3],
updating spawnveg(3) to use the new function for setting the
terminal group. This was done with the intention of improving
performance in interactive shells without reintroducing previous
race conditions[4][5].

[1]: https://sourceware.org/pipermail/libc-alpha/2022-February/136040.html
[2]: https://sourceware.org/git/?p=glibc.git;a=commit;h=342cc934
[3]: https://sourceware.org/git/?p=glibc.git;a=commit;h=6289d28d
[4]: https://github.com/ksh93/ksh/issues/79
[5]: https://www.mail-archive.com/ast-developers@research.att.com/msg00717.html

src/cmd/ksh93/sh/path.c:
- Tell spawnveg(3) to set the terminal process group when launching
  a child process in an interactive shell.

src/cmd/ksh93/sh/xec.c:
- If posix_spawn_file_actions_addtcsetpgrp_np(3) is available,
  allow use of spawnveg(3) (via sh_ntfork()) even with job control
  active.
- sh_ntfork(): Reimplement most of the SIGTSTP handling code
  removed in commit 66c37202.

src/lib/libast/comp/spawnveg.c,
src/lib/libast/misc/procopen.c,
src/lib/libast/features/sys:
- Add support for posix_spawn_file_actions_addtcsetpgrp_np(3).
- Allow spawnveg to set the terminal process group when pgid == 0.
  This was necessary to avoid race conditions when using the new
  function.

src/lib/libast/features/lib:
- Detect posix_spawn_file_actions_addtcsetpgrp_np(3).
- Do not detect an OS spawnveg(3). With the API changes to spawnveg
  in this pull request ksh probably can't use the OS's spawnveg
  function anymore. (That's assuming anything else even provides a
  spawnveg function to begin with, which is unlikely.)

src/lib/libast/features/api,
src/cmd/ksh93/include/defs.h:
- Bump libast version (20220101 => 20220201) due to the spawnveg(3)
  API change.

src/lib/libast/man/spawnveg.3:
- Document the changes to spawnveg(3) in the corresponding man
  page. Currently, it will only use the new tcfd argument if
  posix_spawn_file_actions_addtcsetpgrp_np(3) is supported. This
  could also be implemented for the fork(2) fallback, but for now
  I've avoided changing that since actually using it in the fork
  code would likely require a lot of hackery to avoid attempting
  tcsetpgrp with vfork (the behavior of tcsetpgrp after vfork is
  not portable) and would only benefit systems that don't have
  posix_spawn and vfork (I can't recall any off the top of my head
  that would fall under that category).
- Updated the man page to account for spawnveg's change in
  behavior.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-02-05 13:31:31 +00:00
Martijn Dekker
1375cda934 Default to emacs upon invoking interactive shell
If the VISUAL or EDITOR environment variable is not set to a value
matching *[Vv][Ii]* or *macs* at initialisation time, then ksh does
not turn on any line editor.

This is user-hostile. New users on Unix-like systems typically have
a simple editor like nano preconfigured as their default, or may
not have the VISUAL or EDITOR variable set at all. So if they try
ksh, they find themselves without basic functionality such as arrow
keys and probably go straight back to bash.

The emacs line editor is by far the most widely used, especially
among new users, so ksh should default to that. Most other shells
already do this.

src/cmd/ksh93/sh/main.c: sh_main():
- On an interactive shell, if on editor was turned on based on
  $VISUAL or $EDITOR, turn on emacs before reading input.
2022-02-01 23:47:56 +00:00
Johnothan King
9a5af738ef Add support for more keyboard shortcuts (#410)
Add extra key bindings to the emacs and vi modes

This patch adds the following key bindings to the emacs and vi
editing modes:
- Support for Home key sequences ^[[1~ and ^[[7~ as well as End key
  sequences ^[[4~ and ^[[8~.
- Support for arrow key sequences ^[OA, ^[OB, ^[OC and ^[OD.
- Support for the following keyboard shortcuts (if the platform
  supports the expected escape sequence):
  - Ctrl-Left Arrow:  Go back one word
  - Alt-Left Arrow:   Go back one word     (Not supported on Haiku)
  - Ctrl-Right Arrow: Go forward one word
  - Alt-Right Arrow:  Go forward one word  (Not supported on Haiku)
  - Ctrl-G:           Cancel reverse search
  - Ctrl-Delete:      Delete next word     (Not supported on Haiku)
- Added a key binding for the Insert key, which differs in the
  emacs and vi editing modes:
  - In emacs mode, Insert escapes the next character.
  - In vi mode, Insert will switch the editor to insert mode (like
    in vim).

src/cmd/ksh93/edit/{emacs,vi}.c:
- Add support for the <M-Left> and <M-Right> sequences. Like in
  bash and mksh, these shortcuts move the cursor one word backward
  or forward (like the <Ctrl-Left> and <Ctrl-Right> shortcuts).
- Only attempt to process these shortcuts if the escape sequence
  begins with $'\E[1;'.

src/cmd/ksh93/edit/vi.c:
- If the shell isn't doing a reverse search, insert the bell
  character when Ctrl+G is input.
- Add the Ctrl-Delete shortcut as an alias of 'dw'. Calling
  ed_ungetchar twice does not work for 'dw', so Ctrl-Delete was
  implemented by using a vp->del_word variable.

Co-authored-by: Martijn Dekker <martijn@inlv.org>
2022-01-31 21:51:50 +00:00
Martijn Dekker
9c313c7fe3 Update copyright years in files changed since 1st Jan 2022 2022-01-30 20:49:04 +00:00
Johnothan King
ad9f9ff13e Accumulated fixes for minor issues (#442)
This commit applies various accumulated bugfixes:

- Applied some fixes for compiler warnings based off of the
  following pull requests (with whitespace changes excluded to
  avoid inflating the size of the diff):

  https://github.com/att/ast/pull/281
  https://github.com/att/ast/pull/283
  https://github.com/att/ast/pull/303
  https://github.com/att/ast/pull/304

- clone_type(): Two separate variables in this function share the
  same name. A bugfix from ksh93v- 2013-05-24 was backported to
  avoid conflict issues.

- Backported a minor error message improvement from ksh2020 for
  when the user attempts to run too many coprocesses.

- Backported a ksh2020 bugfix for a file descriptor leak:
  58bc8b56

- Backported ksh2020 bugfixes for unused variable and pointless
  assignment lint warnings:
  47650fe0
  df209c0d
  5e417b00

- Applied a few minor improvements to libast from graphviz:
  e7c03541
  969a7cde

- dtmemory(): Mark unused parameters with NOT_USED(). Based on:
  6ac3ad99

- Applied a few documentation/comment tweaks for the NEWS file,
  printf -v and spawnveg.

- Added a missing regression test for using the rm builtin's -f
  option without additional arguments (this was fixed in
  ksh93u+ 2012-02-14).
2022-01-30 20:42:59 +00:00
Martijn Dekker
2c35a53964 Fix resuming external command run from POSIX function or dot script
This fixes yet another whopper of a bug in job control. And it's
been in every version of ksh93 back to 1995, the beginning of
ast-open-archive. <sigh>

This is also bug number 23 that is fixed by simply removing code.

Reproducer:

1. Run vi, or another suspendable program, from a dot script or
   POSIX function (ksh handles them the same way). So either:
   $ echo vi >v
   $ . ./v
   or:
   $ v() { vi; }
   $ v

2. Suspend vi by typing Ctrl+Z.

3. Not one but two jobs are registered:
   $ jobs -l
   [2] + 85513	Stopped                  . ./v
   [1] - 85512	Stopped                  . ./v

4. 'fg' does not work on either of them, just printing the job
   command name but not resuming the editor. The second job
   disappears from the table after 'fg'.
   $ fg %2
   . ./v
   $ fg %2
   ksh: fg: no such job
   $ fg %1
   . ./v
   $ fg %1
   . ./v

Either way, you're stuck with an unresumable vi that you have to
'kill -9' manually.

src/cmd/ksh93/sh/xec.c: sh_exec(): TFORK:
- Do *not* turn off the SH_MONITOR state flag (which tells ksh to
  keep track of jobs) when running an external command from a
  profile script or dot script/POSIX function. It's obvious that
  this results in an inconsistent job control state as ksh will not
  update it when the external command is suspended. The purpose of
  this nonsense is surely lost to history as it's been there since
  1995 or before. My testing says that removing it doesn't break
  anything. If that turns out to be wrong, then the breakage will
  have to be fixed in a correct way instead.
2022-01-28 05:15:42 +00:00
Martijn Dekker
fe268fcc91 Cygwin: workaround for ksh to execute #!-less scripts with itself
On Cygwin, ksh does not execute scripts without a #! path in a fork
of the ksh process as it does on other systems. Reproducer (run
from ksh):

  $ cat test.sh
  echo "${BASH_VERSION:-not bash}"
  echo "${.sh.version}"
  $ chmod +x test.sh
  $ ./test.sh
  4.4.12(3)-release
  ./test.sh: line 2: ${.sh.version}: bad substitution

The script was executed in bash instead of ksh.

After this fix, the output on Cygwin is like ksh on other systems:

  not bash
  Version AJM 93u+m/1.1.0-alpha+dev 2022-01-26

This also fixes a number of regression test failures, as quite a
few tests create and execute temp scripts without a hashbang path.

Analysis: On Cygwin, execve(2) happily executes shell scripts
without a #! path with /bin/sh (which is bash --posix). However,
ksh relies on execve(2) executing binaries or #! only, as it uses
an ENOEXEC failure to decide whether to fork and execute a #!-less
shell script with a reinitialized copy of itself via exscript().

src/cmd/ksh93/sh/path.c: path_spawn():
- Look at the magic first two bytes of the file; if it is "MZ"
  (Mark Zbikowski, originator of the .exe format) or "#!", continue
  as normal, otherwise simulate an ENOEXEC failure from execve(2)
  which will cause ksh to fall back on #!-less script execution.
2022-01-27 16:08:44 +00:00
Martijn Dekker
172becffea Some more accumulated minor tweaks and cleanups
Notable changes:

src/cmd/ksh93/include/fault.h:
- Get rid of the superflous sh pointer argument in the
  sh_pushcontext() and sh_popcontext() macros. (re: 2d3ec8b6)

src/cmd/ksh93/tests/io.sh:
- Tweak a process substitution test to allow up to a second for
  unused process substitution processes to disappear. On the Alpine
  Linux console (at least the musl libc version), this is needed to
  avoid a test failure as long as no GUI is active. As soon as you
  start X11, this phenomenon disappears, even on the console. Very
  strange, but also very probably not ksh's fault.

src/cmd/ksh93/tests/shtests:
- Instead of just SIGCONT and SIGPIPE, set all signals to default,
  just to be sure to avoid spurious test failures due to signals
  that were ignored on entry. (It made no difference to the
  aforementioned Alpine Linux test failure, so ignored signals had
  nothing to do with that -- but still a good idea.)

.github/workflows/ci.yml:
- On the GitHub CI runs, when testing with SHOPTs disabled, disable
  SHOPT_SPAWN as well, which tests that everything still works
  correctly with the regular fork(2) method.

COPYRIGHT:
- Remove duplicate of BSD license.
2022-01-25 16:13:15 +00:00
Martijn Dekker
cda8fc916f Fix a crashing bug in history expansion
Reproducer:

$ set -o histexpand
$ echo foo !#^:h !#^:&
/usr/local/bin/ksh: :&: no previous substitution
ksh(80822,0x10bc2a5c0) malloc: *** error for object 0x10a13bae3: pointer being freed was not allocated
ksh(80822,0x10bc2a5c0) malloc: *** set a breakpoint in malloc_error_break to debug
Abort

Analysis: In hist_expand(), the 'cc' variable has two functions:
it holds a pointer to a malloc'ed copy of the current line, and is
also used as a temporary pointer with functions like strchr().
After that temporary use, it is set to NULL again, because the
'done:' routine checks if it non-NULL to decide whether to free the
pointer. But if an error occurs, the function may jump straight to
'done' without first setting cc to NULL if it had been used as a
temporary pointer. It then tries to free an unallocated pointer.

src/cmd/ksh93/edit/hexpand.c: hist_expand():
- Eliminate this bad practice by using a separate variable for
  temporary pointer purposes.

(I was unable to reproduce the crash in a pty regression test,
though it is consistently reproducible in a real interactive
session. So I haven't added that test.)
2022-01-24 08:41:27 +00:00
Johnothan King
eaf7662daa Fix history expansion buffer overflow (#434)
History expansion currently crashes under ASan due to a buffer
overflow. Reproducer:

   $ set -H
   $ !!:s/old/new/

Explanation from <https://github.com/att/ast/issues/1369>:
> The problem is the code assumes the buffer allocated for a string
> stream is zero initialized. But the SFIO code uses malloc() to
> allocate the buffer and does not explicitly initialize it with
> memset(). That it works at all, even without ASAN enabled, is
> purely accidental. It will fail if that malloc() returns a block
> that had been previously allocated, used, and freed. Under ASAN
> the buffer is initialized (at least on my system) to a sequence
> of 0xBE bytes. So the strdup() happily tries to duplicate a
> string that is the size of that buffer and fails when it reads
> past the end of the buffer looking for the terminating zero byte.

src/cmd/ksh93/edit/hexpand.c:
- Backport ksh2020 bugfix that avoids assuming the string stream
  has been initialized to zeros:
  cf16bcca
  (minus the incorrect change to the static wm variable).
2022-01-21 02:13:08 +00:00
Martijn Dekker
41829efa06 Various minor cleanups and fixes
The more notable ones are:

src/lib/libast/features/standards:
- Do not redefine _GNU_SOURCE and _FILE_OFFSET_BITS if already
  defined from $CCFLAGS. Thanks to @hyanias for the heads-up.
  (re: 289dd46c)

src/cmd/ksh93/data/builtins.c,
src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/sh/args.c,
src/cmd/ksh93/sh/name.c:
- Remove -T test code activation option. It was basically unused.
  The only thing it did was intentionally introduce a memory leak
  in table_unset() if the 4th bit in the option argument was set.
  A search in ast-open-history reveals a few more trivial test uses
  that were later deleted, but nothing interesting.

src/cmd/ksh93/tests/{basic,path}.sh:
- Skip a couple of tests on AIX avoid hangs, at least one of which
  is not ksh's fault. Thanks to @HansH111 for the report.

src/cmd/ksh93/tests/builtins.sh:
- Change one awk use to a more portable sed invocation to placate
  systems with ancient awk commands, such as AIX. (re: de795e1f)
2022-01-20 00:54:42 +00:00
Martijn Dekker
289dd46c05 build: include standards macros for all AST code (re: 7fb814e1)
Turns out that the standards macros set by features/standards (such
as _GNU_SOURCE for Linux or _DARWIN_SOURCE for macOS) were still
*not* included for most C source files! Instead, they were
selectively included for some files only, sometimes via
FEATURE/standards (the output of features/standards), sometimes
via ast_standards.h which is copied from FEATURE/standards.

Consequently, there were still inconsistencies in the system header
interfaces exposed on Linux, macOS, Solaris, et al. It's no wonder
it sometimes took so much hackery to keep everything building.

Of course, making this consistent had to break things somewhere.
Breakage occurred on 32-bit Linux due to a lot of ugly hackery
involving direct use of internal GNU types like off64_t and
functions like fseek64(). This is now all removed and they are
activated by setting the appropriate feature macro instead, so
these types and functions can be used with their standard names
(off_t, fseek, etc.)

Before committing I've tested these changes on the following
i386/x86_64 systems: Linux (glibc 32 and 64 bit, musl libc 64 bit),
Solaris (32 and 64 bit), illumos (32 and 64 bit), FreeBSD (64 bit),
macOS (64 bit), Cygwin (32 bit), and Haiku (64 bit).

(Note: ast_standards.h is copied from FEATURE/standards, whereas
ast_common.h is copied from FEATURE/common.)

src/lib/libast/include/ast_std.h,
src/lib/libast/stdio/stdhdr.h:
- Include <ast_standards.h> first. This should cause all the AST
  and dependent code (such as ksh) to get the standards macros.

src/lib/libast/features/standards:
- For GNU (glibc), #define _FILE_OFFSET_BITS 64 to get large file
  support with 64-bit offsets.
- Stop GNU and Cygwin <string.h> form defining the GNU version of
  basename(3); on Cygwin, that declaration conflicts with the AST
  version (and with POSIX) by using a const char* argument instead
  of char*. It is deactivated by defining the macro 'basename' (as
  'basename'); this causes GNU string.h to consider it to be
  already defined by the standard libgen.h header.

All other changed files:
- Remove direct use of *64* types and functions and a lot of
  related hackery.
2022-01-20 00:53:22 +00:00
Martijn Dekker
e569f23ef9 bump internal libast version; various minor cleanups
These are minor things I accumulated over the last month or so.

Notable changes:

src/lib/libast/features/api,
src/lib/libast/misc/state.c,
src/lib/libast/comp/conf.tab,
src/cmd/ksh93/include/defs.h:
- Bump internal libast version to 20220101L. We've made a few
  additions to the API, at least pathicase (see 71934570, ca3ec200)
  and astconf_long (see c2ac69b2), so this should have been done
  already. This also updates '/opt/ast/bin/getconf _AST_VERSION'.
- Use AST_VERSION instead of outdated _AST_VERSION.
- In state.c, use AST_VERSION instead of hardcoding the version.

src/cmd/ksh93/sh/xec.c:
- Remove 'restorefd' variable, unused as of 42becab6.
- Remove 'cmdrecurse' function and SH_RUNPROG macro; this was once
  used by a few libcmd commands, but ast-open-archive reveals it's
  unused as of ast 1999-12-25.

src/cmd/ksh93/sh/*.c:
- Where available, use e_dot instead of "." for consistency; it is
  defined as an extern so we might as well use it.

src/cmd/ksh93/tests/*.sh:
- When reporting signal names in fails, include the SIG prefix.
- Fix a broken process hang test in subshell.sh.

src/lib/libast/man/sfdisc.3:
- Removed. The interfaces described here never made it out of AT&T;
  they do not exist in any libast version in ast-open-archive.
  Resolves: https://github.com/ksh93/ksh/issues/426
2022-01-14 19:55:35 +00:00
Martijn Dekker
b509e92241 edit: do not enable multiline mode with no editor active
If neither gmacs/emacs nor vi are active, the multiline mode should
not be enabled even if the multiline option is on. Doing so can
cause inconsistent behaviour, particularly in multibyte locales
where, if the shell is compiled with SHOPT_RAWONLY (as is default),
the no-editor mode is actually handled by vi.c.

Also, the new --histreedit and --histverify options only work in
the emacs or vi editors, or in no-editor mode when handled by vi.
Which means they cannot ever work if neither emacs or vi were
compiled in (i.e. SHOPT_ESH and SHOPT_VSH were both disabled).
In that case, there's no point in compiling in those options.
Come to think of it, the same applies to the multiline option.

All changed files:
- Update SHOPT_ESH/SHOPT_VSH preprocessor directives as per above.

src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/include/shell.h:
- Move definitions of history expansion-related options to shell.h,
  which is where all the other shell options are defined.
2022-01-12 20:39:05 +00:00
Martijn Dekker
a5700d3937 Expose --histreedit and --histverify options (re: 921bbcae)
This adds two long-form shell options that modify history expansion
(-H/--histexpand). If --histreedit is on and a history expansion
fails, the command line is reloaded into the next prompt's edit
buffer, allowing corrections. If --histverify is on, the results of
a history expansion are not immediately executed but instead loaded
into the next prompt's edit buffer, allowing further changes.

SH_HISTREEDIT and SH_HISTVERIFY were already handled all along in
slowread() in io.c and via 'reedit' arguments to functions called
there, but could not be turned on as they were only ever exposed as
shopt options in the removed bash compatibility mode (in theory
only, as it failed to compile). I had overlooked them until now.

So, since the code is there, let's expose these options through the
normal long options interface. They're working fine, and activating
them actually makes history expansion tolerable to use.

src/cmd/ksh93/data/options.c:
- Make these options available as "histreedit" and "histverify".

src/cmd/ksh93/data/builtins.c,
src/cmd/ksh93/sh.1:
- Document the "new" options.

src/cmd/ksh93/include/defs.h:
- Remove unused SH_HISTAPPEND and SH_HISTORY2 macros which were
  part of the removed bash compatibility code. Note that ksh does
  not need a histappend option as it never overwrites the history
  file (in the bash mode, this shopt option was a no-op).
2022-01-12 20:38:30 +00:00
Johnothan King
1a9af9db40 Fix vi mode tab completion with spaces (#413)
Attempting to complete file names in vi mode using tab completion can
fail if the last character on the command line is a space. Reproducer
(note that this bug doesn't occur in emacs mode):
   $ set -o vi
   $ mkdir '/tmp/foo bar'
   $ test -d /tmp/foo\ <Tab>

src/cmd/ksh93/edit/vi.c:
- Don't disable tab completion or reset the tab count just because the
  last character on the command line is a space. This bugfix was
  backported from ksh93v- 2014-06-06.

src/cmd/ksh93/tests/pty.sh:
- Add a regression test for the tab completion bug.
2022-01-07 16:18:28 +00:00
Johnothan King
ca5803419b Fix various typos, man page issues and improve the documentation (#415)
This commit makes various different improvements to the documentation:
- sh.1: Backported (with changes) mandoc warning fixes from ksh2020
  for the ksh93(1) man page: <https://github.com/att/ast/pull/1406>
- Removed unnecessary spaces at the end of lines to fix a few other
  mandoc warnings.
- Fixed various typos and capitalization errors in the documentation.
- ANNOUNCE: Document the addition of the ${.sh.pid} variable
  (re: 9de65210).
- libast/man/str*: Update the man pages for the libast str* functions
  to improve how accurately each function is described.
- ksh93/README: Update regression test/compatibility notes to include
  OpenBSD 7.0, FreeBSD 13.0 and WSL running Ubuntu 20.04.
- Change a few places to store the return value from strlen in a
  size_t variable rather than signed int.
- comp/setlocale.c: To avoid confusion of two separate variables named
  lang, the function local variable has been renamed to langidx.
2022-01-07 16:17:55 +00:00
Johnothan King
d347ec0fc9 Allow ksh to compile on Haiku; implement SIGKILLTHR support (#408)
This commit implements the build fixes required to get ksh running on
Haiku. Note that while ksh does compile, it has a ton of regression test
failures on Haiku.

src/cmd/ksh93/data/signals.c,
src/lib/libast/features/signal.c:
- Add support for the SIGKILLTHR signal, which is supported by BeOS and
  Haiku.
- SIGINFO was missing an entry in the libast feature test, so add one
  (re: 658bba74).

src/cmd/ksh93/RELEASE:
- Add an entry noting that ksh now compiles on Haiku, albeit with many
  regression test failures.

src/cmd/ksh93/{include/terminal.h,sh/path.c}:
- Silence compiler warnings on Haiku.

src/lib/libast/features/mmap:
- The mmap feature test freezes on Haiku, so modify the test to fail
  immediately on that OS.

src/lib/libast/misc/signal.c:
- Avoid redefining the signal definition on Haiku to fix a compiler
  error.

src/lib/libast/features/nl_types:
- For some reason the nl_item typedef on Haiku doesn't work correctly.
  Work around that by creating the nl_item type in the libast nl_types
  feature test.
2022-01-07 16:16:42 +00:00
Martijn Dekker
b590a9f155 [shp cleanup 01..20] all the rest (re: 2d3ec8b6)
This combines 20 cleanup commits from the dev branch.

All changed files:
- Clean up pointer defererences to sh.
- Remove shp arguments from functions.

Other notable changes:

src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/sh/init.c:
- On second thought, get rid of the function version of
  sh_getinterp() as libshell ABI compatibility is moot. We've
  already been breaking that by reordering the sh struct, so there
  is no way it's going to work without recompiling.

src/cmd/ksh93/sh/name.c:
- De-obfuscate the relationship between nv_scan() and scanfilter().
  The former just calls the latter as a static function, there's no
  need to do that via a function pointer and void* type conversions.

src/cmd/ksh93/bltins/typeset.c,
src/cmd/ksh93/sh/name.c,
src/cmd/ksh93/sh/nvdisc.c:
- 'struct adata' and 'struct tdata', defined as local struct types
  in these files, need to have their first three fields in common,
  the first being a pointer to sh. This is because scanfilter() in
  name.c accesses these fields via a type conversion. So the sh
  field needed to be removed in all three at the same time.
  TODO: de-obfuscate: good practice definition via a header file.

src/cmd/ksh93/sh/path.c:
- Naming consistency: reserve the path_ function name prefix for
  externs and rename statics with that prefix.
- The default path was sometimes referred to as the standard path.
  To use one term, rename std_path to defpath and onstdpath() to
  ondefpath().
- De-obfuscate SHOPT_PFSH conditional code by only calling
  pf_execve() (was path_pfexecve()) if that is compiled in.

src/cmd/ksh93/include/streval.h,
src/cmd/ksh93/sh/streval.c:
- Rename extern strval() to arith_strval() for consistency.

src/cmd/ksh93/sh/string.c:
- Remove outdated/incorrect isxdigit() fallback; '#ifnded isxdigit'
  is not a correct test as isxdigit() is specified as a function.
  Plus, it's part of C89/C90 which we now require. (re: ac8991e5)

src/cmd/ksh93/sh/suid_exec.c:
- Replace an incorrect reference to shgd->current_pid with
  getpid(); it cannot work as (contrary to its misleading directory
  placement) suid_exec is an independent libast program with no
  link to ksh or libshell at all. However, no one noticed because
  this was in fallback code for ancient systems without
  setreuid(2). Since that standard function was specified in POSIX
  Issue 4 Version 2 from 1994, we should remove that fallback code
  sometime as part of another obsolete code cleanup operation to
  avoid further bit rot. (re: 843b546c)

src/cmd/ksh93/bltins/print.c: genformat():
- Remove preformat[] which was always empty and had no effect.

src/cmd/ksh93/shell.3:
- Minor copy-edit.
- Remove documentation for nonexistent sh.infile_name. A search
  through ast-open-archive[*] reveals this never existed at all.
- Document sh.savexit (== $?).

src/cmd/ksh93/shell.3,
src/cmd/ksh93/include/shell.h,
src/cmd/ksh93/sh/init.c:
- Remove sh.gd/shgd; this is now unused and was never documented
  or exposed in the shell.h public interface.
- sh_sigcheck() was documented in shell.3 as taking no arguments
  whereas in the actual code it took a shp argument. I decided to
  go with the documentation.
- That leaves sh_parse() as the only documented function that still
  takes an shp argument. I'm just going to go ahead and remove it
  for consistency, reverting sh_parse() to its pre-2003 spec.
- Remove undocumented/unused sh_bltin_tree() function which simply
  returned sh.bltin_tree.
- Bump SH_VERSION to 20220106.
2022-01-07 16:16:31 +00:00
Martijn Dekker
2d3ec8b67a [shp cleanup 00] Reunify the original sh state struct
As observed previously (see 3654ee73, 7e6bbf85, 79d19458), the ksh
93u+ codebase on which we rebased development was in a transition:
AT&T evidently wanted to make it possible to have several shell
interpreter states in the same process, which in theory would have
made it possible to start a complete new shell (not just a
subshell) without forking a new process.

This required transitioning from accessing the 'sh' state struct
directly to accessing it via pointers (usually but not always
called 'shp'), introducing a lot of bug-prone passing around of
those pointers via function arguments and other state structs.

Some of the original 'sh' struct was separated into a 'struct
shared' called 'shgd' a.k.a. 'sh.gd' (global data) instead; these
were global state variables that were going to be shared between
the different main shell environments sharing a process. Yet, for
some reason, that struct was allocated dynamically once at init
time, requiring yet another pointer to access it. <shrug>

None of this ever worked, because that transition was incomplete.
It was much further along in the ksh 93v- beta, but I don't think
it actually worked there either (not very much really did). So,
starting a new shell has always required starting a new process.

So, now that it's clear what they were trying to do, should we try
to make it work? I'm going to go with a firm "no" on that question.

Even non-forking (virtual) subshells, something quite a bit less
ambitious, were already an unmitigated nightmare of bugs. In 93u+m
we fixed a load of bugs related to those, but I'm sure there are
still many left. At the very least there are multiple memory leaks.

I think the ambition to go even further and have complete shells
running separate programs share a process, particularly given the
brittle and buggy state of the existing codebase, is evidence that
the AT&T team, in the final years, had well and truly lost the
ability to think "wait a minute, aren't we in over our heads here,
and why are we doing this again? Is this *actually* a feasible and
useful idea?"

In my view, having entirely separate programs share a process is a
*terrible*, horrible, no-good idea that takes us back to the bad
old days before Unix, when kernels and CPUs were unable to enforce
any memory access restrictions. Programmers are imperfect. If
you're going to run a new program, you need the kernel to enforce
the separation between programs, or you're just asking for memory
corruption and security holes. And that separation is enforced by
starting a new program in a new process. That's what processes are
*for*. And if you need *that* to be radically performance-optimised
then you're probably doing it wrong anyway.

(By the way, I would still argue the same for subshells, even after
we fixed many bugs in virtual subshells. But forking all subshells
would in fact cause many scripts to slow down, and the community
would surely revolt. <sigh>  Maybe I should make it a shell option
instead, so scripts can 'set -o subfork' for reliability.)

It is also unclear how they were going to make something like
'ulimit' work, which can only work in a separate process. There
was no sign of a mechanism to fork a separate program's shell
mid-execution like there is for subshells (sh_subfork()).

Anyway... I had already changed some code here and there to access
the sh state struct directly, but as of this commit I'm beginning
to properly undo this exercise in pointlessness. From now on, we're
exercising pointerlessness instead.

I'll do this in stages to make any problems introduced more
traceable. Stage 0 restores the full 'sh' state struct to its
former static glory and reverts 'shgd' as a separate entity.

src/cmd/ksh93/sh/defs.c,
src/cmd/ksh93/include/defs.h,
src/cmd/ksh93/include/shell.h
src/cmd/ksh93/Mamfile::
- Move 'struct sh_scoped' and 'struct limits' from defs.h to
  shell.h as the sh struct will need their complete definitions.
- Get rid of 'struct shared' (shgd) in defs.h; its members are
  folded back into their original place, the main Shell_t struct
  (sh) in shell.h. There are no name conflicts.
- Get rid of the _SH_PRIVATE macro in defs.h. The members it
  defines are now defined normally in the main Shell_t struct (sh)
  in shell.h.
- To make this possible, move <history.h> and "fault.h" includes
  from defs.h to shell.h and update the Mamfile accordingly.
- Turn sh_getinterp() and shgd into macros that resolve to (&sh).
  This will allow the compiler to optimise out many pointer
  dereferences already.
- Keep extern sh_getinterp() for libshell ABI compatibility.

src/cmd/ksh93/sh/init.c:
- sh_init(): Do not calloc (sh_newof) the sh or shgd structs.
- sh_getinterp(): Keep function for libshell ABI compat.
2022-01-01 02:28:06 +00:00
Johnothan King
8f9d1bec97 Add three options to 'ulimit' (#406)
This patch adds a few extra options to the ulimit command (if the OS
supports them). These options are also present in Bash, although in ksh
additional long forms of each option are available:
  ulimit -k/--kqueues   This is the maximum number of kqueues.
  ulimit -P/--npts      This is the maximum number of pseudo-terminals.
  ulimit -R/--rttime    This is the time a real-time process can run
                        before blocking, in microseconds. When the
                        limit is exceeded, the process is sent SIGXCPU.

Other changes:
- bltins/ulimit.c: Change the formatting from sfprintf and increase the
  size of the tmp buffer to prevent text from being cut off in ulimit
  -a (this was required to add ulimit -R).
- data/limits.c: Add support for using microseconds as a unit.
2021-12-28 22:02:20 +00:00
Johnothan King
0e197eee57 Fix mkservice compile errors and add SHOPT_MKSERVICE (#401)
The unused mkservice and eloop builtins are currently not built, and if
an attempt to compile them is made the build ends in failure. This
commit backports a few build fixes from ksh93v- 2012-08-24 that allow
mkservice and eloop to build (plus an additional compiler warning fix
not in ksh93v-). I've also added a new SHOPT_MKSERVICE setting (turned
off by default) so that mkservice and eloop can be built if the user
chooses to include them in their build of ksh.
2021-12-28 17:51:11 +00:00
Martijn Dekker
e072e7c170 Fix crash in xtrace while processing here-document (re: d7cada7b)
Depending on the OS, the heredoc.sh regression tests, and possibly
others, still crashed with the -x option (xtrace) on.

Analysis: The lexer crashes in lex_advance(). Something has caused
an inconsistent lexer state, and it happened earlier on, so the
backtrace is useless for figuring out where that happened.

But I think I've found it. It's the sh_mactry() call here:

src/cmd/ksh93/sh/xec.c, lines 2800 to 2807 in f7213f03
2800:   if(!(cp=nv_getval(sh_scoped(shp,PS4NOD))))
2801:           cp = "+ ";
2802:   else
2803:   {
2804:           sh_offoption(SH_XTRACE);
2805:           cp = sh_mactry(shp,cp);
2806:           sh_onoption(SH_XTRACE);
2807:   }

sh_mactry() needs to parse the contents of $PS4 to perform
expansions and command substitutions in it, which involves the
lexer. If that happens in a here-document, the lexer is in the C
function call stack, in the middle of parsing the here-document.
Result: inconsistent lexer state. Solution: save and restore lexer
state in sh_mactry().

After this commit, all regression tests should pass with the
'-x'/'--xtrace' option in use, with no errors or crashes.

Note for backporters: this fix depends both on on d7cada7b and on
the consistency fix for the Lex_t type's size applied in a7ed5d9f.

src/cmd/ksh93/include/shlex.h:
- Cosmetic fix: remove a copied & pasted backslash. (re: a7ed5d9f)

src/cmd/ksh93/sh/macro.c: sh_mactry():
- Save and restore the lexer state before letting sh_mactrim()
  indirectly parse and execute code.

src/cmd/ksh93/tests/*.sh:
- Turn off xtrace in various command substitutions that contain
  2>&1 redirections, so that the xtrace output is not caught by
  the command substitutions, causing tests to fail incorrectly.
- Turn off xtrace for a few code blocks with 2>&1 redirections,
  stopping xtrace output from being written to standard output.

Resolves: https://github.com/ksh93/ksh/issues/306 (again)
2021-12-27 04:02:25 +00:00
Martijn Dekker
91a7c2e3e9 Fix crash/freeze upon interrupting command substitution with pipe
On some systems (at least Linux and macOS):

1. Run on a command line: t=$(sleep 10|while :; do :; done)
2. Press Ctrl+C in the first 10 seconds.
3. Execute any other command substitution. The shell crashes.

Analysis: Something in the job_wait() call in the sh_subshell()
restore routine may be interrupted by a signal such as SIGINT on
Linux and macOS. Exactly what that interruptible thing is remains
to be determined. In any case, since job_wait() was invoked after
sh_popcontext(), interrupting it caused the sh_subshell() restore
routine to be aborted, resulting in an inconsistent state of the
shell. The fix is to sh_popcontext() at a later stage instead.

src/cmd/ksh93/sh/subshell.c: sh_subshell():
- Rename struct checkpt buff to checkpoint because it's clearer.
- Move the sh_popcontext() call to near the end, just after
  decreasing the subshell level counters and restoring the global
  subshell data struct to its parent. This seems like a logical
  place for it and could allow other things to be interrupted, too.
- Get rid of the if(shp->subshell) because it is known that the
  value is > 0 at this point.
- The short exit routine run if the subshell forked now needs a new
  sh_popcontext() call, because this is handled before restoring
  the virtual subshell state.
- While we're here, do a little more detransitioning from all those
  pointless shp pointers.

Fixes: https://github.com/ksh93/ksh/issues/397
2021-12-27 03:49:41 +00:00
Johnothan King
f7213f03a2 Fix multiple bugs when using 'alias -p' to print aliases (#398)
This commit was originally intended to fix just one bug with shcomp's
handling of 'alias -p', but while fixing that I found a large number
of related issues in the alias command's -p, -t and -x options. The
current patch provides bugfixes for all of the bugs listed below:

1) Listing aliases in a script with 'alias -p' or 'alias' broke
   shcomp's bytecode output:
   https://github.com/ksh93/ksh/issues/87#issuecomment-813819122

2) Listing individual aliases with the -p option doesn't work:
      $ alias foo=bar bar=foo
      $ alias foo
      foo=bar
      $ alias -p foo  # No output

3) Listing specific tracked aliases with -pt does not display them
   in a reusable format, but rather adds another tracked alias:
      $ hash -r cat vi
      $ alias -pt vi  # No output
      $ alias -pt rm
      $ alias -t
      cat=/usr/bin/cat
      rm=/usr/bin/rm
      vi=/usr/bin/vi

4) Listing all tracked aliases with -pt does not output them in a
   reusable format (the resulting command printed only creates a
   normal alias, which is different from a tracked alias):
      $ hash -r cat
      $ alias -pt
      alias cat=/usr/bin/cat  # Expected 'alias -t cat'

5) Listing a non-existent alias with -p doesn't cause an error:
      $ unalias -a
      $ alias -p notanalias  # No output
      $ echo $?
      0
      $ alias notanalias
      notanalias: alias not found
      $ echo $?
      1
      $ hash -r
      $ alias -pt notacommand  # No output
      $ echo $?
      0

6) Attempting to list 256 non-existent aliases results in exit
   status zero:
      $ unalias -a
      $ alias $(awk -v ORS= 'BEGIN { for(i=0;i<256;i++) print "x "; }')
      x: alias not found
      --cut error message--
      $ echo $?
      0

Changes:

- typeset.c: Avoid printing anything while shcomp is compiling a
  script. This is needed because the alias command is run by shcomp
  to prevent parsing issues.

- b_alias(): To avoid adding tracked aliases with -pt, set
  tdata.aflag to '+' so that setall() and other related functions
  only list tracked aliases.

- b_alias(): Set tdata.pflag to 1 so that setall() and other
  functions recognize -p was passed.

- print_value(): Add support for listing specific aliases with
  'alias -p'.

- setall(): To avoid any issues with zombie tracked aliases (see also
  the regression tests) ignore tracked alias nodes marked with the
  NV_NOALIAS attribute. This bit is set for tracked alias nodes by
  the nv_rehash() function.

- setall(): For backward compatibility, continue incrementing the
  exit status for each invalid alias and tracked alias passed. This
  was already how alias behaved when listing aliases without -p, so
  using -p shouldn't cause a change in behavior:
      $ unalias -a
      $ alias foo bar
      foo: alias not found
      bar: alias not found
      $ echo $?
      2
  To fix bug 6, the exit status is set to one if an enforced 8-bit
  exit status would be zero.

- print_namval(): Set the prefix to 'alias -t' so that listing
  tracked aliases with 'alias -pt' works correctly.

- data/msg.c and include/name.h: Add an error message for when
  'alias -pt' doesn't find a tracked alias.

- tests/alias.sh: Add a ton of regression tests for the bugs fixed in
  this commit.
2021-12-27 03:49:06 +00:00
Martijn Dekker
a1f5c99204 INIT: remove proto, ratz (re: 46593a89, 6137b99a); major cleanup
This takes another step towards cleaning up the build system. We
now do not even pretend to be theoretically compatible with
pre-1989 K&R C compilers or with C++ compilers. In practice, this
had already been broken for many years due to bit rot.

Commit 46593a89 already removed the license handling enormity that
depended on proto, so now we can cleanly remove it altogether. But
we do need to leave some backwards compatibility stubs to keep the
build system compatible with older AST code; it should remain
possible to build older ksh versions with the current build system
(the bin/ and src/cmd/INIT/ directories) for testing purposes.

So as of now there is no more __MANGLE__d rubbish in your generated
header files. This is only about a quarter of a century overdue...

This commit also includes a huge amount of code cleanup to remove
thousands of unused K&R C fallbacks and other cruft, particularly
in libast. This code base should now be a little easier to
understand for people who are familiar with a modern(ish) C
standard.

ratz is now also removed; this was a standalone and simplified 2005
version of gunzip. As of 6137b99a, none of our code uses it, even
theoretically. And the real g(un)zip is now everywhere.

src/cmd/INIT/proto.c, src/cmd/INIT/ratz.c:
- Removed.

COPYRIGHT:
- Remove zlib license; this only applied to ratz.

bin/package, src/cmd/INIT/package.sh:
- Related cleanups.
- Unset LC_ALL before invoking a new shell, respecting the user's
  locale again and avoiding multibyte character corruption on the
  command line.

src/cmd/INIT/proto.sh:
- Add stub for backwards compatibility with Mamfiles that depend on
  proto. It does nothing but pass input without modification and is
  now installed as the new arch/*/bin/proto by src/cmd/INIT/Mamfile.

src/cmd/INIT/iffe.sh:
- Ignore the proto-related -e (--package) and -p (--prototyped)
  options; keep parsing them for backwards compatibility.
- Trim the macros passed to every test to their standard C
  versions, removing K&R C and C++ versions. These are now
  considered to be for backwards compatibility only.

src/cmd/INIT/iffe.tst:
- Remove proto(1) mangling code.
  By the way, iffe can be regression-tested as follows:
        $ bin/package use   # set up environment in a child shell
        $ regress src/cmd/INIT/iffe.tst
        $ exit              # leave package environment

src/cmd/INIT/make.probe, src/cmd/INIT/probe.win32:
- Remove code to handle C++.

src/lib/libast/features/common:
- As in iffe.sh above, trim macros designed for compatibility with
  C++ and ancient C compilers to their standard C versions and
  comment that they are for backwards compatibility with AST code.
  This is needed to keep all the old ast and ksh code compiling.

src/cmd/ksh93/sh/init.c,
src/cmd/ksh93/sh/name.c:
- Clarify libshell ABI compatibility function versions of macros.
  A "proto workaround" comment in the original code mislead me into
  thinking this had something to do with the removed proto(1), but
  it's unrelated. Call the workaround macro BYPASS_MACRO instead.

src/cmd/ksh93/include/defs.h:
- sh_sigcheck() macro: allow &sh as an argument: parenthesise shp.

src/cmd/ksh93/sh/nvtype.c:
- Remove unused nv_mkstruct() function. (re: d0a5cab1)

**/features/*:
- Remove obsolete iffe 'set prototyped' option.

**/Mamfile:
- Remove all references to the ast/prototyped.h header.
- Remove all use of the proto command. Simply copy instead.

*** 850-ish source files: ***
- Remove all '#pragma prototyped' directives.
- Remove all C++ compat code conditional upon defined(__cplusplus).
- Remove all use of the _ARG_ macro, which on standard C expands to
  its argument:
        #define _ARG_(x)        x
  (on K&R C, it expanded to nothing)
- Remove all use of _BEGIN_EXTERNS_ and _END_EXTERNS_ macros (empty
  on standard C; this was for C++ compatibility)
- Reduce all #if __STD_C (standard code) #else (K&R code) #endif
  blocks to the standard code only, without use of the macro.
- Same for _STD_ macro which seems to have had the same function.
- Change all instances of 'Void_t' to standard 'void'.
2021-12-24 07:05:22 +00:00
Johnothan King
3785a0685c Fix process substitutions printing PIDs in profile scripts (#395)
- sh/args.c: A process substitution run in a profile script may print
  its PID as if it was a command spawned with '&'. Reproducer:
     $ cat /tmp/env
     true >(false)
     $ ENV=/tmp/env ksh
     [1]	730227
     $
  This bug is fixed by turning off the SH_PROFILE state while running
  a process substitution.

- sh/subshell.c: The SH_INTERACTIVE fix in 3525535e renders the extra
  check for SH_PROFILE redundant, so it has been removed.

- tests/io.sh: Update the procsub PIDs test to also check the result
  after using process substitution in a profile script.
2021-12-22 13:27:00 +00:00