Merge commit '91ae6ad199035b1cf'
This commit is contained in:
commit
6eb125c18a
11
Build.sh
11
Build.sh
@ -1,8 +1,8 @@
|
||||
#!/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,
|
||||
# 2011, 2012, 2013, 2014, 2015
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016
|
||||
# mirabilos <m@mirbsd.org>
|
||||
#
|
||||
# Provided that these terms and disclaimer and all copyright notices
|
||||
@ -745,6 +745,7 @@ GNU)
|
||||
*tendracc*) ;;
|
||||
*) add_cppflags -D_GNU_SOURCE ;;
|
||||
esac
|
||||
add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
|
||||
# define MKSH__NO_PATH_MAX to use Hurd-only functions
|
||||
add_cppflags -DMKSH__NO_PATH_MAX
|
||||
;;
|
||||
@ -753,6 +754,7 @@ GNU/kFreeBSD)
|
||||
*tendracc*) ;;
|
||||
*) add_cppflags -D_GNU_SOURCE ;;
|
||||
esac
|
||||
add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
|
||||
;;
|
||||
Haiku)
|
||||
add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
|
||||
@ -1239,19 +1241,20 @@ dragonegg|llvm)
|
||||
vv '|' "llc -version"
|
||||
;;
|
||||
esac
|
||||
etd=" on $et"
|
||||
case $et in
|
||||
klibc)
|
||||
add_cppflags -DMKSH_NO_LIMITS
|
||||
;;
|
||||
unknown)
|
||||
# nothing special detected, don’t worry
|
||||
unset et
|
||||
etd=
|
||||
;;
|
||||
*)
|
||||
# huh?
|
||||
;;
|
||||
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
|
||||
|
||||
#
|
||||
|
27
check.t
27
check.t
@ -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 -*-
|
||||
#-
|
||||
# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
# 2011, 2012, 2013, 2014, 2015
|
||||
# 2011, 2012, 2013, 2014, 2015, 2016
|
||||
# mirabilos <m@mirbsd.org>
|
||||
#
|
||||
# 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
|
||||
|
||||
expected-stdout:
|
||||
@(#)MIRBSD KSH R52 2015/12/12
|
||||
@(#)MIRBSD KSH R52 2016/01/20
|
||||
description:
|
||||
Check version of shell.
|
||||
stdin:
|
||||
@ -39,7 +39,7 @@ name: KSH_VERSION
|
||||
category: shell:legacy-no
|
||||
---
|
||||
expected-stdout:
|
||||
@(#)LEGACY KSH R52 2015/12/12
|
||||
@(#)LEGACY KSH R52 2016/01/20
|
||||
description:
|
||||
Check version of legacy shell.
|
||||
stdin:
|
||||
@ -6592,12 +6592,24 @@ description:
|
||||
env-setup: !HOME=/sweet!
|
||||
stdin:
|
||||
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
|
||||
unset A
|
||||
unset A e f g h
|
||||
echo ${A=a=}~ b=~ c=d~ ~
|
||||
export e=~ f=d~
|
||||
command command export g=~ h=d~
|
||||
echo ". $e . $f ."
|
||||
echo ". $g . $h ."
|
||||
expected-stdout:
|
||||
a=/sweet b=/sweet c=d~ /sweet
|
||||
. /sweet . d~ .
|
||||
. /sweet . d~ .
|
||||
a=~ b=~ c=d~ /sweet
|
||||
. /sweet . d~ .
|
||||
. /sweet . d~ .
|
||||
---
|
||||
name: tilde-expand-2
|
||||
description:
|
||||
@ -8515,7 +8527,7 @@ expected-stdout:
|
||||
name: varexpand-substr-3
|
||||
description:
|
||||
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:
|
||||
export x=abcdefghi n=2
|
||||
"$__progname" -c 'echo v${x:(n)}x'
|
||||
@ -8523,12 +8535,13 @@ stdin:
|
||||
"$__progname" -c 'echo x${x:n}x'
|
||||
"$__progname" -c 'echo y${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 $?
|
||||
expected-stdout:
|
||||
vcdefghix
|
||||
wcdefghix
|
||||
zabcdefghix
|
||||
1
|
||||
0
|
||||
expected-stderr-pattern:
|
||||
/x:n.*bad substitution.*\n.*bad substitution/
|
||||
---
|
||||
|
@ -1,5 +1,5 @@
|
||||
# $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,
|
||||
# 2011, 2012, 2013, 2014, 2015
|
||||
@ -596,8 +596,8 @@ done
|
||||
#\unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
|
||||
# LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
|
||||
#p=en_GB.UTF-8
|
||||
#\set -U
|
||||
#\export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
|
||||
#\set -U
|
||||
|
||||
\unset p
|
||||
|
||||
|
13
eval.c
13
eval.c
@ -2,7 +2,7 @@
|
||||
|
||||
/*-
|
||||
* 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>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
#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
|
||||
@ -437,8 +437,6 @@ expand(
|
||||
beg = wdcopy(sp, ATEMP);
|
||||
mid = beg + (wdscan(sp, ADELIM) - sp);
|
||||
stg = beg + (wdscan(sp, CSUBST) - sp);
|
||||
if (mid >= stg)
|
||||
goto unwind_substsyn;
|
||||
mid[-2] = EOS;
|
||||
if (mid[-1] == /*{*/'}') {
|
||||
sp += mid - beg - 1;
|
||||
@ -446,9 +444,8 @@ expand(
|
||||
} else {
|
||||
end = mid +
|
||||
(wdscan(mid, ADELIM) - mid);
|
||||
if (end >= stg ||
|
||||
if (end[-1] != /*{*/ '}')
|
||||
/* more than max delimiters */
|
||||
end[-1] != /*{*/ '}')
|
||||
goto unwind_substsyn;
|
||||
end[-2] = EOS;
|
||||
sp += end - beg - 1;
|
||||
@ -488,8 +485,6 @@ expand(
|
||||
s = wdcopy(sp, ATEMP);
|
||||
p = s + (wdscan(sp, ADELIM) - sp);
|
||||
d = s + (wdscan(sp, CSUBST) - sp);
|
||||
if (p >= d)
|
||||
goto unwind_substsyn;
|
||||
p[-2] = EOS;
|
||||
if (p[-1] == /*{*/'}')
|
||||
d = NULL;
|
||||
@ -1329,7 +1324,7 @@ comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
|
||||
char *name;
|
||||
|
||||
if ((io->ioflag & IOTYPE) != IOREAD)
|
||||
errorf("%s: %s", "funny $() command",
|
||||
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);
|
||||
|
39
exec.c
39
exec.c
@ -23,7 +23,7 @@
|
||||
|
||||
#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
|
||||
#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 == /* bzip */ 0x425A) || (m == /* "MZ" */ 0x4D5A) ||
|
||||
(m == /* "NE" */ 0x4E45) || (m == /* "LX" */ 0x4C58) ||
|
||||
(m == /* ksh93 */ 0x0B13) || (m == /* LZIP */ 0x4C5A) ||
|
||||
(m == /* xz */ 0xFD37 && buf[2] == 'z' && buf[3] == 'X' &&
|
||||
buf[4] == 'Z') || (m == /* 7zip */ 0x377A) ||
|
||||
(m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D))
|
||||
@ -1404,7 +1405,7 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
||||
int u = -1;
|
||||
char *cp = iop->ioname;
|
||||
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;
|
||||
struct ioword iotmp;
|
||||
struct stat statb;
|
||||
@ -1433,14 +1434,27 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
||||
break;
|
||||
|
||||
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
|
||||
* things like /dev/null without error.
|
||||
* allow redirections to things
|
||||
* like /dev/null without error
|
||||
*/
|
||||
if (Flag(FNOCLOBBER) && !(iop->ioflag & IOCLOB) &&
|
||||
(stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
|
||||
flags |= O_EXCL;
|
||||
flags = O_WRONLY;
|
||||
/* but check again after opening */
|
||||
do_fstat = true;
|
||||
}
|
||||
} else {
|
||||
/* >|file or set +C */
|
||||
flags = O_WRONLY | O_CREAT | O_TRUNC;
|
||||
}
|
||||
break;
|
||||
|
||||
case IORDWR:
|
||||
@ -1485,6 +1499,15 @@ iosetup(struct ioword *iop, struct tbl *tp)
|
||||
return (-1);
|
||||
}
|
||||
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) {
|
||||
/* herein() may already have printed message */
|
||||
|
5
expr.c
5
expr.c
@ -2,7 +2,7 @@
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014
|
||||
* 2011, 2012, 2013, 2014, 2016
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
#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[] */
|
||||
enum token {
|
||||
@ -659,6 +659,7 @@ exprtoken(Expr_state *es)
|
||||
es->tok = VAR;
|
||||
} else if (c == '1' && cp[1] == '#') {
|
||||
cp += 2;
|
||||
if (*cp)
|
||||
cp += utf_ptradj(cp);
|
||||
strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
|
||||
goto process_tvar;
|
||||
|
247
funcs.c
247
funcs.c
@ -5,7 +5,7 @@
|
||||
|
||||
/*-
|
||||
* 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>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -38,7 +38,7 @@
|
||||
#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
|
||||
/*
|
||||
@ -103,7 +103,7 @@ const struct builtin mkshbuiltins[] = {
|
||||
{"cd", c_cd},
|
||||
/* dash compatibility hack */
|
||||
{"chdir", c_cd},
|
||||
{"command", c_command},
|
||||
{Tcommand, c_command},
|
||||
{"*=continue", c_brkcont},
|
||||
{"echo", c_print},
|
||||
{"*=eval", c_eval},
|
||||
@ -278,20 +278,18 @@ static void s_put(int);
|
||||
int
|
||||
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 flags = PO_EXPAND | PO_NL;
|
||||
const char *s, *emsg;
|
||||
const char *s;
|
||||
XString xs;
|
||||
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') {
|
||||
/* echo builtin */
|
||||
wp++;
|
||||
/* "echo" builtin */
|
||||
++wp;
|
||||
#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
|
||||
if (Flag(FSH)) {
|
||||
/*
|
||||
@ -299,7 +297,7 @@ c_print(const char **wp)
|
||||
* one that supports -e but does not enable it by
|
||||
* default
|
||||
*/
|
||||
flags = PO_NL;
|
||||
po_exp = false;
|
||||
}
|
||||
#endif
|
||||
if (Flag(FPOSIX) ||
|
||||
@ -309,14 +307,14 @@ c_print(const char **wp)
|
||||
Flag(FAS_BUILTIN)) {
|
||||
/* Debian Policy 10.4 compliant "echo" builtin */
|
||||
if (*wp && !strcmp(*wp, "-n")) {
|
||||
/* we recognise "-n" only as the first arg */
|
||||
flags = 0;
|
||||
wp++;
|
||||
} else
|
||||
/* otherwise, we print everything as-is */
|
||||
flags = PO_NL;
|
||||
/* recognise "-n" only as the first arg */
|
||||
po_nl = false;
|
||||
++wp;
|
||||
}
|
||||
/* print everything as-is */
|
||||
po_exp = false;
|
||||
} else {
|
||||
int nflags = flags;
|
||||
bool new_exp = po_exp, new_nl = po_nl;
|
||||
|
||||
/**
|
||||
* a compromise between sysV and BSD echo commands:
|
||||
@ -329,62 +327,65 @@ c_print(const char **wp)
|
||||
* quences are enabled by default.
|
||||
*/
|
||||
|
||||
while ((s = *wp) && *s == '-' && s[1]) {
|
||||
while (*++s)
|
||||
if (*s == 'n')
|
||||
nflags &= ~PO_NL;
|
||||
else if (*s == 'e')
|
||||
nflags |= PO_EXPAND;
|
||||
else if (*s == 'E')
|
||||
nflags &= ~PO_EXPAND;
|
||||
else
|
||||
/*
|
||||
* bad option: don't use
|
||||
* nflags, print argument
|
||||
*/
|
||||
break;
|
||||
|
||||
if (*s)
|
||||
break;
|
||||
wp++;
|
||||
flags = nflags;
|
||||
print_tradparse_arg:
|
||||
if ((s = *wp) && *s++ == '-' && *s) {
|
||||
print_tradparse_ch:
|
||||
switch ((c = *s++)) {
|
||||
case 'E':
|
||||
new_exp = false;
|
||||
goto print_tradparse_ch;
|
||||
case 'e':
|
||||
new_exp = true;
|
||||
goto print_tradparse_ch;
|
||||
case 'n':
|
||||
new_nl = false;
|
||||
goto print_tradparse_ch;
|
||||
case '\0':
|
||||
po_exp = new_exp;
|
||||
po_nl = new_nl;
|
||||
++wp;
|
||||
goto print_tradparse_arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
int optc;
|
||||
const char *opts = "Rnprsu,";
|
||||
/* "print" builtin */
|
||||
const char *opts = "npRrsu,";
|
||||
const char *emsg;
|
||||
/* print a "--" argument */
|
||||
bool po_pminusminus = false;
|
||||
|
||||
while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
|
||||
switch (optc) {
|
||||
case 'R':
|
||||
/* fake BSD echo command */
|
||||
flags |= PO_PMINUSMINUS;
|
||||
flags &= ~PO_EXPAND;
|
||||
opts = "ne";
|
||||
break;
|
||||
while ((c = ksh_getopt(wp, &builtin_opt, opts)) != -1)
|
||||
switch (c) {
|
||||
case 'e':
|
||||
flags |= PO_EXPAND;
|
||||
po_exp = true;
|
||||
break;
|
||||
case 'n':
|
||||
flags &= ~PO_NL;
|
||||
po_nl = false;
|
||||
break;
|
||||
case 'p':
|
||||
if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
|
||||
bi_errorf("%s: %s", "-p", emsg);
|
||||
bi_errorf("-p: %s", emsg);
|
||||
return (1);
|
||||
}
|
||||
break;
|
||||
case 'R':
|
||||
/* fake BSD echo command */
|
||||
po_pminusminus = true;
|
||||
po_exp = false;
|
||||
opts = "en";
|
||||
break;
|
||||
case 'r':
|
||||
flags &= ~PO_EXPAND;
|
||||
po_exp = false;
|
||||
break;
|
||||
case 's':
|
||||
flags |= PO_HIST;
|
||||
po_hist = true;
|
||||
break;
|
||||
case 'u':
|
||||
if (!*(s = builtin_opt.optarg))
|
||||
fd = 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);
|
||||
}
|
||||
break;
|
||||
@ -393,22 +394,23 @@ c_print(const char **wp)
|
||||
}
|
||||
|
||||
if (!(builtin_opt.info & GI_MINUSMINUS)) {
|
||||
/* treat a lone - like -- */
|
||||
/* treat a lone "-" like "--" */
|
||||
if (wp[builtin_opt.optind] &&
|
||||
ksh_isdash(wp[builtin_opt.optind]))
|
||||
builtin_opt.optind++;
|
||||
} else if (flags & PO_PMINUSMINUS)
|
||||
} else if (po_pminusminus)
|
||||
builtin_opt.optind--;
|
||||
wp += builtin_opt.optind;
|
||||
}
|
||||
|
||||
Xinit(xs, xp, 128, ATEMP);
|
||||
|
||||
while (*wp != NULL) {
|
||||
if (*wp != NULL) {
|
||||
print_read_arg:
|
||||
s = *wp;
|
||||
while ((c = *s++) != '\0') {
|
||||
Xcheck(xs, xp);
|
||||
if ((flags & PO_EXPAND) && c == '\\') {
|
||||
if (po_exp && c == '\\') {
|
||||
s_ptr = s;
|
||||
c = unbksl(false, s_get, s_put);
|
||||
s = s_ptr;
|
||||
@ -416,11 +418,11 @@ c_print(const char **wp)
|
||||
/* rejected by generic function */
|
||||
switch ((c = *s++)) {
|
||||
case 'c':
|
||||
flags &= ~PO_NL;
|
||||
po_nl = false;
|
||||
/* AT&T brain damage */
|
||||
continue;
|
||||
case '\0':
|
||||
s--;
|
||||
--s;
|
||||
c = '\\';
|
||||
break;
|
||||
default:
|
||||
@ -440,18 +442,22 @@ c_print(const char **wp)
|
||||
}
|
||||
Xput(xs, xp, c);
|
||||
}
|
||||
if (*++wp != NULL)
|
||||
if (*++wp != NULL) {
|
||||
Xput(xs, xp, ' ');
|
||||
goto print_read_arg;
|
||||
}
|
||||
if (flags & PO_NL)
|
||||
}
|
||||
if (po_nl)
|
||||
Xput(xs, xp, '\n');
|
||||
|
||||
if (flags & PO_HIST) {
|
||||
c = 0;
|
||||
if (po_hist) {
|
||||
Xput(xs, xp, '\0');
|
||||
histsave(&source->line, Xstring(xs, xp), HIST_STORE, false);
|
||||
Xfree(xs, xp);
|
||||
} else {
|
||||
int len = Xlength(xs, xp);
|
||||
size_t len = Xlength(xs, xp);
|
||||
bool po_coproc = false;
|
||||
int opipe = 0;
|
||||
|
||||
/*
|
||||
@ -461,30 +467,36 @@ c_print(const char **wp)
|
||||
* not enough).
|
||||
*/
|
||||
if (coproc.write >= 0 && coproc.write == fd) {
|
||||
flags |= PO_COPROC;
|
||||
po_coproc = true;
|
||||
opipe = block_pipe();
|
||||
}
|
||||
for (s = Xstring(xs, xp); len > 0; ) {
|
||||
if ((c = write(fd, s, len)) < 0) {
|
||||
if (flags & PO_COPROC)
|
||||
restore_pipe(opipe);
|
||||
|
||||
s = Xstring(xs, xp);
|
||||
while (len > 0) {
|
||||
ssize_t nwritten;
|
||||
|
||||
if ((nwritten = write(fd, s, len)) < 0) {
|
||||
if (errno == EINTR) {
|
||||
/* allow user to ^C out */
|
||||
if (po_coproc)
|
||||
restore_pipe(opipe);
|
||||
/* give the user a chance to ^C out */
|
||||
intrcheck();
|
||||
if (flags & PO_COPROC)
|
||||
/* interrupted, try again */
|
||||
if (po_coproc)
|
||||
opipe = block_pipe();
|
||||
continue;
|
||||
}
|
||||
return (1);
|
||||
c = 1;
|
||||
break;
|
||||
}
|
||||
s += c;
|
||||
len -= c;
|
||||
s += nwritten;
|
||||
len -= nwritten;
|
||||
}
|
||||
if (flags & PO_COPROC)
|
||||
if (po_coproc)
|
||||
restore_pipe(opipe);
|
||||
}
|
||||
|
||||
return (0);
|
||||
return (c);
|
||||
}
|
||||
|
||||
static int
|
||||
@ -1851,13 +1863,15 @@ c_read(const char **wp)
|
||||
enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
|
||||
char delim = '\n';
|
||||
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;
|
||||
const char *ccp;
|
||||
XString xs;
|
||||
size_t xsave = 0;
|
||||
mksh_ttyst tios;
|
||||
bool restore_tios = false;
|
||||
/* to catch read -aN2 foo[i] */
|
||||
bool subarray = false;
|
||||
#if HAVE_SELECT
|
||||
bool hastimeout = false;
|
||||
struct timeval tv, tvlim;
|
||||
@ -2102,6 +2116,7 @@ c_read(const char **wp)
|
||||
XinitN(xs, 128, ATEMP);
|
||||
if (intoarray) {
|
||||
vp = global(*wp);
|
||||
subarray = last_lookup_was_array;
|
||||
if (vp->flag & RDONLY) {
|
||||
c_read_splitro:
|
||||
bi_errorf("read-only: %s", *wp);
|
||||
@ -2110,10 +2125,10 @@ c_read(const char **wp)
|
||||
afree(cp, ATEMP);
|
||||
goto c_read_out;
|
||||
}
|
||||
/* exporting an array is currently pointless */
|
||||
unset(vp, 1);
|
||||
/* counter for array index */
|
||||
c = 0;
|
||||
c = subarray ? arrayindex(vp) : 0;
|
||||
/* exporting an array is currently pointless */
|
||||
unset(vp, subarray ? 0 : 1);
|
||||
}
|
||||
if (!aschars) {
|
||||
/* skip initial IFS whitespace */
|
||||
@ -2215,6 +2230,17 @@ c_read(const char **wp)
|
||||
c_read_gotword:
|
||||
Xput(xs, xp, '\0');
|
||||
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++);
|
||||
} else {
|
||||
vq = global(*wp);
|
||||
@ -2355,16 +2381,14 @@ int
|
||||
c_exitreturn(const char **wp)
|
||||
{
|
||||
int n, how = LEXIT;
|
||||
const char *arg;
|
||||
|
||||
if (ksh_getopt(wp, &builtin_opt, null) == '?')
|
||||
if (wp[1]) {
|
||||
if (wp[2])
|
||||
goto c_exitreturn_err;
|
||||
arg = wp[builtin_opt.optind];
|
||||
|
||||
if (arg)
|
||||
exstat = bi_getn(arg, &n) ? (n & 0xFF) : 1;
|
||||
else if (trap_exstat != -1)
|
||||
exstat = bi_getn(wp[1], &n) ? (n & 0xFF) : 1;
|
||||
} else if (trap_exstat != -1)
|
||||
exstat = trap_exstat;
|
||||
|
||||
if (wp[0][0] == 'r') {
|
||||
/* return */
|
||||
struct env *ep;
|
||||
@ -2385,12 +2409,13 @@ c_exitreturn(const char **wp)
|
||||
how = LSHELL;
|
||||
}
|
||||
|
||||
/* get rid of any i/o redirections */
|
||||
/* get rid of any I/O redirections */
|
||||
quitenv(NULL);
|
||||
unwind(how);
|
||||
/* NOTREACHED */
|
||||
|
||||
c_exitreturn_err:
|
||||
bi_errorf("too many arguments");
|
||||
return (1);
|
||||
}
|
||||
|
||||
@ -3664,10 +3689,11 @@ c_realpath(const char **wp)
|
||||
int
|
||||
c_cat(const char **wp)
|
||||
{
|
||||
int fd = STDIN_FILENO, rv, eno;
|
||||
int fd = STDIN_FILENO, rv;
|
||||
ssize_t n, w;
|
||||
const char *fn = "<stdin>";
|
||||
char *buf, *cp;
|
||||
int opipe = 0;
|
||||
#define MKSH_CAT_BUFSIZ 4096
|
||||
|
||||
/* parse options: POSIX demands we support "-u" as no-op */
|
||||
@ -3689,61 +3715,72 @@ c_cat(const char **wp)
|
||||
return (1);
|
||||
}
|
||||
|
||||
/* catch SIGPIPE */
|
||||
opipe = block_pipe();
|
||||
|
||||
do {
|
||||
if (*wp) {
|
||||
fn = *wp++;
|
||||
if (ksh_isdash(fn))
|
||||
fd = STDIN_FILENO;
|
||||
else if ((fd = binopen2(fn, O_RDONLY)) < 0) {
|
||||
eno = errno;
|
||||
bi_errorf("%s: %s", fn, cstrerror(eno));
|
||||
bi_errorf("%s: %s", fn, cstrerror(errno));
|
||||
rv = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
while (/* CONSTCOND */ 1) {
|
||||
n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
|
||||
eno = errno;
|
||||
if ((n = blocking_read(fd, (cp = buf),
|
||||
MKSH_CAT_BUFSIZ)) == -1) {
|
||||
if (errno == EINTR) {
|
||||
restore_pipe(opipe);
|
||||
/* give the user a chance to ^C out */
|
||||
intrcheck();
|
||||
if (n == -1) {
|
||||
if (eno == EINTR) {
|
||||
/* interrupted, try again */
|
||||
opipe = block_pipe();
|
||||
continue;
|
||||
}
|
||||
/* an error occured during reading */
|
||||
bi_errorf("%s: %s", fn, cstrerror(eno));
|
||||
bi_errorf("%s: %s", fn, cstrerror(errno));
|
||||
rv = 1;
|
||||
break;
|
||||
} else if (n == 0)
|
||||
/* end of file reached */
|
||||
break;
|
||||
while (n) {
|
||||
w = write(STDOUT_FILENO, cp, n);
|
||||
eno = errno;
|
||||
if ((w = write(STDOUT_FILENO, cp, n)) != -1) {
|
||||
n -= w;
|
||||
cp += w;
|
||||
continue;
|
||||
}
|
||||
if (errno == EINTR) {
|
||||
restore_pipe(opipe);
|
||||
/* give the user a chance to ^C out */
|
||||
intrcheck();
|
||||
if (w == -1) {
|
||||
if (eno == EINTR)
|
||||
/* interrupted, try again */
|
||||
opipe = block_pipe();
|
||||
continue;
|
||||
}
|
||||
if (errno == EPIPE) {
|
||||
/* fake receiving signel */
|
||||
rv = ksh_sigmask(SIGPIPE);
|
||||
} else {
|
||||
/* an error occured during writing */
|
||||
bi_errorf("%s: %s", "<stdout>",
|
||||
cstrerror(eno));
|
||||
cstrerror(errno));
|
||||
rv = 1;
|
||||
}
|
||||
if (fd != STDIN_FILENO)
|
||||
close(fd);
|
||||
goto out;
|
||||
}
|
||||
n -= w;
|
||||
cp += w;
|
||||
}
|
||||
}
|
||||
if (fd != STDIN_FILENO)
|
||||
close(fd);
|
||||
} while (*wp);
|
||||
|
||||
out:
|
||||
restore_pipe(opipe);
|
||||
free_osfunc(buf);
|
||||
return (rv);
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2014, 2015
|
||||
* 2011, 2012, 2014, 2015, 2016
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -27,7 +27,7 @@
|
||||
#include <sys/file.h>
|
||||
#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];
|
||||
static struct sigaction Sigact_ign;
|
||||
@ -1213,7 +1213,7 @@ fatal_trap_check(void)
|
||||
do {
|
||||
if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
|
||||
/* return value is used as an exit code */
|
||||
return (128 + p->signal);
|
||||
return (ksh_sigmask(p->signal));
|
||||
++p;
|
||||
} while (--i);
|
||||
return (0);
|
||||
|
11
jobs.c
11
jobs.c
@ -2,7 +2,7 @@
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
|
||||
* 2012, 2013, 2014, 2015
|
||||
* 2012, 2013, 2014, 2015, 2016
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
#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
|
||||
#define mksh_killpg killpg
|
||||
@ -215,14 +215,11 @@ j_init(void)
|
||||
static int
|
||||
proc_errorlevel(Proc *p)
|
||||
{
|
||||
int termsig;
|
||||
|
||||
switch (p->state) {
|
||||
case PEXITED:
|
||||
return ((WEXITSTATUS(p->status)) & 255);
|
||||
case PSIGNALLED:
|
||||
termsig = WTERMSIG(p->status);
|
||||
return ((termsig < 1 || termsig > 127) ? 255 : 128 + termsig);
|
||||
return (ksh_sigmask(WTERMSIG(p->status)));
|
||||
default:
|
||||
return (0);
|
||||
}
|
||||
@ -756,7 +753,7 @@ waitfor(const char *cp, int *sigp)
|
||||
|
||||
if (rv < 0)
|
||||
/* we were interrupted */
|
||||
*sigp = 128 + -rv;
|
||||
*sigp = ksh_sigmask(-rv);
|
||||
|
||||
return (rv);
|
||||
}
|
||||
|
45
lex.c
45
lex.c
@ -2,7 +2,7 @@
|
||||
|
||||
/*-
|
||||
* 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>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
#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
|
||||
@ -94,8 +94,7 @@ static void ungetsc_i(int);
|
||||
static int getsc_uu(void);
|
||||
static void getsc_line(Source *);
|
||||
static int getsc_bn(void);
|
||||
static int s_get(void);
|
||||
static void s_put(int);
|
||||
static int getsc_i(void);
|
||||
static char *get_brace_var(XString *, char *);
|
||||
static bool arraysub(char **);
|
||||
static void gethere(void);
|
||||
@ -112,7 +111,7 @@ static int ignore_backslash_newline;
|
||||
#define o_getsc_u() ((*source->str != '\0') ? *source->str++ : getsc_uu())
|
||||
|
||||
/* retrace helper */
|
||||
#define o_getsc_r(carg) { \
|
||||
#define o_getsc_r(carg) \
|
||||
int cev = (carg); \
|
||||
struct sretrace_info *rp = retrace_info; \
|
||||
\
|
||||
@ -122,17 +121,17 @@ static int ignore_backslash_newline;
|
||||
rp = rp->next; \
|
||||
} \
|
||||
\
|
||||
return (cev); \
|
||||
}
|
||||
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
static int getsc(void);
|
||||
return (cev);
|
||||
|
||||
/* callback */
|
||||
static int
|
||||
getsc(void)
|
||||
getsc_i(void)
|
||||
{
|
||||
o_getsc_r(o_getsc());
|
||||
}
|
||||
|
||||
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
|
||||
#define getsc getsc_i
|
||||
#else
|
||||
static int getsc_r(int);
|
||||
|
||||
@ -269,7 +268,7 @@ yylex(int cf)
|
||||
}
|
||||
/* FALLTHROUGH */
|
||||
case SBASE:
|
||||
if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
|
||||
if (c == '[' && (cf & CMDASN)) {
|
||||
/* temporary */
|
||||
*wp = EOS;
|
||||
if (is_wdvarname(Xstring(ws, wp), false)) {
|
||||
@ -550,8 +549,10 @@ yylex(int cf)
|
||||
* undefined results occur.”).
|
||||
*/
|
||||
statep->ls_bool = false;
|
||||
#ifdef austingroupbugs1015_is_still_not_resolved
|
||||
if (Flag(FPOSIX))
|
||||
break;
|
||||
#endif
|
||||
s2 = statep;
|
||||
base = state_info.base;
|
||||
while (/* CONSTCOND */ 1) {
|
||||
@ -588,8 +589,8 @@ yylex(int cf)
|
||||
*wp++ = CQUOTE;
|
||||
ignore_backslash_newline--;
|
||||
} else if (c == '\\') {
|
||||
if ((c2 = unbksl(true, s_get, s_put)) == -1)
|
||||
c2 = s_get();
|
||||
if ((c2 = unbksl(true, getsc_i, ungetsc)) == -1)
|
||||
c2 = getsc();
|
||||
if (c2 == 0)
|
||||
statep->ls_bool = true;
|
||||
if (!statep->ls_bool) {
|
||||
@ -777,6 +778,7 @@ yylex(int cf)
|
||||
Source *s;
|
||||
|
||||
ungetsc(c2);
|
||||
ungetsc(c);
|
||||
/*
|
||||
* mismatched parenthesis -
|
||||
* assume we were really
|
||||
@ -789,6 +791,7 @@ yylex(int cf)
|
||||
s->start = s->str = s->u.freeme = dp;
|
||||
s->next = source;
|
||||
source = s;
|
||||
ungetsc('('/*)*/);
|
||||
return ('('/*)*/);
|
||||
}
|
||||
} else if (c == '(')
|
||||
@ -1088,7 +1091,7 @@ yylex(int cf)
|
||||
}
|
||||
} else if (cf & ALIAS) {
|
||||
/* 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));
|
||||
afree(dp, ATEMP);
|
||||
}
|
||||
@ -1783,15 +1786,3 @@ pop_state_i(State_info *si, Lex_state *old_end)
|
||||
|
||||
return (si->base + STATE_BSIZE - 1);
|
||||
}
|
||||
|
||||
static int
|
||||
s_get(void)
|
||||
{
|
||||
return (getsc());
|
||||
}
|
||||
|
||||
static void
|
||||
s_put(int c)
|
||||
{
|
||||
ungetsc(c);
|
||||
}
|
||||
|
56
mksh.1
56
mksh.1
@ -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 $
|
||||
.\"-
|
||||
.\" 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>
|
||||
.\"
|
||||
.\" 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
|
||||
.\" 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.
|
||||
.\"
|
||||
@ -179,6 +179,11 @@ script use.
|
||||
Its command language is a superset of the
|
||||
.Xr sh C
|
||||
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?
|
||||
.Nm mksh
|
||||
is a
|
||||
@ -434,7 +439,7 @@ Whitespace and meta-characters can be quoted individually using a backslash
|
||||
or in groups using double
|
||||
.Pq Sq \&"
|
||||
or single
|
||||
.Pq Sq \*(aq
|
||||
.Pq Dq \*(aq
|
||||
quotes.
|
||||
Note that the following characters are also treated specially by the
|
||||
shell and must be quoted if they are to represent themselves:
|
||||
@ -988,7 +993,7 @@ case both the
|
||||
.Ql \e
|
||||
and the newline are stripped.
|
||||
Second, a single quote
|
||||
.Pq Sq \*(aq
|
||||
.Pq Dq \*(aq
|
||||
quotes everything up to the next single quote (this may span lines).
|
||||
Third, a double quote
|
||||
.Pq Sq \&"
|
||||
@ -1001,8 +1006,8 @@ up to the next unquoted double quote.
|
||||
.Ql $
|
||||
and
|
||||
.Ql \`
|
||||
inside double quotes have their usual meaning (i.e. parameter, command, or
|
||||
arithmetic substitution) except no field splitting is carried out on the
|
||||
inside double quotes have their usual meaning (i.e. parameter, arithmetic,
|
||||
or command substitution) except no field splitting is carried out on the
|
||||
results of double-quoted substitutions.
|
||||
If a
|
||||
.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.
|
||||
If a double-quoted string is preceded by an unquoted
|
||||
.Ql $ ,
|
||||
the latter is ignored.
|
||||
the
|
||||
.Ql $
|
||||
is simply ignored.
|
||||
.Ss Backslash expansion
|
||||
In places where backslashes are expanded, certain C and
|
||||
.At
|
||||
@ -1286,6 +1293,8 @@ command which is run in a subshell.
|
||||
For
|
||||
.Pf $( Ns Ar command Ns \&)
|
||||
and
|
||||
.Pf ${\*(Ba\& Ns Ar command Ns \&;}
|
||||
and
|
||||
.Pf ${\ \& Ar command Ns \&;}
|
||||
substitutions, normal quoting rules are used when
|
||||
.Ar command
|
||||
@ -1296,6 +1305,8 @@ form, a
|
||||
followed by any of
|
||||
.Ql $ ,
|
||||
.Ql \` ,
|
||||
.Ql \&"
|
||||
.Pq currently, and violating Tn POSIX ,
|
||||
or
|
||||
.Ql \e
|
||||
is stripped (a
|
||||
@ -1744,7 +1755,7 @@ command below for a list of options).
|
||||
The exit status of the last non-asynchronous command executed.
|
||||
If the last command was killed by a signal,
|
||||
.Ic $?\&
|
||||
is set to 128 plus the signal number.
|
||||
is set to 128 plus the signal number, but at most 255.
|
||||
.It Ev 0
|
||||
The name of the shell, determined as follows:
|
||||
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
|
||||
assignments are performed and exported for the duration of the command.
|
||||
.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
|
||||
.Bl -tag -width false -compact
|
||||
.It Ic \&. Ar file Op Ar arg ...
|
||||
@ -3815,7 +3827,8 @@ Store the result without word splitting into the parameter
|
||||
.Ev REPLY )
|
||||
as array of characters (wide characters if the
|
||||
.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
|
||||
Use the first byte of
|
||||
.Ar x ,
|
||||
@ -4138,6 +4151,12 @@ or
|
||||
case-insensitively; for direct builtin calls depending on the
|
||||
aforementioned environment variables; or for stdin or scripts,
|
||||
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
|
||||
Referencing of an unset parameter, other than
|
||||
.Dq $@
|
||||
@ -6552,6 +6571,7 @@ case ${KSH_VERSION:\-} in
|
||||
esac ;;
|
||||
esac
|
||||
.Ed
|
||||
In near future, (Unicode) locale tracking will be implemented though.
|
||||
.Sh BUGS
|
||||
Suspending (using \*(haZ) pipelines like the one below will only suspend
|
||||
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
|
||||
.Xr memmove 3 .
|
||||
.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
|
||||
.Nm mksh\ R52
|
||||
.Nm mksh\ R52b
|
||||
and up,
|
||||
.\" with vendor patches from insert-your-name-here,
|
||||
compiled without any options impacting functionality, such as
|
||||
|
19
sh.h
19
sh.h
@ -10,7 +10,7 @@
|
||||
|
||||
/*-
|
||||
* Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2014, 2015
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -175,9 +175,9 @@
|
||||
#endif
|
||||
|
||||
#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
|
||||
#define MKSH_VERSION "R52 2015/12/12"
|
||||
#define MKSH_VERSION "R52 2016/01/20"
|
||||
|
||||
/* arithmetic types: C implementation */
|
||||
#if !HAVE_CAN_INTTYPES
|
||||
@ -379,6 +379,8 @@ struct rusage {
|
||||
#define ksh_NSIG 64
|
||||
#endif
|
||||
|
||||
#define ksh_sigmask(sig) (((sig) < 1 || (sig) > 127) ? 255 : 128 + (sig))
|
||||
|
||||
|
||||
/* OS-dependent additions (functions, variables, by OS) */
|
||||
|
||||
@ -862,6 +864,8 @@ EXTERN const char Tgbuiltin[] E_INIT("=builtin");
|
||||
#define Tbuiltin (Tgbuiltin + 1) /* "builtin" */
|
||||
EXTERN const char T_function[] E_INIT(" 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");
|
||||
#define TC_IFSWS (TC_LEX1 + 7) /* space tab newline */
|
||||
|
||||
@ -1186,6 +1190,8 @@ struct tbl {
|
||||
};
|
||||
|
||||
EXTERN struct tbl vtemp;
|
||||
/* set by global() and local() */
|
||||
EXTERN bool last_lookup_was_array;
|
||||
|
||||
/* common flag bits */
|
||||
#define ALLOC BIT(0) /* val.s has been allocated */
|
||||
@ -1628,13 +1634,12 @@ typedef union {
|
||||
#define ALIAS BIT(2) /* recognise alias */
|
||||
#define KEYWORD BIT(3) /* recognise keywords */
|
||||
#define LETEXPR BIT(4) /* get expression inside (( )) */
|
||||
#define VARASN BIT(5) /* check for var=word */
|
||||
#define ARRAYVAR BIT(6) /* parse x[1 & 2] as one word */
|
||||
#define CMDASN BIT(5) /* parse x[1 & 2] as one word, for typeset */
|
||||
#define HEREDOC BIT(6) /* parsing a here document body */
|
||||
#define ESACONLY BIT(7) /* only accept esac keyword */
|
||||
#define CMDWORD BIT(8) /* parsing simple command (alias related) */
|
||||
#define HEREDELIM BIT(9) /* parsing <<,<<- delimiter */
|
||||
#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 */
|
||||
|
||||
@ -1993,7 +1998,7 @@ char *shf_smprintf(const char *, ...)
|
||||
ssize_t shf_vfprintf(struct shf *, const char *, va_list)
|
||||
MKSH_A_FORMAT(__printf__, 2, 0);
|
||||
/* syn.c */
|
||||
int assign_command(const char *);
|
||||
int assign_command(const char *, bool);
|
||||
void initkeywords(void);
|
||||
struct op *compile(Source *, bool);
|
||||
bool parse_usec(const char *, struct timeval *);
|
||||
|
4
shf.c
4
shf.c
@ -25,7 +25,7 @@
|
||||
|
||||
#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() */
|
||||
#define EB_READSW 0x01 /* about to switch to reading */
|
||||
@ -1121,6 +1121,8 @@ cstrerror(int errnum)
|
||||
#endif
|
||||
case EACCES:
|
||||
return ("Permission denied");
|
||||
case EEXIST:
|
||||
return ("File exists");
|
||||
case ENOTDIR:
|
||||
return ("Not a directory");
|
||||
#ifdef EINVAL
|
||||
|
41
syn.c
41
syn.c
@ -2,7 +2,7 @@
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||
* 2011, 2012, 2013, 2014, 2015
|
||||
* 2011, 2012, 2013, 2014, 2015, 2016
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
#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 {
|
||||
int start_token; /* token than began nesting (eg, FOR) */
|
||||
@ -272,7 +272,6 @@ get_command(int cf)
|
||||
int c, iopn = 0, syniocf, lno;
|
||||
struct ioword *iop, **iops;
|
||||
XPtrV args, vars;
|
||||
char *tcp;
|
||||
struct nesting_state old_nesting;
|
||||
|
||||
/* NUFILE is small enough to leave this addition unchecked */
|
||||
@ -281,7 +280,7 @@ get_command(int cf)
|
||||
XPinit(vars, 16);
|
||||
|
||||
syniocf = KEYWORD|sALIAS;
|
||||
switch (c = token(cf|KEYWORD|sALIAS|VARASN)) {
|
||||
switch (c = token(cf|KEYWORD|sALIAS|CMDASN)) {
|
||||
default:
|
||||
REJECT;
|
||||
afree(iops, ATEMP);
|
||||
@ -296,9 +295,18 @@ get_command(int cf)
|
||||
syniocf &= ~(KEYWORD|sALIAS);
|
||||
t = newtp(TCOM);
|
||||
t->lineno = source->line;
|
||||
goto get_command_begin;
|
||||
while (/* CONSTCOND */ 1) {
|
||||
cf = (t->u.evalflags ? ARRAYVAR : 0) |
|
||||
(XPsize(args) == 0 ? sALIAS|VARASN : CMDWORD);
|
||||
bool check_assign_cmd;
|
||||
|
||||
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)) {
|
||||
case REDIR:
|
||||
while ((iop = synio(cf)) != NULL) {
|
||||
@ -316,9 +324,12 @@ get_command(int cf)
|
||||
* dubious but AT&T ksh acts this way
|
||||
*/
|
||||
if (iopn == 0 && XPsize(vars) == 0 &&
|
||||
XPsize(args) == 0 &&
|
||||
assign_command(ident))
|
||||
check_assign_cmd) {
|
||||
if (assign_command(ident, false))
|
||||
t->u.evalflags = DOVACHECK;
|
||||
else if (strcmp(ident, Tcommand) != 0)
|
||||
check_assign_cmd = false;
|
||||
}
|
||||
if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
|
||||
is_wdvarassign(yylval.cp))
|
||||
XPput(vars, yylval.cp);
|
||||
@ -329,6 +340,8 @@ get_command(int cf)
|
||||
case '(' /*)*/:
|
||||
if (XPsize(args) == 0 && XPsize(vars) == 1 &&
|
||||
is_wdvarassign(yylval.cp)) {
|
||||
char *tcp;
|
||||
|
||||
/* wdarrassign: foo=(bar) */
|
||||
ACCEPT;
|
||||
|
||||
@ -400,6 +413,7 @@ get_command(int cf)
|
||||
case LWORD:
|
||||
break;
|
||||
case '(': /*)*/
|
||||
c = '(';
|
||||
goto Subshell;
|
||||
default:
|
||||
syntaxerr(NULL);
|
||||
@ -431,7 +445,7 @@ get_command(int cf)
|
||||
case FOR:
|
||||
case SELECT:
|
||||
t = newtp((c == FOR) ? TFOR : TSELECT);
|
||||
musthave(LWORD, ARRAYVAR);
|
||||
musthave(LWORD, CMDASN);
|
||||
if (!is_wdvarname(yylval.cp, true))
|
||||
yyerror("%s: %s\n", c == FOR ? "for" : Tselect,
|
||||
"bad identifier");
|
||||
@ -572,7 +586,7 @@ elsepart(void)
|
||||
{
|
||||
struct op *t;
|
||||
|
||||
switch (token(KEYWORD|sALIAS|VARASN)) {
|
||||
switch (token(KEYWORD|sALIAS|CMDASN)) {
|
||||
case ELSE:
|
||||
if ((t = c_list(true)) == NULL)
|
||||
syntaxerr(NULL);
|
||||
@ -943,13 +957,14 @@ compile(Source *s, bool skiputf8bom)
|
||||
* $
|
||||
*/
|
||||
int
|
||||
assign_command(const char *s)
|
||||
assign_command(const char *s, bool docommand)
|
||||
{
|
||||
if (!*s)
|
||||
return (0);
|
||||
return ((strcmp(s, Talias) == 0) ||
|
||||
(strcmp(s, Texport) == 0) ||
|
||||
(strcmp(s, Treadonly) == 0) ||
|
||||
(docommand && (strcmp(s, Tcommand) == 0)) ||
|
||||
(strcmp(s, Ttypeset) == 0));
|
||||
}
|
||||
|
||||
@ -991,7 +1006,7 @@ static const char db_gthan[] = { CHAR, '>', EOS };
|
||||
static Test_op
|
||||
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;
|
||||
char *save = NULL;
|
||||
Test_op ret = TO_NONOP;
|
||||
@ -1037,7 +1052,7 @@ static const char *
|
||||
dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED,
|
||||
bool do_eval MKSH_A_UNUSED)
|
||||
{
|
||||
int c = tpeek(ARRAYVAR);
|
||||
int c = tpeek(CMDASN);
|
||||
|
||||
if (c != LWORD)
|
||||
return (NULL);
|
||||
|
21
tree.c
21
tree.c
@ -2,7 +2,7 @@
|
||||
|
||||
/*-
|
||||
* Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
|
||||
* 2011, 2012, 2013, 2015
|
||||
* 2011, 2012, 2013, 2015, 2016
|
||||
* mirabilos <m@mirbsd.org>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -23,7 +23,7 @@
|
||||
|
||||
#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
|
||||
|
||||
@ -318,6 +318,10 @@ wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
|
||||
case EOS:
|
||||
return (--wp);
|
||||
case ADELIM:
|
||||
if (*wp == /*{*/'}') {
|
||||
++wp;
|
||||
goto wdvarput_csubst;
|
||||
}
|
||||
case CHAR:
|
||||
c = *wp++;
|
||||
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);
|
||||
break;
|
||||
case CSUBST:
|
||||
if (*wp++ == '}')
|
||||
if (*wp++ == '}') {
|
||||
wdvarput_csubst:
|
||||
shf_putc('}', shf);
|
||||
}
|
||||
return (wp);
|
||||
case OPAT:
|
||||
shf_putchar(*wp++, shf);
|
||||
@ -581,8 +587,10 @@ wdscan(const char *wp, int c)
|
||||
case EOS:
|
||||
return (wp);
|
||||
case ADELIM:
|
||||
if (c == ADELIM)
|
||||
if (c == ADELIM && nest == 0)
|
||||
return (wp + 1);
|
||||
if (*wp == /*{*/'}')
|
||||
goto wdscan_csubst;
|
||||
/* FALLTHROUGH */
|
||||
case CHAR:
|
||||
case QCHAR:
|
||||
@ -604,6 +612,7 @@ wdscan(const char *wp, int c)
|
||||
;
|
||||
break;
|
||||
case CSUBST:
|
||||
wdscan_csubst:
|
||||
wp++;
|
||||
if (c == CSUBST && nest == 0)
|
||||
return (wp);
|
||||
@ -807,6 +816,10 @@ dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
|
||||
shf_puts("EOS", shf);
|
||||
return (--wp);
|
||||
case ADELIM:
|
||||
if (*wp == /*{*/'}') {
|
||||
shf_puts("]ADELIM(})", shf);
|
||||
return (wp + 1);
|
||||
}
|
||||
shf_puts("ADELIM=", shf);
|
||||
if (0)
|
||||
case CHAR:
|
||||
|
65
var.c
65
var.c
@ -2,7 +2,7 @@
|
||||
|
||||
/*-
|
||||
* 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>
|
||||
*
|
||||
* Provided that these terms and disclaimer and all copyright notices
|
||||
@ -28,7 +28,7 @@
|
||||
#include <sys/sysctl.h>
|
||||
#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
|
||||
@ -218,14 +218,16 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
|
||||
return (n);
|
||||
}
|
||||
|
||||
#define vn vname.ro
|
||||
/*
|
||||
* Search for variable, if not found create globally.
|
||||
*/
|
||||
struct tbl *
|
||||
global(const char *n)
|
||||
{
|
||||
struct block *l = e->loc;
|
||||
struct tbl *vp;
|
||||
union mksh_cchack vname;
|
||||
struct block *l = e->loc;
|
||||
int c;
|
||||
bool array;
|
||||
uint32_t h, val;
|
||||
@ -234,9 +236,9 @@ global(const char *n)
|
||||
* check to see if this is an array;
|
||||
* dereference namerefs; must come first
|
||||
*/
|
||||
n = array_index_calc(n, &array, &val);
|
||||
h = hash(n);
|
||||
c = (unsigned char)n[0];
|
||||
vn = array_index_calc(n, &array, &val);
|
||||
h = hash(vn);
|
||||
c = (unsigned char)vn[0];
|
||||
if (!ksh_isalphx(c)) {
|
||||
if (array)
|
||||
errorf("bad substitution");
|
||||
@ -246,15 +248,15 @@ global(const char *n)
|
||||
vp->areap = ATEMP;
|
||||
*vp->name = 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(vp, l->argv[c], KSH_RETURN_ERROR);
|
||||
vp->flag |= RDONLY;
|
||||
return (vp);
|
||||
goto out;
|
||||
}
|
||||
vp->flag |= RDONLY;
|
||||
if (n[1] != '\0')
|
||||
return (vp);
|
||||
if (vn[1] != '\0')
|
||||
goto out;
|
||||
vp->flag |= ISSET|INTEGER;
|
||||
switch (c) {
|
||||
case '$':
|
||||
@ -278,17 +280,24 @@ global(const char *n)
|
||||
default:
|
||||
vp->flag &= ~(ISSET|INTEGER);
|
||||
}
|
||||
return (vp);
|
||||
goto out;
|
||||
}
|
||||
l = varsearch(e->loc, &vp, n, h);
|
||||
if (vp != NULL)
|
||||
return (array ? arraysearch(vp, val) : vp);
|
||||
vp = ktenter(&l->vars, n, h);
|
||||
l = varsearch(e->loc, &vp, vn, h);
|
||||
if (vp != NULL) {
|
||||
if (array)
|
||||
vp = arraysearch(vp, val);
|
||||
goto out;
|
||||
}
|
||||
vp = ktenter(&l->vars, vn, h);
|
||||
if (array)
|
||||
vp = arraysearch(vp, val);
|
||||
vp->flag |= DEFINED;
|
||||
if (special(n))
|
||||
if (special(vn))
|
||||
vp->flag |= SPECIAL;
|
||||
out:
|
||||
last_lookup_was_array = array;
|
||||
if (vn != n)
|
||||
afree(vname.rw, ATEMP);
|
||||
return (vp);
|
||||
}
|
||||
|
||||
@ -298,8 +307,9 @@ global(const char *n)
|
||||
struct tbl *
|
||||
local(const char *n, bool copy)
|
||||
{
|
||||
struct block *l = e->loc;
|
||||
struct tbl *vp;
|
||||
union mksh_cchack vname;
|
||||
struct block *l = e->loc;
|
||||
bool array;
|
||||
uint32_t h, val;
|
||||
|
||||
@ -307,20 +317,20 @@ local(const char *n, bool copy)
|
||||
* check to see if this is an array;
|
||||
* dereference namerefs; must come first
|
||||
*/
|
||||
n = array_index_calc(n, &array, &val);
|
||||
h = hash(n);
|
||||
if (!ksh_isalphx(*n)) {
|
||||
vn = array_index_calc(n, &array, &val);
|
||||
h = hash(vn);
|
||||
if (!ksh_isalphx(*vn)) {
|
||||
vp = &vtemp;
|
||||
vp->flag = DEFINED|RDONLY;
|
||||
vp->type = 0;
|
||||
vp->areap = ATEMP;
|
||||
return (vp);
|
||||
goto out;
|
||||
}
|
||||
vp = ktenter(&l->vars, n, h);
|
||||
vp = ktenter(&l->vars, vn, h);
|
||||
if (copy && !(vp->flag & DEFINED)) {
|
||||
struct tbl *vq;
|
||||
|
||||
varsearch(l->next, &vq, n, h);
|
||||
varsearch(l->next, &vq, vn, h);
|
||||
if (vq != NULL) {
|
||||
vp->flag |= vq->flag &
|
||||
(EXPORT | INTEGER | RDONLY | LJUST | RJUST |
|
||||
@ -333,10 +343,15 @@ local(const char *n, bool copy)
|
||||
if (array)
|
||||
vp = arraysearch(vp, val);
|
||||
vp->flag |= DEFINED;
|
||||
if (special(n))
|
||||
if (special(vn))
|
||||
vp->flag |= SPECIAL;
|
||||
out:
|
||||
last_lookup_was_array = array;
|
||||
if (vn != n)
|
||||
afree(vname.rw, ATEMP);
|
||||
return (vp);
|
||||
}
|
||||
#undef vn
|
||||
|
||||
/* get variable string value */
|
||||
char *
|
||||
@ -1492,7 +1507,7 @@ arrayname(const char *str)
|
||||
const char *p;
|
||||
char *rv;
|
||||
|
||||
if ((p = cstrchr(str, '[')) == 0)
|
||||
if (!(p = cstrchr(str, '[')))
|
||||
/* Shouldn't happen, but why worry? */
|
||||
strdupx(rv, str, ATEMP);
|
||||
else
|
||||
|
Loading…
x
Reference in New Issue
Block a user