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