1 ## oils_failures_allowed: 0
2 ## compare_shells: dash bash mksh zsh ash
3
4 # printf
5 # bash-completion uses this odd printf -v construction. It seems to mostly use
6 # %s and %q though.
7 #
8 # %s should just be
9 # declare $var='val'
10 #
11 # NOTE:
12 # /usr/bin/printf %q "'" seems wrong.
13 # $ /usr/bin/printf %q "'"
14 # ''\'''
15 #
16 # I suppose it is technically correct, but it looks very ugly.
17
18 #### printf with no args
19 printf
20 ## status: 2
21 ## OK mksh/zsh status: 1
22 ## stdout-json: ""
23
24 #### printf -v %s
25 var=foo
26 printf -v $var %s 'hello there'
27 argv.py "$foo"
28 ## STDOUT:
29 ['hello there']
30 ## END
31 ## N-I mksh/zsh/ash STDOUT:
32 -v['']
33 ## END
34 ## N-I dash STDOUT:
35 ['']
36 ## END
37
38 #### printf -v %q
39 val='"quoted" with spaces and \'
40
41 # quote 'val' and store it in foo
42 printf -v foo %q "$val"
43 # then round trip back to eval
44 eval "bar=$foo"
45
46 # debugging:
47 #echo foo="$foo"
48 #echo bar="$bar"
49 #echo val="$val"
50
51 test "$bar" = "$val" && echo OK
52 ## STDOUT:
53 OK
54 ## END
55 ## N-I mksh/zsh/ash stdout-json: "-v"
56 ## N-I mksh/zsh/ash status: 1
57 ## N-I dash stdout-json: ""
58 ## N-I dash status: 1
59
60 #### printf -v a[1]
61 a=(a b c)
62 printf -v 'a[1]' %s 'foo'
63 echo status=$?
64 argv.py "${a[@]}"
65 ## STDOUT:
66 status=0
67 ['a', 'foo', 'c']
68 ## END
69 ## N-I mksh/zsh STDOUT:
70 -vstatus=0
71 ['a', 'b', 'c']
72 ## END
73 ## N-I dash/ash stdout-json: ""
74 ## N-I dash/ash status: 2
75
76 #### printf -v syntax error
77 printf -v 'a[' %s 'foo'
78 echo status=$?
79 ## STDOUT:
80 status=2
81 ## END
82 ## N-I ash/mksh/zsh stdout: -vstatus=0
83
84 #### dynamic declare instead of %s
85 var=foo
86 declare $var='hello there'
87 argv.py "$foo"
88 ## STDOUT:
89 ['hello there']
90 ## END
91 ## N-I dash/mksh/ash STDOUT:
92 ['']
93 ## END
94
95 #### dynamic declare instead of %q
96 var=foo
97 val='"quoted" with spaces and \'
98 # I think this is bash 4.4 only.
99 declare $var="${val@Q}"
100 echo "$foo"
101 ## STDOUT:
102 '"quoted" with spaces and \'
103 ## END
104 ## OK osh STDOUT:
105 $'"quoted" with spaces and \\'
106 ## END
107 ## N-I dash/ash stdout-json: ""
108 ## N-I dash/ash status: 2
109 ## N-I mksh stdout-json: "\n"
110 ## N-I zsh stdout-json: ""
111 ## N-I zsh status: 1
112
113 #### printf -v dynamic scope
114 case $SH in mksh|zsh|dash|ash) echo not implemented; exit ;; esac
115 # OK so printf is like assigning to a var.
116 # printf -v foo %q "$bar" is like
117 # foo=${bar@Q}
118 dollar='dollar'
119 f() {
120 local mylocal=foo
121 printf -v dollar %q '$' # assign foo to a quoted dollar
122 printf -v mylocal %q 'mylocal'
123 echo dollar=$dollar
124 echo mylocal=$mylocal
125 }
126 echo dollar=$dollar
127 echo --
128 f
129 echo --
130 echo dollar=$dollar
131 echo mylocal=$mylocal
132 ## STDOUT:
133 dollar=dollar
134 --
135 dollar=\$
136 mylocal=mylocal
137 --
138 dollar=\$
139 mylocal=
140 ## END
141 ## OK osh STDOUT:
142 dollar=dollar
143 --
144 dollar='$'
145 mylocal=mylocal
146 --
147 dollar='$'
148 mylocal=
149 ## END
150 ## N-I dash/ash/mksh/zsh STDOUT:
151 not implemented
152 ## END
153
154 #### printf with too few arguments
155 printf -- '-%s-%s-%s-\n' 'a b' 'x y'
156 ## STDOUT:
157 -a b-x y--
158 ## END
159
160 #### printf with too many arguments
161 printf -- '-%s-%s-\n' a b c d e
162 ## STDOUT:
163 -a-b-
164 -c-d-
165 -e--
166 ## END
167
168 #### printf width strings
169 printf '[%5s]\n' abc
170 printf '[%-5s]\n' abc
171 ## STDOUT:
172 [ abc]
173 [abc ]
174 ## END
175
176 #### printf integer
177 printf '%d\n' 42
178 printf '%i\n' 42 # synonym
179 printf '%d\n' \'a # if first character is a quote, use character code
180 printf '%d\n' \"a # double quotes work too
181 printf '[%5d]\n' 42
182 printf '[%-5d]\n' 42
183 printf '[%05d]\n' 42
184 #printf '[%-05d]\n' 42 # the leading 0 is meaningless
185 #[42 ]
186 ## STDOUT:
187 42
188 42
189 97
190 97
191 [ 42]
192 [42 ]
193 [00042]
194 ## END
195
196 #### printf %6.4d -- "precision" does padding for integers
197 printf '[%6.4d]\n' 42
198 printf '[%.4d]\n' 42
199 printf '[%6.d]\n' 42
200 echo --
201 printf '[%6.4d]\n' -42
202 printf '[%.4d]\n' -42
203 printf '[%6.d]\n' -42
204 ## STDOUT:
205 [ 0042]
206 [0042]
207 [ 42]
208 --
209 [ -0042]
210 [-0042]
211 [ -42]
212 ## END
213
214 #### printf %6.4x X o
215 printf '[%6.4x]\n' 42
216 printf '[%.4x]\n' 42
217 printf '[%6.x]\n' 42
218 echo --
219 printf '[%6.4X]\n' 42
220 printf '[%.4X]\n' 42
221 printf '[%6.X]\n' 42
222 echo --
223 printf '[%6.4o]\n' 42
224 printf '[%.4o]\n' 42
225 printf '[%6.o]\n' 42
226 ## STDOUT:
227 [ 002a]
228 [002a]
229 [ 2a]
230 --
231 [ 002A]
232 [002A]
233 [ 2A]
234 --
235 [ 0052]
236 [0052]
237 [ 52]
238 ## END
239
240 #### %06d zero padding vs. %6.6d
241 printf '[%06d]\n' 42
242 printf '[%06d]\n' -42 # 6 TOTAL
243 echo --
244 printf '[%6.6d]\n' 42
245 printf '[%6.6d]\n' -42 # 6 + 1 for the - sign!!!
246 ## STDOUT:
247 [000042]
248 [-00042]
249 --
250 [000042]
251 [-000042]
252 ## END
253
254 #### %06x %06X %06o
255 printf '[%06x]\n' 42
256 printf '[%06X]\n' 42
257 printf '[%06o]\n' 42
258 ## STDOUT:
259 [00002a]
260 [00002A]
261 [000052]
262 ## END
263
264 #### %06s is no-op
265 printf '(%6s)\n' 42
266 printf '(%6s)\n' -42
267 printf '(%06s)\n' 42
268 printf '(%06s)\n' -42
269 echo status=$?
270 ## STDOUT:
271 ( 42)
272 ( -42)
273 ( 42)
274 ( -42)
275 status=0
276 ## END
277 # mksh is stricter
278 ## OK mksh STDOUT:
279 ( 42)
280 ( -42)
281 ((status=1
282 ## END
283
284 #### printf %6.4s does both truncation and padding
285 printf '[%6s]\n' foo
286 printf '[%6.4s]\n' foo
287 printf '[%-6.4s]\n' foo
288 printf '[%6s]\n' spam-eggs
289 printf '[%6.4s]\n' spam-eggs
290 printf '[%-6.4s]\n' spam-eggs
291 ## STDOUT:
292 [ foo]
293 [ foo]
294 [foo ]
295 [spam-eggs]
296 [ spam]
297 [spam ]
298 ## END
299
300 #### printf %6.0s and %0.0s
301 printf '[%6.0s]\n' foo
302 printf '[%0.0s]\n' foo
303 ## STDOUT:
304 [ ]
305 []
306 ## END
307 ## N-I mksh stdout-json: "[ ]\n["
308 ## N-I mksh status: 1
309
310 #### printf %6.s and %0.s
311 printf '[%6.s]\n' foo
312 printf '[%0.s]\n' foo
313 ## STDOUT:
314 [ ]
315 []
316 ## END
317 ## BUG zsh STDOUT:
318 [ foo]
319 [foo]
320 ## END
321 ## N-I mksh stdout-json: "[ ]\n["
322 ## N-I mksh status: 1
323
324 #### printf %*.*s (width/precision from args)
325 printf '[%*s]\n' 9 hello
326 printf '[%.*s]\n' 3 hello
327 printf '[%*.3s]\n' 9 hello
328 printf '[%9.*s]\n' 3 hello
329 printf '[%*.*s]\n' 9 3 hello
330 ## STDOUT:
331 [ hello]
332 [hel]
333 [ hel]
334 [ hel]
335 [ hel]
336 ## END
337
338 #### unsigned / octal / hex
339 printf '[%u]\n' 42
340 printf '[%o]\n' 42
341 printf '[%x]\n' 42
342 printf '[%X]\n' 42
343 echo
344
345 printf '[%X]\n' \'a # if first character is a quote, use character code
346 printf '[%X]\n' \'ab # extra chars ignored
347
348 ## STDOUT:
349 [42]
350 [52]
351 [2a]
352 [2A]
353
354 [61]
355 [61]
356 ## END
357
358 #### unsigned / octal / hex big
359
360 for big in $(( 1 << 32 )) $(( (1 << 63) - 1 )); do
361 printf '[%u]\n' $big
362 printf '[%o]\n' $big
363 printf '[%x]\n' $big
364 printf '[%X]\n' $big
365 echo
366 done
367
368 ## STDOUT:
369 [4294967296]
370 [40000000000]
371 [100000000]
372 [100000000]
373
374 [9223372036854775807]
375 [777777777777777777777]
376 [7fffffffffffffff]
377 [7FFFFFFFFFFFFFFF]
378
379 ## END
380
381 ## BUG mksh STDOUT:
382 [1]
383 [1]
384 [1]
385 [1]
386
387 [2147483647]
388 [17777777777]
389 [7fffffff]
390 [7FFFFFFF]
391
392 ## END
393
394 #### empty string (osh is more strict)
395 printf '%d\n' ''
396 ## OK osh stdout-json: ""
397 ## OK osh status: 1
398 ## OK ash status: 1
399 ## STDOUT:
400 0
401 ## END
402
403 #### No char after ' => zero code point
404
405 # most shells use 0 here
406 printf '%d\n' \'
407 printf '%d\n' \"
408
409 ## OK mksh status: 1
410 ## STDOUT:
411 0
412 0
413 ## END
414
415 #### Unicode char with '
416 case $SH in mksh) echo 'weird bug'; exit ;; esac
417
418 # the mu character is U+03BC
419
420 printf '%x\n' \'μ
421 printf '%u\n' \'μ
422 printf '%o\n' \'μ
423 echo
424
425 u3=三
426 # u4=😘
427
428 printf '%x\n' \'$u3
429 printf '%u\n' \'$u3
430 printf '%o\n' \'$u3
431 echo
432
433 # mksh DOES respect unicode on the new Debian bookworm.
434 # but even building the SAME SOURCE from scratch, somehow it doesn't on Ubuntu 8.
435 # TBH I should probably just upgrade the mksh version.
436 #
437 # $ ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
438 # printf: warning: : character(s) following character constant have been ignored
439 # 206
440 #
441 # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ cat /etc/os-release
442 # NAME="Ubuntu"
443 # VERSION="18.04.5 LTS (Bionic Beaver)"
444 # ID=ubuntu
445 # ID_LIKE=debian
446 # PRETTY_NAME="Ubuntu 18.04.5 LTS"
447 # VERSION_ID="18.04"
448 # HOME_URL="https://www.ubuntu.com/"
449 # SUPPORT_URL="https://help.ubuntu.com/"
450 # BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
451 # PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
452 # VERSION_CODENAME=bionic
453 # UBUNTU_CODENAME=bionic
454 # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ env|egrep 'LC|LANG'
455 # LANG=en_US.UTF-8
456 # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_CTYPE=C.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
457 # printf: warning: : character(s) following character constant have been ignored
458 # 206
459 # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LANG=C.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
460 # printf: warning: : character(s) following character constant have been ignored
461 # 206
462 # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_ALL=C.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
463 # printf: warning: : character(s) following character constant have been ignored
464 # 206
465 # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_ALL=en_US.UTF-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
466 # printf: warning: : character(s) following character constant have been ignored
467 # 206
468 # andy@lenny:~/wedge/oils-for-unix.org/pkg/mksh/R52c$ LC_ALL=en_US.utf-8 ./mksh -c 'printf "%u\n" \"$1' dummy $'\u03bc'
469 # printf: warning: : character(s) following character constant have been ignored
470 # 206
471
472
473 ## STDOUT:
474 3bc
475 956
476 1674
477
478 4e09
479 19977
480 47011
481
482 ## END
483 ## BUG dash/ash STDOUT:
484 ce
485 206
486 316
487
488 e4
489 228
490 344
491
492 ## END
493
494 ## BUG mksh STDOUT:
495 weird bug
496 ## END
497
498 #### Invalid UTF-8
499
500 echo bytes1
501 not_utf8=$(python2 -c 'print("\xce\xce")')
502
503 printf '%x\n' \'$not_utf8
504 printf '%u\n' \'$not_utf8
505 printf '%o\n' \'$not_utf8
506 echo
507
508 echo bytes2
509 not_utf8=$(python2 -c 'print("\xbc\xbc")')
510 printf '%x\n' \'$not_utf8
511 printf '%u\n' \'$not_utf8
512 printf '%o\n' \'$not_utf8
513 echo
514
515 # Copied from data_lang/utf8_test.cc
516
517 echo overlong2
518 overlong2=$(python2 -c 'print("\xC1\x81")')
519 printf '%x\n' \'$overlong2
520 printf '%u\n' \'$overlong2
521 printf '%o\n' \'$overlong2
522 echo
523
524 echo overlong3
525 overlong3=$(python2 -c 'print("\xE0\x81\x81")')
526 printf '%x\n' \'$overlong3
527 printf '%u\n' \'$overlong3
528 printf '%o\n' \'$overlong3
529 echo
530
531 ## STDOUT:
532 bytes1
533 ce
534 206
535 316
536
537 bytes2
538 bc
539 188
540 274
541
542 overlong2
543 c1
544 193
545 301
546
547 overlong3
548 e0
549 224
550 340
551
552 ## END
553
554
555 #### Too large
556 case $SH in mksh) echo 'weird bug'; exit ;; esac
557
558 echo too large
559 too_large=$(python2 -c 'print("\xF4\x91\x84\x91")')
560 printf '%x\n' \'$too_large
561 printf '%u\n' \'$too_large
562 printf '%o\n' \'$too_large
563 echo
564
565 ## STDOUT:
566 too large
567 111111
568 1118481
569 4210421
570
571 ## END
572
573 ## BUG dash/ash STDOUT:
574 too large
575 f4
576 244
577 364
578
579 ## END
580
581 ## BUG mksh STDOUT:
582 weird bug
583 ## END
584
585 # osh rejects code points that are too large for a DIFFERENT reason
586
587 ## OK osh STDOUT:
588 too large
589 f4
590 244
591 364
592
593 ## END
594
595
596 #### negative numbers with unsigned / octal / hex
597 printf '[%u]\n' -42
598 echo status=$?
599
600 printf '[%o]\n' -42
601 echo status=$?
602
603 printf '[%x]\n' -42
604 echo status=$?
605
606 printf '[%X]\n' -42
607 echo status=$?
608
609 ## STDOUT:
610 [18446744073709551574]
611 status=0
612 [1777777777777777777726]
613 status=0
614 [ffffffffffffffd6]
615 status=0
616 [FFFFFFFFFFFFFFD6]
617 status=0
618 ## END
619
620 # osh DISALLOWS this because the output depends on the machine architecture.
621 ## N-I osh STDOUT:
622 status=1
623 status=1
624 status=1
625 status=1
626 ## END
627
628 #### printf floating point (not required, but they all implement it)
629 printf '[%f]\n' 3.14159
630 printf '[%.2f]\n' 3.14159
631 printf '[%8.2f]\n' 3.14159
632 printf '[%-8.2f]\n' 3.14159
633 printf '[%-f]\n' 3.14159
634 printf '[%-f]\n' 3.14
635 ## STDOUT:
636 [3.141590]
637 [3.14]
638 [ 3.14]
639 [3.14 ]
640 [3.141590]
641 [3.140000]
642 ## END
643 ## N-I osh stdout-json: ""
644 ## N-I osh status: 2
645
646 #### printf floating point with - and 0
647 printf '[%8.4f]\n' 3.14
648 printf '[%08.4f]\n' 3.14
649 printf '[%8.04f]\n' 3.14 # meaning less 0
650 printf '[%08.04f]\n' 3.14
651 echo ---
652 # these all boil down to the same thing. The -, 8, and 4 are respected, but
653 # none of the 0 are.
654 printf '[%-8.4f]\n' 3.14
655 printf '[%-08.4f]\n' 3.14
656 printf '[%-8.04f]\n' 3.14
657 printf '[%-08.04f]\n' 3.14
658 ## STDOUT:
659 [ 3.1400]
660 [003.1400]
661 [ 3.1400]
662 [003.1400]
663 ---
664 [3.1400 ]
665 [3.1400 ]
666 [3.1400 ]
667 [3.1400 ]
668 ## END
669 ## N-I osh STDOUT:
670 ---
671 ## END
672 ## N-I osh status: 2
673
674 #### printf eE fF gG
675 printf '[%e]\n' 3.14
676 printf '[%E]\n' 3.14
677 printf '[%f]\n' 3.14
678 # bash is the only one that implements %F? Is it a synonym?
679 #printf '[%F]\n' 3.14
680 printf '[%g]\n' 3.14
681 printf '[%G]\n' 3.14
682 ## STDOUT:
683 [3.140000e+00]
684 [3.140000E+00]
685 [3.140000]
686 [3.14]
687 [3.14]
688 ## END
689 ## N-I osh stdout-json: ""
690 ## N-I osh status: 2
691
692 #### printf backslash escapes
693 argv.py "$(printf 'a\tb')"
694 argv.py "$(printf '\xE2\x98\xA0')"
695 argv.py "$(printf '\044e')"
696 argv.py "$(printf '\0377')" # out of range
697 ## STDOUT:
698 ['a\tb']
699 ['\xe2\x98\xa0']
700 ['$e']
701 ['\x1f7']
702 ## END
703 ## N-I dash STDOUT:
704 ['a\tb']
705 ['\\xE2\\x98\\xA0']
706 ['$e']
707 ['\x1f7']
708 ## END
709
710 #### printf octal backslash escapes
711 argv.py "$(printf '\0377')"
712 argv.py "$(printf '\377')"
713 ## STDOUT:
714 ['\x1f7']
715 ['\xff']
716 ## END
717
718 #### printf unicode backslash escapes
719 argv.py "$(printf '\u2620')"
720 argv.py "$(printf '\U0000065f')"
721 ## STDOUT:
722 ['\xe2\x98\xa0']
723 ['\xd9\x9f']
724 ## END
725 ## N-I dash/ash STDOUT:
726 ['\\u2620']
727 ['\\U0000065f']
728 ## END
729
730 #### printf invalid backslash escape (is ignored)
731 printf '[\Z]\n'
732 ## STDOUT:
733 [\Z]
734 ## END
735
736 #### printf % escapes
737 printf '[%%]\n'
738 ## STDOUT:
739 [%]
740 ## END
741
742 #### printf %b backslash escaping
743 printf '[%s]\n' '\044' # escapes not evaluated
744 printf '[%b]\n' '\044' # YES, escapes evaluated
745 echo status=$?
746 ## STDOUT:
747 [\044]
748 [$]
749 status=0
750 ## END
751
752 #### printf %b with \c early return
753 printf '[%b]\n' 'ab\ncd\cxy'
754 echo $?
755 ## STDOUT:
756 [ab
757 cd0
758 ## END
759
760 #### printf %c -- doesn't respect UTF-8! Bad.
761 twomu=$'\u03bc\u03bc'
762 printf '[%s]\n' "$twomu"
763 printf '%c' "$twomu" | wc --bytes
764 ## STDOUT:
765 [μμ]
766 1
767 ## END
768 ## N-I dash STDOUT:
769 [$\u03bc\u03bc]
770 1
771 ## END
772 ## N-I ash STDOUT:
773 [\u03bc\u03bc]
774 1
775 ## END
776 ## N-I osh STDOUT:
777 [μμ]
778 0
779 ## END
780
781 #### printf invalid format
782 printf '%z' 42
783 echo status=$?
784 printf '%-z' 42
785 echo status=$?
786 ## STDOUT:
787 status=1
788 status=1
789 ## END
790 # osh emits parse errors
791 ## OK dash/osh STDOUT:
792 status=2
793 status=2
794 ## END
795
796 #### printf %q
797 x='a b'
798 printf '[%q]\n' "$x"
799 ## STDOUT:
800 ['a b']
801 ## END
802 ## OK bash/zsh STDOUT:
803 [a\ b]
804 ## END
805 ## N-I ash/dash stdout-json: "["
806 ## N-I ash status: 1
807 ## N-I dash status: 2
808
809 #### printf %6q (width)
810 # NOTE: coreutils /usr/bin/printf does NOT implement this %6q !!!
811 x='a b'
812 printf '[%6q]\n' "$x"
813 printf '[%1q]\n' "$x"
814 ## STDOUT:
815 [ 'a b']
816 ['a b']
817 ## END
818 ## OK bash/zsh STDOUT:
819 [ a\ b]
820 [a\ b]
821 ## END
822 ## N-I mksh/ash/dash stdout-json: "[["
823 ## N-I mksh/ash status: 1
824 ## N-I dash status: 2
825
826 #### printf negative numbers
827 printf '[%d] ' -42
828 echo status=$?
829 printf '[%i] ' -42
830 echo status=$?
831
832 # extra LEADING space too
833 printf '[%d] ' ' -42'
834 echo status=$?
835 printf '[%i] ' ' -42'
836 echo status=$?
837
838 # extra TRAILING space too
839 printf '[%d] ' ' -42 '
840 echo status=$?
841 printf '[%i] ' ' -42 '
842 echo status=$?
843
844 # extra TRAILING chars
845 printf '[%d] ' ' -42z'
846 echo status=$?
847 printf '[%i] ' ' -42z'
848 echo status=$?
849
850 exit 0 # ok
851
852 ## STDOUT:
853 [-42] status=0
854 [-42] status=0
855 [-42] status=0
856 [-42] status=0
857 [-42] status=1
858 [-42] status=1
859 [-42] status=1
860 [-42] status=1
861 ## END
862 # zsh is LESS STRICT
863 ## OK zsh STDOUT:
864 [-42] status=0
865 [-42] status=0
866 [-42] status=0
867 [-42] status=0
868 [-42] status=0
869 [-42] status=0
870 [0] status=1
871 [0] status=1
872 ## END
873
874 # osh is like zsh but has a hard failure (TODO: could be an option?)
875 ## OK osh STDOUT:
876 [-42] status=0
877 [-42] status=0
878 [-42] status=0
879 [-42] status=0
880 [-42] status=0
881 [-42] status=0
882 status=1
883 status=1
884 ## END
885
886 # ash is MORE STRICT
887 ## OK ash STDOUT:
888 [-42] status=0
889 [-42] status=0
890 [-42] status=0
891 [-42] status=0
892 [0] status=1
893 [0] status=1
894 [0] status=1
895 [0] status=1
896 ## END
897
898
899 #### printf + and space flags
900 # I didn't know these existed -- I only knew about - and 0 !
901 printf '[%+d]\n' 42
902 printf '[%+d]\n' -42
903 printf '[% d]\n' 42
904 printf '[% d]\n' -42
905 ## STDOUT:
906 [+42]
907 [-42]
908 [ 42]
909 [-42]
910 ## END
911 ## N-I osh stdout-json: ""
912 ## N-I osh status: 2
913
914 #### printf # flag
915 # I didn't know these existed -- I only knew about - and 0 !
916 # Note: '#' flag for integers outputs a prefix ONLY WHEN the value is non-zero
917 printf '[%#o][%#o]\n' 0 42
918 printf '[%#x][%#x]\n' 0 42
919 printf '[%#X][%#X]\n' 0 42
920 echo ---
921 # Note: '#' flag for %f, %g always outputs the decimal point.
922 printf '[%.0f][%#.0f]\n' 3 3
923 # Note: In addition, '#' flag for %g does not omit zeroes in fraction
924 printf '[%g][%#g]\n' 3 3
925 ## STDOUT:
926 [0][052]
927 [0][0x2a]
928 [0][0X2A]
929 ---
930 [3][3.]
931 [3][3.00000]
932 ## END
933 ## N-I osh STDOUT:
934 ---
935 ## END
936 ## N-I osh status: 2
937
938 #### Runtime error for invalid integer
939 x=3abc
940 printf '%d\n' $x
941 echo status=$?
942 printf '%d\n' xyz
943 echo status=$?
944 ## STDOUT:
945 3
946 status=1
947 0
948 status=1
949 ## END
950 # zsh should exit 1 in both cases
951 ## BUG zsh STDOUT:
952 0
953 status=1
954 0
955 status=0
956 ## END
957 # fails but also prints 0 instead of 3abc
958 ## BUG ash STDOUT:
959 0
960 status=1
961 0
962 status=1
963 ## END
964 # osh doesn't print anything invalid
965 ## OK osh STDOUT:
966 status=1
967 status=1
968 ## END
969
970 #### %(strftime format)T
971 # The result depends on timezone
972 export TZ=Asia/Tokyo
973 printf '%(%Y-%m-%d)T\n' 1557978599
974 export TZ=US/Eastern
975 printf '%(%Y-%m-%d)T\n' 1557978599
976 echo status=$?
977 ## STDOUT:
978 2019-05-16
979 2019-05-15
980 status=0
981 ## END
982 ## N-I mksh/zsh/ash STDOUT:
983 status=1
984 ## END
985 ## N-I dash STDOUT:
986 status=2
987 ## END
988
989 #### %(strftime format)T doesn't respect TZ if not exported
990
991 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
992
993 TZ=Portugal # NOT exported
994 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
995
996 # TZ is respected
997 export TZ=Portugal
998 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
999
1000 #echo $localtime
1001 #echo $tz
1002
1003 if ! test "$localtime" = "$tz"; then
1004 echo 'not equal'
1005 fi
1006 ## STDOUT:
1007 not equal
1008 ## END
1009 ## N-I mksh/zsh/ash/dash stdout-json: ""
1010
1011 #### %(strftime format)T TZ in environ but not in shell's memory
1012
1013 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
1014
1015 # TZ is respected
1016 export TZ=Portugal
1017 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1018
1019 unset TZ # unset in the shell, but still in the environment
1020
1021 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1022
1023 if ! test "$localtime" = "$tz"; then
1024 echo 'not equal'
1025 fi
1026
1027 ## STDOUT:
1028 not equal
1029 ## END
1030 ## N-I mksh/zsh/ash/dash stdout-json: ""
1031
1032 #### %10.5(strftime format)T
1033 # The result depends on timezone
1034 export TZ=Asia/Tokyo
1035 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1036 export TZ=US/Eastern
1037 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1038 echo status=$?
1039 ## STDOUT:
1040 [ 2019-]
1041 [ 2019-]
1042 status=0
1043 ## END
1044 ## N-I dash/mksh/zsh/ash STDOUT:
1045 [[status=1
1046 ## END
1047 ## N-I dash STDOUT:
1048 [[status=2
1049 ## END
1050
1051 #### Regression for 'printf x y'
1052 printf x y
1053 printf '%s\n' z
1054 ## STDOUT:
1055 xz
1056 ## END
1057
1058 #### bash truncates long strftime string at 128
1059
1060 case $SH in (ash|dash|mksh|zsh) exit ;; esac
1061
1062 strftime-format() {
1063 local n=$1
1064
1065 # Prints increasingly long format strings:
1066 # %(%Y)T %(%Y)T %(%Y%Y)T ...
1067
1068 echo -n '%('
1069 for i in $(seq $n); do
1070 echo -n '%Y'
1071 done
1072 echo -n ')T'
1073 }
1074
1075 printf $(strftime-format 1) | wc --bytes
1076 printf $(strftime-format 10) | wc --bytes
1077 printf $(strftime-format 30) | wc --bytes
1078 printf $(strftime-format 31) | wc --bytes
1079 printf $(strftime-format 32) | wc --bytes
1080
1081 case $SH in
1082 (*/_bin/cxx-dbg/*)
1083 # Ensure that oils-for-unix detects the truncation of a fixed buffer.
1084 # bash has a buffer of 128.
1085
1086 set +o errexit
1087 (
1088 printf $(strftime-format 1000)
1089 )
1090 status=$?
1091 if test $status -ne 1; then
1092 echo FAIL
1093 fi
1094 ;;
1095 esac
1096
1097 ## STDOUT:
1098 4
1099 40
1100 120
1101 124
1102 0
1103 ## END
1104 ## OK osh STDOUT:
1105 4
1106 40
1107 120
1108 124
1109 128
1110 ## END
1111
1112 ## N-I ash/dash/mksh/zsh STDOUT:
1113 ## END
1114
1115
1116 #### printf with explicit NUL byte
1117 case $SH in (dash|ash) return ;; esac
1118
1119 printf $'x\U0z'
1120
1121 printf $'\U0z'
1122
1123 ## stdout-json: "x"
1124 ## OK zsh stdout-repr: "x\0z\0z"
1125 ## N-I dash/ash stdout-json: ""
1126
1127 #### printf positive integer overflow
1128
1129 # %i seems like a synonym for %d
1130
1131 for fmt in '%u\n' '%d\n'; do
1132 # bash considers this in range for %u
1133 # same with mksh
1134 # zsh cuts everything off after 19 digits
1135 # ash truncates everything
1136 printf "$fmt" '18446744073709551615'
1137 echo status=$?
1138 printf "$fmt" '18446744073709551616'
1139 echo status=$?
1140 echo
1141 done
1142
1143 ## STDOUT:
1144 status=1
1145 status=1
1146
1147 status=1
1148 status=1
1149
1150 ## END
1151
1152 ## OK bash status: 0
1153 ## OK bash STDOUT:
1154 18446744073709551615
1155 status=0
1156 18446744073709551615
1157 status=0
1158
1159 9223372036854775807
1160 status=0
1161 9223372036854775807
1162 status=0
1163
1164 ## END
1165
1166 ## OK dash/mksh status: 0
1167 ## OK dash/mksh STDOUT:
1168 18446744073709551615
1169 status=0
1170 18446744073709551615
1171 status=1
1172
1173 9223372036854775807
1174 status=1
1175 9223372036854775807
1176 status=1
1177
1178 ## END
1179
1180 ## BUG ash status: 0
1181 ## BUG ash STDOUT:
1182 18446744073709551615
1183 status=0
1184 0
1185 status=1
1186
1187 0
1188 status=1
1189 0
1190 status=1
1191
1192 ## END
1193
1194 ## BUG zsh status: 0
1195 ## BUG zsh STDOUT:
1196 1844674407370955161
1197 status=0
1198 1844674407370955161
1199 status=0
1200
1201 1844674407370955161
1202 status=0
1203 1844674407370955161
1204 status=0
1205
1206 ## END
1207
1208 #### printf negative integer overflow
1209
1210 # %i seems like a synonym for %d
1211
1212 for fmt in '%u\n' '%d\n'; do
1213
1214 printf "$fmt" '-18446744073709551615'
1215 echo status=$?
1216 printf "$fmt" '-18446744073709551616'
1217 echo status=$?
1218 echo
1219 done
1220
1221 ## STDOUT:
1222 status=1
1223 status=1
1224
1225 status=1
1226 status=1
1227
1228 ## END
1229
1230 ## OK bash status: 0
1231 ## OK bash STDOUT:
1232 1
1233 status=0
1234 18446744073709551615
1235 status=0
1236
1237 -9223372036854775808
1238 status=0
1239 -9223372036854775808
1240 status=0
1241
1242 ## END
1243
1244 ## OK dash/mksh status: 0
1245 ## OK dash/mksh STDOUT:
1246 1
1247 status=0
1248 18446744073709551615
1249 status=1
1250
1251 -9223372036854775808
1252 status=1
1253 -9223372036854775808
1254 status=1
1255
1256 ## END
1257
1258 ## BUG zsh status: 0
1259 ## BUG zsh STDOUT:
1260 16602069666338596455
1261 status=0
1262 16602069666338596455
1263 status=0
1264
1265 -1844674407370955161
1266 status=0
1267 -1844674407370955161
1268 status=0
1269
1270 ## END
1271
1272 ## BUG ash status: 0
1273 ## BUG ash STDOUT:
1274 0
1275 status=1
1276 0
1277 status=1
1278
1279 0
1280 status=1
1281 0
1282 status=1
1283
1284 ## END