+= support for variables and arrays
This commit is contained in:
18
check.t
18
check.t
@ -1,4 +1,4 @@
|
|||||||
# $MirOS: src/bin/mksh/check.t,v 1.448 2011/05/02 22:52:49 tg Exp $
|
# $MirOS: src/bin/mksh/check.t,v 1.449 2011/05/04 23:16:00 tg Exp $
|
||||||
# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas 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: 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 $
|
# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
|
||||||
@ -6209,6 +6209,22 @@ expected-stdout:
|
|||||||
2g 009 .
|
2g 009 .
|
||||||
2h 00001 00002 .
|
2h 00001 00002 .
|
||||||
---
|
---
|
||||||
|
name: arrays-9
|
||||||
|
description:
|
||||||
|
Check that we can concatenate parameters and arrays
|
||||||
|
stdin:
|
||||||
|
unset foo; foo=bar; foo+=baz; echo 1 $foo .
|
||||||
|
unset foo; typeset -i16 foo=10; foo+=20; echo 2 $foo .
|
||||||
|
unset foo; foo=(bar); foo+=(baz); echo 3 ${!foo[*]} : ${foo[*]} .
|
||||||
|
unset foo; foo=(foo bar); foo+=(baz); echo 4 ${!foo[*]} : ${foo[*]} .
|
||||||
|
unset foo; foo=([2]=foo [0]=bar); foo+=(baz [5]=quux); echo 5 ${!foo[*]} : ${foo[*]} .
|
||||||
|
expected-stdout:
|
||||||
|
1 barbaz .
|
||||||
|
2 16#a20 .
|
||||||
|
3 0 1 : bar baz .
|
||||||
|
4 0 1 2 : foo bar baz .
|
||||||
|
5 0 2 3 5 : bar foo baz quux .
|
||||||
|
---
|
||||||
name: varexpand-substr-1
|
name: varexpand-substr-1
|
||||||
description:
|
description:
|
||||||
Check if bash-style substring expansion works
|
Check if bash-style substring expansion works
|
||||||
|
19
misc.c
19
misc.c
@ -29,7 +29,7 @@
|
|||||||
#include <grp.h>
|
#include <grp.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.164 2011/03/28 21:31:01 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.165 2011/05/04 23:16:02 tg Exp $");
|
||||||
|
|
||||||
/* type bits for unsigned char */
|
/* type bits for unsigned char */
|
||||||
unsigned char chtypes[UCHAR_MAX + 1];
|
unsigned char chtypes[UCHAR_MAX + 1];
|
||||||
@ -281,7 +281,8 @@ parse_args(const char **argv,
|
|||||||
const char *array = NULL;
|
const char *array = NULL;
|
||||||
Getopt go;
|
Getopt go;
|
||||||
size_t i;
|
size_t i;
|
||||||
int optc, sortargs = 0, arrayset = 0;
|
int optc, arrayset = 0;
|
||||||
|
bool sortargs = false;
|
||||||
|
|
||||||
/* First call? Build option strings... */
|
/* First call? Build option strings... */
|
||||||
if (cmd_opts[0] == '\0') {
|
if (cmd_opts[0] == '\0') {
|
||||||
@ -400,7 +401,7 @@ parse_args(const char **argv,
|
|||||||
break;
|
break;
|
||||||
/* -s: sort positional params (AT&T ksh stupidity) */
|
/* -s: sort positional params (AT&T ksh stupidity) */
|
||||||
if (what == OF_SET && optc == 's') {
|
if (what == OF_SET && optc == 's') {
|
||||||
sortargs = 1;
|
sortargs = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
for (i = 0; i < NELEM(options); i++)
|
for (i = 0; i < NELEM(options); i++)
|
||||||
@ -427,9 +428,15 @@ parse_args(const char **argv,
|
|||||||
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
|
*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
|
||||||
argv[go.optind]);
|
argv[go.optind]);
|
||||||
|
|
||||||
if (arrayset && (!*array || *skip_varname(array, false))) {
|
if (arrayset) {
|
||||||
bi_errorf("%s: %s", array, "is not an identifier");
|
const char *ccp = NULL;
|
||||||
return (-1);
|
|
||||||
|
if (*array)
|
||||||
|
ccp = skip_varname(array, false);
|
||||||
|
if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
|
||||||
|
bi_errorf("%s: %s", array, "is not an identifier");
|
||||||
|
return (-1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (sortargs) {
|
if (sortargs) {
|
||||||
for (i = go.optind; argv[i]; i++)
|
for (i = go.optind; argv[i]; i++)
|
||||||
|
9
mksh.1
9
mksh.1
@ -1,4 +1,4 @@
|
|||||||
.\" $MirOS: src/bin/mksh/mksh.1,v 1.257 2011/05/02 22:57:22 tg Exp $
|
.\" $MirOS: src/bin/mksh/mksh.1,v 1.258 2011/05/04 23:16:03 tg Exp $
|
||||||
.\" $OpenBSD: ksh.1,v 1.140 2011/04/23 10:14:59 sobrado Exp $
|
.\" $OpenBSD: ksh.1,v 1.140 2011/04/23 10:14:59 sobrado Exp $
|
||||||
.\"-
|
.\"-
|
||||||
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
|
||||||
@ -72,7 +72,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: May 2 2011 $
|
.Dd $Mdocdate: May 4 2011 $
|
||||||
.\"
|
.\"
|
||||||
.\" Check which macro package we use
|
.\" Check which macro package we use
|
||||||
.\"
|
.\"
|
||||||
@ -1332,6 +1332,9 @@ exported; see below for the implications of this).
|
|||||||
Note that both the parameter name and the
|
Note that both the parameter name and the
|
||||||
.Ql =
|
.Ql =
|
||||||
must be unquoted for the shell to recognise a parameter assignment.
|
must be unquoted for the shell to recognise a parameter assignment.
|
||||||
|
The construct
|
||||||
|
.Ic FOO+=baz
|
||||||
|
is also recognised; the old and new values are immediately concatenated.
|
||||||
The fourth way of setting a parameter is with the
|
The fourth way of setting a parameter is with the
|
||||||
.Ic export ,
|
.Ic export ,
|
||||||
.Ic readonly ,
|
.Ic readonly ,
|
||||||
@ -3798,7 +3801,7 @@ and also supported by
|
|||||||
.At
|
.At
|
||||||
.Nm ksh93
|
.Nm ksh93
|
||||||
is:
|
is:
|
||||||
.Ic foo=(a b c)
|
.Ic foo=(a b c); foo+=(d e)
|
||||||
.Pp
|
.Pp
|
||||||
Another
|
Another
|
||||||
.At
|
.At
|
||||||
|
83
var.c
83
var.c
@ -26,7 +26,7 @@
|
|||||||
#include <sys/sysctl.h>
|
#include <sys/sysctl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.119 2011/03/27 18:50:06 tg Exp $");
|
__RCSID("$MirOS: src/bin/mksh/var.c,v 1.120 2011/05/04 23:16:05 tg Exp $");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Variables
|
* Variables
|
||||||
@ -678,6 +678,7 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|||||||
char *tvar;
|
char *tvar;
|
||||||
const char *val;
|
const char *val;
|
||||||
int len;
|
int len;
|
||||||
|
bool vappend = false;
|
||||||
|
|
||||||
/* check for valid variable name, search for value */
|
/* check for valid variable name, search for value */
|
||||||
val = skip_varname(var, false);
|
val = skip_varname(var, false);
|
||||||
@ -706,9 +707,13 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|||||||
}
|
}
|
||||||
val += len;
|
val += len;
|
||||||
}
|
}
|
||||||
if (*val == '=')
|
if (val[0] == '=' || (val[0] == '+' && val[1] == '=')) {
|
||||||
strndupx(tvar, var, val++ - var, ATEMP);
|
strndupx(tvar, var, val - var, ATEMP);
|
||||||
else {
|
if (*val++ == '+') {
|
||||||
|
++val;
|
||||||
|
vappend = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
/* Importing from original environment: must have an = */
|
/* Importing from original environment: must have an = */
|
||||||
if (set & IMPORT)
|
if (set & IMPORT)
|
||||||
return (NULL);
|
return (NULL);
|
||||||
@ -752,8 +757,10 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|||||||
|
|
||||||
vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;
|
vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;
|
||||||
|
|
||||||
/* only allow export flag to be set. AT&T ksh allows any attribute to
|
/*
|
||||||
* be changed which means it can be truncated or modified (-L/-R/-Z/-i)
|
* only allow export flag to be set; AT&T ksh allows any
|
||||||
|
* attribute to be changed which means it can be truncated or
|
||||||
|
* modified (-L/-R/-Z/-i)
|
||||||
*/
|
*/
|
||||||
if ((vpbase->flag&RDONLY) &&
|
if ((vpbase->flag&RDONLY) &&
|
||||||
(val || clr || (set & ~EXPORT)))
|
(val || clr || (set & ~EXPORT)))
|
||||||
@ -793,8 +800,9 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|||||||
t->flag &= ~ALLOC;
|
t->flag &= ~ALLOC;
|
||||||
}
|
}
|
||||||
t->flag = (t->flag | set) & ~clr;
|
t->flag = (t->flag | set) & ~clr;
|
||||||
/* Don't change base if assignment is to be done,
|
/*
|
||||||
* in case assignment fails.
|
* Don't change base if assignment is to be
|
||||||
|
* done, in case assignment fails.
|
||||||
*/
|
*/
|
||||||
if ((set & INTEGER) && base > 0 && (!val || t != vp))
|
if ((set & INTEGER) && base > 0 && (!val || t != vp))
|
||||||
t->type = base;
|
t->type = base;
|
||||||
@ -802,9 +810,11 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|||||||
t->u2.field = field;
|
t->u2.field = field;
|
||||||
if (fake_assign) {
|
if (fake_assign) {
|
||||||
if (!setstr(t, s, KSH_RETURN_ERROR)) {
|
if (!setstr(t, s, KSH_RETURN_ERROR)) {
|
||||||
/* Somewhat arbitrary action here:
|
/*
|
||||||
* zap contents of variable, but keep
|
* Somewhat arbitrary action
|
||||||
* the flag settings.
|
* here: zap contents of
|
||||||
|
* variable, but keep the flag
|
||||||
|
* settings.
|
||||||
*/
|
*/
|
||||||
ok = false;
|
ok = false;
|
||||||
if (t->flag & INTEGER)
|
if (t->flag & INTEGER)
|
||||||
@ -825,6 +835,14 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (val != NULL) {
|
if (val != NULL) {
|
||||||
|
char *tval;
|
||||||
|
|
||||||
|
if (vappend) {
|
||||||
|
tval = shf_smprintf("%s%s", str_val(vp), val);
|
||||||
|
val = tval;
|
||||||
|
} else
|
||||||
|
tval = NULL;
|
||||||
|
|
||||||
if (vp->flag&INTEGER) {
|
if (vp->flag&INTEGER) {
|
||||||
/* do not zero base before assignment */
|
/* do not zero base before assignment */
|
||||||
setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
|
setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
|
||||||
@ -834,6 +852,9 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base)
|
|||||||
} else
|
} else
|
||||||
/* setstr can't fail (readonly check already done) */
|
/* setstr can't fail (readonly check already done) */
|
||||||
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
|
setstr(vp, val, KSH_RETURN_ERROR | 0x4);
|
||||||
|
|
||||||
|
if (tval != NULL)
|
||||||
|
afree(tval, ATEMP);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* only x[0] is ever exported, so use vpbase */
|
/* only x[0] is ever exported, so use vpbase */
|
||||||
@ -942,7 +963,8 @@ is_wdvarassign(const char *s)
|
|||||||
{
|
{
|
||||||
const char *p = skip_wdvarname(s, true);
|
const char *p = skip_wdvarname(s, true);
|
||||||
|
|
||||||
return (p != s && p[0] == CHAR && p[1] == '=');
|
return (p != s && p[0] == CHAR &&
|
||||||
|
(p[1] == '=' || (p[1] == '+' && p[2] == CHAR && p[3] == '=')));
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -1301,33 +1323,42 @@ mksh_uari_t
|
|||||||
set_array(const char *var, bool reset, const char **vals)
|
set_array(const char *var, bool reset, const char **vals)
|
||||||
{
|
{
|
||||||
struct tbl *vp, *vq;
|
struct tbl *vp, *vq;
|
||||||
mksh_uari_t i;
|
mksh_uari_t i, j = 0;
|
||||||
const char *ccp;
|
const char *ccp;
|
||||||
#ifndef MKSH_SMALL
|
char *cp = NULL;
|
||||||
char *cp;
|
|
||||||
mksh_uari_t j;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* to get local array, use "typeset foo; set -A foo" */
|
/* to get local array, use "typeset foo; set -A foo" */
|
||||||
vp = global(var);
|
i = strlen(var);
|
||||||
|
if (i > 0 && var[i - 1] == '+') {
|
||||||
|
/* append mode */
|
||||||
|
reset = false;
|
||||||
|
strndupx(cp, var, i - 1, ATEMP);
|
||||||
|
}
|
||||||
|
vp = global(cp ? cp : var);
|
||||||
|
|
||||||
/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
|
/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
|
||||||
if ((vp->flag&RDONLY))
|
if ((vp->flag&RDONLY))
|
||||||
errorfx(2, "%s: %s", var, "is read only");
|
errorfx(2, "%s: %s", cp ? cp : var, "is read only");
|
||||||
/* This code is quite non-optimal */
|
/* This code is quite non-optimal */
|
||||||
if (reset)
|
if (reset)
|
||||||
/* trash existing values and attributes */
|
/* trash existing values and attributes */
|
||||||
unset(vp, 1);
|
unset(vp, 1);
|
||||||
/* todo: would be nice for assignment to completely succeed or
|
/*
|
||||||
|
* todo: would be nice for assignment to completely succeed or
|
||||||
* completely fail. Only really effects integer arrays:
|
* completely fail. Only really effects integer arrays:
|
||||||
* evaluation of some of vals[] may fail...
|
* evaluation of some of vals[] may fail...
|
||||||
*/
|
*/
|
||||||
i = 0;
|
i = 0;
|
||||||
#ifndef MKSH_SMALL
|
if (cp != NULL) {
|
||||||
j = 0;
|
/* find out where to set when appending */
|
||||||
#else
|
for (vq = vp; vq; vq = vq->u.array) {
|
||||||
#define j i
|
if (!(vq->flag & ISSET))
|
||||||
#endif
|
continue;
|
||||||
|
if (arrayindex(vq) >= j)
|
||||||
|
j = arrayindex(vq) + 1;
|
||||||
|
}
|
||||||
|
afree(cp, ATEMP);
|
||||||
|
}
|
||||||
while ((ccp = vals[i])) {
|
while ((ccp = vals[i])) {
|
||||||
#ifndef MKSH_SMALL
|
#ifndef MKSH_SMALL
|
||||||
if (*ccp == '[') {
|
if (*ccp == '[') {
|
||||||
@ -1356,9 +1387,7 @@ set_array(const char *var, bool reset, const char **vals)
|
|||||||
/* would be nice to deal with errors here... (see above) */
|
/* would be nice to deal with errors here... (see above) */
|
||||||
setstr(vq, ccp, KSH_RETURN_ERROR);
|
setstr(vq, ccp, KSH_RETURN_ERROR);
|
||||||
i++;
|
i++;
|
||||||
#ifndef MKSH_SMALL
|
|
||||||
j++;
|
j++;
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (i);
|
return (i);
|
||||||
|
Reference in New Issue
Block a user