From 4345dd1fb8e3b51195d8bd260c563a697182d304 Mon Sep 17 00:00:00 2001 From: tg Date: Wed, 4 May 2011 23:16:05 +0000 Subject: [PATCH] += support for variables and arrays --- check.t | 18 ++++++++++++- misc.c | 19 ++++++++----- mksh.1 | 9 ++++--- var.c | 83 ++++++++++++++++++++++++++++++++++++++------------------- 4 files changed, 92 insertions(+), 37 deletions(-) diff --git a/check.t b/check.t index 0fd7005..f770311 100644 --- a/check.t +++ b/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: 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 $ @@ -6209,6 +6209,22 @@ expected-stdout: 2g 009 . 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 description: Check if bash-style substring expansion works diff --git a/misc.c b/misc.c index a26beab..2529066 100644 --- a/misc.c +++ b/misc.c @@ -29,7 +29,7 @@ #include #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 */ unsigned char chtypes[UCHAR_MAX + 1]; @@ -281,7 +281,8 @@ parse_args(const char **argv, const char *array = NULL; Getopt go; size_t i; - int optc, sortargs = 0, arrayset = 0; + int optc, arrayset = 0; + bool sortargs = false; /* First call? Build option strings... */ if (cmd_opts[0] == '\0') { @@ -400,7 +401,7 @@ parse_args(const char **argv, break; /* -s: sort positional params (AT&T ksh stupidity) */ if (what == OF_SET && optc == 's') { - sortargs = 1; + sortargs = true; break; } for (i = 0; i < NELEM(options); i++) @@ -427,9 +428,15 @@ parse_args(const char **argv, *setargsp = !arrayset && ((go.info & GI_MINUSMINUS) || argv[go.optind]); - if (arrayset && (!*array || *skip_varname(array, false))) { - bi_errorf("%s: %s", array, "is not an identifier"); - return (-1); + if (arrayset) { + const char *ccp = NULL; + + 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) { for (i = go.optind; argv[i]; i++) diff --git a/mksh.1 b/mksh.1 index 59eb08c..9b79f60 100644 --- a/mksh.1 +++ b/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 $ .\"- .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, @@ -72,7 +72,7 @@ .\" with -mandoc, it might implement .Mx itself, but we want to .\" 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 .\" @@ -1332,6 +1332,9 @@ exported; see below for the implications of this). Note that both the parameter name and the .Ql = 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 .Ic export , .Ic readonly , @@ -3798,7 +3801,7 @@ and also supported by .At .Nm ksh93 is: -.Ic foo=(a b c) +.Ic foo=(a b c); foo+=(d e) .Pp Another .At diff --git a/var.c b/var.c index 5f6fe94..6a71a3a 100644 --- a/var.c +++ b/var.c @@ -26,7 +26,7 @@ #include #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 @@ -678,6 +678,7 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base) char *tvar; const char *val; int len; + bool vappend = false; /* check for valid variable name, search for value */ val = skip_varname(var, false); @@ -706,9 +707,13 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base) } val += len; } - if (*val == '=') - strndupx(tvar, var, val++ - var, ATEMP); - else { + if (val[0] == '=' || (val[0] == '+' && val[1] == '=')) { + strndupx(tvar, var, val - var, ATEMP); + if (*val++ == '+') { + ++val; + vappend = true; + } + } else { /* Importing from original environment: must have an = */ if (set & IMPORT) 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; - /* 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) && (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 = (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)) t->type = base; @@ -802,9 +810,11 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base) t->u2.field = field; if (fake_assign) { if (!setstr(t, s, KSH_RETURN_ERROR)) { - /* Somewhat arbitrary action here: - * zap contents of variable, but keep - * the flag settings. + /* + * Somewhat arbitrary action + * here: zap contents of + * variable, but keep the flag + * settings. */ ok = false; if (t->flag & INTEGER) @@ -825,6 +835,14 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base) } 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) { /* do not zero base before assignment */ setstr(vp, val, KSH_UNWIND_ERROR | 0x4); @@ -834,6 +852,9 @@ typeset(const char *var, Tflag set, Tflag clr, int field, int base) } else /* setstr can't fail (readonly check already done) */ setstr(vp, val, KSH_RETURN_ERROR | 0x4); + + if (tval != NULL) + afree(tval, ATEMP); } /* 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); - 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) { struct tbl *vp, *vq; - mksh_uari_t i; + mksh_uari_t i, j = 0; const char *ccp; -#ifndef MKSH_SMALL - char *cp; - mksh_uari_t j; -#endif + char *cp = NULL; /* 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 */ 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 */ if (reset) /* trash existing values and attributes */ 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: * evaluation of some of vals[] may fail... */ i = 0; -#ifndef MKSH_SMALL - j = 0; -#else -#define j i -#endif + if (cp != NULL) { + /* find out where to set when appending */ + for (vq = vp; vq; vq = vq->u.array) { + if (!(vq->flag & ISSET)) + continue; + if (arrayindex(vq) >= j) + j = arrayindex(vq) + 1; + } + afree(cp, ATEMP); + } while ((ccp = vals[i])) { #ifndef MKSH_SMALL 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) */ setstr(vq, ccp, KSH_RETURN_ERROR); i++; -#ifndef MKSH_SMALL j++; -#endif } return (i);