Merge commit '91ae6ad199035b1cf'

This commit is contained in:
KO Myung-Hun 2016-01-24 16:36:38 +09:00
commit 6eb125c18a
16 changed files with 394 additions and 252 deletions

View File

@ -1,8 +1,8 @@
#!/bin/sh #!/bin/sh
srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.693 2015/12/12 22:25:10 tg Exp $' srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.695 2016/01/02 20:11:31 tg Exp $'
#- #-
# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015 # 2011, 2012, 2013, 2014, 2015, 2016
# mirabilos <m@mirbsd.org> # mirabilos <m@mirbsd.org>
# #
# Provided that these terms and disclaimer and all copyright notices # Provided that these terms and disclaimer and all copyright notices
@ -745,6 +745,7 @@ GNU)
*tendracc*) ;; *tendracc*) ;;
*) add_cppflags -D_GNU_SOURCE ;; *) add_cppflags -D_GNU_SOURCE ;;
esac esac
add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
# define MKSH__NO_PATH_MAX to use Hurd-only functions # define MKSH__NO_PATH_MAX to use Hurd-only functions
add_cppflags -DMKSH__NO_PATH_MAX add_cppflags -DMKSH__NO_PATH_MAX
;; ;;
@ -753,6 +754,7 @@ GNU/kFreeBSD)
*tendracc*) ;; *tendracc*) ;;
*) add_cppflags -D_GNU_SOURCE ;; *) add_cppflags -D_GNU_SOURCE ;;
esac esac
add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
;; ;;
Haiku) Haiku)
add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1 add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
@ -1239,19 +1241,20 @@ dragonegg|llvm)
vv '|' "llc -version" vv '|' "llc -version"
;; ;;
esac esac
etd=" on $et"
case $et in case $et in
klibc) klibc)
add_cppflags -DMKSH_NO_LIMITS add_cppflags -DMKSH_NO_LIMITS
;; ;;
unknown) unknown)
# nothing special detected, dont worry # nothing special detected, dont worry
unset et etd=
;; ;;
*) *)
# huh? # huh?
;; ;;
esac esac
$e "$bi==> which compiler seems to be used...$ao $ui$ct${et+ on $et}$ao" $e "$bi==> which compiler seems to be used...$ao $ui$ct$etd$ao"
rmf conftest.c conftest.o conftest a.out* a.exe* conftest.exe* vv.out rmf conftest.c conftest.o conftest a.out* a.exe* conftest.exe* vv.out
# #

27
check.t
View File

@ -1,8 +1,8 @@
# $MirOS: src/bin/mksh/check.t,v 1.716 2015/12/12 23:31:15 tg Exp $ # $MirOS: src/bin/mksh/check.t,v 1.721 2016/01/20 21:34:09 tg Exp $
# -*- mode: sh -*- # -*- mode: sh -*-
#- #-
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015 # 2011, 2012, 2013, 2014, 2015, 2016
# mirabilos <m@mirbsd.org> # mirabilos <m@mirbsd.org>
# #
# Provided that these terms and disclaimer and all copyright notices # Provided that these terms and disclaimer and all copyright notices
@ -30,7 +30,7 @@
# (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://openbsd.cs.toronto.edu/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
expected-stdout: expected-stdout:
@(#)MIRBSD KSH R52 2015/12/12 @(#)MIRBSD KSH R52 2016/01/20
description: description:
Check version of shell. Check version of shell.
stdin: stdin:
@ -39,7 +39,7 @@ name: KSH_VERSION
category: shell:legacy-no category: shell:legacy-no
--- ---
expected-stdout: expected-stdout:
@(#)LEGACY KSH R52 2015/12/12 @(#)LEGACY KSH R52 2016/01/20
description: description:
Check version of legacy shell. Check version of legacy shell.
stdin: stdin:
@ -6592,12 +6592,24 @@ description:
env-setup: !HOME=/sweet! env-setup: !HOME=/sweet!
stdin: stdin:
echo ${A=a=}~ b=~ c=d~ ~ echo ${A=a=}~ b=~ c=d~ ~
export e=~ f=d~
command command export g=~ h=d~
echo ". $e . $f ."
echo ". $g . $h ."
set -o posix set -o posix
unset A unset A e f g h
echo ${A=a=}~ b=~ c=d~ ~ echo ${A=a=}~ b=~ c=d~ ~
export e=~ f=d~
command command export g=~ h=d~
echo ". $e . $f ."
echo ". $g . $h ."
expected-stdout: expected-stdout:
a=/sweet b=/sweet c=d~ /sweet a=/sweet b=/sweet c=d~ /sweet
. /sweet . d~ .
. /sweet . d~ .
a=~ b=~ c=d~ /sweet a=~ b=~ c=d~ /sweet
. /sweet . d~ .
. /sweet . d~ .
--- ---
name: tilde-expand-2 name: tilde-expand-2
description: description:
@ -8515,7 +8527,7 @@ expected-stdout:
name: varexpand-substr-3 name: varexpand-substr-3
description: description:
Check that some things that work in bash fail. Check that some things that work in bash fail.
This is by design. And that some things fail in both. This is by design. Oh and vice versa, nowadays.
stdin: stdin:
export x=abcdefghi n=2 export x=abcdefghi n=2
"$__progname" -c 'echo v${x:(n)}x' "$__progname" -c 'echo v${x:(n)}x'
@ -8523,12 +8535,13 @@ stdin:
"$__progname" -c 'echo x${x:n}x' "$__progname" -c 'echo x${x:n}x'
"$__progname" -c 'echo y${x:}x' "$__progname" -c 'echo y${x:}x'
"$__progname" -c 'echo z${x}x' "$__progname" -c 'echo z${x}x'
# next fails only in bash
"$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $? "$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $?
expected-stdout: expected-stdout:
vcdefghix vcdefghix
wcdefghix wcdefghix
zabcdefghix zabcdefghix
1 0
expected-stderr-pattern: expected-stderr-pattern:
/x:n.*bad substitution.*\n.*bad substitution/ /x:n.*bad substitution.*\n.*bad substitution/
--- ---

View File

@ -1,5 +1,5 @@
# $Id$ # $Id$
# $MirOS: src/bin/mksh/dot.mkshrc,v 1.103 2015/12/12 18:47:40 tg Exp $ # $MirOS: src/bin/mksh/dot.mkshrc,v 1.104 2015/12/31 21:00:12 tg Exp $
#- #-
# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010, # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
# 2011, 2012, 2013, 2014, 2015 # 2011, 2012, 2013, 2014, 2015
@ -596,8 +596,8 @@ done
#\unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \ #\unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
# LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME # LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
#p=en_GB.UTF-8 #p=en_GB.UTF-8
#\set -U
#\export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p #\export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
#\set -U
\unset p \unset p

13
eval.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015 * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.178 2015/12/12 22:24:07 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/eval.c,v 1.180 2016/01/19 23:12:12 tg Exp $");
/* /*
* string expansion * string expansion
@ -437,8 +437,6 @@ expand(
beg = wdcopy(sp, ATEMP); beg = wdcopy(sp, ATEMP);
mid = beg + (wdscan(sp, ADELIM) - sp); mid = beg + (wdscan(sp, ADELIM) - sp);
stg = beg + (wdscan(sp, CSUBST) - sp); stg = beg + (wdscan(sp, CSUBST) - sp);
if (mid >= stg)
goto unwind_substsyn;
mid[-2] = EOS; mid[-2] = EOS;
if (mid[-1] == /*{*/'}') { if (mid[-1] == /*{*/'}') {
sp += mid - beg - 1; sp += mid - beg - 1;
@ -446,9 +444,8 @@ expand(
} else { } else {
end = mid + end = mid +
(wdscan(mid, ADELIM) - mid); (wdscan(mid, ADELIM) - mid);
if (end >= stg || if (end[-1] != /*{*/ '}')
/* more than max delimiters */ /* more than max delimiters */
end[-1] != /*{*/ '}')
goto unwind_substsyn; goto unwind_substsyn;
end[-2] = EOS; end[-2] = EOS;
sp += end - beg - 1; sp += end - beg - 1;
@ -488,8 +485,6 @@ expand(
s = wdcopy(sp, ATEMP); s = wdcopy(sp, ATEMP);
p = s + (wdscan(sp, ADELIM) - sp); p = s + (wdscan(sp, ADELIM) - sp);
d = s + (wdscan(sp, CSUBST) - sp); d = s + (wdscan(sp, CSUBST) - sp);
if (p >= d)
goto unwind_substsyn;
p[-2] = EOS; p[-2] = EOS;
if (p[-1] == /*{*/'}') if (p[-1] == /*{*/'}')
d = NULL; d = NULL;
@ -1329,7 +1324,7 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
char *name; char *name;
if ((io->ioflag & IOTYPE) != IOREAD) if ((io->ioflag & IOTYPE) != IOREAD)
errorf("%s: %s", "funny $() command", errorf("%s: %s", T_funny_command,
snptreef(NULL, 32, "%R", io)); snptreef(NULL, 32, "%R", io));
shf = shf_open(name = evalstr(io->ioname, DOTILDE), O_RDONLY, shf = shf_open(name = evalstr(io->ioname, DOTILDE), O_RDONLY,
0, SHF_MAPHI | SHF_CLEXEC); 0, SHF_MAPHI | SHF_CLEXEC);

39
exec.c
View File

@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.168 2015/10/09 21:36:55 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/exec.c,v 1.170 2015/12/31 21:03:47 tg Exp $");
#ifndef MKSH_DEFAULT_EXECSHELL #ifndef MKSH_DEFAULT_EXECSHELL
#define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh" #define MKSH_DEFAULT_EXECSHELL MKSH_UNIXROOT "/bin/sh"
@ -1013,6 +1013,7 @@ scriptexec(struct op *tp, const char **ap)
(m == /* ECOFF_SH */ 0x0500 || m == 0x0005) || (m == /* ECOFF_SH */ 0x0500 || m == 0x0005) ||
(m == /* bzip */ 0x425A) || (m == /* "MZ" */ 0x4D5A) || (m == /* bzip */ 0x425A) || (m == /* "MZ" */ 0x4D5A) ||
(m == /* "NE" */ 0x4E45) || (m == /* "LX" */ 0x4C58) || (m == /* "NE" */ 0x4E45) || (m == /* "LX" */ 0x4C58) ||
(m == /* ksh93 */ 0x0B13) || (m == /* LZIP */ 0x4C5A) ||
(m == /* xz */ 0xFD37 && buf[2] == 'z' && buf[3] == 'X' && (m == /* xz */ 0xFD37 && buf[2] == 'z' && buf[3] == 'X' &&
buf[4] == 'Z') || (m == /* 7zip */ 0x377A) || buf[4] == 'Z') || (m == /* 7zip */ 0x377A) ||
(m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D)) (m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D))
@ -1404,7 +1405,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
int u = -1; int u = -1;
char *cp = iop->ioname; char *cp = iop->ioname;
int iotype = iop->ioflag & IOTYPE; int iotype = iop->ioflag & IOTYPE;
bool do_open = true, do_close = false; bool do_open = true, do_close = false, do_fstat = false;
int flags = 0; int flags = 0;
struct ioword iotmp; struct ioword iotmp;
struct stat statb; struct stat statb;
@ -1433,14 +1434,27 @@ iosetup(struct ioword *iop, struct tbl *tp)
break; break;
case IOWRITE: case IOWRITE:
flags = O_WRONLY | O_CREAT | O_TRUNC; if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB)) {
/* >file under set -C */
if (stat(cp, &statb)) {
/* nonexistent file */
flags = O_WRONLY | O_CREAT | O_EXCL;
} else if (S_ISREG(statb.st_mode)) {
/* regular file, refuse clobbering */
goto clobber_refused;
} else {
/* /*
* The stat() is here to allow redirections to * allow redirections to things
* things like /dev/null without error. * like /dev/null without error
*/ */
if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB) && flags = O_WRONLY;
(stat(cp, &statb) < 0 || S_ISREG(statb.st_mode))) /* but check again after opening */
flags |= O_EXCL; do_fstat = true;
}
} else {
/* >|file or set +C */
flags = O_WRONLY | O_CREAT | O_TRUNC;
}
break; break;
case IORDWR: case IORDWR:
@ -1485,6 +1499,15 @@ iosetup(struct ioword *iop, struct tbl *tp)
return (-1); return (-1);
} }
u = binopen3(cp, flags, 0666); u = binopen3(cp, flags, 0666);
if (do_fstat && u >= 0) {
/* prevent race conditions */
if (fstat(u, &statb) || S_ISREG(statb.st_mode)) {
close(u);
clobber_refused:
u = -1;
errno = EEXIST;
}
}
} }
if (u < 0) { if (u < 0) {
/* herein() may already have printed message */ /* herein() may already have printed message */

5
expr.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014 * 2011, 2012, 2013, 2014, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.80 2015/11/29 17:05:00 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/expr.c,v 1.81 2016/01/14 21:17:50 tg Exp $");
/* the order of these enums is constrained by the order of opinfo[] */ /* the order of these enums is constrained by the order of opinfo[] */
enum token { enum token {
@ -659,6 +659,7 @@ exprtoken(Expr_state *es)
es->tok = VAR; es->tok = VAR;
} else if (c == '1' && cp[1] == '#') { } else if (c == '1' && cp[1] == '#') {
cp += 2; cp += 2;
if (*cp)
cp += utf_ptradj(cp); cp += utf_ptradj(cp);
strndupx(tvar, es->tokp, cp - es->tokp, ATEMP); strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
goto process_tvar; goto process_tvar;

247
funcs.c
View File

@ -5,7 +5,7 @@
/*- /*-
* Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
* 2010, 2011, 2012, 2013, 2014, 2015 * 2010, 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -38,7 +38,7 @@
#endif #endif
#endif #endif
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.288 2015/12/12 19:27:36 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.293 2016/01/20 21:34:11 tg Exp $");
#if HAVE_KILLPG #if HAVE_KILLPG
/* /*
@ -103,7 +103,7 @@ const struct builtin mkshbuiltins[] = {
{"cd", c_cd}, {"cd", c_cd},
/* dash compatibility hack */ /* dash compatibility hack */
{"chdir", c_cd}, {"chdir", c_cd},
{"command", c_command}, {Tcommand, c_command},
{"*=continue", c_brkcont}, {"*=continue", c_brkcont},
{"echo", c_print}, {"echo", c_print},
{"*=eval", c_eval}, {"*=eval", c_eval},
@ -278,20 +278,18 @@ static void s_put(int);
int int
c_print(const char **wp) c_print(const char **wp)
{ {
#define PO_NL BIT(0) /* print newline */
#define PO_EXPAND BIT(1) /* expand backslash sequences */
#define PO_PMINUSMINUS BIT(2) /* print a -- argument */
#define PO_HIST BIT(3) /* print to history instead of stdout */
#define PO_COPROC BIT(4) /* printing to coprocess: block SIGPIPE */
int fd = 1, c; int fd = 1, c;
int flags = PO_EXPAND | PO_NL; const char *s;
const char *s, *emsg;
XString xs; XString xs;
char *xp; char *xp;
/* print newline; expand backslash sequences */
bool po_nl = true, po_exp = true;
/* print to history instead of file descriptor / stdout */
bool po_hist = false;
if (wp[0][0] == 'e') { if (wp[0][0] == 'e') {
/* echo builtin */ /* "echo" builtin */
wp++; ++wp;
#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT #ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
if (Flag(FSH)) { if (Flag(FSH)) {
/* /*
@ -299,7 +297,7 @@ c_print(const char **wp)
* one that supports -e but does not enable it by * one that supports -e but does not enable it by
* default * default
*/ */
flags = PO_NL; po_exp = false;
} }
#endif #endif
if (Flag(FPOSIX) || if (Flag(FPOSIX) ||
@ -309,14 +307,14 @@ c_print(const char **wp)
Flag(FAS_BUILTIN)) { Flag(FAS_BUILTIN)) {
/* Debian Policy 10.4 compliant "echo" builtin */ /* Debian Policy 10.4 compliant "echo" builtin */
if (*wp && !strcmp(*wp, "-n")) { if (*wp && !strcmp(*wp, "-n")) {
/* we recognise "-n" only as the first arg */ /* recognise "-n" only as the first arg */
flags = 0; po_nl = false;
wp++; ++wp;
} else }
/* otherwise, we print everything as-is */ /* print everything as-is */
flags = PO_NL; po_exp = false;
} else { } else {
int nflags = flags; bool new_exp = po_exp, new_nl = po_nl;
/** /**
* a compromise between sysV and BSD echo commands: * a compromise between sysV and BSD echo commands:
@ -329,62 +327,65 @@ c_print(const char **wp)
* quences are enabled by default. * quences are enabled by default.
*/ */
while ((s = *wp) && *s == '-' && s[1]) { print_tradparse_arg:
while (*++s) if ((s = *wp) && *s++ == '-' && *s) {
if (*s == 'n') print_tradparse_ch:
nflags &= ~PO_NL; switch ((c = *s++)) {
else if (*s == 'e') case 'E':
nflags |= PO_EXPAND; new_exp = false;
else if (*s == 'E') goto print_tradparse_ch;
nflags &= ~PO_EXPAND; case 'e':
else new_exp = true;
/* goto print_tradparse_ch;
* bad option: don't use case 'n':
* nflags, print argument new_nl = false;
*/ goto print_tradparse_ch;
break; case '\0':
po_exp = new_exp;
if (*s) po_nl = new_nl;
break; ++wp;
wp++; goto print_tradparse_arg;
flags = nflags; }
} }
} }
} else { } else {
int optc; /* "print" builtin */
const char *opts = "Rnprsu,"; const char *opts = "npRrsu,";
const char *emsg;
/* print a "--" argument */
bool po_pminusminus = false;
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1) while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1)
switch (optc) { switch (c) {
case 'R':
/* fake BSD echo command */
flags |= PO_PMINUSMINUS;
flags &= ~PO_EXPAND;
opts = "ne";
break;
case 'e': case 'e':
flags |= PO_EXPAND; po_exp = true;
break; break;
case 'n': case 'n':
flags &= ~PO_NL; po_nl = false;
break; break;
case 'p': case 'p':
if ((fd = coproc_getfd(W_OK, &emsg)) < 0) { if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
bi_errorf("%s: %s", "-p", emsg); bi_errorf("-p: %s", emsg);
return (1); return (1);
} }
break; break;
case 'R':
/* fake BSD echo command */
po_pminusminus = true;
po_exp = false;
opts = "en";
break;
case 'r': case 'r':
flags &= ~PO_EXPAND; po_exp = false;
break; break;
case 's': case 's':
flags |= PO_HIST; po_hist = true;
break; break;
case 'u': case 'u':
if (!*(s = builtin_opt.optarg)) if (!*(s = builtin_opt.optarg))
fd = 0; fd = 0;
else if ((fd = check_fd(s, W_OK, &emsg)) < 0) { else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
bi_errorf("%s: %s: %s", "-u", s, emsg); bi_errorf("-u%s: %s", s, emsg);
return (1); return (1);
} }
break; break;
@ -393,22 +394,23 @@ c_print(const char **wp)
} }
if (!(builtin_opt.info & GI_MINUSMINUS)) { if (!(builtin_opt.info & GI_MINUSMINUS)) {
/* treat a lone - like -- */ /* treat a lone "-" like "--" */
if (wp[builtin_opt.optind] && if (wp[builtin_opt.optind] &&
ksh_isdash(wp[builtin_opt.optind])) ksh_isdash(wp[builtin_opt.optind]))
builtin_opt.optind++; builtin_opt.optind++;
} else if (flags & PO_PMINUSMINUS) } else if (po_pminusminus)
builtin_opt.optind--; builtin_opt.optind--;
wp += builtin_opt.optind; wp += builtin_opt.optind;
} }
Xinit(xs, xp, 128, ATEMP); Xinit(xs, xp, 128, ATEMP);
while (*wp != NULL) { if (*wp != NULL) {
print_read_arg:
s = *wp; s = *wp;
while ((c = *s++) != '\0') { while ((c = *s++) != '\0') {
Xcheck(xs, xp); Xcheck(xs, xp);
if ((flags & PO_EXPAND) && c == '\\') { if (po_exp && c == '\\') {
s_ptr = s; s_ptr = s;
c = unbksl(false, s_get, s_put); c = unbksl(false, s_get, s_put);
s = s_ptr; s = s_ptr;
@ -416,11 +418,11 @@ c_print(const char **wp)
/* rejected by generic function */ /* rejected by generic function */
switch ((c = *s++)) { switch ((c = *s++)) {
case 'c': case 'c':
flags &= ~PO_NL; po_nl = false;
/* AT&T brain damage */ /* AT&T brain damage */
continue; continue;
case '\0': case '\0':
s--; --s;
c = '\\'; c = '\\';
break; break;
default: default:
@ -440,18 +442,22 @@ c_print(const char **wp)
} }
Xput(xs, xp, c); Xput(xs, xp, c);
} }
if (*++wp != NULL) if (*++wp != NULL) {
Xput(xs, xp, ' '); Xput(xs, xp, ' ');
goto print_read_arg;
} }
if (flags & PO_NL) }
if (po_nl)
Xput(xs, xp, '\n'); Xput(xs, xp, '\n');
if (flags & PO_HIST) { c = 0;
if (po_hist) {
Xput(xs, xp, '\0'); Xput(xs, xp, '\0');
histsave(&source->line, Xstring(xs, xp), HIST_STORE, false); histsave(&source->line, Xstring(xs, xp), HIST_STORE, false);
Xfree(xs, xp); Xfree(xs, xp);
} else { } else {
int len = Xlength(xs, xp); size_t len = Xlength(xs, xp);
bool po_coproc = false;
int opipe = 0; int opipe = 0;
/* /*
@ -461,30 +467,36 @@ c_print(const char **wp)
* not enough). * not enough).
*/ */
if (coproc.write >= 0 && coproc.write == fd) { if (coproc.write >= 0 && coproc.write == fd) {
flags |= PO_COPROC; po_coproc = true;
opipe = block_pipe(); opipe = block_pipe();
} }
for (s = Xstring(xs, xp); len > 0; ) {
if ((c = write(fd, s, len)) < 0) { s = Xstring(xs, xp);
if (flags & PO_COPROC) while (len > 0) {
restore_pipe(opipe); ssize_t nwritten;
if ((nwritten = write(fd, s, len)) < 0) {
if (errno == EINTR) { if (errno == EINTR) {
/* allow user to ^C out */ if (po_coproc)
restore_pipe(opipe);
/* give the user a chance to ^C out */
intrcheck(); intrcheck();
if (flags & PO_COPROC) /* interrupted, try again */
if (po_coproc)
opipe = block_pipe(); opipe = block_pipe();
continue; continue;
} }
return (1); c = 1;
break;
} }
s += c; s += nwritten;
len -= c; len -= nwritten;
} }
if (flags & PO_COPROC) if (po_coproc)
restore_pipe(opipe); restore_pipe(opipe);
} }
return (0); return (c);
} }
static int static int
@ -1851,13 +1863,15 @@ c_read(const char **wp)
enum { LINES, BYTES, UPTO, READALL } readmode = LINES; enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
char delim = '\n'; char delim = '\n';
size_t bytesleft = 128, bytesread; size_t bytesleft = 128, bytesread;
struct tbl *vp /* FU gcc */ = NULL, *vq; struct tbl *vp /* FU gcc */ = NULL, *vq = NULL;
char *cp, *allocd = NULL, *xp; char *cp, *allocd = NULL, *xp;
const char *ccp; const char *ccp;
XString xs; XString xs;
size_t xsave = 0; size_t xsave = 0;
mksh_ttyst tios; mksh_ttyst tios;
bool restore_tios = false; bool restore_tios = false;
/* to catch read -aN2 foo[i] */
bool subarray = false;
#if HAVE_SELECT #if HAVE_SELECT
bool hastimeout = false; bool hastimeout = false;
struct timeval tv, tvlim; struct timeval tv, tvlim;
@ -2102,6 +2116,7 @@ c_read(const char **wp)
XinitN(xs, 128, ATEMP); XinitN(xs, 128, ATEMP);
if (intoarray) { if (intoarray) {
vp = global(*wp); vp = global(*wp);
subarray = last_lookup_was_array;
if (vp->flag & RDONLY) { if (vp->flag & RDONLY) {
c_read_splitro: c_read_splitro:
bi_errorf("read-only: %s", *wp); bi_errorf("read-only: %s", *wp);
@ -2110,10 +2125,10 @@ c_read(const char **wp)
afree(cp, ATEMP); afree(cp, ATEMP);
goto c_read_out; goto c_read_out;
} }
/* exporting an array is currently pointless */
unset(vp, 1);
/* counter for array index */ /* counter for array index */
c = 0; c = subarray ? arrayindex(vp) : 0;
/* exporting an array is currently pointless */
unset(vp, subarray ? 0 : 1);
} }
if (!aschars) { if (!aschars) {
/* skip initial IFS whitespace */ /* skip initial IFS whitespace */
@ -2215,6 +2230,17 @@ c_read(const char **wp)
c_read_gotword: c_read_gotword:
Xput(xs, xp, '\0'); Xput(xs, xp, '\0');
if (intoarray) { if (intoarray) {
if (subarray) {
/* array element passed, accept first read */
if (vq) {
bi_errorf("nested arrays not yet supported");
goto c_read_spliterr;
}
vq = vp;
if (c)
/* [0] doesn't */
vq->flag |= AINDEX;
} else
vq = arraysearch(vp, c++); vq = arraysearch(vp, c++);
} else { } else {
vq = global(*wp); vq = global(*wp);
@ -2355,16 +2381,14 @@ int
c_exitreturn(const char **wp) c_exitreturn(const char **wp)
{ {
int n, how = LEXIT; int n, how = LEXIT;
const char *arg;
if (ksh_getopt(wp, &builtin_opt, null) == '?') if (wp[1]) {
if (wp[2])
goto c_exitreturn_err; goto c_exitreturn_err;
arg = wp[builtin_opt.optind]; exstat = bi_getn(wp[1], &n) ? (n & 0xFF) : 1;
} else if (trap_exstat != -1)
if (arg)
exstat = bi_getn(arg, &n) ? (n & 0xFF) : 1;
else if (trap_exstat != -1)
exstat = trap_exstat; exstat = trap_exstat;
if (wp[0][0] == 'r') { if (wp[0][0] == 'r') {
/* return */ /* return */
struct env *ep; struct env *ep;
@ -2385,12 +2409,13 @@ c_exitreturn(const char **wp)
how = LSHELL; how = LSHELL;
} }
/* get rid of any i/o redirections */ /* get rid of any I/O redirections */
quitenv(NULL); quitenv(NULL);
unwind(how); unwind(how);
/* NOTREACHED */ /* NOTREACHED */
c_exitreturn_err: c_exitreturn_err:
bi_errorf("too many arguments");
return (1); return (1);
} }
@ -3664,10 +3689,11 @@ c_realpath(const char **wp)
int int
c_cat(const char **wp) c_cat(const char **wp)
{ {
int fd = STDIN_FILENO, rv, eno; int fd = STDIN_FILENO, rv;
ssize_t n, w; ssize_t n, w;
const char *fn = "<stdin>"; const char *fn = "<stdin>";
char *buf, *cp; char *buf, *cp;
int opipe = 0;
#define MKSH_CAT_BUFSIZ 4096 #define MKSH_CAT_BUFSIZ 4096
/* parse options: POSIX demands we support "-u" as no-op */ /* parse options: POSIX demands we support "-u" as no-op */
@ -3689,61 +3715,72 @@ c_cat(const char **wp)
return (1); return (1);
} }
/* catch SIGPIPE */
opipe = block_pipe();
do { do {
if (*wp) { if (*wp) {
fn = *wp++; fn = *wp++;
if (ksh_isdash(fn)) if (ksh_isdash(fn))
fd = STDIN_FILENO; fd = STDIN_FILENO;
else if ((fd = binopen2(fn, O_RDONLY)) < 0) { else if ((fd = binopen2(fn, O_RDONLY)) < 0) {
eno = errno; bi_errorf("%s: %s", fn, cstrerror(errno));
bi_errorf("%s: %s", fn, cstrerror(eno));
rv = 1; rv = 1;
continue; continue;
} }
} }
while (/* CONSTCOND */ 1) { while (/* CONSTCOND */ 1) {
n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ); if ((n = blocking_read(fd, (cp = buf),
eno = errno; MKSH_CAT_BUFSIZ)) == -1) {
if (errno == EINTR) {
restore_pipe(opipe);
/* give the user a chance to ^C out */ /* give the user a chance to ^C out */
intrcheck(); intrcheck();
if (n == -1) {
if (eno == EINTR) {
/* interrupted, try again */ /* interrupted, try again */
opipe = block_pipe();
continue; continue;
} }
/* an error occured during reading */ /* an error occured during reading */
bi_errorf("%s: %s", fn, cstrerror(eno)); bi_errorf("%s: %s", fn, cstrerror(errno));
rv = 1; rv = 1;
break; break;
} else if (n == 0) } else if (n == 0)
/* end of file reached */ /* end of file reached */
break; break;
while (n) { while (n) {
w = write(STDOUT_FILENO, cp, n); if ((w = write(STDOUT_FILENO, cp, n)) != -1) {
eno = errno; n -= w;
cp += w;
continue;
}
if (errno == EINTR) {
restore_pipe(opipe);
/* give the user a chance to ^C out */ /* give the user a chance to ^C out */
intrcheck(); intrcheck();
if (w == -1) {
if (eno == EINTR)
/* interrupted, try again */ /* interrupted, try again */
opipe = block_pipe();
continue; continue;
}
if (errno == EPIPE) {
/* fake receiving signel */
rv = ksh_sigmask(SIGPIPE);
} else {
/* an error occured during writing */ /* an error occured during writing */
bi_errorf("%s: %s", "<stdout>", bi_errorf("%s: %s", "<stdout>",
cstrerror(eno)); cstrerror(errno));
rv = 1; rv = 1;
}
if (fd != STDIN_FILENO) if (fd != STDIN_FILENO)
close(fd); close(fd);
goto out; goto out;
} }
n -= w;
cp += w;
}
} }
if (fd != STDIN_FILENO) if (fd != STDIN_FILENO)
close(fd); close(fd);
} while (*wp); } while (*wp);
out: out:
restore_pipe(opipe);
free_osfunc(buf); free_osfunc(buf);
return (rv); return (rv);
} }

View File

@ -3,7 +3,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2014, 2015 * 2011, 2012, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -27,7 +27,7 @@
#include <sys/file.h> #include <sys/file.h>
#endif #endif
__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.151 2015/11/29 17:05:01 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.152 2016/01/14 23:18:08 tg Exp $");
Trap sigtraps[ksh_NSIG + 1]; Trap sigtraps[ksh_NSIG + 1];
static struct sigaction Sigact_ign; static struct sigaction Sigact_ign;
@ -1213,7 +1213,7 @@ fatal_trap_check(void)
do { do {
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL))) if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
/* return value is used as an exit code */ /* return value is used as an exit code */
return (128 + p->signal); return (ksh_sigmask(p->signal));
++p; ++p;
} while (--i); } while (--i);
return (0); return (0);

11
jobs.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
* 2012, 2013, 2014, 2015 * 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.116 2015/10/09 16:11:15 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.117 2016/01/14 23:18:09 tg Exp $");
#if HAVE_KILLPG #if HAVE_KILLPG
#define mksh_killpg killpg #define mksh_killpg killpg
@ -215,14 +215,11 @@ j_init(void)
static int static int
proc_errorlevel(Proc *p) proc_errorlevel(Proc *p)
{ {
int termsig;
switch (p->state) { switch (p->state) {
case PEXITED: case PEXITED:
return ((WEXITSTATUS(p->status)) & 255); return ((WEXITSTATUS(p->status)) & 255);
case PSIGNALLED: case PSIGNALLED:
termsig = WTERMSIG(p->status); return (ksh_sigmask(WTERMSIG(p->status)));
return ((termsig < 1 || termsig > 127) ? 255 : 128 + termsig);
default: default:
return (0); return (0);
} }
@ -756,7 +753,7 @@ waitfor(const char *cp, int *sigp)
if (rv < 0) if (rv < 0)
/* we were interrupted */ /* we were interrupted */
*sigp = 128 + -rv; *sigp = ksh_sigmask(-rv);
return (rv); return (rv);
} }

45
lex.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015 * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.214 2015/12/12 19:05:52 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/lex.c,v 1.218 2016/01/20 21:34:12 tg Exp $");
/* /*
* states while lexing word * states while lexing word
@ -94,8 +94,7 @@ static void ungetsc_i(int);
static int getsc_uu(void); static int getsc_uu(void);
static void getsc_line(Source *); static void getsc_line(Source *);
static int getsc_bn(void); static int getsc_bn(void);
static int s_get(void); static int getsc_i(void);
static void s_put(int);
static char *get_brace_var(XString *, char *); static char *get_brace_var(XString *, char *);
static bool arraysub(char **); static bool arraysub(char **);
static void gethere(void); static void gethere(void);
@ -112,7 +111,7 @@ static int ignore_backslash_newline;
#define o_getsc_u() ((*source->str != '\0') ? *source->str++ : getsc_uu()) #define o_getsc_u() ((*source->str != '\0') ? *source->str++ : getsc_uu())
/* retrace helper */ /* retrace helper */
#define o_getsc_r(carg) { \ #define o_getsc_r(carg) \
int cev = (carg); \ int cev = (carg); \
struct sretrace_info *rp = retrace_info; \ struct sretrace_info *rp = retrace_info; \
\ \
@ -122,17 +121,17 @@ static int ignore_backslash_newline;
rp = rp->next; \ rp = rp->next; \
} \ } \
\ \
return (cev); \ return (cev);
}
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
static int getsc(void);
/* callback */
static int static int
getsc(void) getsc_i(void)
{ {
o_getsc_r(o_getsc()); o_getsc_r(o_getsc());
} }
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
#define getsc getsc_i
#else #else
static int getsc_r(int); static int getsc_r(int);
@ -269,7 +268,7 @@ yylex(int cf)
} }
/* FALLTHROUGH */ /* FALLTHROUGH */
case SBASE: case SBASE:
if (c == '[' && (cf & (VARASN|ARRAYVAR))) { if (c == '[' && (cf & CMDASN)) {
/* temporary */ /* temporary */
*wp = EOS; *wp = EOS;
if (is_wdvarname(Xstring(ws, wp), false)) { if (is_wdvarname(Xstring(ws, wp), false)) {
@ -550,8 +549,10 @@ yylex(int cf)
* undefined results occur.). * undefined results occur.).
*/ */
statep->ls_bool = false; statep->ls_bool = false;
#ifdef austingroupbugs1015_is_still_not_resolved
if (Flag(FPOSIX)) if (Flag(FPOSIX))
break; break;
#endif
s2 = statep; s2 = statep;
base = state_info.base; base = state_info.base;
while (/* CONSTCOND */ 1) { while (/* CONSTCOND */ 1) {
@ -588,8 +589,8 @@ yylex(int cf)
*wp++ = CQUOTE; *wp++ = CQUOTE;
ignore_backslash_newline--; ignore_backslash_newline--;
} else if (c == '\\') { } else if (c == '\\') {
if ((c2 = unbksl(true, s_get, s_put)) == -1) if ((c2 = unbksl(true, getsc_i, ungetsc)) == -1)
c2 = s_get(); c2 = getsc();
if (c2 == 0) if (c2 == 0)
statep->ls_bool = true; statep->ls_bool = true;
if (!statep->ls_bool) { if (!statep->ls_bool) {
@ -777,6 +778,7 @@ yylex(int cf)
Source *s; Source *s;
ungetsc(c2); ungetsc(c2);
ungetsc(c);
/* /*
* mismatched parenthesis - * mismatched parenthesis -
* assume we were really * assume we were really
@ -789,6 +791,7 @@ yylex(int cf)
s->start = s->str = s->u.freeme = dp; s->start = s->str = s->u.freeme = dp;
s->next = source; s->next = source;
source = s; source = s;
ungetsc('('/*)*/);
return ('('/*)*/); return ('('/*)*/);
} }
} else if (c == '(') } else if (c == '(')
@ -1088,7 +1091,7 @@ yylex(int cf)
} }
} else if (cf & ALIAS) { } else if (cf & ALIAS) {
/* retain typeset et al. even when quoted */ /* retain typeset et al. even when quoted */
if (assign_command((dp = wdstrip(yylval.cp, 0)))) if (assign_command((dp = wdstrip(yylval.cp, 0)), true))
strlcpy(ident, dp, sizeof(ident)); strlcpy(ident, dp, sizeof(ident));
afree(dp, ATEMP); afree(dp, ATEMP);
} }
@ -1783,15 +1786,3 @@ pop_state_i(State_info *si, Lex_state *old_end)
return (si->base + STATE_BSIZE - 1); return (si->base + STATE_BSIZE - 1);
} }
static int
s_get(void)
{
return (getsc());
}
static void
s_put(int c)
{
ungetsc(c);
}

56
mksh.1
View File

@ -1,8 +1,8 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.383 2015/12/12 22:25:14 tg Exp $ .\" $MirOS: src/bin/mksh/mksh.1,v 1.388 2016/01/20 22:04:54 tg Exp $
.\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $ .\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
.\"- .\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
.\" 2010, 2011, 2012, 2013, 2014, 2015 .\" 2010, 2011, 2012, 2013, 2014, 2015, 2016
.\" mirabilos <m@mirbsd.org> .\" mirabilos <m@mirbsd.org>
.\" .\"
.\" Provided that these terms and disclaimer and all copyright notices .\" Provided that these terms and disclaimer and all copyright notices
@ -74,7 +74,7 @@
.\" with -mandoc, it might implement .Mx itself, but we want to .\" with -mandoc, it might implement .Mx itself, but we want to
.\" use our own definition. And .Dd must come *first*, always. .\" use our own definition. And .Dd must come *first*, always.
.\" .\"
.Dd $Mdocdate: December 12 2015 $ .Dd $Mdocdate: January 20 2016 $
.\" .\"
.\" Check which macro package we use, and do other -mdoc setup. .\" Check which macro package we use, and do other -mdoc setup.
.\" .\"
@ -179,6 +179,11 @@ script use.
Its command language is a superset of the Its command language is a superset of the
.Xr sh C .Xr sh C
shell language and largely compatible to the original Korn shell. shell language and largely compatible to the original Korn shell.
At times, this manual page may give scripting advice; while it
sometimes does take portable shell scripting or various standards
into account all information is first and foremost presented with
.Nm
in mind and should be taken as such.
.Ss I'm an Android user, so what's mksh? .Ss I'm an Android user, so what's mksh?
.Nm mksh .Nm mksh
is a is a
@ -434,7 +439,7 @@ Whitespace and meta-characters can be quoted individually using a backslash
or in groups using double or in groups using double
.Pq Sq \&" .Pq Sq \&"
or single or single
.Pq Sq \*(aq .Pq Dq \*(aq
quotes. quotes.
Note that the following characters are also treated specially by the Note that the following characters are also treated specially by the
shell and must be quoted if they are to represent themselves: shell and must be quoted if they are to represent themselves:
@ -988,7 +993,7 @@ case both the
.Ql \e .Ql \e
and the newline are stripped. and the newline are stripped.
Second, a single quote Second, a single quote
.Pq Sq \*(aq .Pq Dq \*(aq
quotes everything up to the next single quote (this may span lines). quotes everything up to the next single quote (this may span lines).
Third, a double quote Third, a double quote
.Pq Sq \&" .Pq Sq \&"
@ -1001,8 +1006,8 @@ up to the next unquoted double quote.
.Ql $ .Ql $
and and
.Ql \` .Ql \`
inside double quotes have their usual meaning (i.e. parameter, command, or inside double quotes have their usual meaning (i.e. parameter, arithmetic,
arithmetic substitution) except no field splitting is carried out on the or command substitution) except no field splitting is carried out on the
results of double-quoted substitutions. results of double-quoted substitutions.
If a If a
.Ql \e .Ql \e
@ -1026,7 +1031,9 @@ characters inside can be escaped and do not terminate the string then);
the expanded result is treated as any other single-quoted string. the expanded result is treated as any other single-quoted string.
If a double-quoted string is preceded by an unquoted If a double-quoted string is preceded by an unquoted
.Ql $ , .Ql $ ,
the latter is ignored. the
.Ql $
is simply ignored.
.Ss Backslash expansion .Ss Backslash expansion
In places where backslashes are expanded, certain C and In places where backslashes are expanded, certain C and
.At .At
@ -1286,6 +1293,8 @@ command which is run in a subshell.
For For
.Pf $( Ns Ar command Ns \&) .Pf $( Ns Ar command Ns \&)
and and
.Pf ${\*(Ba\& Ns Ar command Ns \&;}
and
.Pf ${\ \& Ar command Ns \&;} .Pf ${\ \& Ar command Ns \&;}
substitutions, normal quoting rules are used when substitutions, normal quoting rules are used when
.Ar command .Ar command
@ -1296,6 +1305,8 @@ form, a
followed by any of followed by any of
.Ql $ , .Ql $ ,
.Ql \` , .Ql \` ,
.Ql \&"
.Pq currently, and violating Tn POSIX ,
or or
.Ql \e .Ql \e
is stripped (a is stripped (a
@ -1744,7 +1755,7 @@ command below for a list of options).
The exit status of the last non-asynchronous command executed. The exit status of the last non-asynchronous command executed.
If the last command was killed by a signal, If the last command was killed by a signal,
.Ic $?\& .Ic $?\&
is set to 128 plus the signal number. is set to 128 plus the signal number, but at most 255.
.It Ev 0 .It Ev 0
The name of the shell, determined as follows: The name of the shell, determined as follows:
the first argument to the first argument to
@ -2965,7 +2976,8 @@ Builtins that are not special:
Once the type of command has been determined, any command-line parameter Once the type of command has been determined, any command-line parameter
assignments are performed and exported for the duration of the command. assignments are performed and exported for the duration of the command.
.Pp .Pp
The following describes the special and regular built-in commands: The following describes the special and regular built-in commands and
builtin-like reserved words:
.Pp .Pp
.Bl -tag -width false -compact .Bl -tag -width false -compact
.It Ic \&. Ar file Op Ar arg ... .It Ic \&. Ar file Op Ar arg ...
@ -3815,7 +3827,8 @@ Store the result without word splitting into the parameter
.Ev REPLY ) .Ev REPLY )
as array of characters (wide characters if the as array of characters (wide characters if the
.Ic utf8\-mode .Ic utf8\-mode
option is enacted, octets otherwise). option is enacted, octets otherwise); the codepoints are
encoded as decimal numbers by default.
.It Fl d Ar x .It Fl d Ar x
Use the first byte of Use the first byte of
.Ar x , .Ar x ,
@ -4138,6 +4151,12 @@ or
case-insensitively; for direct builtin calls depending on the case-insensitively; for direct builtin calls depending on the
aforementioned environment variables; or for stdin or scripts, aforementioned environment variables; or for stdin or scripts,
if the input begins with a UTF-8 Byte Order Mark. if the input begins with a UTF-8 Byte Order Mark.
.Pp
In near future, locale tracking will be implemented, which means that
.Ic set Fl +U
is changed whenever one of the
.Tn POSIX
locale-related environment variables changes.
.It Fl u \*(Ba Fl o Ic nounset .It Fl u \*(Ba Fl o Ic nounset
Referencing of an unset parameter, other than Referencing of an unset parameter, other than
.Dq $@ .Dq $@
@ -6552,6 +6571,7 @@ case ${KSH_VERSION:\-} in
esac ;; esac ;;
esac esac
.Ed .Ed
In near future, (Unicode) locale tracking will be implemented though.
.Sh BUGS .Sh BUGS
Suspending (using \*(haZ) pipelines like the one below will only suspend Suspending (using \*(haZ) pipelines like the one below will only suspend
the currently running part of the pipeline; in this example, the currently running part of the pipeline; in this example,
@ -6574,8 +6594,20 @@ when multiple shells are accessing the file; the rollover process
for the in-memory portion of the history is slow, should use for the in-memory portion of the history is slow, should use
.Xr memmove 3 . .Xr memmove 3 .
.Pp .Pp
Handling of backslash plus double-quote inside the (deprecated)
.Pf \` Ns Ar command Ns \`
form of command substitution when the substitution itself is
also inside double quotes currently deliberately violates
.Tn POSIX
even in
.Fl o Ic posix
mode until Austin group bug 1015 has been resolved either way,
as the current wording of the standard prohibits the current
and historic practice of several shells which several scripts
(admittedly wrongly) depend on.
.Pp
This document attempts to describe This document attempts to describe
.Nm mksh\ R52 .Nm mksh\ R52b
and up, and up,
.\" with vendor patches from insert-your-name-here, .\" with vendor patches from insert-your-name-here,
compiled without any options impacting functionality, such as compiled without any options impacting functionality, such as

19
sh.h
View File

@ -10,7 +10,7 @@
/*- /*-
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015 * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -175,9 +175,9 @@
#endif #endif
#ifdef EXTERN #ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.751 2015/12/12 22:25:15 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/sh.h,v 1.757 2016/01/20 21:34:13 tg Exp $");
#endif #endif
#define MKSH_VERSION "R52 2015/12/12" #define MKSH_VERSION "R52 2016/01/20"
/* arithmetic types: C implementation */ /* arithmetic types: C implementation */
#if !HAVE_CAN_INTTYPES #if !HAVE_CAN_INTTYPES
@ -379,6 +379,8 @@ struct rusage {
#define ksh_NSIG 64 #define ksh_NSIG 64
#endif #endif
#define ksh_sigmask(sig) (((sig) < 1 || (sig) > 127) ? 255 : 128 + (sig))
/* OS-dependent additions (functions, variables, by OS) */ /* OS-dependent additions (functions, variables, by OS) */
@ -862,6 +864,8 @@ EXTERN const char Tgbuiltin[] E_INIT("=builtin");
#define Tbuiltin (Tgbuiltin + 1) /* "builtin" */ #define Tbuiltin (Tgbuiltin + 1) /* "builtin" */
EXTERN const char T_function[] E_INIT(" function"); EXTERN const char T_function[] E_INIT(" function");
#define Tfunction (T_function + 1) /* "function" */ #define Tfunction (T_function + 1) /* "function" */
EXTERN const char T_funny_command[] E_INIT("funny $() command");
#define Tcommand (T_funny_command + 10) /* "command" */
EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n"); EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
#define TC_IFSWS (TC_LEX1 + 7) /* space tab newline */ #define TC_IFSWS (TC_LEX1 + 7) /* space tab newline */
@ -1186,6 +1190,8 @@ struct tbl {
}; };
EXTERN struct tbl vtemp; EXTERN struct tbl vtemp;
/* set by global() and local() */
EXTERN bool last_lookup_was_array;
/* common flag bits */ /* common flag bits */
#define ALLOC BIT(0) /* val.s has been allocated */ #define ALLOC BIT(0) /* val.s has been allocated */
@ -1628,13 +1634,12 @@ typedef union {
#define ALIAS BIT(2) /* recognise alias */ #define ALIAS BIT(2) /* recognise alias */
#define KEYWORD BIT(3) /* recognise keywords */ #define KEYWORD BIT(3) /* recognise keywords */
#define LETEXPR BIT(4) /* get expression inside (( )) */ #define LETEXPR BIT(4) /* get expression inside (( )) */
#define VARASN BIT(5) /* check for var=word */ #define CMDASN BIT(5) /* parse x[1 & 2] as one word, for typeset */
#define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */ #define HEREDOC BIT(6) /* parsing a here document body */
#define ESACONLY BIT(7) /* only accept esac keyword */ #define ESACONLY BIT(7) /* only accept esac keyword */
#define CMDWORD BIT(8) /* parsing simple command (alias related) */ #define CMDWORD BIT(8) /* parsing simple command (alias related) */
#define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */ #define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */
#define LQCHAR BIT(10) /* source string contains QCHAR */ #define LQCHAR BIT(10) /* source string contains QCHAR */
#define HEREDOC BIT(11) /* parsing a here document body */
#define HERES 10 /* max number of << in line */ #define HERES 10 /* max number of << in line */
@ -1993,7 +1998,7 @@ char *shf_smprintf(const char *, ...)
ssize_t shf_vfprintf(struct shf *, const char *, va_list) ssize_t shf_vfprintf(struct shf *, const char *, va_list)
MKSH_A_FORMAT(__printf__, 2, 0); MKSH_A_FORMAT(__printf__, 2, 0);
/* syn.c */ /* syn.c */
int assign_command(const char *); int assign_command(const char *, bool);
void initkeywords(void); void initkeywords(void);
struct op *compile(Source *, bool); struct op *compile(Source *, bool);
bool parse_usec(const char *, struct timeval *); bool parse_usec(const char *, struct timeval *);

4
shf.c
View File

@ -25,7 +25,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.68 2015/10/09 15:38:36 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/shf.c,v 1.69 2015/12/31 20:38:59 tg Exp $");
/* flags to shf_emptybuf() */ /* flags to shf_emptybuf() */
#define EB_READSW 0x01 /* about to switch to reading */ #define EB_READSW 0x01 /* about to switch to reading */
@ -1121,6 +1121,8 @@ cstrerror(int errnum)
#endif #endif
case EACCES: case EACCES:
return ("Permission denied"); return ("Permission denied");
case EEXIST:
return ("File exists");
case ENOTDIR: case ENOTDIR:
return ("Not a directory"); return ("Not a directory");
#ifdef EINVAL #ifdef EINVAL

41
syn.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
* 2011, 2012, 2013, 2014, 2015 * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.107 2015/12/12 21:03:53 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/syn.c,v 1.109 2016/01/19 23:12:15 tg Exp $");
struct nesting_state { struct nesting_state {
int start_token; /* token than began nesting (eg, FOR) */ int start_token; /* token than began nesting (eg, FOR) */
@ -272,7 +272,6 @@ get_command(int cf)
int c, iopn = 0, syniocf, lno; int c, iopn = 0, syniocf, lno;
struct ioword *iop, **iops; struct ioword *iop, **iops;
XPtrV args, vars; XPtrV args, vars;
char *tcp;
struct nesting_state old_nesting; struct nesting_state old_nesting;
/* NUFILE is small enough to leave this addition unchecked */ /* NUFILE is small enough to leave this addition unchecked */
@ -281,7 +280,7 @@ get_command(int cf)
XPinit(vars, 16); XPinit(vars, 16);
syniocf = KEYWORD|sALIAS; syniocf = KEYWORD|sALIAS;
switch (c = token(cf|KEYWORD|sALIAS|VARASN)) { switch (c = token(cf|KEYWORD|sALIAS|CMDASN)) {
default: default:
REJECT; REJECT;
afree(iops, ATEMP); afree(iops, ATEMP);
@ -296,9 +295,18 @@ get_command(int cf)
syniocf &= ~(KEYWORD|sALIAS); syniocf &= ~(KEYWORD|sALIAS);
t = newtp(TCOM); t = newtp(TCOM);
t->lineno = source->line; t->lineno = source->line;
goto get_command_begin;
while (/* CONSTCOND */ 1) { while (/* CONSTCOND */ 1) {
cf = (t->u.evalflags ? ARRAYVAR : 0) | bool check_assign_cmd;
(XPsize(args) == 0 ? sALIAS|VARASN : CMDWORD);
if (XPsize(args) == 0) {
get_command_begin:
check_assign_cmd = true;
cf = sALIAS | CMDASN;
} else if (t->u.evalflags)
cf = CMDWORD | CMDASN;
else
cf = CMDWORD;
switch (tpeek(cf)) { switch (tpeek(cf)) {
case REDIR: case REDIR:
while ((iop = synio(cf)) != NULL) { while ((iop = synio(cf)) != NULL) {
@ -316,9 +324,12 @@ get_command(int cf)
* dubious but AT&T ksh acts this way * dubious but AT&T ksh acts this way
*/ */
if (iopn == 0 && XPsize(vars) == 0 && if (iopn == 0 && XPsize(vars) == 0 &&
XPsize(args) == 0 && check_assign_cmd) {
assign_command(ident)) if (assign_command(ident, false))
t->u.evalflags = DOVACHECK; t->u.evalflags = DOVACHECK;
else if (strcmp(ident, Tcommand) != 0)
check_assign_cmd = false;
}
if ((XPsize(args) == 0 || Flag(FKEYWORD)) && if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
is_wdvarassign(yylval.cp)) is_wdvarassign(yylval.cp))
XPput(vars, yylval.cp); XPput(vars, yylval.cp);
@ -329,6 +340,8 @@ get_command(int cf)
case '(' /*)*/: case '(' /*)*/:
if (XPsize(args) == 0 && XPsize(vars) == 1 && if (XPsize(args) == 0 && XPsize(vars) == 1 &&
is_wdvarassign(yylval.cp)) { is_wdvarassign(yylval.cp)) {
char *tcp;
/* wdarrassign: foo=(bar) */ /* wdarrassign: foo=(bar) */
ACCEPT; ACCEPT;
@ -400,6 +413,7 @@ get_command(int cf)
case LWORD: case LWORD:
break; break;
case '(': /*)*/ case '(': /*)*/
c = '(';
goto Subshell; goto Subshell;
default: default:
syntaxerr(NULL); syntaxerr(NULL);
@ -431,7 +445,7 @@ get_command(int cf)
case FOR: case FOR:
case SELECT: case SELECT:
t = newtp((c == FOR) ? TFOR : TSELECT); t = newtp((c == FOR) ? TFOR : TSELECT);
musthave(LWORD, ARRAYVAR); musthave(LWORD, CMDASN);
if (!is_wdvarname(yylval.cp, true)) if (!is_wdvarname(yylval.cp, true))
yyerror("%s: %s\n", c == FOR ? "for" : Tselect, yyerror("%s: %s\n", c == FOR ? "for" : Tselect,
"bad identifier"); "bad identifier");
@ -572,7 +586,7 @@ elsepart(void)
{ {
struct op *t; struct op *t;
switch (token(KEYWORD|sALIAS|VARASN)) { switch (token(KEYWORD|sALIAS|CMDASN)) {
case ELSE: case ELSE:
if ((t = c_list(true)) == NULL) if ((t = c_list(true)) == NULL)
syntaxerr(NULL); syntaxerr(NULL);
@ -943,13 +957,14 @@ compile(Source *s, bool skiputf8bom)
* $ * $
*/ */
int int
assign_command(const char *s) assign_command(const char *s, bool docommand)
{ {
if (!*s) if (!*s)
return (0); return (0);
return ((strcmp(s, Talias) == 0) || return ((strcmp(s, Talias) == 0) ||
(strcmp(s, Texport) == 0) || (strcmp(s, Texport) == 0) ||
(strcmp(s, Treadonly) == 0) || (strcmp(s, Treadonly) == 0) ||
(docommand && (strcmp(s, Tcommand) == 0)) ||
(strcmp(s, Ttypeset) == 0)); (strcmp(s, Ttypeset) == 0));
} }
@ -991,7 +1006,7 @@ static const char db_gthan[] = { CHAR, '>', EOS };
static Test_op static Test_op
dbtestp_isa(Test_env *te, Test_meta meta) dbtestp_isa(Test_env *te, Test_meta meta)
{ {
int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN)); int c = tpeek(CMDASN | (meta == TM_BINOP ? 0 : CONTIN));
bool uqword; bool uqword;
char *save = NULL; char *save = NULL;
Test_op ret = TO_NONOP; Test_op ret = TO_NONOP;
@ -1037,7 +1052,7 @@ static const char *
dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED, dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED,
bool do_eval MKSH_A_UNUSED) bool do_eval MKSH_A_UNUSED)
{ {
int c = tpeek(ARRAYVAR); int c = tpeek(CMDASN);
if (c != LWORD) if (c != LWORD)
return (NULL); return (NULL);

21
tree.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2015 * 2011, 2012, 2013, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -23,7 +23,7 @@
#include "sh.h" #include "sh.h"
__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.79 2015/12/12 19:08:58 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/tree.c,v 1.80 2016/01/14 22:30:43 tg Exp $");
#define INDENT 8 #define INDENT 8
@ -318,6 +318,10 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
case EOS: case EOS:
return (--wp); return (--wp);
case ADELIM: case ADELIM:
if (*wp == /*{*/'}') {
++wp;
goto wdvarput_csubst;
}
case CHAR: case CHAR:
c = *wp++; c = *wp++;
shf_putc(c, shf); shf_putc(c, shf);
@ -383,8 +387,10 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
wp = wdvarput(shf, wp, 0, opmode); wp = wdvarput(shf, wp, 0, opmode);
break; break;
case CSUBST: case CSUBST:
if (*wp++ == '}') if (*wp++ == '}') {
wdvarput_csubst:
shf_putc('}', shf); shf_putc('}', shf);
}
return (wp); return (wp);
case OPAT: case OPAT:
shf_putchar(*wp++, shf); shf_putchar(*wp++, shf);
@ -581,8 +587,10 @@ wdscan(const char *wp, int c)
case EOS: case EOS:
return (wp); return (wp);
case ADELIM: case ADELIM:
if (c == ADELIM) if (c == ADELIM && nest == 0)
return (wp + 1); return (wp + 1);
if (*wp == /*{*/'}')
goto wdscan_csubst;
/* FALLTHROUGH */ /* FALLTHROUGH */
case CHAR: case CHAR:
case QCHAR: case QCHAR:
@ -604,6 +612,7 @@ wdscan(const char *wp, int c)
; ;
break; break;
case CSUBST: case CSUBST:
wdscan_csubst:
wp++; wp++;
if (c == CSUBST && nest == 0) if (c == CSUBST && nest == 0)
return (wp); return (wp);
@ -807,6 +816,10 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
shf_puts("EOS", shf); shf_puts("EOS", shf);
return (--wp); return (--wp);
case ADELIM: case ADELIM:
if (*wp == /*{*/'}') {
shf_puts("]ADELIM(})", shf);
return (wp + 1);
}
shf_puts("ADELIM=", shf); shf_puts("ADELIM=", shf);
if (0) if (0)
case CHAR: case CHAR:

65
var.c
View File

@ -2,7 +2,7 @@
/*- /*-
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
* 2011, 2012, 2013, 2014, 2015 * 2011, 2012, 2013, 2014, 2015, 2016
* mirabilos <m@mirbsd.org> * mirabilos <m@mirbsd.org>
* *
* Provided that these terms and disclaimer and all copyright notices * Provided that these terms and disclaimer and all copyright notices
@ -28,7 +28,7 @@
#include <sys/sysctl.h> #include <sys/sysctl.h>
#endif #endif
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.195 2015/10/09 16:11:19 tg Exp $"); __RCSID("$MirOS: src/bin/mksh/var.c,v 1.197 2016/01/14 22:49:33 tg Exp $");
/*- /*-
* Variables * Variables
@ -218,14 +218,16 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
return (n); return (n);
} }
#define vn vname.ro
/* /*
* Search for variable, if not found create globally. * Search for variable, if not found create globally.
*/ */
struct tbl * struct tbl *
global(const char *n) global(const char *n)
{ {
struct block *l = e->loc;
struct tbl *vp; struct tbl *vp;
union mksh_cchack vname;
struct block *l = e->loc;
int c; int c;
bool array; bool array;
uint32_t h, val; uint32_t h, val;
@ -234,9 +236,9 @@ global(const char *n)
* check to see if this is an array; * check to see if this is an array;
* dereference namerefs; must come first * dereference namerefs; must come first
*/ */
n = array_index_calc(n, &array, &val); vn = array_index_calc(n, &array, &val);
h = hash(n); h = hash(vn);
c = (unsigned char)n[0]; c = (unsigned char)vn[0];
if (!ksh_isalphx(c)) { if (!ksh_isalphx(c)) {
if (array) if (array)
errorf("bad substitution"); errorf("bad substitution");
@ -246,15 +248,15 @@ global(const char *n)
vp->areap = ATEMP; vp->areap = ATEMP;
*vp->name = c; *vp->name = c;
if (ksh_isdigit(c)) { if (ksh_isdigit(c)) {
if (getn(n, &c) && (c <= l->argc)) if (getn(vn, &c) && (c <= l->argc))
/* setstr can't fail here */ /* setstr can't fail here */
setstr(vp, l->argv[c], KSH_RETURN_ERROR); setstr(vp, l->argv[c], KSH_RETURN_ERROR);
vp->flag |= RDONLY; vp->flag |= RDONLY;
return (vp); goto out;
} }
vp->flag |= RDONLY; vp->flag |= RDONLY;
if (n[1] != '\0') if (vn[1] != '\0')
return (vp); goto out;
vp->flag |= ISSET|INTEGER; vp->flag |= ISSET|INTEGER;
switch (c) { switch (c) {
case '$': case '$':
@ -278,17 +280,24 @@ global(const char *n)
default: default:
vp->flag &= ~(ISSET|INTEGER); vp->flag &= ~(ISSET|INTEGER);
} }
return (vp); goto out;
} }
l = varsearch(e->loc, &vp, n, h); l = varsearch(e->loc, &vp, vn, h);
if (vp != NULL) if (vp != NULL) {
return (array ? arraysearch(vp, val) : vp); if (array)
vp = ktenter(&l->vars, n, h); vp = arraysearch(vp, val);
goto out;
}
vp = ktenter(&l->vars, vn, h);
if (array) if (array)
vp = arraysearch(vp, val); vp = arraysearch(vp, val);
vp->flag |= DEFINED; vp->flag |= DEFINED;
if (special(n)) if (special(vn))
vp->flag |= SPECIAL; vp->flag |= SPECIAL;
out:
last_lookup_was_array = array;
if (vn != n)
afree(vname.rw, ATEMP);
return (vp); return (vp);
} }
@ -298,8 +307,9 @@ global(const char *n)
struct tbl * struct tbl *
local(const char *n, bool copy) local(const char *n, bool copy)
{ {
struct block *l = e->loc;
struct tbl *vp; struct tbl *vp;
union mksh_cchack vname;
struct block *l = e->loc;
bool array; bool array;
uint32_t h, val; uint32_t h, val;
@ -307,20 +317,20 @@ local(const char *n, bool copy)
* check to see if this is an array; * check to see if this is an array;
* dereference namerefs; must come first * dereference namerefs; must come first
*/ */
n = array_index_calc(n, &array, &val); vn = array_index_calc(n, &array, &val);
h = hash(n); h = hash(vn);
if (!ksh_isalphx(*n)) { if (!ksh_isalphx(*vn)) {
vp = &vtemp; vp = &vtemp;
vp->flag = DEFINED|RDONLY; vp->flag = DEFINED|RDONLY;
vp->type = 0; vp->type = 0;
vp->areap = ATEMP; vp->areap = ATEMP;
return (vp); goto out;
} }
vp = ktenter(&l->vars, n, h); vp = ktenter(&l->vars, vn, h);
if (copy && !(vp->flag & DEFINED)) { if (copy && !(vp->flag & DEFINED)) {
struct tbl *vq; struct tbl *vq;
varsearch(l->next, &vq, n, h); varsearch(l->next, &vq, vn, h);
if (vq != NULL) { if (vq != NULL) {
vp->flag |= vq->flag & vp->flag |= vq->flag &
(EXPORT | INTEGER | RDONLY | LJUST | RJUST | (EXPORT | INTEGER | RDONLY | LJUST | RJUST |
@ -333,10 +343,15 @@ local(const char *n, bool copy)
if (array) if (array)
vp = arraysearch(vp, val); vp = arraysearch(vp, val);
vp->flag |= DEFINED; vp->flag |= DEFINED;
if (special(n)) if (special(vn))
vp->flag |= SPECIAL; vp->flag |= SPECIAL;
out:
last_lookup_was_array = array;
if (vn != n)
afree(vname.rw, ATEMP);
return (vp); return (vp);
} }
#undef vn
/* get variable string value */ /* get variable string value */
char * char *
@ -1492,7 +1507,7 @@ arrayname(const char *str)
const char *p; const char *p;
char *rv; char *rv;
if ((p = cstrchr(str, '[')) == 0) if (!(p = cstrchr(str, '[')))
/* Shouldn't happen, but why worry? */ /* Shouldn't happen, but why worry? */
strdupx(rv, str, ATEMP); strdupx(rv, str, ATEMP);
else else