diff --git a/NEWS b/NEWS index f6d47edae..ee998203f 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,11 @@ Any uppercase BUG_* names are modernish shell bug IDs. an incompletely defined 'foo_t' built-in comamnd that will crash the shell when used. Instead, it is now silently ignored for backwards compatibility. +- A bug in which the output of a two dimensional sparse indexed array would + cause the second subscript to be treated as an associative array when read + back in has been fixed. Elements that are sparse indexed arrays are now + prefixed with "typeset -a". + 2022-02-05: - Fixed: for indexed arrays, given an unset array member a[i] with i > 0, diff --git a/src/cmd/ksh93/include/argnod.h b/src/cmd/ksh93/include/argnod.h index 508f85630..54b7370a2 100644 --- a/src/cmd/ksh93/include/argnod.h +++ b/src/cmd/ksh93/include/argnod.h @@ -120,6 +120,7 @@ struct argnod #define ARG_QUOTED 0x20 /* word contained quote characters */ #define ARG_MESSAGE 0x40 /* contains international string */ #define ARG_APPEND 0x80 /* for += assignment */ +#define ARG_ARRAY 0x2 /* for typeset -a */ /* The following can be passed as options to sh_macexpand() */ #define ARG_ARITH 0x100 /* arithmetic expansion */ #define ARG_OPTIMIZE 0x200 /* try to optimize */ diff --git a/src/cmd/ksh93/include/shlex.h b/src/cmd/ksh93/include/shlex.h index 5ae9f5103..5bd8ca89b 100644 --- a/src/cmd/ksh93/include/shlex.h +++ b/src/cmd/ksh93/include/shlex.h @@ -90,6 +90,7 @@ typedef struct _shlex_ char noreserv; /* reserved works not legal */ int inlineno; /* saved value of sh.inlineno */ int firstline; /* saved value of sh.st.firstline */ + int assignlevel; /* nesting level for assignment */ #if SHOPT_KIA Sfio_t *kiafile; /* kia output file */ Sfio_t *kiatmp; /* kia reference file */ diff --git a/src/cmd/ksh93/sh/name.c b/src/cmd/ksh93/sh/name.c index 1c4591684..8f508a01d 100644 --- a/src/cmd/ksh93/sh/name.c +++ b/src/cmd/ksh93/sh/name.c @@ -308,13 +308,15 @@ void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ) else { stakseek(0); - if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE))) + if(*arg->argval==0 && arg->argchn.ap && !(arg->argflag&~(ARG_APPEND|ARG_QUOTED|ARG_MESSAGE|ARG_ARRAY))) { int flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN); int sub=0; struct fornod *fp=(struct fornod*)arg->argchn.ap; register Shnode_t *tp=fp->fortre; flag |= (flags&(NV_NOSCOPE|NV_STATIC|NV_FARRAY)); + if(arg->argflag&ARG_ARRAY) + array |= NV_IARRAY; if(arg->argflag&ARG_QUOTED) cp = sh_mactrim(fp->fornam,-1); else diff --git a/src/cmd/ksh93/sh/nvtree.c b/src/cmd/ksh93/sh/nvtree.c index 03960afed..befd8fd74 100644 --- a/src/cmd/ksh93/sh/nvtree.c +++ b/src/cmd/ksh93/sh/nvtree.c @@ -587,6 +587,9 @@ void nv_outnode(Namval_t *np, Sfio_t* out, int indent, int special) tabs=0; if(associative||special) { + Namarr_t *aq; + if(mp && (aq=nv_arrayptr(mp)) && !aq->fun && array_elem(aq) < nv_aimax(mp)+1) + sfwrite(out,"typeset -a ",11); if(!(fmtq = nv_getsub(np))) break; sfprintf(out,"[%s]",sh_fmtq(fmtq)); diff --git a/src/cmd/ksh93/sh/parse.c b/src/cmd/ksh93/sh/parse.c index 965291753..86fdb3e23 100644 --- a/src/cmd/ksh93/sh/parse.c +++ b/src/cmd/ksh93/sh/parse.c @@ -377,6 +377,7 @@ void *sh_parse(Sfio_t *iop, int flag) return((void*)sh_trestore(iop)); fcsave(&sav_input); sh.st.staklist = 0; + lexp->assignlevel = 0; lexp->noreserv = 0; lexp->heredoc = 0; lexp->inlineno = sh.inlineno; @@ -967,6 +968,30 @@ static Shnode_t *funct(Lex_t *lexp) return(t); } +static int check_array(Lex_t *lexp) +{ + int n,c; + if(lexp->token==0 && strcmp(lexp->arg->argval, SYSTYPESET->nvname)==0) + { + while((c=fcgetc(n))==' ' || c=='\t'); + if(c=='-') + { + if(fcgetc(n)=='a') + { + lexp->assignok = SH_ASSIGN; + lexp->noreserv = 1; + sh_lex(lexp); + return(1); + } + else + fcseek(-2); + } + else + fcseek(-1); + } + return(0); +} + /* * Compound assignment */ @@ -978,6 +1003,7 @@ static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type) Stk_t *stkp = sh.stk; int array=0, index=0; Namval_t *np; + lexp->assignlevel++; n = strlen(ap->argval)-1; if(ap->argval[n]!='=') sh_syntax(lexp); @@ -1002,14 +1028,14 @@ static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type) ap->argflag &= ARG_QUOTED; ap->argflag |= array; lexp->assignok = SH_ASSIGN; - if(type==NV_ARRAY) + if(type&NV_ARRAY) { lexp->noreserv = 1; lexp->assignok = 0; } else lexp->aliasok = 2; - array= (type==NV_ARRAY)?SH_ARRAY:0; + array= (type&NV_ARRAY)?SH_ARRAY:0; if((n=skipnl(lexp,0))==RPAREN || n==LPAREN) { struct argnod *ar,*aq,**settail; @@ -1148,6 +1174,7 @@ static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type) } *tp = (Shnode_t*)ac; lexp->assignok = 0; + lexp->assignlevel--; return(ap); } @@ -1432,7 +1459,9 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io) Stk_t *stkp = sh.stk; struct argnod **argtail; struct argnod **settail; - int cmdarg=0; + int cmdarg = 0; + int type = 0; + int was_assign = 0; int argno = 0; int assignment = 0; int key_on = (!(flag&SH_NOIO) && sh_isoption(SH_KEYWORD)); @@ -1452,8 +1481,11 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io) t->comnamq = 0; t->comstate = 0; settail = &(t->comset); + if(lexp->assignlevel && (flag&SH_ARRAY) && check_array(lexp)) + type |= NV_ARRAY; while(lexp->token==0) { + was_assign = 0; argp = lexp->arg; if(*argp->argval==LBRACE && (flag&SH_FUNDEF) && argp->argval[1]==0) { @@ -1531,6 +1563,8 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io) } retry: tok = sh_lex(lexp); + if(was_assign && check_array(lexp)) + type = NV_ARRAY; if(tok==LABLSYM && (flag&SH_ASSIGN)) lexp->token = tok = 0; if((tok==IPROCSYM || tok==OPROCSYM)) @@ -1546,7 +1580,6 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io) if(argp->argflag&ARG_ASSIGN) { int intypeset = lexp->intypeset; - int type = 0; lexp->intypeset = 0; if(t->comnamp == SYSCOMPOUND) type = NV_COMVAR; @@ -1558,18 +1591,21 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io) if(*ap->argval!='-') break; if(strchr(ap->argval,'T')) - type = NV_TYPE; + type |= NV_TYPE; else if(strchr(ap->argval,'a')) - type = NV_ARRAY; + type |= NV_ARRAY; else if(strchr(ap->argval,'C')) - type = NV_COMVAR; + type |= NV_COMVAR; else continue; - break; } } argp = assign(lexp,argp,type); + if(type==NV_ARRAY) + argp->argflag |= ARG_ARRAY; lexp->intypeset = intypeset; + if(lexp->assignlevel) + was_assign = 1; if(associative) lexp->assignok |= SH_ASSIGN; goto retry; diff --git a/src/cmd/ksh93/sh/tdump.c b/src/cmd/ksh93/sh/tdump.c index 3701fc371..830e3f33a 100644 --- a/src/cmd/ksh93/sh/tdump.c +++ b/src/cmd/ksh93/sh/tdump.c @@ -160,7 +160,7 @@ static int p_arg(register const struct argnod *arg) struct fornod *fp; while(arg) { - if((n = strlen(arg->argval)) || (arg->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED))) + if((n = strlen(arg->argval)) || (arg->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED|ARG_ARRAY))) fp=0; else { diff --git a/src/cmd/ksh93/sh/trestore.c b/src/cmd/ksh93/sh/trestore.c index a8effa0ac..1907b773a 100644 --- a/src/cmd/ksh93/sh/trestore.c +++ b/src/cmd/ksh93/sh/trestore.c @@ -195,7 +195,7 @@ static struct argnod *r_arg(void) ap = (struct argnod*)stkfreeze(stkp,0); if(*ap->argval==0 && (ap->argflag&ARG_EXP)) ap->argchn.ap = (struct argnod*)r_tree(); - else if(*ap->argval==0 && (ap->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED))==0) + else if(*ap->argval==0 && (ap->argflag&~(ARG_APPEND|ARG_MESSAGE|ARG_QUOTED|ARG_ARRAY))==0) { struct fornod *fp = (struct fornod*)getnode(fornod); fp->fortyp = sfgetu(infile); diff --git a/src/cmd/ksh93/tests/arrays.sh b/src/cmd/ksh93/tests/arrays.sh index b29b6d360..a5df18cc3 100755 --- a/src/cmd/ksh93/tests/arrays.sh +++ b/src/cmd/ksh93/tests/arrays.sh @@ -803,5 +803,21 @@ got=$(set +x; redirect 2>&1; foo[42]=''; : ${foo[42]:?}) [[ $got == *"$exp" ]] || err_exit '${array[index]:?error} does not throw error' \ "(expected match of *$(printf %q "$exp"), got $(printf %q "$got"))" +# ====== +# A multidimensional indexed array should be printed correctly by +# 'typeset -p'. Additionally, the printed command must produce the +# same result when reinput to the shell. +unset foo +typeset -a foo[1][2]=bar +exp='typeset -a foo=(typeset -a [1]=([2]=bar) )' +got=$(typeset -p foo) +[[ $exp == "$got" ]] || err_exit "Multidimensional indexed arrays are not printed correctly with 'typeset -p'" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" +unset foo +typeset -a foo=(typeset -a [1]=([2]=bar) ) +got=$(typeset -p foo) +[[ $exp == "$got" ]] || err_exit "Output from 'typeset -p' for indexed array cannot be used for reinput" \ + "(expected $(printf %q "$exp"), got $(printf %q "$got"))" + # ====== exit $((Errors<125?Errors:125)) diff --git a/src/cmd/ksh93/tests/arrays2.sh b/src/cmd/ksh93/tests/arrays2.sh index 9094da06f..da6290909 100755 --- a/src/cmd/ksh93/tests/arrays2.sh +++ b/src/cmd/ksh93/tests/arrays2.sh @@ -159,7 +159,7 @@ done $SHELL 2> /dev/null -c 'compound c;float -a c.ar;(( c.ar[2][3][3] = 5))' || 'multidimensional arrays in arithmetic expressions not working' -expected='typeset -a -l -E c.ar=([2]=([3]=([3]=5) ) )' +expected='typeset -a -l -E c.ar=(typeset -a [2]=(typeset -a [3]=([3]=5) ) )' unset c float c.ar c.ar[2][3][3]=5 diff --git a/src/cmd/ksh93/tests/comvar.sh b/src/cmd/ksh93/tests/comvar.sh index 6b2e74ca1..f25a351bb 100755 --- a/src/cmd/ksh93/tests/comvar.sh +++ b/src/cmd/ksh93/tests/comvar.sh @@ -668,7 +668,7 @@ compound -a c.board for ((i=2; i < 4; i++)) do c.board[1][$i]=(foo=bar) done -exp=$'(\n\ttypeset -C -a board=(\n\t\t[1]=(\n\t\t\t[2]=(\n\t\t\t\tfoo=bar\n\t\t\t)\n\t\t\t[3]=(\n\t\t\t\tfoo=bar\n\t\t\t)\n\t\t)\n\t)\n)' +exp=$'(\n\ttypeset -C -a board=(\n\t\ttypeset -a [1]=(\n\t\t\t[2]=(\n\t\t\t\tfoo=bar\n\t\t\t)\n\t\t\t[3]=(\n\t\t\t\tfoo=bar\n\t\t\t)\n\t\t)\n\t)\n)' [[ "$(print -v c)" == "$exp" ]] || err_exit 'compound variable assignment to two dimensional array not working' unset zz diff --git a/src/cmd/ksh93/tests/nameref.sh b/src/cmd/ksh93/tests/nameref.sh index 25c9a5427..72e0827eb 100755 --- a/src/cmd/ksh93/tests/nameref.sh +++ b/src/cmd/ksh93/tests/nameref.sh @@ -593,7 +593,7 @@ function read_c read -C v } print "( typeset -i x=36 ) " | read_c ar[5][9][2] -exp=$'(\n\t[5]=(\n\t\t[9]=(\n\t\t\t[2]=(\n\t\t\t\ttypeset -i x=36\n\t\t\t)\n\t\t)\n\t)\n)' +exp=$'(\n\ttypeset -a [5]=(\n\t\ttypeset -a [9]=(\n\t\t\t[2]=(\n\t\t\t\ttypeset -i x=36\n\t\t\t)\n\t\t)\n\t)\n)' [[ $(print -v ar) == "$exp" ]] || err_exit 'read into nameref of global array instance from within a function fails' function read_c @@ -606,7 +606,7 @@ function main compound -a ar nameref nar=ar print "( typeset -i x=36 ) " | read_c nar[5][9][2] - exp=$'(\n\t[5]=(\n\t\t[9]=(\n\t\t\t[2]=(\n\t\t\t\ttypeset -i x=36\n\t\t\t)\n\t\t)\n\t)\n)' + exp=$'(\n\ttypeset -a [5]=(\n\t\ttypeset -a [9]=(\n\t\t\t[2]=(\n\t\t\t\ttypeset -i x=36\n\t\t\t)\n\t\t)\n\t)\n)' [[ $(print -v nar) == "$exp" ]] || err_exit 'read from a nameref variable from calling scope fails' } main diff --git a/src/cmd/ksh93/tests/treemove.sh b/src/cmd/ksh93/tests/treemove.sh index 37936db50..d14573bb1 100755 --- a/src/cmd/ksh93/tests/treemove.sh +++ b/src/cmd/ksh93/tests/treemove.sh @@ -108,7 +108,7 @@ function m x_t c.x[4][5][8].field x_t x typeset -m c.x[4][6][9].field=x - exp=$'(\n\ttypeset -C -a x=(\n\t\t[4]=(\n\t\t\t[5]=(\n\t\t\t\t[8]=(\n\t\t\t\t\tx_t field=(\n\t\t\t\t\t\thello=world\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\t[6]=(\n\t\t\t\t[9]=(\n\t\t\t\t\tx_t field=(\n\t\t\t\t\t\thello=world\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t)\n)' + exp=$'(\n\ttypeset -C -a x=(\n\t\ttypeset -a [4]=(\n\t\t\ttypeset -a [5]=(\n\t\t\t\t[8]=(\n\t\t\t\t\tx_t field=(\n\t\t\t\t\t\thello=world\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t\ttypeset -a [6]=(\n\t\t\t\t[9]=(\n\t\t\t\t\tx_t field=(\n\t\t\t\t\t\thello=world\n\t\t\t\t\t)\n\t\t\t\t)\n\t\t\t)\n\t\t)\n\t)\n)' [[ $(print -v c) == "$exp" ]] || err_exit "typeset -m c.x[4][6][9].field=x where x is a type is not working" } m