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 #env
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/mksh STDOUT:
484 ce
485 206
486 316
487
488 e4
489 228
490 344
491
492 ## END
493
494 #### Invalid UTF-8
495
496 echo bytes1
497 not_utf8=$(python2 -c 'print("\xce\xce")')
498
499 printf '%x\n' \'$not_utf8
500 printf '%u\n' \'$not_utf8
501 printf '%o\n' \'$not_utf8
502 echo
503
504 echo bytes2
505 not_utf8=$(python2 -c 'print("\xbc\xbc")')
506 printf '%x\n' \'$not_utf8
507 printf '%u\n' \'$not_utf8
508 printf '%o\n' \'$not_utf8
509 echo
510
511 # Copied from data_lang/utf8_test.cc
512
513 echo overlong2
514 overlong2=$(python2 -c 'print("\xC1\x81")')
515 printf '%x\n' \'$overlong2
516 printf '%u\n' \'$overlong2
517 printf '%o\n' \'$overlong2
518 echo
519
520 echo overlong3
521 overlong3=$(python2 -c 'print("\xE0\x81\x81")')
522 printf '%x\n' \'$overlong3
523 printf '%u\n' \'$overlong3
524 printf '%o\n' \'$overlong3
525 echo
526
527 ## STDOUT:
528 bytes1
529 ce
530 206
531 316
532
533 bytes2
534 bc
535 188
536 274
537
538 overlong2
539 c1
540 193
541 301
542
543 overlong3
544 e0
545 224
546 340
547
548 ## END
549
550
551 #### Too large
552
553 echo too large
554 too_large=$(python2 -c 'print("\xF4\x91\x84\x91")')
555 printf '%x\n' \'$too_large
556 printf '%u\n' \'$too_large
557 printf '%o\n' \'$too_large
558 echo
559
560 ## STDOUT:
561 too large
562 111111
563 1118481
564 4210421
565
566 ## END
567
568 ## BUG dash/ash/mksh STDOUT:
569 too large
570 f4
571 244
572 364
573
574 ## END
575
576 # osh rejects code points that are too large for a DIFFERENT reason
577
578 ## OK osh STDOUT:
579 too large
580 f4
581 244
582 364
583
584 ## END
585
586
587 #### negative numbers with unsigned / octal / hex
588 printf '[%u]\n' -42
589 printf '[%o]\n' -42
590 printf '[%x]\n' -42
591 printf '[%X]\n' -42
592 ## STDOUT:
593 [18446744073709551574]
594 [1777777777777777777726]
595 [ffffffffffffffd6]
596 [FFFFFFFFFFFFFFD6]
597 ## END
598
599 # osh DISALLOWS this because the output depends on the machine architecture.
600 ## N-I osh stdout-json: ""
601 ## N-I osh status: 1
602
603 #### printf floating point (not required, but they all implement it)
604 printf '[%f]\n' 3.14159
605 printf '[%.2f]\n' 3.14159
606 printf '[%8.2f]\n' 3.14159
607 printf '[%-8.2f]\n' 3.14159
608 printf '[%-f]\n' 3.14159
609 printf '[%-f]\n' 3.14
610 ## STDOUT:
611 [3.141590]
612 [3.14]
613 [ 3.14]
614 [3.14 ]
615 [3.141590]
616 [3.140000]
617 ## END
618 ## N-I osh stdout-json: ""
619 ## N-I osh status: 2
620
621 #### printf floating point with - and 0
622 printf '[%8.4f]\n' 3.14
623 printf '[%08.4f]\n' 3.14
624 printf '[%8.04f]\n' 3.14 # meaning less 0
625 printf '[%08.04f]\n' 3.14
626 echo ---
627 # these all boil down to the same thing. The -, 8, and 4 are respected, but
628 # none of the 0 are.
629 printf '[%-8.4f]\n' 3.14
630 printf '[%-08.4f]\n' 3.14
631 printf '[%-8.04f]\n' 3.14
632 printf '[%-08.04f]\n' 3.14
633 ## STDOUT:
634 [ 3.1400]
635 [003.1400]
636 [ 3.1400]
637 [003.1400]
638 ---
639 [3.1400 ]
640 [3.1400 ]
641 [3.1400 ]
642 [3.1400 ]
643 ## END
644 ## N-I osh STDOUT:
645 ---
646 ## END
647 ## N-I osh status: 2
648
649 #### printf eE fF gG
650 printf '[%e]\n' 3.14
651 printf '[%E]\n' 3.14
652 printf '[%f]\n' 3.14
653 # bash is the only one that implements %F? Is it a synonym?
654 #printf '[%F]\n' 3.14
655 printf '[%g]\n' 3.14
656 printf '[%G]\n' 3.14
657 ## STDOUT:
658 [3.140000e+00]
659 [3.140000E+00]
660 [3.140000]
661 [3.14]
662 [3.14]
663 ## END
664 ## N-I osh stdout-json: ""
665 ## N-I osh status: 2
666
667 #### printf backslash escapes
668 argv.py "$(printf 'a\tb')"
669 argv.py "$(printf '\xE2\x98\xA0')"
670 argv.py "$(printf '\044e')"
671 argv.py "$(printf '\0377')" # out of range
672 ## STDOUT:
673 ['a\tb']
674 ['\xe2\x98\xa0']
675 ['$e']
676 ['\x1f7']
677 ## END
678 ## N-I dash STDOUT:
679 ['a\tb']
680 ['\\xE2\\x98\\xA0']
681 ['$e']
682 ['\x1f7']
683 ## END
684
685 #### printf octal backslash escapes
686 argv.py "$(printf '\0377')"
687 argv.py "$(printf '\377')"
688 ## STDOUT:
689 ['\x1f7']
690 ['\xff']
691 ## END
692
693 #### printf unicode backslash escapes
694 argv.py "$(printf '\u2620')"
695 argv.py "$(printf '\U0000065f')"
696 ## STDOUT:
697 ['\xe2\x98\xa0']
698 ['\xd9\x9f']
699 ## END
700 ## N-I dash/ash STDOUT:
701 ['\\u2620']
702 ['\\U0000065f']
703 ## END
704
705 #### printf invalid backslash escape (is ignored)
706 printf '[\Z]\n'
707 ## STDOUT:
708 [\Z]
709 ## END
710
711 #### printf % escapes
712 printf '[%%]\n'
713 ## STDOUT:
714 [%]
715 ## END
716
717 #### printf %b backslash escaping
718 printf '[%s]\n' '\044' # escapes not evaluated
719 printf '[%b]\n' '\044' # YES, escapes evaluated
720 echo status=$?
721 ## STDOUT:
722 [\044]
723 [$]
724 status=0
725 ## END
726
727 #### printf %b with \c early return
728 printf '[%b]\n' 'ab\ncd\cxy'
729 echo $?
730 ## STDOUT:
731 [ab
732 cd0
733 ## END
734
735 #### printf %c -- doesn't respect UTF-8! Bad.
736 twomu=$'\u03bc\u03bc'
737 printf '[%s]\n' "$twomu"
738 printf '%c' "$twomu" | wc --bytes
739 ## STDOUT:
740 [μμ]
741 1
742 ## END
743 ## N-I dash STDOUT:
744 [$\u03bc\u03bc]
745 1
746 ## END
747 ## N-I ash STDOUT:
748 [\u03bc\u03bc]
749 1
750 ## END
751 ## N-I osh STDOUT:
752 [μμ]
753 0
754 ## END
755
756 #### printf invalid format
757 printf '%z' 42
758 echo status=$?
759 printf '%-z' 42
760 echo status=$?
761 ## STDOUT:
762 status=1
763 status=1
764 ## END
765 # osh emits parse errors
766 ## OK dash/osh STDOUT:
767 status=2
768 status=2
769 ## END
770
771 #### printf %q
772 x='a b'
773 printf '[%q]\n' "$x"
774 ## STDOUT:
775 ['a b']
776 ## END
777 ## OK bash/zsh STDOUT:
778 [a\ b]
779 ## END
780 ## N-I ash/dash stdout-json: "["
781 ## N-I ash status: 1
782 ## N-I dash status: 2
783
784 #### printf %6q (width)
785 # NOTE: coreutils /usr/bin/printf does NOT implement this %6q !!!
786 x='a b'
787 printf '[%6q]\n' "$x"
788 printf '[%1q]\n' "$x"
789 ## STDOUT:
790 [ 'a b']
791 ['a b']
792 ## END
793 ## OK bash/zsh STDOUT:
794 [ a\ b]
795 [a\ b]
796 ## END
797 ## N-I mksh/ash/dash stdout-json: "[["
798 ## N-I mksh/ash status: 1
799 ## N-I dash status: 2
800
801 #### printf negative numbers
802 printf '[%d] ' -42
803 echo status=$?
804 printf '[%i] ' -42
805 echo status=$?
806
807 # extra LEADING space too
808 printf '[%d] ' ' -42'
809 echo status=$?
810 printf '[%i] ' ' -42'
811 echo status=$?
812
813 # extra TRAILING space too
814 printf '[%d] ' ' -42 '
815 echo status=$?
816 printf '[%i] ' ' -42 '
817 echo status=$?
818
819 # extra TRAILING chars
820 printf '[%d] ' ' -42z'
821 echo status=$?
822 printf '[%i] ' ' -42z'
823 echo status=$?
824
825 exit 0 # ok
826
827 ## STDOUT:
828 [-42] status=0
829 [-42] status=0
830 [-42] status=0
831 [-42] status=0
832 [-42] status=1
833 [-42] status=1
834 [-42] status=1
835 [-42] status=1
836 ## END
837 # zsh is LESS STRICT
838 ## OK zsh STDOUT:
839 [-42] status=0
840 [-42] status=0
841 [-42] status=0
842 [-42] status=0
843 [-42] status=0
844 [-42] status=0
845 [0] status=1
846 [0] status=1
847 ## END
848
849 # osh is like zsh but has a hard failure (TODO: could be an option?)
850 ## OK osh STDOUT:
851 [-42] status=0
852 [-42] status=0
853 [-42] status=0
854 [-42] status=0
855 [-42] status=0
856 [-42] status=0
857 status=1
858 status=1
859 ## END
860
861 # ash is MORE STRICT
862 ## OK ash STDOUT:
863 [-42] status=0
864 [-42] status=0
865 [-42] status=0
866 [-42] status=0
867 [0] status=1
868 [0] status=1
869 [0] status=1
870 [0] status=1
871 ## END
872
873
874 #### printf + and space flags
875 # I didn't know these existed -- I only knew about - and 0 !
876 printf '[%+d]\n' 42
877 printf '[%+d]\n' -42
878 printf '[% d]\n' 42
879 printf '[% d]\n' -42
880 ## STDOUT:
881 [+42]
882 [-42]
883 [ 42]
884 [-42]
885 ## END
886 ## N-I osh stdout-json: ""
887 ## N-I osh status: 2
888
889 #### printf # flag
890 # I didn't know these existed -- I only knew about - and 0 !
891 # Note: '#' flag for integers outputs a prefix ONLY WHEN the value is non-zero
892 printf '[%#o][%#o]\n' 0 42
893 printf '[%#x][%#x]\n' 0 42
894 printf '[%#X][%#X]\n' 0 42
895 echo ---
896 # Note: '#' flag for %f, %g always outputs the decimal point.
897 printf '[%.0f][%#.0f]\n' 3 3
898 # Note: In addition, '#' flag for %g does not omit zeroes in fraction
899 printf '[%g][%#g]\n' 3 3
900 ## STDOUT:
901 [0][052]
902 [0][0x2a]
903 [0][0X2A]
904 ---
905 [3][3.]
906 [3][3.00000]
907 ## END
908 ## N-I osh STDOUT:
909 ---
910 ## END
911 ## N-I osh status: 2
912
913 #### Runtime error for invalid integer
914 x=3abc
915 printf '%d\n' $x
916 echo status=$?
917 printf '%d\n' xyz
918 echo status=$?
919 ## STDOUT:
920 3
921 status=1
922 0
923 status=1
924 ## END
925 # zsh should exit 1 in both cases
926 ## BUG zsh STDOUT:
927 0
928 status=1
929 0
930 status=0
931 ## END
932 # fails but also prints 0 instead of 3abc
933 ## BUG ash STDOUT:
934 0
935 status=1
936 0
937 status=1
938 ## END
939 # osh doesn't print anything invalid
940 ## OK osh STDOUT:
941 status=1
942 status=1
943 ## END
944
945 #### %(strftime format)T
946 # The result depends on timezone
947 export TZ=Asia/Tokyo
948 printf '%(%Y-%m-%d)T\n' 1557978599
949 export TZ=US/Eastern
950 printf '%(%Y-%m-%d)T\n' 1557978599
951 echo status=$?
952 ## STDOUT:
953 2019-05-16
954 2019-05-15
955 status=0
956 ## END
957 ## N-I mksh/zsh/ash STDOUT:
958 status=1
959 ## END
960 ## N-I dash STDOUT:
961 status=2
962 ## END
963
964 #### %(strftime format)T doesn't respect TZ if not exported
965
966 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
967
968 TZ=Portugal # NOT exported
969 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
970
971 # TZ is respected
972 export TZ=Portugal
973 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
974
975 #echo $localtime
976 #echo $tz
977
978 if ! test "$localtime" = "$tz"; then
979 echo 'not equal'
980 fi
981 ## STDOUT:
982 not equal
983 ## END
984 ## N-I mksh/zsh/ash/dash stdout-json: ""
985
986 #### %(strftime format)T TZ in environ but not in shell's memory
987
988 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
989
990 # TZ is respected
991 export TZ=Portugal
992 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
993
994 unset TZ # unset in the shell, but still in the environment
995
996 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
997
998 if ! test "$localtime" = "$tz"; then
999 echo 'not equal'
1000 fi
1001
1002 ## STDOUT:
1003 not equal
1004 ## END
1005 ## N-I mksh/zsh/ash/dash stdout-json: ""
1006
1007 #### %10.5(strftime format)T
1008 # The result depends on timezone
1009 export TZ=Asia/Tokyo
1010 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1011 export TZ=US/Eastern
1012 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1013 echo status=$?
1014 ## STDOUT:
1015 [ 2019-]
1016 [ 2019-]
1017 status=0
1018 ## END
1019 ## N-I dash/mksh/zsh/ash STDOUT:
1020 [[status=1
1021 ## END
1022 ## N-I dash STDOUT:
1023 [[status=2
1024 ## END
1025
1026 #### Regression for 'printf x y'
1027 printf x y
1028 printf '%s\n' z
1029 ## STDOUT:
1030 xz
1031 ## END
1032
1033 #### bash truncates long strftime string at 128
1034
1035 case $SH in (ash|dash|mksh|zsh) exit ;; esac
1036
1037 strftime-format() {
1038 local n=$1
1039
1040 # Prints increasingly long format strings:
1041 # %(%Y)T %(%Y)T %(%Y%Y)T ...
1042
1043 echo -n '%('
1044 for i in $(seq $n); do
1045 echo -n '%Y'
1046 done
1047 echo -n ')T'
1048 }
1049
1050 printf $(strftime-format 1) | wc --bytes
1051 printf $(strftime-format 10) | wc --bytes
1052 printf $(strftime-format 30) | wc --bytes
1053 printf $(strftime-format 31) | wc --bytes
1054 printf $(strftime-format 32) | wc --bytes
1055
1056 case $SH in
1057 (*/_bin/cxx-dbg/*)
1058 # Ensure that oils-for-unix detects the truncation of a fixed buffer.
1059 # bash has a buffer of 128.
1060
1061 set +o errexit
1062 (
1063 printf $(strftime-format 1000)
1064 )
1065 status=$?
1066 if test $status -ne 1; then
1067 echo FAIL
1068 fi
1069 ;;
1070 esac
1071
1072 ## STDOUT:
1073 4
1074 40
1075 120
1076 124
1077 0
1078 ## END
1079 ## OK osh STDOUT:
1080 4
1081 40
1082 120
1083 124
1084 128
1085 ## END
1086
1087 ## N-I ash/dash/mksh/zsh STDOUT:
1088 ## END
1089
1090
1091 #### printf with explicit NUL byte
1092 case $SH in (dash|ash) return ;; esac
1093
1094 printf $'x\U0z'
1095
1096 printf $'\U0z'
1097
1098 ## stdout-json: "x"
1099 ## OK zsh stdout-repr: "x\0z\0z"
1100 ## N-I dash/ash stdout-json: ""