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

Allow proper tilde expansion overrides (#225)

Until now, when performing any tilde expansion like ~/foo or
~user/foo, ksh added a placeholder built-in command called
'.sh.tilde', ostensibly with the intention to allow users to
override it with a shell function or custom builtin. The multishell
ksh93 repo <https://github.com/multishell/ksh93/> shows this was
added sometime between 2002-06-28 and 2004-02-29. However, it has
never worked and crashed the shell.

This commit replaces that with something that works. Specific tilde
expansions can now be overridden using .set or .get discipline
functions associated with the .sh.tilde variable (see manual,
Discipline Functions).

For example, you can use either of:

.sh.tilde.set()
{
        case ${.sh.value} in
        '~tmp') .sh.value=${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}} ;;
        '~doc') .sh.value=~/Documents ;;
        '~ksh') .sh.value=/usr/local/src/ksh93/ksh ;;
        esac
}

.sh.tilde.get()
{
        case ${.sh.tilde} in
        '~tmp') .sh.value=${XDG_RUNTIME_DIR:-${TMPDIR:-/tmp}} ;;
        '~doc') .sh.value=~/Documents ;;
        '~ksh') .sh.value=/usr/local/src/ksh93/ksh ;;
        esac
}

src/cmd/ksh93/include/variables.h,
src/cmd/ksh93/data/variables.c:
- Add SH_TILDENOD for a new ${.sh.tilde} predefined variable.
  It is initially unset.

src/cmd/ksh93/sh/macro.c:
- sh_btilde(): Removed.
- tilde_expand2(): Rewritten. I started out with the tiny version
  of this function from the 2002-06-28 version of ksh. It uses the
  stack instead of sfio, which is more efficient. A bugfix for
  $HOME == '/' was retrofitted so that ~/foo does not become
  //foo instead of /foo. The rest is entirely new code.
     To implement the override functionality, it now checks if
  ${.sh.tilde} has any discipline function associated with it.
  If it does, it assigns the tilde expression to ${.sh.tilde} using
  nv_putval(), triggering the .set discipline, and then reads it
  back using nv_getval(), triggering the .get discipline. The
  resulting value is used if it is nonempty and does not still
  start with a tilde.

src/cmd/ksh93/bltins/typeset.c,
src/cmd/ksh93/tests/builtins.sh:
- Since ksh no longer adds a dummy '.sh.tilde' builtin, remove the
  ad-hoc hack that suppressed it from the output of 'builtin'.

src/cmd/ksh93/tests/tilde.sh:
- Add tests verifying everything I can think of, as well as tests
  for bugs found and fixed during this rewrite.

src/cmd/ksh93/tests/pty.sh:
- Add test verifying that the .sh.tilde.set() discipline does not
  modify the exit status value ($?) when performing tilde expansion
  as part of tab completion.

src/cmd/ksh93/sh.1:
- Instead of "tilde substitution", call the basic mechanism "tilde
  expansion", which is the term used everywhere else (including the
  1995 Bolsky/Korn ksh book).
- Document the new override feature.

Resolves: https://github.com/ksh93/ksh/issues/217
This commit is contained in:
Martijn Dekker 2021-03-17 21:07:14 +00:00 committed by GitHub
parent 595a0a5684
commit 936a1939a8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 190 additions and 76 deletions

View file

@ -689,9 +689,6 @@ v=$($SHELL 2> /dev/null +o rc -ic $'getopts a:bc: opt --man\nprint $?')
read baz <<< 'foo\\\\bar'
[[ $baz == 'foo\\bar' ]] || err_exit 'read of foo\\\\bar not getting foo\\bar'
: ~root
[[ $(builtin) == *.sh.tilde* ]] && err_exit 'builtin contains .sh.tilde'
# ======
# Check that I/O errors are detected <https://github.com/att/ast/issues/1093>
actual=$(

View file

@ -40,8 +40,6 @@ Darwin | FreeBSD | Linux )
exit 0 ;;
esac
integer lineno=1
# On some systems, the stty command does not appear to work correctly on a pty pseudoterminal.
# To avoid false regressions, we have to set 'erase' and 'kill' on the real terminal.
if test -t 0 2>/dev/null </dev/tty && stty_restore=$(stty -g </dev/tty)
@ -81,7 +79,7 @@ function tst
do if [[ $text == *debug* ]]
then print -u2 -r -- "$text"
else offset=${text/*: line +([[:digit:]]):*/\1}
err_exit "${text/: line $offset:/: line $(( lineno + offset)):}"
err\_exit "$lineno" "${text/: line $offset:/: line $(( lineno + offset)):}"
fi
done
}
@ -92,7 +90,7 @@ unset EDITOR
export VISUAL=vi PS1=':test-!: ' PS2='> ' PS4=': ' ENV=/./dev/null EXINIT= HISTFILE= TERM=dumb
if ! pty $bintrue < /dev/null
then err_exit pty command hangs on $bintrue -- tests skipped
then warning "pty command hangs on $bintrue -- tests skipped"
exit 0
fi
@ -734,6 +732,7 @@ w echo "Exit status is: $?"
u Exit status is: 1
!
# err_exit #
((SHOPT_ESH)) && ((SHOPT_VSH)) && tst $LINENO <<"!"
L crash after switching from emacs to vi mode
@ -754,5 +753,24 @@ r ^:test-2: echo Success\r\n$
r ^Success\r\n$
!
# err_exit #
((SHOPT_VSH || SHOPT_ESH)) && tst $LINENO <<"!"
L value of $? after tilde expansion in tab completion
# Make sure that a .sh.tilde.set discipline function
# cannot influence the exit status.
w [[ -o ?vi ]] || set -o emacs
w .sh.tilde.set() { true; }
w HOME=/tmp
w false ~\t
u false /tmp
w echo "Exit status is: $?"
u Exit status is: 1
w (exit 42)
w echo $? ~\t
u 42 /tmp
!
# ======
exit $((Errors<125?Errors:125))

View file

@ -19,6 +19,7 @@
########################################################################
. "${SHTESTS_COMMON:-${0%/*}/_common}"
saveHOME=$HOME
if $SHELL -c '[[ ~root == /* ]]'
then x=$(print -r -- ~root)
@ -87,12 +88,84 @@ chmod +x $tmp/tilde
nl=$'\n'
[[ $($tmp/tilde foo) == "$PWD$nl$PWD" ]] 2> /dev/null || err_exit 'tilde fails inside a script run by name'
# ======
# Tilde expansion should not change the value of $HOME.
HOME=/
: ~/foo
[[ $HOME == / ]] || err_exit "tilde expansion changes \$HOME (value: $(printf %q "$HOME"))"
# ======
# After unsetting HOME, ~ should expand to the current user's OS-configured home directory.
unset HOME
exp=~${ id -un; }
got=~
[[ $got == /* && -d $got ]] || err_exit "expansion of bare tilde breaks after unsetting HOME (value: $(printf %q "$got"))"
HOME=$tmp
[[ $got == "$exp" ]] || err_exit 'expansion of bare tilde breaks after unsetting HOME' \
"(expected $(printf %q "$exp"), got $(printf %q "$got"))"
HOME=$saveHOME
# ======
# Tilde expansion discipline function tests
# This nonfunctional mess was removed in ksh 93u+m ...
if builtin .sh.tilde 2>/dev/null
then got=$(.sh.tilde & wait "$!" 2>&1)
((!(e = $?))) || err_exit ".sh.tilde builtin crashes the shell" \
"(got status $e$( ((e>128)) && print -n / && kill -l "$e"), $(printf %q "$got"))"
fi
# ... and replaced by a proper use of discipline functions that allows customising tilde expansion.
((.sh.version >= 20210316)) &&
for disc in get set
do (
ulimit -t unlimited 2>/dev/null # fork subshell to cope with a possible crash
eval ".sh.tilde.$disc()
{
case \${.sh.${ [[ $disc == get ]] && print tilde || print value; }} in
'~tmp') .sh.value=\$tmp ;;
'~INC') .sh.value=\$((++i)) ;;
'~spc') .sh.value=$'one\ttwo three\n\tfour' ;;
'~') .sh.value=~/addition ;; # this should not recurse
esac
}"
got=~/foo
exp=$HOME/addition/foo
[[ $got == "$exp" ]] || err_exit "$disc discipline: bare tilde expansion:" \
"expected $(printf %q "$exp"), got $(printf %q "$got")"
.sh.tilde=oldvalue
got=$(print ~tmp/foo.$$; print "${.sh.tilde}")
exp=$tmp/foo.$$$'\n'$tmp
[[ $got == "$exp" ]] || err_exit "$disc discipline: result left in \${.sh.tilde}:" \
"expected $(printf %q "$tmp"), got $(printf %q "${.sh.tilde}")"
[[ ${.sh.tilde} == oldvalue ]] || err_exit "$disc discipline: \${.sh.tilde} subshell leak"
i=0
set -- ~INC ~INC ~INC ~INC ~INC
got=$#,$1,$2,$3,$4,$5
exp=5,1,2,3,4,5
[[ $got == "$exp" ]] || err_exit "$disc discipline: counter:" \
"expected $(printf %q "$exp"), got $(printf %q "$got")"
((i==5)) || err_exit "$disc discipline: counter: $i != 5"
set -- ~spc ~spc ~spc
got=$#,$1,$2,$3
exp=$'3,one\ttwo three\n\tfour,one\ttwo three\n\tfour,one\ttwo three\n\tfour'
[[ $got == "$exp" ]] || err_exit "$disc discipline: quoting of whitespace:" \
"expected $(printf %q "$exp"), got $(printf %q "$got")"
print "$Errors" >$tmp/Errors
) &
wait "$!" 2>crashmsg
if ((!(e = $?)))
then read Errors <$tmp/Errors
else err_exit ".sh.tilde.$disc discipline function crashes the shell" \
"(got status $e$( ((e>128)) && print -n / && kill -l "$e"), $(printf %q "$(<crashmsg)"))"
fi
done
# ======
exit $((Errors<125?Errors:125))

View file

@ -937,6 +937,7 @@ set -- \
".sh.math" \
".sh.pool" \
".sh.pid" \
".sh.tilde" \
"SHLVL" \
"CSWIDTH"