1 # Run with
2 #
3 # $ test/spec.sh nameref
4
5 #### pass array by reference
6 show_value() {
7 local -n array_name=$1
8 local idx=$2
9 echo "${array_name[$idx]}"
10 }
11 shadock=(ga bu zo meu)
12 show_value shadock 2
13 ## stdout: zo
14
15 #### mutate array by reference
16 set1() {
17 local -n array_name=$1
18 local val=$2
19 array_name[1]=$val
20 }
21 shadock=(a b c d)
22 set1 shadock ZZZ
23 echo ${shadock[@]}
24 ## STDOUT:
25 a ZZZ c d
26 ## END
27
28 #### pass assoc array by reference
29 show_value() {
30 local -n array_name=$1
31 local idx=$2
32 echo "${array_name[$idx]}"
33 }
34 days=([monday]=eggs [tuesday]=bread [sunday]=jam)
35 show_value days sunday
36 ## stdout: jam
37 ## BUG mksh stdout: [monday]=eggs
38 # mksh note: it coerces "days" to 0? Horrible.
39
40 #### pass local array by reference, relying on DYNAMIC SCOPING
41 show_value() {
42 local -n array_name=$1
43 local idx=$2
44 echo "${array_name[$idx]}"
45 }
46 caller() {
47 local shadock=(ga bu zo meu)
48 show_value shadock 2
49 }
50 caller
51 ## stdout: zo
52 # mksh appears not to have local arrays!
53 ## BUG mksh stdout-json: ""
54 ## BUG mksh status: 1
55
56
57 #### flag -n and +n
58 x=foo
59
60 ref=x
61
62 echo ref=$ref
63
64 typeset -n ref
65 echo ref=$ref
66
67 # mutate underlying var
68 x=bar
69 echo ref=$ref
70
71 typeset +n ref
72 echo ref=$ref
73
74 ## STDOUT:
75 ref=x
76 ref=foo
77 ref=bar
78 ref=x
79 ## END
80
81 #### mutating through nameref: ref=
82 x=XX
83 y=YY
84
85 ref=x
86 ref=y
87 echo 1 ref=$ref
88
89 # now it's a reference
90 typeset -n ref
91
92 echo 2 ref=$ref # prints YY
93
94 ref=XXXX
95 echo 3 ref=$ref # it actually prints y, which is XXXX
96
97 # now Y is mutated!
98 echo 4 y=$y
99
100 ## STDOUT:
101 1 ref=y
102 2 ref=YY
103 3 ref=XXXX
104 4 y=XXXX
105 ## END
106
107
108 #### flag -n combined ${!ref} -- bash INVERTS
109 foo=FOO # should NOT use this
110
111 x=foo
112 ref=x
113
114 echo ref=$ref
115 echo "!ref=${!ref}"
116
117 echo 'NOW A NAMEREF'
118
119 typeset -n ref
120 echo ref=$ref
121 echo "!ref=${!ref}"
122
123 ## STDOUT:
124 ref=x
125 !ref=foo
126 NOW A NAMEREF
127 ref=foo
128 !ref=x
129 ## END
130 ## N-I mksh STDOUT:
131 ref=x
132 !ref=ref
133 NOW A NAMEREF
134 ref=foo
135 !ref=x
136 ## END
137
138 #### named ref with $# doesn't work
139 set -- one two three
140
141 ref='#'
142 echo ref=$ref
143 typeset -n ref
144 echo ref=$ref
145
146 ## STDOUT:
147 ref=#
148 ref=#
149 ## END
150
151 # mksh does respect it!! Gah.
152 ## OK mksh STDOUT:
153 ref=#
154 ref=3
155 ## END
156
157
158 #### named ref with $# and shopt -s strict_nameref
159 shopt -s strict_nameref
160
161 ref='#'
162 echo ref=$ref
163 typeset -n ref
164 echo ref=$ref
165 ## STDOUT:
166 ref=#
167 ## END
168 ## status: 1
169 ## N-I bash status: 0
170 ## N-I bash STDOUT:
171 ref=#
172 ref=#
173 ## END
174 ## N-I mksh status: 0
175 ## N-I mksh STDOUT:
176 ref=#
177 ref=0
178 ## END
179
180 #### named ref with 1 $1 etc.
181 set -- one two three
182
183 x=X
184
185 ref='1'
186 echo ref=$ref
187 typeset -n ref
188 echo ref=$ref
189
190 # BUG: This is really assigning '1', which is INVALID
191 # with strict_nameref that degrades!!!
192 ref2='$1'
193 echo ref2=$ref2
194 typeset -n ref2
195 echo ref2=$ref2
196
197 x=foo
198
199 ref3='x'
200 echo ref3=$ref3
201 typeset -n ref3
202 echo ref3=$ref3
203
204 ## STDOUT:
205 ref=1
206 ref=1
207 ref2=$1
208 ref2=$1
209 ref3=x
210 ref3=foo
211 ## END
212 ## BUG mksh status: 1
213 ## BUG mksh STDOUT:
214 ref=1
215 ref=one
216 ref2=$1
217 ## END
218
219 #### assign to invalid ref
220 ref=1 # mksh makes this READ-ONLY! Because it's not valid.
221
222 echo ref=$ref
223 typeset -n ref
224 echo ref=$ref
225
226 ref=foo
227 echo ref=$ref
228 ## STDOUT:
229 ref=1
230 ref=1
231 ref=foo
232 ## END
233 ## OK mksh status: 2
234 ## OK mksh STDOUT:
235 ref=1
236 ref=
237 ## END
238
239 #### assign to invalid ref with strict_nameref
240 case $SH in *bash|*mksh) exit ;; esac
241
242 shopt -s strict_nameref
243
244 ref=1
245
246 echo ref=$ref
247 typeset -n ref
248 echo ref=$ref
249
250 ref=foo
251 echo ref=$ref
252 ## status: 1
253 ## STDOUT:
254 ref=1
255 ## END
256 ## N-I bash/mksh status: 0
257 ## N-I bash/mksh stdout-json: ""
258
259 #### name ref on Undef cell
260 typeset -n ref
261
262 # This is technically incorrect: an undefined name shouldn't evaluate to empty
263 # string. mksh doesn't allow it.
264 echo ref=$ref
265
266 echo nounset
267 set -o nounset
268 echo ref=$ref
269 ## status: 1
270 ## STDOUT:
271 ref=
272 nounset
273 ## END
274 ## OK mksh stdout-json: ""
275
276 #### assign to empty nameref and invalid nameref
277 typeset -n ref
278 echo ref=$ref
279
280 # this is a no-op in bash, should be stricter
281 ref=x
282 echo ref=$ref
283
284 typeset -n ref2=undef
285 echo ref2=$ref2
286 ref2=x
287 echo ref2=$ref2
288
289 ## STDOUT:
290 ref=
291 ref=
292 ref2=
293 ref2=x
294 ## END
295
296 # mksh gives a good error: empty nameref target
297 ## OK mksh status: 1
298 ## OK mksh stdout-json: ""
299
300 #### -n attribute before it has a value
301 typeset -n ref
302
303 echo ref=$ref
304
305 # Now that it's a string, it still has the -n attribute
306 x=XX
307 ref=x
308 echo ref=$ref
309
310 ## STDOUT:
311 ref=
312 ref=XX
313 ## END
314 ## N-I mksh status: 1
315 ## N-I mksh stdout-json: ""
316
317 #### -n attribute on array is hard error, not a warning
318 x=X
319 typeset -n ref #=x
320 echo hi
321
322 # bash prints warning: REMOVES the nameref attribute here!
323 ref=(x y)
324 echo ref=$ref
325
326 ## status: 1
327 ## STDOUT:
328 hi
329 ## END
330 ## N-I mksh status: 1
331 ## N-I mksh stdout-json: ""
332 ## BUG bash status: 0
333 ## BUG bash STDOUT:
334 hi
335 ref=x
336 ## END
337
338 #### exported nameref
339 x=foo
340 typeset -n -x ref=x
341
342 # hm bash ignores it but mksh doesn't. maybe disallow it.
343 printenv.py x ref
344 echo ---
345 export x
346 printenv.py x ref
347 ## STDOUT:
348 None
349 x
350 ---
351 foo
352 x
353 ## END
354 ## OK mksh STDOUT:
355 None
356 None
357 ---
358 foo
359 None
360 ## END
361
362
363 #### readonly nameref doesn't prevent assigning through it
364
365 # hm bash also ignores -r when -n is set
366
367 x=XX
368 typeset -n -r ref=x
369
370 echo ref=$ref
371
372 # it feels like I shouldn't be able to mutate this?
373 ref=XXXX
374 echo ref=$ref
375
376 x=X
377 echo x=$x
378
379 ## STDOUT:
380 ref=XX
381 ref=XXXX
382 x=X
383 ## END
384
385 #### readonly var can't be assigned through nameref
386
387 x=X
388 typeset -n -r ref=x
389
390 echo ref=$ref
391
392 # it feels like I shouldn't be able to mutate this?
393 ref=XX
394 echo ref=$ref
395
396 # now the underling variable is immutable
397 typeset -r x
398
399 ref=XXX
400 echo ref=$ref
401 echo x=$x
402
403 ## status: 1
404 ## OK mksh status: 2
405 ## STDOUT:
406 ref=X
407 ref=XX
408 ## END
409
410 ## OK bash status: 0
411 ## OK bash STDOUT:
412 ref=X
413 ref=XX
414 ref=XX
415 x=XX
416 ## END
417
418 #### unset nameref
419 x=X
420 typeset -n ref=x
421 echo ref=$ref
422
423 # this works
424 unset ref
425 echo ref=$ref
426 echo x=$x
427
428 ## STDOUT:
429 ref=X
430 ref=
431 x=
432 ## END
433
434 #### Chain of namerefs
435 x=foo
436 typeset -n ref=x
437 typeset -n ref_to_ref=ref
438 echo ref_to_ref=$ref_to_ref
439 echo ref=$ref
440 ## STDOUT:
441 ref_to_ref=foo
442 ref=foo
443 ## END
444
445 #### Mutually recursive namerefs detected on READ
446 typeset -n ref1=ref2
447 typeset -n ref2=ref1
448 echo defined
449 echo ref1=$ref1
450 echo ref2=$ref1
451 ## status: 1
452 ## STDOUT:
453 defined
454 ## END
455 ## OK mksh stdout-json: ""
456 ## BUG bash status: 0
457 ## BUG bash STDOUT:
458 defined
459 ref1=
460 ref2=
461 ## END
462
463 #### Mutually recursive namerefs detected on WRITE
464 typeset -n ref1=ref2
465 typeset -n ref2=ref1 # not detected here
466 echo defined $?
467 ref1=z # detected here
468 echo mutated $?
469 ## status: 1
470 ## STDOUT:
471 defined 0
472 ## END
473 ## OK mksh stdout-json: ""
474 ## BUG bash status: 0
475 ## BUG bash STDOUT:
476 defined 0
477 mutated 1
478 ## END
479
480 #### Dynamic scope with namerefs
481
482 f3() {
483 local -n ref=$1
484 ref=x
485 }
486
487 f2() {
488 f3 "$@"
489 }
490
491 f1() {
492 local F1=F1
493 echo F1=$F1
494 f2 F1
495 echo F1=$F1
496 }
497 f1
498
499 ## STDOUT:
500 F1=F1
501 F1=x
502 ## END
503
504
505 #### change reference itself
506 x=XX
507 y=YY
508 typeset -n ref=x
509 echo ref=$ref
510 echo x=$x
511 echo y=$y
512
513 echo ----
514 typeset -n ref=y
515 echo ref=$ref
516 echo x=$x
517 echo y=$y
518 echo ----
519 ref=z
520 echo ref=$ref
521 echo x=$x
522 echo y=$y
523
524 ## STDOUT:
525 ref=XX
526 x=XX
527 y=YY
528 ----
529 ref=YY
530 x=XX
531 y=YY
532 ----
533 ref=z
534 x=XX
535 y=z
536 ## END
537
538 #### a[2] in nameref (eval_unsafe_arith)
539 shopt -s eval_unsafe_arith
540
541 typeset -n ref='a[2]'
542 a=(zero one two three)
543 echo ref=$ref
544 ## STDOUT:
545 ref=two
546 ## END
547
548 #### a[expr] in nameref (eval_unsafe_arith)
549 shopt -s eval_unsafe_arith
550
551 # this confuses code and data
552 typeset -n ref='a[$(echo 2) + 1]'
553 a=(zero one two three)
554 echo ref=$ref
555 ## STDOUT:
556 ref=three
557 ## END
558
559 #### a[@] in nameref (eval_unsafe_arith)
560 shopt -s eval_unsafe_arith
561
562 # this confuses code and data
563 typeset -n ref='a[@]'
564 a=('A B' C)
565 argv.py ref "$ref" # READ through ref works
566 ref=(X Y Z) # WRITE through doesn't work
567 echo status=$?
568 argv.py 'ref[@]' "${ref[@]}"
569 argv.py ref "$ref" # JOINING mangles the array?
570 argv.py 'a[@]' "${a[@]}"
571 ## STDOUT:
572 ['ref', 'A B C']
573 status=1
574 ['ref[@]']
575 ['ref', 'A B C']
576 ['a[@]', 'A B', 'C']
577 ## END
578 ## OK mksh status: 1
579 ## OK mksh stdout-json: ""
580
581 #### mutate through nameref: ref[0]=
582
583 # This is DIFFERENT than the nameref itself being 'array[0]' !
584
585 array=(X Y Z)
586 typeset -n ref=array
587 ref[0]=xx
588 echo ${array[@]}
589 ## STDOUT:
590 xx Y Z
591 ## END
592
593 #### bad mutation through nameref: ref[0]= where ref is array[0]
594 array=(X Y Z)
595 typeset -n ref='array[0]'
596 ref[0]=foo # error in bash: 'array[0]': not a valid identifier
597 echo status=$?
598 echo ${array[@]}
599 ## STDOUT:
600 status=1
601 X Y Z
602 ## END
603 ## BUG mksh STDOUT:
604 status=0
605 foo Y Z
606 ## END
607
608 #### @ in nameref isn't supported, unlike in ${!ref}
609 shopt -s eval_unsafe_arith
610
611 set -- A B
612 typeset -n ref='@' # bash gives an error here
613 echo status=$?
614
615 echo ref=$ref # bash doesn't give an error here
616 echo status=$?
617 ## status: 1
618 ## stdout-json: ""
619 ## OK bash status: 0
620 ## OK bash STDOUT:
621 status=1
622 ref=
623 status=0
624 ## END
625
626 #### Unquoted assoc reference on RHS
627 typeset -A bashup_ev_r
628 bashup_ev_r['foo']=bar
629
630 p() {
631 local s=foo
632 local -n e=bashup_ev["$s"] f=bashup_ev_r["$s"]
633 # Different!
634 #local e=bashup_ev["$s"] f=bashup_ev_r["$s"]
635 argv.py "$f"
636 }
637 p
638 ## STDOUT:
639 ['bar']
640 ## END
641 ## N-I mksh stdout-json: ""
642 ## N-I mksh status: 1