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 %c -- doesn't respect UTF-8! Bad.
745 twomu=$'\u03bc\u03bc'
746 printf '[%s]\n' "$twomu"
747 printf '%c' "$twomu" | wc --bytes
748 ## STDOUT:
749 [μμ]
750 1
751 ## END
752 ## N-I dash STDOUT:
753 [$\u03bc\u03bc]
754 1
755 ## END
756 ## N-I ash STDOUT:
757 [\u03bc\u03bc]
758 1
759 ## END
760 ## N-I osh STDOUT:
761 [μμ]
762 0
763 ## END
764
765 #### printf invalid format
766 printf '%z' 42
767 echo status=$?
768 printf '%-z' 42
769 echo status=$?
770 ## STDOUT:
771 status=1
772 status=1
773 ## END
774 # osh emits parse errors
775 ## OK dash/osh STDOUT:
776 status=2
777 status=2
778 ## END
779
780 #### printf %q
781 x='a b'
782 printf '[%q]\n' "$x"
783 ## STDOUT:
784 ['a b']
785 ## END
786 ## OK bash/zsh STDOUT:
787 [a\ b]
788 ## END
789 ## N-I ash/dash stdout-json: "["
790 ## N-I ash status: 1
791 ## N-I dash status: 2
792
793 #### printf %6q (width)
794 # NOTE: coreutils /usr/bin/printf does NOT implement this %6q !!!
795 x='a b'
796 printf '[%6q]\n' "$x"
797 printf '[%1q]\n' "$x"
798 ## STDOUT:
799 [ 'a b']
800 ['a b']
801 ## END
802 ## OK bash/zsh STDOUT:
803 [ a\ b]
804 [a\ b]
805 ## END
806 ## N-I mksh/ash/dash stdout-json: "[["
807 ## N-I mksh/ash status: 1
808 ## N-I dash status: 2
809
810 #### printf negative numbers
811 printf '[%d] ' -42
812 echo status=$?
813 printf '[%i] ' -42
814 echo status=$?
815
816 # extra LEADING space too
817 printf '[%d] ' ' -42'
818 echo status=$?
819 printf '[%i] ' ' -42'
820 echo status=$?
821
822 # extra TRAILING space too
823 printf '[%d] ' ' -42 '
824 echo status=$?
825 printf '[%i] ' ' -42 '
826 echo status=$?
827
828 # extra TRAILING chars
829 printf '[%d] ' ' -42z'
830 echo status=$?
831 printf '[%i] ' ' -42z'
832 echo status=$?
833
834 exit 0 # ok
835
836 ## STDOUT:
837 [-42] status=0
838 [-42] status=0
839 [-42] status=0
840 [-42] status=0
841 [-42] status=1
842 [-42] status=1
843 [-42] status=1
844 [-42] status=1
845 ## END
846 # zsh is LESS STRICT
847 ## OK zsh STDOUT:
848 [-42] status=0
849 [-42] status=0
850 [-42] status=0
851 [-42] status=0
852 [-42] status=0
853 [-42] status=0
854 [0] status=1
855 [0] status=1
856 ## END
857
858 # osh is like zsh but has a hard failure (TODO: could be an option?)
859 ## OK osh STDOUT:
860 [-42] status=0
861 [-42] status=0
862 [-42] status=0
863 [-42] status=0
864 [-42] status=0
865 [-42] status=0
866 status=1
867 status=1
868 ## END
869
870 # ash is MORE STRICT
871 ## OK ash STDOUT:
872 [-42] status=0
873 [-42] status=0
874 [-42] status=0
875 [-42] status=0
876 [0] status=1
877 [0] status=1
878 [0] status=1
879 [0] status=1
880 ## END
881
882
883 #### printf + and space flags
884 # I didn't know these existed -- I only knew about - and 0 !
885 printf '[%+d]\n' 42
886 printf '[%+d]\n' -42
887 printf '[% d]\n' 42
888 printf '[% d]\n' -42
889 ## STDOUT:
890 [+42]
891 [-42]
892 [ 42]
893 [-42]
894 ## END
895 ## N-I osh stdout-json: ""
896 ## N-I osh status: 2
897
898 #### printf # flag
899 # I didn't know these existed -- I only knew about - and 0 !
900 # Note: '#' flag for integers outputs a prefix ONLY WHEN the value is non-zero
901 printf '[%#o][%#o]\n' 0 42
902 printf '[%#x][%#x]\n' 0 42
903 printf '[%#X][%#X]\n' 0 42
904 echo ---
905 # Note: '#' flag for %f, %g always outputs the decimal point.
906 printf '[%.0f][%#.0f]\n' 3 3
907 # Note: In addition, '#' flag for %g does not omit zeroes in fraction
908 printf '[%g][%#g]\n' 3 3
909 ## STDOUT:
910 [0][052]
911 [0][0x2a]
912 [0][0X2A]
913 ---
914 [3][3.]
915 [3][3.00000]
916 ## END
917 ## N-I osh STDOUT:
918 ---
919 ## END
920 ## N-I osh status: 2
921
922 #### Runtime error for invalid integer
923 x=3abc
924 printf '%d\n' $x
925 echo status=$?
926 printf '%d\n' xyz
927 echo status=$?
928 ## STDOUT:
929 3
930 status=1
931 0
932 status=1
933 ## END
934 # zsh should exit 1 in both cases
935 ## BUG zsh STDOUT:
936 0
937 status=1
938 0
939 status=0
940 ## END
941 # fails but also prints 0 instead of 3abc
942 ## BUG ash STDOUT:
943 0
944 status=1
945 0
946 status=1
947 ## END
948 # osh doesn't print anything invalid
949 ## OK osh STDOUT:
950 status=1
951 status=1
952 ## END
953
954 #### %(strftime format)T
955 # The result depends on timezone
956 export TZ=Asia/Tokyo
957 printf '%(%Y-%m-%d)T\n' 1557978599
958 export TZ=US/Eastern
959 printf '%(%Y-%m-%d)T\n' 1557978599
960 echo status=$?
961 ## STDOUT:
962 2019-05-16
963 2019-05-15
964 status=0
965 ## END
966 ## N-I mksh/zsh/ash STDOUT:
967 status=1
968 ## END
969 ## N-I dash STDOUT:
970 status=2
971 ## END
972
973 #### %(strftime format)T doesn't respect TZ if not exported
974
975 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
976
977 TZ=Portugal # NOT exported
978 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
979
980 # TZ is respected
981 export TZ=Portugal
982 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
983
984 #echo $localtime
985 #echo $tz
986
987 if ! test "$localtime" = "$tz"; then
988 echo 'not equal'
989 fi
990 ## STDOUT:
991 not equal
992 ## END
993 ## N-I mksh/zsh/ash/dash stdout-json: ""
994
995 #### %(strftime format)T TZ in environ but not in shell's memory
996
997 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
998
999 # TZ is respected
1000 export TZ=Portugal
1001 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1002
1003 unset TZ # unset in the shell, but still in the environment
1004
1005 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1006
1007 if ! test "$localtime" = "$tz"; then
1008 echo 'not equal'
1009 fi
1010
1011 ## STDOUT:
1012 not equal
1013 ## END
1014 ## N-I mksh/zsh/ash/dash stdout-json: ""
1015
1016 #### %10.5(strftime format)T
1017 # The result depends on timezone
1018 export TZ=Asia/Tokyo
1019 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1020 export TZ=US/Eastern
1021 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1022 echo status=$?
1023 ## STDOUT:
1024 [ 2019-]
1025 [ 2019-]
1026 status=0
1027 ## END
1028 ## N-I mksh/zsh/ash STDOUT:
1029 [[status=1
1030 ## END
1031 ## N-I dash STDOUT:
1032 [[status=2
1033 ## END
1034
1035 #### Regression for 'printf x y'
1036 printf x y
1037 printf '%s\n' z
1038 ## STDOUT:
1039 xz
1040 ## END
1041
1042 #### bash truncates long strftime string at 128
1043
1044 case $SH in ash|dash|mksh|zsh) exit ;; esac
1045
1046 strftime-format() {
1047 local n=$1
1048
1049 # Prints increasingly long format strings:
1050 # %(%Y)T %(%Y)T %(%Y%Y)T ...
1051
1052 echo -n '%('
1053 for i in $(seq $n); do
1054 echo -n '%Y'
1055 done
1056 echo -n ')T'
1057 }
1058
1059 printf $(strftime-format 1) | wc --bytes
1060 printf $(strftime-format 10) | wc --bytes
1061 printf $(strftime-format 30) | wc --bytes
1062 printf $(strftime-format 31) | wc --bytes
1063 printf $(strftime-format 32) | wc --bytes
1064
1065 case $SH in
1066 (*/_bin/cxx-dbg/*)
1067 # Ensure that oils-for-unix detects the truncation of a fixed buffer.
1068 # bash has a buffer of 128.
1069
1070 set +o errexit
1071 (
1072 printf $(strftime-format 1000)
1073 )
1074 status=$?
1075 if test $status -ne 1; then
1076 echo FAIL
1077 fi
1078 ;;
1079 esac
1080
1081 ## STDOUT:
1082 4
1083 40
1084 120
1085 124
1086 0
1087 ## END
1088 ## OK osh STDOUT:
1089 4
1090 40
1091 120
1092 124
1093 128
1094 ## END
1095
1096 ## N-I ash/dash/mksh/zsh STDOUT:
1097 ## END
1098
1099 #### printf positive integer overflow
1100
1101 # %i seems like a synonym for %d
1102
1103 for fmt in '%u\n' '%d\n'; do
1104 # bash considers this in range for %u
1105 # same with mksh
1106 # zsh cuts everything off after 19 digits
1107 # ash truncates everything
1108 printf "$fmt" '18446744073709551615'
1109 echo status=$?
1110 printf "$fmt" '18446744073709551616'
1111 echo status=$?
1112 echo
1113 done
1114
1115 ## STDOUT:
1116 status=1
1117 status=1
1118
1119 status=1
1120 status=1
1121
1122 ## END
1123
1124 ## OK bash status: 0
1125 ## OK bash STDOUT:
1126 18446744073709551615
1127 status=0
1128 18446744073709551615
1129 status=0
1130
1131 9223372036854775807
1132 status=0
1133 9223372036854775807
1134 status=0
1135
1136 ## END
1137
1138 ## OK dash/mksh status: 0
1139 ## OK dash/mksh STDOUT:
1140 18446744073709551615
1141 status=0
1142 18446744073709551615
1143 status=1
1144
1145 9223372036854775807
1146 status=1
1147 9223372036854775807
1148 status=1
1149
1150 ## END
1151
1152 ## BUG ash status: 0
1153 ## BUG ash STDOUT:
1154 18446744073709551615
1155 status=0
1156 0
1157 status=1
1158
1159 0
1160 status=1
1161 0
1162 status=1
1163
1164 ## END
1165
1166 ## BUG zsh status: 0
1167 ## BUG zsh STDOUT:
1168 1844674407370955161
1169 status=0
1170 1844674407370955161
1171 status=0
1172
1173 1844674407370955161
1174 status=0
1175 1844674407370955161
1176 status=0
1177
1178 ## END
1179
1180 #### printf negative integer overflow
1181
1182 # %i seems like a synonym for %d
1183
1184 for fmt in '%u\n' '%d\n'; do
1185
1186 printf "$fmt" '-18446744073709551615'
1187 echo status=$?
1188 printf "$fmt" '-18446744073709551616'
1189 echo status=$?
1190 echo
1191 done
1192
1193 ## STDOUT:
1194 status=1
1195 status=1
1196
1197 status=1
1198 status=1
1199
1200 ## END
1201
1202 ## OK bash status: 0
1203 ## OK bash STDOUT:
1204 1
1205 status=0
1206 18446744073709551615
1207 status=0
1208
1209 -9223372036854775808
1210 status=0
1211 -9223372036854775808
1212 status=0
1213
1214 ## END
1215
1216 ## OK dash/mksh status: 0
1217 ## OK dash/mksh STDOUT:
1218 1
1219 status=0
1220 18446744073709551615
1221 status=1
1222
1223 -9223372036854775808
1224 status=1
1225 -9223372036854775808
1226 status=1
1227
1228 ## END
1229
1230 ## BUG zsh status: 0
1231 ## BUG zsh STDOUT:
1232 16602069666338596455
1233 status=0
1234 16602069666338596455
1235 status=0
1236
1237 -1844674407370955161
1238 status=0
1239 -1844674407370955161
1240 status=0
1241
1242 ## END
1243
1244 ## BUG ash status: 0
1245 ## BUG ash STDOUT:
1246 0
1247 status=1
1248 0
1249 status=1
1250
1251 0
1252 status=1
1253 0
1254 status=1
1255
1256 ## END
1257
1258 #### printf %b does backslash escaping
1259
1260 printf '[%s]\n' '\044' # escapes not evaluated
1261 printf '[%b]\n' '\044' # YES, escapes evaluated
1262 echo
1263
1264 printf '[%s]\n' '\x7e' # escapes not evaluated
1265 printf '[%b]\n' '\x7e' # YES, escapes evaluated
1266 echo
1267
1268 # not a valid escape
1269 printf '[%s]\n' '\A'
1270 printf '[%b]\n' '\A'
1271
1272 ## STDOUT:
1273 [\044]
1274 [$]
1275
1276 [\x7e]
1277 [~]
1278
1279 [\A]
1280 [\A]
1281 ## END
1282
1283 ## N-I dash STDOUT:
1284 [\044]
1285 [$]
1286
1287 [\x7e]
1288 [\x7e]
1289
1290 [\A]
1291 [\A]
1292 ## END
1293
1294 #### printf %b unicode escapes
1295
1296 printf '[%s]\n' '\u03bc' # escapes not evaluated
1297 printf '[%b]\n' '\u03bc' # YES, escapes evaluated
1298
1299 ## STDOUT:
1300 [\u03bc]
1301 [μ]
1302 ## END
1303
1304 ## N-I dash/ash STDOUT:
1305 [\u03bc]
1306 [\u03bc]
1307 ## END
1308
1309 #### printf %b respects \c early return
1310 printf '[%b]\n' 'ab\ncd\cxy'
1311 echo $?
1312 ## STDOUT:
1313 [ab
1314 cd0
1315 ## END
1316
1317
1318 #### printf %b supports octal escapes, both \141 and \0141
1319
1320 printf 'three %b\n' '\141' # di
1321 printf 'four %b\n' '\0141'
1322 echo
1323
1324 # trailing 9
1325 printf '%b\n' '\1419'
1326 printf '%b\n' '\01419'
1327
1328 # Notes:
1329 #
1330 # - echo -e:
1331 # - NO 3 digit octal - echo -e '\141' does not work
1332 # - YES 4 digit octal
1333 # - printf %b
1334 # - YES 3 digit octal
1335 # - YES 4 digit octal
1336 # - printf string (outer)
1337 # - YES 3 digit octal
1338 # - NO 4 digit octal
1339 # - $'' and $PS1
1340 # - YES 3 digit octal
1341 # - NO 4 digit octal
1342
1343 ## STDOUT:
1344 three a
1345 four a
1346
1347 a9
1348 a9
1349 ## END
1350
1351 ## N-I zsh STDOUT:
1352 three \141
1353 four a
1354
1355 \1419
1356 a9
1357 ## END
1358
1359 #### printf %b with truncated octal escapes
1360
1361 # 8 is not a valid octal digit
1362
1363 printf '%b\n' '\558'
1364 printf '%b\n' '\0558'
1365 echo
1366
1367 show_bytes() {
1368 od -A n -t x1
1369 }
1370 printf '%b' '\7' | show_bytes
1371 printf '%b' '\07' | show_bytes
1372 printf '%b' '\007' | show_bytes
1373 printf '%b' '\0007' | show_bytes
1374
1375 ## STDOUT:
1376 -8
1377 -8
1378
1379 07
1380 07
1381 07
1382 07
1383 ## END
1384
1385 ## N-I zsh STDOUT:
1386 \558
1387 -8
1388
1389 5c 37
1390 07
1391 07
1392 07
1393 ## END