+= support for variables and arrays

This commit is contained in:
tg
2011-05-04 23:16:05 +00:00
parent 0d6e622b14
commit 4345dd1fb8
4 changed files with 92 additions and 37 deletions

18
check.t
View File

@ -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
View File

@ -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
View File

@ -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
View File

@ -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);