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 printf '[%o]\n' -42
599 printf '[%x]\n' -42
600 printf '[%X]\n' -42
601 ## STDOUT:
602 [18446744073709551574]
603 [1777777777777777777726]
604 [ffffffffffffffd6]
605 [FFFFFFFFFFFFFFD6]
606 ## END
607
608 # osh DISALLOWS this because the output depends on the machine architecture.
609 ## N-I osh stdout-json: ""
610 ## N-I osh status: 1
611
612 #### printf floating point (not required, but they all implement it)
613 printf '[%f]\n' 3.14159
614 printf '[%.2f]\n' 3.14159
615 printf '[%8.2f]\n' 3.14159
616 printf '[%-8.2f]\n' 3.14159
617 printf '[%-f]\n' 3.14159
618 printf '[%-f]\n' 3.14
619 ## STDOUT:
620 [3.141590]
621 [3.14]
622 [ 3.14]
623 [3.14 ]
624 [3.141590]
625 [3.140000]
626 ## END
627 ## N-I osh stdout-json: ""
628 ## N-I osh status: 2
629
630 #### printf floating point with - and 0
631 printf '[%8.4f]\n' 3.14
632 printf '[%08.4f]\n' 3.14
633 printf '[%8.04f]\n' 3.14 # meaning less 0
634 printf '[%08.04f]\n' 3.14
635 echo ---
636 # these all boil down to the same thing. The -, 8, and 4 are respected, but
637 # none of the 0 are.
638 printf '[%-8.4f]\n' 3.14
639 printf '[%-08.4f]\n' 3.14
640 printf '[%-8.04f]\n' 3.14
641 printf '[%-08.04f]\n' 3.14
642 ## STDOUT:
643 [ 3.1400]
644 [003.1400]
645 [ 3.1400]
646 [003.1400]
647 ---
648 [3.1400 ]
649 [3.1400 ]
650 [3.1400 ]
651 [3.1400 ]
652 ## END
653 ## N-I osh STDOUT:
654 ---
655 ## END
656 ## N-I osh status: 2
657
658 #### printf eE fF gG
659 printf '[%e]\n' 3.14
660 printf '[%E]\n' 3.14
661 printf '[%f]\n' 3.14
662 # bash is the only one that implements %F? Is it a synonym?
663 #printf '[%F]\n' 3.14
664 printf '[%g]\n' 3.14
665 printf '[%G]\n' 3.14
666 ## STDOUT:
667 [3.140000e+00]
668 [3.140000E+00]
669 [3.140000]
670 [3.14]
671 [3.14]
672 ## END
673 ## N-I osh stdout-json: ""
674 ## N-I osh status: 2
675
676 #### printf backslash escapes
677 argv.py "$(printf 'a\tb')"
678 argv.py "$(printf '\xE2\x98\xA0')"
679 argv.py "$(printf '\044e')"
680 argv.py "$(printf '\0377')" # out of range
681 ## STDOUT:
682 ['a\tb']
683 ['\xe2\x98\xa0']
684 ['$e']
685 ['\x1f7']
686 ## END
687 ## N-I dash STDOUT:
688 ['a\tb']
689 ['\\xE2\\x98\\xA0']
690 ['$e']
691 ['\x1f7']
692 ## END
693
694 #### printf octal backslash escapes
695 argv.py "$(printf '\0377')"
696 argv.py "$(printf '\377')"
697 ## STDOUT:
698 ['\x1f7']
699 ['\xff']
700 ## END
701
702 #### printf unicode backslash escapes
703 argv.py "$(printf '\u2620')"
704 argv.py "$(printf '\U0000065f')"
705 ## STDOUT:
706 ['\xe2\x98\xa0']
707 ['\xd9\x9f']
708 ## END
709 ## N-I dash/ash STDOUT:
710 ['\\u2620']
711 ['\\U0000065f']
712 ## END
713
714 #### printf invalid backslash escape (is ignored)
715 printf '[\Z]\n'
716 ## STDOUT:
717 [\Z]
718 ## END
719
720 #### printf % escapes
721 printf '[%%]\n'
722 ## STDOUT:
723 [%]
724 ## END
725
726 #### printf %b backslash escaping
727 printf '[%s]\n' '\044' # escapes not evaluated
728 printf '[%b]\n' '\044' # YES, escapes evaluated
729 echo status=$?
730 ## STDOUT:
731 [\044]
732 [$]
733 status=0
734 ## END
735
736 #### printf %b with \c early return
737 printf '[%b]\n' 'ab\ncd\cxy'
738 echo $?
739 ## STDOUT:
740 [ab
741 cd0
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 dash/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
1100 #### printf with explicit NUL byte
1101 case $SH in (dash|ash) return ;; esac
1102
1103 printf $'x\U0z'
1104
1105 printf $'\U0z'
1106
1107 ## stdout-json: "x"
1108 ## OK zsh stdout-repr: "x\0z\0z"
1109 ## N-I dash/ash stdout-json: ""