mirror of
https://https.git.savannah.gnu.org/git/bash.git
synced 2026-08-12 05:00:50 +02:00
first set of changes to eliminate array subscript double expansion in math contexts
This commit is contained in:
+7
-10
@@ -520,13 +520,11 @@ argv[1] = <y>
|
||||
<X> <X> <X> <X>
|
||||
./array23.sub: line 22: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ")
|
||||
./array23.sub: line 23: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ")
|
||||
foo
|
||||
0
|
||||
foo
|
||||
foo
|
||||
foo
|
||||
6
|
||||
./array23.sub: line 34: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )")
|
||||
./array23.sub: line 24: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ")
|
||||
./array23.sub: line 26: $( echo >&2 foo ) : syntax error: operand expected (error token is "$( echo >&2 foo ) ")
|
||||
./array23.sub: line 30: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )")
|
||||
./array23.sub: line 33: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )")
|
||||
./array23.sub: line 34: $index: syntax error: operand expected (error token is "$index")
|
||||
./array23.sub: line 35: $( echo >&2 foo ): syntax error: operand expected (error token is "$( echo >&2 foo )")
|
||||
0
|
||||
0
|
||||
@@ -743,10 +741,9 @@ argv[1] = <b>
|
||||
argv[2] = <a>
|
||||
argv[1] = <b+a>
|
||||
7
|
||||
./array27.sub: line 24: a[]]=7 : syntax error: invalid arithmetic operator (error token is "]=7 ")
|
||||
7
|
||||
declare -A A=([$'\t']="2" [" "]="2" )
|
||||
./array27.sub: line 36: ((: A[]]=2 : syntax error: invalid arithmetic operator (error token is "]=2 ")
|
||||
declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["@"]="2" )
|
||||
declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["]"]="2" ["@"]="2" )
|
||||
declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["]"]="2" ["@"]="2" )
|
||||
./array27.sub: line 52: A[]]: bad array subscript
|
||||
declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" )
|
||||
|
||||
@@ -283,3 +283,12 @@ declare -A v3=(["1 2"]="3 4 5" ["\$xtra"]="xtra" )
|
||||
declare -A v1=(["20 40 80"]="new xtra" ["1 2"]="3 4 5" )
|
||||
declare -A v2=(["20 40 80"]="new xtra" ["1 2"]="3 4 5" )
|
||||
declare -A v3=(["1 2"]="3 4 5" ["\$xtra"]="new xtra" )
|
||||
declare -A assoc=(["*"]="star" ["!"]="bang" ["@"]="at" )
|
||||
at
|
||||
star
|
||||
declare -A a=(["@"]="at" )
|
||||
./assoc13.sub: line 22: ia[@]: bad array subscript
|
||||
declare -a ia
|
||||
declare -A a=(["@"]="at2" )
|
||||
declare -A a=(["@"]=" string" )
|
||||
declare -A a=(["*"]="star2" ["@"]="at" )
|
||||
|
||||
@@ -247,3 +247,6 @@ ${THIS_SH} ./assoc11.sub
|
||||
|
||||
# more kvpair associative array assignment tests
|
||||
${THIS_SH} ./assoc12.sub
|
||||
|
||||
# assignment to @ and *
|
||||
${THIS_SH} ./assoc13.sub
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
# assignment to @ and *
|
||||
|
||||
declare -A assoc
|
||||
key=@
|
||||
key2=*
|
||||
|
||||
assoc[$key]=at
|
||||
assoc[$key2]=star
|
||||
assoc[!]=bang
|
||||
declare -p assoc
|
||||
|
||||
echo ${assoc[$key]}
|
||||
echo ${assoc[$key2]}
|
||||
unset assoc
|
||||
|
||||
declare -A a
|
||||
|
||||
a[@]=at
|
||||
declare -p a
|
||||
|
||||
declare -a ia
|
||||
ia[@]=garbage
|
||||
|
||||
declare -p ia
|
||||
|
||||
declare a[@]=at2
|
||||
declare -p a
|
||||
|
||||
unset a ia
|
||||
|
||||
declare -A a
|
||||
printf -v a[@] "%10s" string
|
||||
|
||||
declare -p a
|
||||
unset a
|
||||
|
||||
declare -A a
|
||||
declare a[$key2]=star
|
||||
declare a[@]=at
|
||||
declare a[*]=star2
|
||||
|
||||
declare -p a
|
||||
unset a
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
declare -A assoc=(["x],b[\$(echo uname >&2)"]="1" )
|
||||
declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="1" )
|
||||
declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="2" )
|
||||
./quotearray.tests: line 31: ((: 'assoc[x\],b\[\$(echo uname >&2)]++' : syntax error: operand expected (error token is "'assoc[x\],b\[\$(echo uname >&2)]++' ")
|
||||
declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="2" )
|
||||
./quotearray.tests: line 34: ((: 'assoc[x\],b\[\$(echo uname >&2)]'++ : syntax error: operand expected (error token is "'assoc[x\],b\[\$(echo uname >&2)]'++ ")
|
||||
declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="2" )
|
||||
declare -A assoc=(["\$key"]="1" ["x],b[\$(echo uname >&2)"]="3" )
|
||||
4
|
||||
klmnopqrst
|
||||
klmnopqrst
|
||||
klmno
|
||||
klmnopqrst
|
||||
declare -A A=(["\$(echo %)"]="5" [%]="10" ["]"]="10" )
|
||||
declare -A A=(["~"]="42" )
|
||||
42
|
||||
declare -A A=(["~"]="43" )
|
||||
42
|
||||
declare -A A=(["~"]="43" ["~0"]="43" )
|
||||
12
|
||||
declare -a a=([0]="12" [1]="42")
|
||||
2
|
||||
2
|
||||
declare -Ai assoc=(["']"]="2" ["\$var"]="1" )
|
||||
105
|
||||
declare -A assoc=(["\` echo >&2 foo\`"]="42" ["\$( echo >&2 bar)"]="63" )
|
||||
./quotearray.tests: line 139: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)")
|
||||
./quotearray.tests: line 143: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)")
|
||||
1
|
||||
./quotearray.tests: line 146: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)")
|
||||
1
|
||||
./quotearray.tests: line 149: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)")
|
||||
1
|
||||
./quotearray.tests: line 152: x],b[$(echo uname >&2): syntax error: invalid arithmetic operator (error token is "],b[$(echo uname >&2)")
|
||||
1
|
||||
declare -A assoc
|
||||
0
|
||||
0
|
||||
1
|
||||
0
|
||||
0
|
||||
0
|
||||
declare -A assoc=(["\` echo >&2 foo\`"]="128" [0]="0" ["]"]="12" ["x],b[\$(echo uname >&2)"]="42" ["~"]="42" ["\$( echo 2>& date)"]="foo" )
|
||||
foo
|
||||
0
|
||||
0
|
||||
./quotearray1.sub: line 67: 0\],b\[1: syntax error: invalid arithmetic operator (error token is "\],b\[1")
|
||||
declare -a array
|
||||
0
|
||||
0
|
||||
0
|
||||
0
|
||||
1
|
||||
1
|
||||
declare -A aa=(["\$( echo 2>& date)"]="foo" )
|
||||
foo
|
||||
0
|
||||
1
|
||||
declare -A a=([1]="1" [0]="0" [" "]="11" )
|
||||
7
|
||||
7
|
||||
declare -A A=([$'\t']="2" [" "]="2" )
|
||||
declare -A A=([$'\t']="2" ["*"]="2" [" "]="2" ["]"]="2" ["@"]="2" )
|
||||
./quotearray2.sub: line 54: read: `A[]]': not a valid identifier
|
||||
declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" )
|
||||
./quotearray2.sub: line 62: printf: `A[]]': not a valid identifier
|
||||
declare -A A=([$'\t']="X" ["*"]="X" [" "]="X" ["@"]="X" )
|
||||
./quotearray2.sub: line 70: declare: `A[]]=X': not a valid identifier
|
||||
declare -A A=(["*"]="X" ["@"]="X" )
|
||||
./quotearray2.sub: line 78: declare: `A[]]=X': not a valid identifier
|
||||
declare -A A=(["*"]="X" ["@"]="X" )
|
||||
./quotearray2.sub: line 89: let: assoc[x],b[$(echo: bad array subscript (error token is "b[$(echo")
|
||||
declare -A assoc
|
||||
declare -A assoc=(["\$var"]="value" )
|
||||
declare -A assoc=(["\$var"]="value" )
|
||||
declare -A assoc=(["\$var"]="value" )
|
||||
declare -A assoc=()
|
||||
declare -A a=()
|
||||
declare -A a=(["\$(echo foo)"]="1" )
|
||||
declare -A a=()
|
||||
declare -A a=(["\$(echo foo)"]="1" )
|
||||
./quotearray3.sub: line 66: typeset: assoc: not found
|
||||
1
|
||||
./quotearray3.sub: line 71: \@: syntax error: operand expected (error token is "\@")
|
||||
1
|
||||
@@ -0,0 +1,157 @@
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# a set of tests for associative arrays in arithmetic contexts
|
||||
|
||||
declare -A assoc
|
||||
key='x],b[$(echo uname >&2)'
|
||||
|
||||
(( assoc[$key]++ ))
|
||||
declare -p assoc
|
||||
|
||||
(( assoc['$key']++ ))
|
||||
declare -p assoc
|
||||
|
||||
(( assoc["$key"]++ ))
|
||||
declare -p assoc
|
||||
|
||||
declare -A assoc
|
||||
|
||||
(( 'assoc[$key]++' ))
|
||||
declare -p assoc
|
||||
|
||||
(( 'assoc[$key]'++ ))
|
||||
declare -p assoc
|
||||
|
||||
(( "assoc[$key]++" ))
|
||||
declare -p assoc
|
||||
|
||||
unset assoc
|
||||
|
||||
typeset -A a
|
||||
b="80's"
|
||||
|
||||
((++a[$b]))
|
||||
|
||||
((++a["$b"]))
|
||||
[[ $((++a[$b])) ]]
|
||||
[[ $((++a["$b"])) ]]
|
||||
|
||||
echo ${a[$b]}
|
||||
unset a
|
||||
|
||||
declare -A A
|
||||
|
||||
string=abcdefghijklmnopqrstuvwxyz
|
||||
|
||||
echo ${string:10:10}
|
||||
k1='%'
|
||||
k2='$(echo %)'
|
||||
|
||||
A[%]=10
|
||||
A[']']=10
|
||||
A[$k2]=5
|
||||
|
||||
k3=']'
|
||||
|
||||
echo ${string:A[%]:A[$k1]}
|
||||
echo ${string:A[%]:A[$k2]}
|
||||
echo ${string:A[%]:A[$k3]}
|
||||
|
||||
declare -p A
|
||||
|
||||
unset A string key
|
||||
|
||||
key='~'
|
||||
|
||||
declare -A A
|
||||
A[$key]=42
|
||||
|
||||
declare -p A
|
||||
|
||||
echo $(( A[$key]++ ))
|
||||
declare -p A
|
||||
|
||||
key='~0'
|
||||
A[$key]=42
|
||||
echo $(( A[$key]++ ))
|
||||
declare -p A
|
||||
|
||||
declare -a a
|
||||
key='~-2'
|
||||
a[0]=12
|
||||
a[$key]=42
|
||||
echo $(( a[7<(4+2)] ))
|
||||
|
||||
declare -p a
|
||||
|
||||
unset A a key
|
||||
|
||||
declare -A A
|
||||
declare -a a
|
||||
|
||||
sString="devel packager's guide"
|
||||
i=2
|
||||
|
||||
A["$sString"]=$i
|
||||
a[$i]=$sString
|
||||
|
||||
echo "${A[${a[i]}]}"
|
||||
echo ${A["${a[i]}"]}
|
||||
|
||||
unset A a
|
||||
|
||||
#LANG=C
|
||||
unset var assoc
|
||||
var=\'\]
|
||||
declare -Ai assoc
|
||||
assoc[$var]=1
|
||||
assoc[$var]+=1
|
||||
((assoc['$var']++))
|
||||
typeset -p assoc
|
||||
|
||||
unset assoc
|
||||
|
||||
declare -A assoc
|
||||
key1='` echo >&2 foo`'
|
||||
key2='$( echo >&2 bar)'
|
||||
|
||||
assoc[$key1]=42
|
||||
assoc[$key2]=63
|
||||
|
||||
echo $(( assoc[$key1] + assoc[$key2] ))
|
||||
declare -p assoc
|
||||
unset assoc
|
||||
|
||||
declare -a a
|
||||
key='x],b[$(echo uname >&2)'
|
||||
a[$key]=42
|
||||
|
||||
expr='a[$key]'
|
||||
|
||||
(( $expr ))
|
||||
echo $?
|
||||
|
||||
echo $(( $expr ))
|
||||
echo $?
|
||||
|
||||
echo $(( expr ))
|
||||
echo $?
|
||||
|
||||
(( expr ))
|
||||
echo $?
|
||||
|
||||
${THIS_SH} ./quotearray1.sub
|
||||
${THIS_SH} ./quotearray2.sub
|
||||
${THIS_SH} ./quotearray3.sub
|
||||
@@ -0,0 +1,105 @@
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# arithmetic operators for conditional commands and arithmetic commands
|
||||
|
||||
declare -A assoc
|
||||
declare -a index
|
||||
|
||||
key='x],b[$(echo uname >&2)'
|
||||
key1=']'
|
||||
key2='` echo >&2 foo`'
|
||||
key3='~'
|
||||
key4='7<(4+2)'
|
||||
key5='$( echo 2>& date)'
|
||||
|
||||
[[ -n assoc[$key] ]]
|
||||
declare -p assoc
|
||||
|
||||
assoc[$key]=42
|
||||
assoc[$key1]=12
|
||||
assoc[$key2]=128
|
||||
assoc[$key3]=42
|
||||
assoc[0]=0
|
||||
|
||||
[[ assoc[$key] -eq assoc[$key] ]]
|
||||
echo $?
|
||||
|
||||
[[ assoc[$key] -gt assoc[$key1] ]]
|
||||
echo $?
|
||||
|
||||
[[ assoc[$key2] -lt assoc[$key] ]]
|
||||
echo $?
|
||||
|
||||
[[ assoc[$key] -eq assoc[$key3] ]]
|
||||
echo $?
|
||||
|
||||
[[ index[7<(4+2)] -le assoc[0] ]]
|
||||
echo $?
|
||||
[[ index[$key4] -le assoc[0] ]]
|
||||
echo $?
|
||||
|
||||
assoc[$key5]=foo
|
||||
declare -p assoc
|
||||
|
||||
echo "${assoc[$key5]}"
|
||||
|
||||
[[ -v assoc[$key5] ]]
|
||||
echo $?
|
||||
[[ -v assoc[$key] ]]
|
||||
echo $?
|
||||
|
||||
unset assoc
|
||||
|
||||
declare -a array
|
||||
index='0],b[1';
|
||||
((array[$index]++))
|
||||
|
||||
declare -p array
|
||||
|
||||
unset array
|
||||
|
||||
declare -A assoc
|
||||
|
||||
assoc[$key]=42
|
||||
assoc[$key4]=42
|
||||
|
||||
[[ -v assoc[$key] ]]
|
||||
echo $?
|
||||
[[ -v assoc["$key"] ]]
|
||||
echo $?
|
||||
|
||||
[[ -v assoc[$key4] ]]
|
||||
echo $?
|
||||
[[ -v assoc["$key4"] ]]
|
||||
echo $?
|
||||
|
||||
[[ -v assoc['$key'] ]]
|
||||
echo $?
|
||||
[[ -v assoc['$key4'] ]]
|
||||
echo $?
|
||||
|
||||
declare -A aa
|
||||
aa[$key5]=foo
|
||||
|
||||
declare -p aa
|
||||
echo "${aa[$key5]}"
|
||||
|
||||
[[ -v aa[$key5] ]]
|
||||
echo $?
|
||||
|
||||
[[ -v aa[$key] ]]
|
||||
echo $?
|
||||
|
||||
unset aa key
|
||||
@@ -0,0 +1,92 @@
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# assoc_expand_once for builtins
|
||||
|
||||
typeset -A a
|
||||
a[0]=0 a[1]=1
|
||||
|
||||
let "a[\" \"]=11" ; typeset -p a ; a[0]=0
|
||||
|
||||
unset a
|
||||
|
||||
# tests for `problem' keys when using associative arrays and assoc_expand_once
|
||||
# deal with problems for now; this is a placeholder for if and when I fix them
|
||||
|
||||
typeset -A a
|
||||
|
||||
k='['
|
||||
echo $(( a[$k]=7 ))
|
||||
|
||||
k=']'
|
||||
echo $(( a[$k]=7 ))
|
||||
|
||||
unset a
|
||||
|
||||
declare -A A
|
||||
|
||||
for k in $'\t' ' '; do
|
||||
(( A[$k]=2 ))
|
||||
done
|
||||
declare -p A
|
||||
|
||||
for k in ']' '*' '@'; do
|
||||
(( A[$k]=2 ))
|
||||
done
|
||||
|
||||
declare -p A
|
||||
|
||||
unset A
|
||||
declare -A A
|
||||
|
||||
for k in $'\t' ' ' ']' '*' '@'; do
|
||||
read "A[$k]" <<< X
|
||||
done
|
||||
declare -p A
|
||||
|
||||
unset A
|
||||
declare -A A
|
||||
|
||||
for k in $'\t' ' ' ']' '*' '@'; do
|
||||
printf -v "A[$k]" "%s" X
|
||||
done
|
||||
declare -p A
|
||||
|
||||
unset A
|
||||
declare -A A
|
||||
|
||||
for k in ']' '*' '@'; do
|
||||
declare A[$k]=X
|
||||
done
|
||||
declare -p A
|
||||
|
||||
unset A
|
||||
declare -A A
|
||||
|
||||
for k in ']' '*' '@'; do
|
||||
declare "A[$k]=X"
|
||||
done
|
||||
declare -p A
|
||||
|
||||
unset A
|
||||
|
||||
# this isn't right yet, but changes will show up here
|
||||
shopt -s assoc_expand_once
|
||||
declare -A assoc
|
||||
key='x],b[$(echo uname >&2)'
|
||||
|
||||
let assoc[$key]++
|
||||
declare -p assoc
|
||||
|
||||
unset assoc
|
||||
@@ -0,0 +1,74 @@
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# assoc_expand_once and unset builtin, which is treated specially
|
||||
|
||||
declare -A assoc
|
||||
|
||||
var=x123
|
||||
assoc['$var']=value
|
||||
declare -p assoc
|
||||
|
||||
shopt -u assoc_expand_once
|
||||
unset "assoc[$var]"
|
||||
declare -p assoc
|
||||
|
||||
unset 'assoc[$var]'
|
||||
declare -p assoc
|
||||
|
||||
assoc['$var']=value
|
||||
shopt -s assoc_expand_once
|
||||
unset 'assoc[$var]'
|
||||
declare -p assoc
|
||||
|
||||
unset assoc
|
||||
|
||||
declare -A a
|
||||
a['$(echo foo)']=1
|
||||
|
||||
unset 'a[$(echo foo)]'
|
||||
declare -p a
|
||||
|
||||
key='$(echo foo)'
|
||||
a[$key]=1
|
||||
|
||||
unset 'a[$key]'
|
||||
declare -p a
|
||||
|
||||
a[$key]=1
|
||||
unset "a[$key]"
|
||||
declare -p a
|
||||
|
||||
a[$key]=1
|
||||
unset a[$key]
|
||||
declare -p a
|
||||
|
||||
unset a
|
||||
|
||||
typeset -A assoc
|
||||
key=@
|
||||
|
||||
assoc[@]=at
|
||||
assoc[!]=bang
|
||||
|
||||
unset -v assoc[$key]
|
||||
typeset -p assoc
|
||||
|
||||
test -v assoc[$key]
|
||||
echo $?
|
||||
|
||||
[[ -v assoc[$key] ]]
|
||||
echo $?
|
||||
|
||||
unset assoc
|
||||
@@ -0,0 +1,2 @@
|
||||
${THIS_SH} ./quotearray.tests >${BASH_TSTOUT} 2>&1
|
||||
diff ${BASH_TSTOUT} quotearray.right && rm -f ${BASH_TSTOUT}
|
||||
Reference in New Issue
Block a user