merge the nameref code, using mksh standard scoping as discussed

This commit is contained in:
tg 2009-09-06 17:42:15 +00:00
parent 574d6725aa
commit 9531e12b36
7 changed files with 244 additions and 28 deletions

115
check.t
View File

@ -1,4 +1,4 @@
# $MirOS: src/bin/mksh/check.t,v 1.302 2009/08/30 21:01:59 tg Exp $
# $MirOS: src/bin/mksh/check.t,v 1.303 2009/09/06 17:42:11 tg Exp $
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
@ -4197,7 +4197,7 @@ name: xxx-param-subst-qmark-1
description:
Check suppresion of error message with null string. According to
POSIX, it shouldn't print the error as 'word' isn't ommitted.
ksh88, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error,
ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error,
that's why the condition is reversed.
stdin:
unset foo
@ -4731,6 +4731,7 @@ expected-stdout:
integer='typeset -i'
local=typeset
login='exec login'
nameref='typeset -n'
nohup='nohup '
r='fc -e -'
source='PATH=$PATH:. command .'
@ -4753,6 +4754,7 @@ expected-stdout:
integer='typeset -i'
local=typeset
login='exec login'
nameref='typeset -n'
nohup='nohup '
r='fc -e -'
source='PATH=$PATH:. command .'
@ -4799,6 +4801,7 @@ expected-stdout:
integer='typeset -i'
local=typeset
login='exec login'
nameref='typeset -n'
nohup='nohup '
r='fc -e -'
source='PATH=$PATH:. command .'
@ -4823,6 +4826,7 @@ expected-stdout:
integer='typeset -i'
local=typeset
login='exec login'
nameref='typeset -n'
nohup='nohup '
r='fc -e -'
source='PATH=$PATH:. command .'
@ -4846,6 +4850,7 @@ expected-stdout:
integer='typeset -i'
local=typeset
login='exec login'
nameref='typeset -n'
nohup='nohup '
r='fc -e -'
source='PATH=$PATH:. command .'
@ -4868,6 +4873,7 @@ expected-stdout:
integer='typeset -i'
local=typeset
login='exec login'
nameref='typeset -n'
nohup='nohup '
r='fc -e -'
source='PATH=$PATH:. command .'
@ -5006,6 +5012,33 @@ expected-stdout:
g 0<0>1<1>2<4> = g
h 0<0>1<1>2<4> = h
---
name: arrays-7
description:
Check if we can get the array keys (indices) for indexed arrays,
Korn shell style, in some corner cases
stdin:
print !arz: ${!arz}
print !arz[0]: ${!arz[0]}
print !arz[1]: ${!arz[1]}
arz=foo
print !arz: ${!arz}
print !arz[0]: ${!arz[0]}
print !arz[1]: ${!arz[1]}
unset arz
print !arz: ${!arz}
print !arz[0]: ${!arz[0]}
print !arz[1]: ${!arz[1]}
expected-stdout:
!arz: 0
!arz[0]:
!arz[1]:
!arz: arz
!arz[0]: 0
!arz[1]:
!arz: 0
!arz[0]:
!arz[1]:
---
name: varexpand-substr-1
description:
Check if bash-style substring expansion works
@ -6100,3 +6133,81 @@ expected-stdout:
mypid=nz
=15
---
name: nameref-1
description:
Testsuite for nameref (bound variables)
stdin:
bar=global
typeset -n ir2=bar
typeset -n ind=ir2
print !ind: ${!ind}
print ind: $ind
print !ir2: ${!ir2}
print ir2: $ir2
typeset +n ind
print !ind: ${!ind}
print ind: $ind
typeset -n ir2=ind
print !ir2: ${!ir2}
print ir2: $ir2
set|grep ^ir2|sed 's/^/s1: /'
typeset|grep ' ir2'|sed -e 's/^/s2: /' -e 's/nameref/typeset -n/'
blub=(e1 e2 e3)
typeset -n ind=blub
typeset -n ir2=blub[2]
print !ind[1]: ${!ind[1]}
print !ir2: $!ir2
print ind[1]: ${ind[1]}
print ir2: $ir2
expected-stdout:
!ind: bar
ind: global
!ir2: bar
ir2: global
!ind: ind
ind: ir2
!ir2: ind
ir2: ir2
s1: ir2=ind
s2: typeset -n ir2
!ind[1]: 1
!ir2: ir2
ind[1]: e2
ir2: e3
---
name: nameref-2da
description:
Testsuite for nameref (bound variables)
Functions, argument given directly, after local
stdin:
function foo {
typeset bar=lokal baz=auch
typeset -n v=bar
print entering
print !v: ${!v}
print !bar: ${!bar}
print !baz: ${!baz}
print bar: $bar
print v: $v
v=123
print bar: $bar
print v: $v
print exiting
}
bar=global
print bar: $bar
foo bar
print bar: $bar
expected-stdout:
bar: global
entering
!v: bar
!bar: bar
!baz: baz
bar: lokal
v: lokal
bar: 123
v: 123
exiting
bar: global
---

26
eval.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.68 2009/08/28 22:39:09 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.69 2009/09/06 17:42:12 tg Exp $");
/*
* string expansion
@ -1021,8 +1021,7 @@ varsub(Expand *xp, const char *sp, const char *word,
if (!(vp->flag&ISSET))
continue;
XPput(wv, c == '!' ? shf_smprintf("%lu",
vp->flag & AINDEX ?
(unsigned long)vp->ua.index : 0) :
arrayindex(vp)) :
str_val(vp));
}
if (XPsize(wv) == 0) {
@ -1037,14 +1036,27 @@ varsub(Expand *xp, const char *sp, const char *word,
state = XARG;
}
} else {
if (*sp == '!' && sp[1])
return (-1);
/* Can't assign things like $! or $1 */
if ((stype & 0x7f) == '=' &&
ctype(*sp, C_VAR1 | C_DIGIT))
return (-1);
xp->var = global(sp);
xp->str = str_val(xp->var);
if (*sp == '!' && sp[1]) {
++sp;
xp->var = global(sp);
if (cstrchr(sp, '[')) {
if (xp->var->flag & ISSET)
xp->str = shf_smprintf("%lu",
arrayindex(xp->var));
else
xp->str = null;
} else if (xp->var->flag & ISSET)
xp->str = xp->var->name;
else
xp->str = "0"; /* ksh93 compat */
} else {
xp->var = global(sp);
xp->str = str_val(xp->var);
}
state = XSUB;
}
}

26
funcs.c
View File

@ -25,7 +25,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.128 2009/08/30 21:02:00 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.129 2009/09/06 17:42:12 tg Exp $");
#if HAVE_KILLPG
/*
@ -47,6 +47,8 @@ __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.128 2009/08/30 21:02:00 tg Exp $");
#define c_ulimit c_label
#endif
extern uint8_t set_refflag;
/* A leading = means assignments before command are kept;
* a leading * means a POSIX special builtin;
* a leading + means a POSIX regular builtin
@ -896,7 +898,7 @@ c_typeset(const char **wp)
}
/* see comment below regarding possible opions */
opts = istset ? "L#R#UZ#afi#lprtux" : "p";
opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
fieldstr = basestr = NULL;
builtin_opt.flags |= GF_PLUSOPT;
@ -947,6 +949,9 @@ c_typeset(const char **wp)
case 'l':
flag = LCASEV;
break;
case 'n':
set_refflag = (builtin_opt.info & GI_PLUS) ? 2 : 1;
break;
case 'p':
/* export, readonly: POSIX -p flag */
/* typeset: show values as well */
@ -995,13 +1000,14 @@ c_typeset(const char **wp)
builtin_opt.optind++;
}
if (func && ((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT))) {
if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) || set_refflag)) {
bi_errorf("only -t, -u and -x options may be used with -f");
set_refflag = 0;
return (1);
}
if (wp[builtin_opt.optind]) {
/* Take care of exclusions.
* At this point, flags in fset are cleared in fclr and vise
* At this point, flags in fset are cleared in fclr and vice
* versa. This property should be preserved.
*/
if (fset & LCASEV) /* LCASEV has priority over UCASEV_AL */
@ -1015,8 +1021,8 @@ c_typeset(const char **wp)
/* Setting these attributes clears the others, unless they
* are also set in this command
*/
if (fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
INTEGER | INT_U | INT_L))
if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
INTEGER | INT_U | INT_L)) || set_refflag)
fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
LCASEV | INTEGER | INT_U | INT_L);
}
@ -1047,9 +1053,11 @@ c_typeset(const char **wp)
"%s() %T\n", wp[i], f->val.t);
} else if (!typeset(wp[i], fset, fclr, field, base)) {
bi_errorf("%s: not identifier", wp[i]);
set_refflag = 0;
return (1);
}
}
set_refflag = 0;
return (rv);
}
@ -1112,6 +1120,8 @@ c_typeset(const char **wp)
* be suitable for re-entry...
*/
shf_puts("typeset ", shl_stdout);
if (((vp->flag&(ARRAY|ASSOC))==ASSOC))
shf_puts("-n ", shl_stdout);
if ((vp->flag&INTEGER))
shf_puts("-i ", shl_stdout);
if ((vp->flag&EXPORT))
@ -1160,9 +1170,7 @@ c_typeset(const char **wp)
if ((vp->flag&ARRAY) && any_set)
shprintf("%s[%lu]",
vp->name,
vp->flag & AINDEX ?
(unsigned long)vp->ua.index :
0);
arrayindex(vp));
else
shf_puts(vp->name, shl_stdout);
if (thing == '-' && (vp->flag&ISSET)) {

3
main.c
View File

@ -33,7 +33,7 @@
#include <locale.h>
#endif
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.143 2009/08/29 11:26:44 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.144 2009/09/06 17:42:13 tg Exp $");
extern char **environ;
@ -67,6 +67,7 @@ static const char *initcoms[] = {
"autoload=typeset -fu",
"functions=typeset -f",
"history=fc -l",
"nameref=typeset -n",
"nohup=nohup ",
r_fc_e_,
"source=PATH=$PATH:. command .",

31
mksh.1
View File

@ -1,4 +1,4 @@
.\" $MirOS: src/bin/mksh/mksh.1,v 1.180 2009/09/05 17:12:49 tg Exp $
.\" $MirOS: src/bin/mksh/mksh.1,v 1.181 2009/09/06 17:42:13 tg Exp $
.\" $OpenBSD: ksh.1,v 1.129 2009/05/28 06:09:06 jmc Exp $
.\"-
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
@ -48,7 +48,7 @@
.el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
..
.\"-
.Dd $Mdocdate: September 5 2009 $
.Dd $Mdocdate: September 6 2009 $
.Dt MKSH 1
.Os MirBSD
.Sh NAME
@ -883,6 +883,7 @@ history=\*(aqfc \-l\*(aq
integer=\*(aqtypeset \-i\*(aq
local=\*(aqtypeset\*(aq
login=\*(aqexec login\*(aq
nameref=\*(aqtypeset -n\*(aq
nohup=\*(aqnohup \*(aq
r=\*(aqfc \-e \-\*(aq
stop=\*(aqkill \-STOP\*(aq
@ -1261,6 +1262,18 @@ of the string value of parameter
The number of elements in the array
.Ar name .
.Pp
.It Pf ${! Ns Ar name Ns }
The name of the variable referred to by
.Ar name .
This will be
.Ar name
except when
.Ar name
is a name reference (bound variable), created by the
.Ic nameref
command (which is an alias for
.Ic typeset Fl n ) .
.Pp
.It Pf ${! Ns Ar name Ns [*]}
.It Pf ${! Ns Ar name Ns [@]}
The names of indices (keys) in the array
@ -4110,7 +4123,7 @@ A command that exits with a zero value.
.Pp
.It Xo
.Ic typeset
.Oo Op Ic +\-alprtUux
.Oo Op Ic +\-alpnrtUux
.Op Fl L Ns Op Ar n
.Op Fl R Ns Op Ar n
.Op Fl Z Ns Op Ar n
@ -4191,6 +4204,18 @@ All upper case characters in values are converted to lower case.
when used with the
.Fl i
option.)
.It Fl n
Create a bound variable (name reference): any access to the variable
.Ar name
will access the variable
.Ar value
in the current scope (this is different from
.At
.Nm ksh93 ! )
instead.
This can be used by functions to access variables whose names are
passed as parametres, instead of using
.Ic eval .
.It Fl p
Print complete
.Ic typeset

6
sh.h
View File

@ -134,7 +134,7 @@
#endif
#ifdef EXTERN
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.337 2009/08/30 21:02:01 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.338 2009/09/06 17:42:14 tg Exp $");
#endif
#define MKSH_VERSION "R39 2009/08/30"
@ -906,6 +906,7 @@ struct tbl { /* table item */
#define EXPRINEVAL BIT(23) /* contents currently being evaluated */
#define EXPRLVALUE BIT(24) /* useable as lvalue (temp flag) */
#define AINDEX BIT(25) /* array index >0 = ua.index filled in */
#define ASSOC BIT(26) /* ARRAY ? associative : reference */
/* flag bits used for taliases/builtins/aliases/keywords/functions */
#define KEEPASN BIT(8) /* keep command assignments (eg, var=x cmd) */
#define FINUSE BIT(9) /* function being executed */
@ -919,6 +920,9 @@ struct tbl { /* table item */
#define USERATTRIB (EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL|\
LCASEV|UCASEV_AL|INT_U|INT_L)
#define arrayindex(vp) ((unsigned long)((vp)->flag & AINDEX ? \
(vp)->ua.index : 0))
/* command types */
#define CNONE 0 /* undefined */
#define CSHELL 1 /* built-in */

65
var.c
View File

@ -22,7 +22,7 @@
#include "sh.h"
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.86 2009/08/28 22:44:47 tg Exp $");
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.87 2009/09/06 17:42:15 tg Exp $");
/*
* Variables
@ -47,6 +47,8 @@ static const char *array_index_calc(const char *, bool *, uint32_t *);
static int rnd_get(void);
static void rnd_set(unsigned long);
uint8_t set_refflag = 0;
/*
* create a new block for function calls and simple commands
* assume caller has allocated and set up e->loc
@ -135,7 +137,7 @@ initvar(void)
}
/* Used to calculate an array index for global()/local(). Sets *arrayp to
* non-zero if this is an array, sets *valp to the array index, returns
* true if this is an array, sets *valp to the array index, returns
* the basename of the array.
*/
static const char *
@ -143,9 +145,39 @@ array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
{
const char *p;
int len;
char *ap = NULL;
*arrayp = false;
redo_from_ref:
p = skip_varname(n, false);
if (!set_refflag && (p != n) && ksh_isalphx(n[0])) {
struct block *l = e->loc;
struct tbl *vp;
char *vn;
uint32_t h;
strndupx(vn, n, p - n, ATEMP);
h = hash(vn);
/* check if this is a reference */
for (l = e->loc; ; l = l->next) {
if ((vp = ktsearch(&l->vars, vn, h)) != NULL)
break;
if (l->next == NULL)
break;
}
afree(vn, ATEMP);
if (vp && (vp->flag & (DEFINED|ASSOC|ARRAY)) ==
(DEFINED|ASSOC)) {
char *cp;
/* gotcha! */
cp = shf_smprintf("%s%s", str_val(vp), p);
afree(ap, ATEMP);
n = ap = cp;
goto redo_from_ref;
}
}
if (p != n && *p == '[' && (len = array_ref_len(p))) {
char *sub, *tmp;
mksh_ari_t rval;
@ -646,6 +678,9 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
if (*val == '[') {
int len;
if (set_refflag)
errorf("%s: reference variable cannot be an array",
var);
len = array_ref_len(val);
if (len == 0)
return (NULL);
@ -680,6 +715,26 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
vp = (set&LOCAL) ? local(tvar, (set & LOCAL_COPY) ? true : false) :
global(tvar);
if (set_refflag == 2 && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
vp->flag &= ~ASSOC;
else if (set_refflag == 1) {
if (vp->flag & ARRAY) {
struct tbl *a, *tmp;
/* Free up entire array */
for (a = vp->u.array; a; ) {
tmp = a;
a = a->u.array;
if (tmp->flag & ALLOC)
afree(tmp->val.s, tmp->areap);
afree(tmp, tmp->areap);
}
vp->u.array = NULL;
vp->flag &= ~ARRAY;
}
vp->flag |= ASSOC;
}
set &= ~(LOCAL|LOCAL_COPY);
vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;
@ -803,8 +858,8 @@ unset(struct tbl *vp, int array_ref)
}
/* return a pointer to the first char past a legal variable name (returns the
* argument if there is no legal name, returns * a pointer to the terminating
* null if whole string is legal).
* argument if there is no legal name, returns a pointer to the terminating
* NUL if whole string is legal).
*/
const char *
skip_varname(const char *s, int aok)
@ -1240,7 +1295,7 @@ arraysearch(struct tbl *vp, uint32_t val)
struct tbl *prev, *curr, *new;
size_t len;
vp->flag |= ARRAY|DEFINED;
vp->flag = (vp->flag | (ARRAY|DEFINED)) & ~ASSOC;
/* The table entry is always [0] */
if (val == 0)
return (vp);