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