1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00

Fix the output of typeset -p for two dimensional indexed arrays (#454)

In ksh93v- 2012-10-04 the following bugfix is noted in the changelog
(this fix was most likely part of ksh93v- 2012-09-27, although that
version is not archived anywhere):
12-09-21  A bug in which the output of a two dimensional sparse
          indexed array would cause the second subscript be treated
          as an associative array when read back in has been fixed.
          Elements that are sparse indexed arrays now are prefixed
          type "typeset -a".

Below is a before and after of this change:

   # Before
   $ typeset -a foo[1][2]=bar
   $ typeset -p foo
   typeset -a foo=([1]=([2]=bar) )

   # After
   $ typeset -a foo[1][2]=bar
   $ typeset -p foo
   typeset -a foo=(typeset -a [1]=([2]=bar) )

src/cmd/ksh93/sh/*.c:
- Backport changes from ksh93v- to print 'typeset -a' before sparse
  indexed arrays and properly handle 'typeset -a' in reinput
  commands from 'typeset -p'.

src/cmd/ksh93/tests:
- Add two regression tests to arrays.sh for this change.
- Update the existing regression tests for compatibility with the
  new printed typeset output.
This commit is contained in:
Johnothan King 2022-02-08 11:40:17 -08:00 committed by Martijn Dekker
parent e6d0187dd8
commit 787058bdbf
13 changed files with 80 additions and 16 deletions

5
NEWS
View file

@ -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 an incompletely defined 'foo_t' built-in comamnd that will crash the shell
when used. Instead, it is now silently ignored for backwards compatibility. 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: 2022-02-05:
- Fixed: for indexed arrays, given an unset array member a[i] with i > 0, - Fixed: for indexed arrays, given an unset array member a[i] with i > 0,

View file

@ -120,6 +120,7 @@ struct argnod
#define ARG_QUOTED 0x20 /* word contained quote characters */ #define ARG_QUOTED 0x20 /* word contained quote characters */
#define ARG_MESSAGE 0x40 /* contains international string */ #define ARG_MESSAGE 0x40 /* contains international string */
#define ARG_APPEND 0x80 /* for += assignment */ #define ARG_APPEND 0x80 /* for += assignment */
#define ARG_ARRAY 0x2 /* for typeset -a */
/* The following can be passed as options to sh_macexpand() */ /* The following can be passed as options to sh_macexpand() */
#define ARG_ARITH 0x100 /* arithmetic expansion */ #define ARG_ARITH 0x100 /* arithmetic expansion */
#define ARG_OPTIMIZE 0x200 /* try to optimize */ #define ARG_OPTIMIZE 0x200 /* try to optimize */

View file

@ -90,6 +90,7 @@ typedef struct _shlex_
char noreserv; /* reserved works not legal */ char noreserv; /* reserved works not legal */
int inlineno; /* saved value of sh.inlineno */ int inlineno; /* saved value of sh.inlineno */
int firstline; /* saved value of sh.st.firstline */ int firstline; /* saved value of sh.st.firstline */
int assignlevel; /* nesting level for assignment */
#if SHOPT_KIA #if SHOPT_KIA
Sfio_t *kiafile; /* kia output file */ Sfio_t *kiafile; /* kia output file */
Sfio_t *kiatmp; /* kia reference file */ Sfio_t *kiatmp; /* kia reference file */

View file

@ -308,13 +308,15 @@ void nv_setlist(register struct argnod *arg,register int flags, Namval_t *typ)
else else
{ {
stakseek(0); 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 flag = (NV_VARNAME|NV_ARRAY|NV_ASSIGN);
int sub=0; int sub=0;
struct fornod *fp=(struct fornod*)arg->argchn.ap; struct fornod *fp=(struct fornod*)arg->argchn.ap;
register Shnode_t *tp=fp->fortre; register Shnode_t *tp=fp->fortre;
flag |= (flags&(NV_NOSCOPE|NV_STATIC|NV_FARRAY)); flag |= (flags&(NV_NOSCOPE|NV_STATIC|NV_FARRAY));
if(arg->argflag&ARG_ARRAY)
array |= NV_IARRAY;
if(arg->argflag&ARG_QUOTED) if(arg->argflag&ARG_QUOTED)
cp = sh_mactrim(fp->fornam,-1); cp = sh_mactrim(fp->fornam,-1);
else else

View file

@ -587,6 +587,9 @@ void nv_outnode(Namval_t *np, Sfio_t* out, int indent, int special)
tabs=0; tabs=0;
if(associative||special) 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))) if(!(fmtq = nv_getsub(np)))
break; break;
sfprintf(out,"[%s]",sh_fmtq(fmtq)); sfprintf(out,"[%s]",sh_fmtq(fmtq));

View file

@ -377,6 +377,7 @@ void *sh_parse(Sfio_t *iop, int flag)
return((void*)sh_trestore(iop)); return((void*)sh_trestore(iop));
fcsave(&sav_input); fcsave(&sav_input);
sh.st.staklist = 0; sh.st.staklist = 0;
lexp->assignlevel = 0;
lexp->noreserv = 0; lexp->noreserv = 0;
lexp->heredoc = 0; lexp->heredoc = 0;
lexp->inlineno = sh.inlineno; lexp->inlineno = sh.inlineno;
@ -967,6 +968,30 @@ static Shnode_t *funct(Lex_t *lexp)
return(t); 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 * Compound assignment
*/ */
@ -978,6 +1003,7 @@ static struct argnod *assign(Lex_t *lexp, register struct argnod *ap, int type)
Stk_t *stkp = sh.stk; Stk_t *stkp = sh.stk;
int array=0, index=0; int array=0, index=0;
Namval_t *np; Namval_t *np;
lexp->assignlevel++;
n = strlen(ap->argval)-1; n = strlen(ap->argval)-1;
if(ap->argval[n]!='=') if(ap->argval[n]!='=')
sh_syntax(lexp); 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 &= ARG_QUOTED;
ap->argflag |= array; ap->argflag |= array;
lexp->assignok = SH_ASSIGN; lexp->assignok = SH_ASSIGN;
if(type==NV_ARRAY) if(type&NV_ARRAY)
{ {
lexp->noreserv = 1; lexp->noreserv = 1;
lexp->assignok = 0; lexp->assignok = 0;
} }
else else
lexp->aliasok = 2; 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) if((n=skipnl(lexp,0))==RPAREN || n==LPAREN)
{ {
struct argnod *ar,*aq,**settail; 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; *tp = (Shnode_t*)ac;
lexp->assignok = 0; lexp->assignok = 0;
lexp->assignlevel--;
return(ap); return(ap);
} }
@ -1433,6 +1460,8 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io)
struct argnod **argtail; struct argnod **argtail;
struct argnod **settail; struct argnod **settail;
int cmdarg = 0; int cmdarg = 0;
int type = 0;
int was_assign = 0;
int argno = 0; int argno = 0;
int assignment = 0; int assignment = 0;
int key_on = (!(flag&SH_NOIO) && sh_isoption(SH_KEYWORD)); 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->comnamq = 0;
t->comstate = 0; t->comstate = 0;
settail = &(t->comset); settail = &(t->comset);
if(lexp->assignlevel && (flag&SH_ARRAY) && check_array(lexp))
type |= NV_ARRAY;
while(lexp->token==0) while(lexp->token==0)
{ {
was_assign = 0;
argp = lexp->arg; argp = lexp->arg;
if(*argp->argval==LBRACE && (flag&SH_FUNDEF) && argp->argval[1]==0) 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: retry:
tok = sh_lex(lexp); tok = sh_lex(lexp);
if(was_assign && check_array(lexp))
type = NV_ARRAY;
if(tok==LABLSYM && (flag&SH_ASSIGN)) if(tok==LABLSYM && (flag&SH_ASSIGN))
lexp->token = tok = 0; lexp->token = tok = 0;
if((tok==IPROCSYM || tok==OPROCSYM)) 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) if(argp->argflag&ARG_ASSIGN)
{ {
int intypeset = lexp->intypeset; int intypeset = lexp->intypeset;
int type = 0;
lexp->intypeset = 0; lexp->intypeset = 0;
if(t->comnamp == SYSCOMPOUND) if(t->comnamp == SYSCOMPOUND)
type = NV_COMVAR; type = NV_COMVAR;
@ -1558,18 +1591,21 @@ static Shnode_t *simple(Lex_t *lexp,int flag, struct ionod *io)
if(*ap->argval!='-') if(*ap->argval!='-')
break; break;
if(strchr(ap->argval,'T')) if(strchr(ap->argval,'T'))
type = NV_TYPE; type |= NV_TYPE;
else if(strchr(ap->argval,'a')) else if(strchr(ap->argval,'a'))
type = NV_ARRAY; type |= NV_ARRAY;
else if(strchr(ap->argval,'C')) else if(strchr(ap->argval,'C'))
type = NV_COMVAR; type |= NV_COMVAR;
else else
continue; continue;
break;
} }
} }
argp = assign(lexp,argp,type); argp = assign(lexp,argp,type);
if(type==NV_ARRAY)
argp->argflag |= ARG_ARRAY;
lexp->intypeset = intypeset; lexp->intypeset = intypeset;
if(lexp->assignlevel)
was_assign = 1;
if(associative) if(associative)
lexp->assignok |= SH_ASSIGN; lexp->assignok |= SH_ASSIGN;
goto retry; goto retry;

View file

@ -160,7 +160,7 @@ static int p_arg(register const struct argnod *arg)
struct fornod *fp; struct fornod *fp;
while(arg) 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; fp=0;
else else
{ {

View file

@ -195,7 +195,7 @@ static struct argnod *r_arg(void)
ap = (struct argnod*)stkfreeze(stkp,0); ap = (struct argnod*)stkfreeze(stkp,0);
if(*ap->argval==0 && (ap->argflag&ARG_EXP)) if(*ap->argval==0 && (ap->argflag&ARG_EXP))
ap->argchn.ap = (struct argnod*)r_tree(); 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); struct fornod *fp = (struct fornod*)getnode(fornod);
fp->fortyp = sfgetu(infile); fp->fortyp = sfgetu(infile);

View file

@ -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' \ [[ $got == *"$exp" ]] || err_exit '${array[index]:?error} does not throw error' \
"(expected match of *$(printf %q "$exp"), got $(printf %q "$got"))" "(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)) exit $((Errors<125?Errors:125))

View file

@ -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' $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 unset c
float c.ar float c.ar
c.ar[2][3][3]=5 c.ar[2][3][3]=5

View file

@ -668,7 +668,7 @@ compound -a c.board
for ((i=2; i < 4; i++)) for ((i=2; i < 4; i++))
do c.board[1][$i]=(foo=bar) do c.board[1][$i]=(foo=bar)
done 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' [[ "$(print -v c)" == "$exp" ]] || err_exit 'compound variable assignment to two dimensional array not working'
unset zz unset zz

View file

@ -593,7 +593,7 @@ function read_c
read -C v read -C v
} }
print "( typeset -i x=36 ) " | read_c ar[5][9][2] 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' [[ $(print -v ar) == "$exp" ]] || err_exit 'read into nameref of global array instance from within a function fails'
function read_c function read_c
@ -606,7 +606,7 @@ function main
compound -a ar compound -a ar
nameref nar=ar nameref nar=ar
print "( typeset -i x=36 ) " | read_c nar[5][9][2] 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' [[ $(print -v nar) == "$exp" ]] || err_exit 'read from a nameref variable from calling scope fails'
} }
main main

View file

@ -108,7 +108,7 @@ function m
x_t c.x[4][5][8].field x_t c.x[4][5][8].field
x_t x x_t x
typeset -m c.x[4][6][9].field=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" [[ $(print -v c) == "$exp" ]] || err_exit "typeset -m c.x[4][6][9].field=x where x is a type is not working"
} }
m m