Merge remote-tracking branch 'mksh/master'

This commit is contained in:
KO Myung-Hun 2016-06-29 08:17:29 +09:00
commit 0d730e1ad9
10 changed files with 361 additions and 182 deletions

View File

@ -1,5 +1,5 @@
#!/bin/sh
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.697 2016/03/04 18:28:39 tg Exp $'
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.698 2016/06/25 23:49:12 tg Exp $'
#-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015, 2016
@ -1648,9 +1648,12 @@ ac_ifcpp 'ifdef MKSH_NOPROSPECTOFWORK' isset_MKSH_NOPROSPECTOFWORK '' \
check_categories="$check_categories arge nojsig"
ac_ifcpp 'ifdef MKSH_ASSUME_UTF8' isset_MKSH_ASSUME_UTF8 '' \
'if the default UTF-8 mode is specified' && : "${HAVE_SETLOCALE_CTYPE=0}"
ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
'if traditional/conservative fd use is requested' && \
check_categories="$check_categories convfds"
if ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
'if traditional/conservative fd use is requested'; then
check_categories="$check_categories convfds"
else
echo >&2 "WARNING: not building with -DMKSH_CONSERVATIVE_FDS is deprecated"
fi
#ac_ifcpp 'ifdef MKSH_DISABLE_DEPRECATED' isset_MKSH_DISABLE_DEPRECATED '' \
# "if deprecated features are to be omitted" && \
# check_categories="$check_categories nodeprecated"
@ -2655,7 +2658,7 @@ MKSH_BINSHPOSIX if */sh or */-sh, enable set -o posix
MKSH_BINSHREDUCED if */sh or */-sh, enable set -o sh
MKSH_CLRTOEOL_STRING "\033[K"
MKSH_CLS_STRING "\033[;H\033[J"
MKSH_CONSERVATIVE_FDS fd 0-9 for scripts, shell only up to 31
MKSH_CONSERVATIVE_FDS fd 0-9 for scripts, shell only up to 31 (soon default)
MKSH_DEFAULT_EXECSHELL "/bin/sh" (do not change)
MKSH_DEFAULT_PROFILEDIR "/etc" (do not change)
MKSH_DEFAULT_TMPDIR "/tmp" (do not change)

170
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.731 2016/05/05 22:58:19 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.739 2016/06/26 00:44:55 tg Exp $
# -*- mode: sh -*-
#-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
@ -27,10 +27,10 @@
# http://svnweb.freebsd.org/base/head/bin/test/tests/legacy_test.sh?view=co&content-type=text%2Fplain
#
# Integrated testsuites from:
# (2013/12/02 20:39:44) http://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
# (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
expected-stdout:
@(#)MIRBSD KSH R52 2016/05/05
@(#)MIRBSD KSH R52 2016/06/25
description:
Check version of shell.
stdin:
@ -39,7 +39,7 @@ name: KSH_VERSION
category: shell:legacy-no
---
expected-stdout:
@(#)LEGACY KSH R52 2016/05/05
@(#)LEGACY KSH R52 2016/06/25
description:
Check version of legacy shell.
stdin:
@ -199,7 +199,7 @@ description:
stdin:
alias X='case '
alias Y=Z
X Y in 'Y') echo is y ;; Z) echo is z ; esac
X Y in 'Y') echo is y ;; Z) echo is z ;; esac
expected-stdout:
is z
---
@ -1311,6 +1311,7 @@ stdin:
(echo 38 ${IFS+x'a'y} / "${IFS+x'a'y}" .) 2>/dev/null || echo failed in 38
foo="x'a'y"; (echo 39 ${foo%*'a'*} / "${foo%*'a'*}" .) 2>/dev/null || echo failed in 39
foo="a b c"; (echo -n '40 '; ./pfs "${foo#a}"; echo .) 2>/dev/null || echo failed in 40
(foo() { return 100; }; foo; echo 41 ${#+${#:+${#?}}\ \}\}\}}) 2>/dev/null || echo failed in 41
expected-stdout:
1 }z
2 ''z}
@ -1352,6 +1353,7 @@ expected-stdout:
38 xay / x'a'y .
39 x' / x' .
40 < b c> .
41 3 }}}
---
name: expand-unglob-dblq
description:
@ -1400,6 +1402,7 @@ stdin:
(echo "$1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz") 2>/dev/null || \
echo "$1 QSTN brac -> error"
}
: '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
tl_norm 1 -
tl_norm 2 ''
tl_norm 3 x
@ -1530,6 +1533,7 @@ stdin:
(echo $1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz) 2>/dev/null || \
echo "$1 QSTN brac -> error"
}
: '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}' '}}}'
tl_norm 1 -
tl_norm 2 ''
tl_norm 3 x
@ -1637,7 +1641,7 @@ expected-exit: 1
---
name: expand-weird-1
description:
Check corner case of trim expansion vs. $# vs. ${#var}
Check corner cases of trim expansion vs. $# vs. ${#var} vs. ${var?}
stdin:
set 1 2 3 4 5 6 7 8 9 10 11
echo ${#} # value of $#
@ -1645,24 +1649,60 @@ stdin:
echo ${##1} # $# trimmed 1
set 1 2 3 4 5 6 7 8 9 10 11 12
echo ${##1}
expected-stdout:
11
2
1
2
---
name: expand-weird-2
description:
Check corner case of ${var?} vs. ${#var}
stdin:
(exit 0)
echo $? = ${#?} .
(exit 111)
echo $? = ${#?} .
expected-stdout:
11
2
1
2
0 = 1 .
111 = 3 .
---
name: expand-weird-2
description:
Check more substitution and extension corner cases
stdin:
:& set -C; pid=$$; sub=$!; flg=$-; set -- i; exec 3>x.tmp
#echo "D: !=$! #=$# \$=$$ -=$- ?=$?"
echo >&3 3 = s^${!-word} , ${#-word} , p^${$-word} , f^${--word} , ${?-word} .
echo >&3 4 = ${!+word} , ${#+word} , ${$+word} , ${-+word} , ${?+word} .
echo >&3 5 = s^${!=word} , ${#=word} , p^${$=word} , f^${-=word} , ${?=word} .
echo >&3 6 = s^${!?word} , ${#?word} , p^${$?word} , f^${-?word} , ${??word} .
echo >&3 7 = sl^${#!} , ${##} , pl^${#$} , fl^${#-} , ${#?} .
echo >&3 8 = sw^${%!} , ${%#} , pw^${%$} , fw^${%-} , ${%?} .
echo >&3 9 = ${!!} , s^${!#} , ${!$} , s^${!-} , s^${!?} .
echo >&3 10 = s^${!#pattern} , ${##pattern} , p^${$#pattern} , f^${-#pattern} , ${?#pattern} .
echo >&3 11 = s^${!%pattern} , ${#%pattern} , p^${$%pattern} , f^${-%pattern} , ${?%pattern} .
echo >&3 12 = $# : ${##} , ${##1} .
set --
echo >&3 14 = $# : ${##} , ${##1} .
set -- 1 2 3 4 5
echo >&3 16 = $# : ${##} , ${##1} .
set -- 1 2 3 4 5 6 7 8 9 a b c d e
echo >&3 18 = $# : ${##} , ${##1} .
exec 3>&-
<x.tmp sed \
-e "s/ pl^${#pid} / PID /g" -e "s/ sl^${#sub} / SUB /g" -e "s/ fl^${#flg} / FLG /g" \
-e "s/ pw^${%pid} / PID /g" -e "s/ sw^${%sub} / SUB /g" -e "s/ fw^${%flg} / FLG /g" \
-e "s/ p^$pid / PID /g" -e "s/ s^$sub / SUB /g" -e "s/ f^$flg / FLG /g"
expected-stdout:
3 = SUB , 1 , PID , FLG , 0 .
4 = word , word , word , word , word .
5 = SUB , 1 , PID , FLG , 0 .
6 = SUB , 1 , PID , FLG , 0 .
7 = SUB , 1 , PID , FLG , 1 .
8 = SUB , 1 , PID , FLG , 1 .
9 = ! , SUB , $ , SUB , SUB .
10 = SUB , 1 , PID , FLG , 0 .
11 = SUB , 1 , PID , FLG , 0 .
12 = 1 : 1 , .
14 = 0 : 1 , 0 .
16 = 5 : 1 , 5 .
18 = 14 : 2 , 4 .
---
name: expand-weird-3
description:
Check that trimming works with positional parameters (Debian #48453)
@ -2573,6 +2613,10 @@ stdin:
eval "$fnd"
foo
print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |"
x=y
foo
typeset -f foo
print -r -- "| vc={$vc} vd={$vd} |"
# check append
v=<<-
vapp1
@ -2598,6 +2642,19 @@ expected-stdout:
} vc={=c u \x40=
} vd={=d $x \x40=
} |
function foo {
vc=<<-
=c $x \x40=
<<
vd=<<-""
=d $x \x40=
}
| vc={=c y \x40=
} vd={=d $x \x40=
} |
| vapp1^vapp2^ |
---
name: heredoc-12
@ -2755,6 +2812,28 @@ expected-stdout:
C:echo line 5
x-en
---
name: heredoc-comsub-6
description:
Check here documents and here strings can be used
without a specific command, like $(<) (extension)
stdin:
foo=bar
x=$(<<<EO${foo}F)
echo "3<$x>"
y=$(<<-EOF
hi!
$foo) is not a problem
EOF)
echo "7<$y>"
expected-stdout:
3<EObarF>
7<hi!
bar) is not a problem>
---
name: heredoc-subshell-1
description:
Tests for here documents in subshells, taken from Austin ML
@ -8909,17 +8988,45 @@ name: print-funny-chars
description:
Check print builtin's capability to output designated characters
stdin:
print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
print '<\x00>'
print '<\x01>'
print '<\u0000>'
print '<\u0001>'
{
print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
print '<\x00>'
print '<\x01>'
print '<\u0000>'
print '<\u0001>'
} | {
# integer-base-one-3Ar
typeset -Uui16 -Z11 pos=0
typeset -Uui16 -Z5 hv=2147483647
dasc=
if read -arN -1 line; then
typeset -i1 line
i=0
while (( i < ${#line[*]} )); do
hv=${line[i++]}
if (( (pos & 15) == 0 )); then
(( pos )) && print "$dasc|"
print -n "${pos#16#} "
dasc=' |'
fi
print -n "${hv#16#} "
if (( (hv < 32) || (hv > 126) )); then
dasc=$dasc.
else
dasc=$dasc${line[i-1]#1#}
fi
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
fi
while (( pos & 15 )); do
print -n ' '
(( (pos++ & 15) == 7 )) && print -n -- '- '
done
(( hv == 2147483647 )) || print "$dasc|"
}
expected-stdout:
<däÛÃâ¬Ã@>
<>
<>
<>
<>
00000000 3C 64 E4 DB C3 9B E2 82 - AC C3 9B 40 3E 0A 3C 00 |<d.........@>.<.|
00000010 3E 0A 3C 01 3E 0A 3C 00 - 3E 0A 3C 01 3E 0A |>.<.>.<.>.<.>.|
---
name: print-bksl-c
description:
@ -11973,6 +12080,17 @@ expected-stdout:
done
expected-stderr-pattern: /.*-x.*option/
---
name: utilities-getopts-3
description:
Check unsetting OPTARG
stdin:
set -- -x arg -y
getopts x:y opt && echo "${OPTARG-unset}"
getopts x:y opt && echo "${OPTARG-unset}"
expected-stdout:
arg
unset
---
name: wcswidth-1
description:
Check the new wcswidth feature

76
eval.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.187 2016/05/05 22:45:57 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.189 2016/06/26 00:44:57 tg Exp $");
/*
* string expansion
@ -1061,7 +1061,7 @@ varsub(Expand *xp, const char *sp, const char *word,
int c;
int state; /* next state: XBASE, XARG, XSUB, XNULLSUB */
int stype; /* substitution type */
int slen;
int slen = 0;
const char *p;
struct tbl *vp;
bool zero_ok = false;
@ -1140,10 +1140,24 @@ varsub(Expand *xp, const char *sp, const char *word,
xp->str = shf_smprintf("%d", c);
return (XSUB);
}
if (stype == '!' && c != '\0' && *word == CSUBST) {
sp++;
if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
p[2] == ']') {
c = '!';
stype = 0;
goto arraynames;
}
xp->var = global(sp);
xp->str = p ? shf_smprintf("%s[%lu]",
xp->var->name, arrayindex(xp->var)) : xp->var->name;
*stypep = 0;
return (XSUB);
}
/* Check for qualifiers in word part */
stype = 0;
c = word[slen = 0] == CHAR ? word[1] : 0;
c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
if (c == ':') {
slen += 2;
stype = 0x80;
@ -1182,8 +1196,6 @@ varsub(Expand *xp, const char *sp, const char *word,
return (-1);
if (!stype && *word != CSUBST)
return (-1);
*stypep = stype;
*slenp = slen;
c = sp[0];
if (c == '*' || c == '@') {
@ -1230,9 +1242,9 @@ varsub(Expand *xp, const char *sp, const char *word,
case 0x100 | 'Q':
return (-1);
}
c = 0;
arraynames:
XPinit(wv, 32);
if ((c = sp[0]) == '!')
++sp;
vp = global(arrayname(sp));
for (; vp; vp = vp->u.array) {
if (!(vp->flag&ISSET))
@ -1254,23 +1266,12 @@ varsub(Expand *xp, const char *sp, const char *word,
state = XARG;
}
} else {
/* Can't assign things like $! or $1 */
if ((stype & 0x17F) == '=' &&
xp->var = global(sp);
xp->str = str_val(xp->var);
/* can't assign things like $! or $1 */
if ((stype & 0x17F) == '=' && !*xp->str &&
ctype(*sp, C_VAR1 | C_DIGIT))
return (-1);
if (*sp == '!' && sp[1] && !ctype(sp[1], C_VAR1)) {
++sp;
xp->var = global(sp);
if (vstrchr(sp, '['))
xp->str = shf_smprintf("%s[%lu]",
xp->var->name,
arrayindex(xp->var));
else
xp->str = xp->var->name;
} else {
xp->var = global(sp);
xp->str = str_val(xp->var);
}
state = XSUB;
}
@ -1288,6 +1289,8 @@ varsub(Expand *xp, const char *sp, const char *word,
if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
(ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
errorf("%s: parameter not set", sp);
*stypep = stype;
*slenp = slen;
return (state);
}
@ -1323,14 +1326,31 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
struct ioword *io = *t->ioact;
char *name;
if ((io->ioflag & IOTYPE) != IOREAD)
switch (io->ioflag & IOTYPE) {
case IOREAD:
shf = shf_open(name = evalstr(io->ioname, DOTILDE),
O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
if (shf == NULL)
warningf(!Flag(FTALKING), "%s: %s %s: %s",
name, "can't open", "$(<...) input",
cstrerror(errno));
break;
case IOHERE:
if (!herein(io, &name)) {
xp->str = name;
/* as $(…) requires, trim trailing newlines */
name += strlen(name);
while (name > xp->str && name[-1] == '\n')
--name;
*name = '\0';
return (XSUB);
}
shf = NULL;
break;
default:
errorf("%s: %s", T_funny_command,
snptreef(NULL, 32, "%R", io));
shf = shf_open(name = evalstr(io->ioname, DOTILDE), O_RDONLY,
0, SHF_MAPHI | SHF_CLEXEC);
if (shf == NULL)
warningf(!Flag(FTALKING), "%s: %s %s: %s", name,
"can't open", "$(<...) input", cstrerror(errno));
}
} else if (fn == FUNSUB) {
int ofd1;
struct temp *tf = NULL;

59
exec.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.173 2016/04/09 16:41:07 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.175 2016/06/26 00:44:58 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@ -34,7 +34,6 @@ static int comexec(struct op *, struct tbl * volatile, const char **,
static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
static int call_builtin(struct tbl *, const char **, const char *, bool);
static int iosetup(struct ioword *, struct tbl *);
static int herein(struct ioword *, char **);
static const char *do_selectargs(const char **, bool);
static Test_op dbteste_isa(Test_env *, Test_meta);
static const char *dbteste_getopnd(Test_env *, Test_op, bool);
@ -60,7 +59,6 @@ execute(struct op * volatile t,
const char *s, *ccp;
struct ioword **iowp;
struct tbl *tp = NULL;
char *cp;
if (t == NULL)
return (0);
@ -79,11 +77,18 @@ execute(struct op * volatile t,
/* we want to run an executable, do some variance checks */
if (t->type == TCOM) {
/*
* Clear subst_exstat before argument expansion. Used by
* null commands (see comexec() and c_eval()) and by c_set().
*/
subst_exstat = 0;
/* for $LINENO */
current_lineno = t->lineno;
/* check if this is 'var=<<EOF' */
/*XXX this is broken, dont use! */
/*XXX https://bugs.launchpad.net/mksh/+bug/1380389 */
if (
/* we have zero arguments, i.e. no programme to run */
/* we have zero arguments, i.e. no program to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
t->vars[0] != NULL && t->vars[1] == NULL &&
@ -97,42 +102,20 @@ execute(struct op * volatile t,
/* and has no right-hand side (i.e. "varname=") */
ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
/* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
ccp[3] == '=' && ccp[4] == EOS)) &&
/* plus we can have a here document content */
herein(t->ioact[0], &cp) == 0 && cp && *cp) {
char *sp = cp, *dp;
size_t n = ccp - t->vars[0] + (ccp[1] == '+' ? 4 : 2);
size_t z;
ccp[3] == '=' && ccp[4] == EOS))) {
char *cp, *dp;
/* drop redirection (will be garbage collected) */
t->ioact = NULL;
/* set variable to its expanded value */
z = strlen(cp);
if (notoktomul(z, 2) || notoktoadd(z * 2, n + 1))
internal_errorf(Toomem, (size_t)-1);
dp = alloc(z * 2 + n + 1, APERM);
memcpy(dp, t->vars[0], n);
t->vars[0] = dp;
dp += n;
while (*sp) {
*dp++ = QCHAR;
*dp++ = *sp++;
}
*dp = EOS;
if ((rv = herein(t->ioact[0], &cp) /*? 1 : 0*/))
cp = NULL;
dp = shf_smprintf("%s%s", evalstr(t->vars[0],
DOASNTILDE | DOSCALAR), rv ? null : cp);
typeset(dp, Flag(FEXPORT) ? EXPORT : 0, 0, 0, 0);
/* free the expanded value */
afree(cp, APERM);
afree(dp, ATEMP);
goto Break;
}
/*
* Clear subst_exstat before argument expansion. Used by
* null commands (see comexec() and c_eval()) and by c_set().
*/
subst_exstat = 0;
/* for $LINENO */
current_lineno = t->lineno;
/*
* POSIX says expand command words first, then redirections,
* and assignments last..
@ -1615,7 +1598,7 @@ hereinval(struct ioword *iop, int sub, char **resbuf, struct shf *shf)
return (0);
}
static int
int
herein(struct ioword *iop, char **resbuf)
{
int fd = -1;

39
funcs.c
View File

@ -38,7 +38,7 @@
#endif
#endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.295 2016/02/26 20:56:43 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.297 2016/06/26 00:44:25 tg Exp $");
#if HAVE_KILLPG
/*
@ -2004,20 +2004,20 @@ c_read(const char **wp)
}
#endif
bytesread = blocking_read(fd, xp, bytesleft);
if (bytesread == (size_t)-1) {
/* interrupted */
if (errno == EINTR && fatal_trap_check()) {
/*
* Was the offending signal one that would
* normally kill a process? If so, pretend
* the read was killed.
*/
rv = 2;
goto c_read_out;
if ((bytesread = blocking_read(fd, xp, bytesleft)) == (size_t)-1) {
if (errno == EINTR) {
/* check whether the signal would normally kill */
if (!fatal_trap_check()) {
/* no, just ignore the signal */
goto c_read_readloop;
}
/* pretend the read was killed */
} else {
/* unexpected error */
bi_errorf("%s", cstrerror(errno));
}
/* just ignore the signal */
goto c_read_readloop;
rv = 2;
goto c_read_out;
}
c_read_didread:
@ -2855,6 +2855,7 @@ c_test(const char **wp)
int argc, rv, invert = 0;
Test_env te;
Test_op op;
Test_meta tm;
const char *lhs, **swp;
te.flags = 0;
@ -2915,6 +2916,16 @@ c_test(const char **wp)
rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
goto ptest_out;
}
if (ptest_isa(&te, tm = TM_AND) || ptest_isa(&te, tm = TM_OR)) {
/* XSI */
argc = test_eval(&te, TO_STNZE, lhs, NULL, true);
rv = test_eval(&te, TO_STNZE, *te.pos.wp++, NULL, true);
if (tm == TM_AND)
rv = argc && rv;
else
rv = argc || rv;
goto ptest_out;
}
/* back up to lhs */
te.pos.wp = swp;
if (ptest_isa(&te, TM_NOT)) {

45
lex.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.224 2016/05/05 22:45:58 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.225 2016/06/25 23:55:00 tg Exp $");
/*
* states while lexing word
@ -1574,14 +1574,29 @@ get_brace_var(XString *wsp, char *wp)
c = getsc();
/* State machine to figure out where the variable part ends. */
switch (state) {
case PS_SAW_BANG:
if (ctype(c, C_VAR1))
goto out;
case PS_SAW_HASH:
if (ctype(c, C_VAR1)) {
char c2;
if (0)
/* FALLTHROUGH */
c2 = getsc();
ungetsc(c2);
if (c2 != /*{*/ '}') {
ungetsc(c);
goto out;
}
}
goto ps_common;
case PS_SAW_BANG:
switch (c) {
case '@':
case '#':
case '-':
case '?':
goto out;
}
goto ps_common;
case PS_INITIAL:
switch (c) {
switch (c) {
case '%':
state = PS_SAW_PERCENT;
goto next;
@ -1594,24 +1609,12 @@ get_brace_var(XString *wsp, char *wp)
}
/* FALLTHROUGH */
case PS_SAW_PERCENT:
case PS_SAW_HASH:
ps_common:
if (ksh_isalphx(c))
state = PS_IDENT;
else if (ksh_isdigit(c))
state = PS_NUMBER;
else if (c == '#') {
if (state == PS_SAW_HASH) {
char c2;
c2 = getsc();
ungetsc(c2);
if (c2 != /*{*/ '}') {
ungetsc(c);
goto out;
}
}
state = PS_VAR1;
} else if (ctype(c, C_VAR1))
else if (ctype(c, C_VAR1))
state = PS_VAR1;
else
goto out;

73
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.395 2016/05/05 22:45:58 tg Exp $
.\" $MirOS: src/bin/mksh/mksh.1,v 1.399 2016/06/25 23:52:06 tg Exp $
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
@ -76,7 +76,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always.
.\"
.Dd $Mdocdate: May 5 2016 $
.Dd $Mdocdate: June 25 2016 $
.\"
.\" Check which macro package we use, and do other -mdoc setup.
.\"
@ -2456,16 +2456,19 @@ This is called a here string.
Standard input is duplicated from file descriptor
.Ar fd .
.Ar fd
can be a number, indicating the number of an existing file descriptor;
can be a single digit, indicating the number of an existing file descriptor;
the letter
.Ql p ,
indicating the file descriptor associated with the output of the current
co-process; or the character
.Ql \- ,
indicating standard input is to be closed.
Note that
.Ar fd
is limited to a single digit in most shell implementations.
.Pp
Note that the current version of
.Nm
supports some two-digit fd numbers in some environments;
this feature is deprecated and will be removed from a subsequent release
in favour of a ksh93-/perl-style "named file descriptors" feature.
.It \*(Gt& Ns Ar fd
Same as
.Ic \*(Lt& ,
@ -2477,7 +2480,7 @@ This is a deprecated (legacy) GNU
.Nm bash
extension supported by
.Nm
which also supports the preceding explicit fd number, for example,
which also supports the preceding explicit fd digit, for example,
.Ic 3&\*(Gt Ns Ar file
is the same as
.Ic 3\*(Gt Ns Ar file 2\*(Gt&3
@ -2506,7 +2509,7 @@ extensions.
In any of the above redirections, the file descriptor that is redirected
(i.e. standard input or standard output)
can be explicitly given by preceding the
redirection with a number (portably, only a single digit).
redirection with a single digit.
Parameter, command, and arithmetic
substitutions, tilde substitutions, and (if the shell is interactive)
file name generation are all performed on the
@ -2723,9 +2726,15 @@ Rotate left (right); the result is similar to shift (see
.Ic \*(Lt\*(Lt )
except that the bits shifted out at one end are shifted in
at the other end, instead of zero or sign bits.
.Pp
.Em Note :
These operators are deprecated; in a subsequent mksh release,
.Ic \*(ha\*(Lt No and Ic \*(ha\*(Gt
.No will replace them, and Ic \*(Gt\*(Gt\*(Gt
will be an arithmetic right shift.
.It \*(Lt\*(Lt \*(Gt\*(Gt
Shift left (right); the result is the left argument with its bits shifted left
(right) by the amount given in the right argument.
Shift left (right); the result is the left argument with its bits logically
shifted left (right) by the amount given in the right argument.
.It + \- * /
Addition, subtraction, multiplication, and division.
.It %
@ -5445,16 +5454,20 @@ Note that editing command names are used only with the
command.
Furthermore, many editing commands are useful only on terminals with
a visible cursor.
The default bindings were chosen to resemble corresponding
Emacs key bindings.
The user's
.Xr tty 4
characters (e.g.\&
.Dv ERASE )
are bound to
reasonable substitutes and override the default bindings.
reasonable substitutes and override the default bindings;
their customary values are shown in parentheses below.
The default bindings were chosen to resemble corresponding
Emacs key bindings:
.Bl -tag -width Ds
.It abort: \*(haC, \*(haG
.It Xo abort:
.No INTR Pq \*(haC ,
.No \*(haG
.Xc
Abort the current command, empty the line buffer and
set the exit state to interrupted.
.It auto\-insert: Op Ar n
@ -5528,7 +5541,8 @@ command above.
Note that \*(haI is usually generated by the TAB (tabulator) key.
.It Xo delete\-char\-backward:
.Op Ar n
.No ERASE , \*(ha? , \*(haH
.No ERASE Pq \*(haH ,
.No \*(ha? , \*(haH
.Xc
Deletes
.Ar n
@ -5542,7 +5556,9 @@ Deletes
characters after the cursor.
.It Xo delete\-word\-backward:
.Op Ar n
.No WERASE , \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
.No Pfx1+ERASE Pq \*(ha[\*(haH ,
.No WERASE Pq \*(haW ,
.No \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
.Xc
Deletes
.Ar n
@ -5592,14 +5608,14 @@ Moves to the end of the history.
Moves the cursor to the end of the input line.
.It eot: \*(ha_
Acts as an end-of-file; this is useful because edit-mode input disables
normal terminal input canonicalization.
normal terminal input canonicalisation.
.It Xo eot\-or\-delete:
.Op Ar n
.No \*(haD
.No EOF Pq \*(haD
.Xc
Acts as
.Ic eot
if alone on a line; otherwise acts as
If alone on a line, same as
.Ic eot ,
otherwise,
.Ic delete\-char\-forward .
.It error: (not bound)
Error (ring the bell).
@ -5631,8 +5647,13 @@ word.
.Xc
Goes to history number
.Ar n .
.It kill\-line: KILL
.It Xo kill\-line:
.No KILL Pq \*(haU
.Xc
Deletes the entire input line.
If Ctrl-U should only delete the line up to the cursor, use:
.Pp
.D1 $ bind \-m \*(haU='\*(ha[0\*(haK'
.It kill\-region: \*(haW
Deletes the input between the cursor and the mark.
.It Xo kill\-to\-eol:
@ -5669,12 +5690,14 @@ This is only useful after an
.Ic search\-history
or
.Ic search\-history\-up .
.It no\-op: QUIT
.It Xo no\-op:
.No QUIT Pq \*(ha\e
.Xc
This does nothing.
.It prefix\-1: \*(ha[
Introduces a 2-character command sequence.
.It prefix\-2: \*(haX , \*(ha[[ , \*(ha[O
Introduces a 2-character command sequence.
Introduces a multi-character command sequence.
.It Xo prev\-hist\-word:
.Op Ar n
.No \*(ha[. , \*(ha[_
@ -6589,7 +6612,7 @@ for the in-memory portion of the history is slow, should use
.Xr memmove 3 .
.Pp
This document attempts to describe
.Nm mksh\ R52c+CVS
.Nm mksh\ R52d
and up,
.\" with vendor patches from insert-your-name-here,
compiled without any options impacting functionality, such as

9
sh.h
View File

@ -175,9 +175,9 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.771 2016/05/05 22:56:14 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.774 2016/06/26 00:44:59 tg Exp $");
#endif
#define MKSH_VERSION "R52 2016/05/05"
#define MKSH_VERSION "R52 2016/06/25"
/* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES
@ -1082,7 +1082,11 @@ EXTERN bool builtin_spec;
EXTERN char *current_wd;
/* input line size */
#ifdef MKSH_SMALL
#define LINE (4096 - ALLOC_OVERHEAD)
#else
#define LINE (16384 - ALLOC_OVERHEAD)
#endif
/*
* Minimum required space to work with on a line - if the prompt leaves
* less space than this on a line, the prompt is truncated.
@ -1748,6 +1752,7 @@ int search_access(const char *, int);
const char *search_path(const char *, const char *, int, int *);
void pr_menu(const char * const *);
void pr_list(char * const *);
int herein(struct ioword *, char **);
/* expr.c */
int evaluate(const char *, mksh_ari_t *, int, bool);
int v_evaluate(struct tbl *, const char *, volatile int, bool);

7
shf.c
View File

@ -25,7 +25,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.73 2016/05/05 22:56:15 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.74 2016/05/17 15:36:35 tg Exp $");
/* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */
@ -774,7 +774,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
size_t field, precision, len;
unsigned long lnum;
/* %#o produces the longest output */
char numbuf[(8 * sizeof(long) + 2) / 3 + 1];
char numbuf[(8 * sizeof(long) + 2) / 3 + 1 + /* NUL */ 1];
/* this stuff for dealing with the buffer */
ssize_t nwritten = 0;
@ -914,6 +914,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
integral:
flags |= FL_NUMBER;
cp = numbuf + sizeof(numbuf);
*--cp = '\0';
switch (c) {
case 'd':
@ -964,7 +965,7 @@ shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
}
}
}
len = numbuf + sizeof(numbuf) - (s = cp);
len = numbuf + sizeof(numbuf) - 1 - (s = cp);
if (flags & FL_DOT) {
if (precision > len) {
field = precision;

52
tree.c
View File

@ -23,7 +23,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.83 2016/03/04 14:26:16 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.85 2016/06/26 00:09:35 tg Exp $");
#define INDENT 8
@ -49,6 +49,7 @@ ptree(struct op *t, int indent, struct shf *shf)
struct ioword **ioact;
struct op *t1;
int i;
const char *ccp;
Chain:
if (t == NULL)
@ -56,12 +57,9 @@ ptree(struct op *t, int indent, struct shf *shf)
switch (t->type) {
case TCOM:
prevent_semicolon = false;
/*
* special-case 'var=<<EOF' (rough; see
* exec.c:execute() for full code)
*/
/* special-case 'var=<<EOF' (cf. exec.c:execute) */
if (
/* we have zero arguments, i.e. no programme to run */
/* we have zero arguments, i.e. no program to run */
t->args[0] == NULL &&
/* we have exactly one variable assignment */
t->vars[0] != NULL && t->vars[1] == NULL &&
@ -69,7 +67,13 @@ ptree(struct op *t, int indent, struct shf *shf)
t->ioact != NULL && t->ioact[0] != NULL &&
t->ioact[1] == NULL &&
/* of type "here document" (or "here string") */
(t->ioact[0]->ioflag & IOTYPE) == IOHERE) {
(t->ioact[0]->ioflag & IOTYPE) == IOHERE &&
/* the variable assignment begins with a valid varname */
(ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
/* and has no right-hand side (i.e. "varname=") */
ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
/* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
ccp[3] == '=' && ccp[4] == EOS))) {
fptreef(shf, indent, "%S", t->vars[0]);
break;
}
@ -322,26 +326,34 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
++wp;
goto wdvarput_csubst;
}
/* FALLTHROUGH */
case CHAR:
c = *wp++;
shf_putc(c, shf);
break;
case QCHAR: {
bool doq;
case QCHAR:
c = *wp++;
doq = (c == '"' || c == '`' || c == '$' || c == '\\');
if (opmode & WDS_TPUTS) {
if (quotelevel == 0)
doq = true;
} else {
doq = false;
}
if (doq)
shf_putc('\\', shf);
if (opmode & WDS_TPUTS)
switch (c) {
case '\n':
if (quotelevel == 0) {
c = '\'';
shf_putc(c, shf);
shf_putc('\n', shf);
}
break;
default:
if (quotelevel == 0)
/* FALLTHROUGH */
case '"':
case '`':
case '$':
case '\\':
shf_putc('\\', shf);
break;
}
shf_putc(c, shf);
break;
}
case COMSUB:
shf_puts("$(", shf);
cs = ")";