1 ## oils_failures_allowed: 1
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 ASCII
745
746 printf '%c\n' a
747 printf '%c\n' ABC
748 printf '%cZ\n' ABC
749
750 ## STDOUT:
751 a
752 A
753 AZ
754 ## END
755
756 #### printf %c unicode - prints the first BYTE of a string - it does not respect UTF-8
757
758 # TODO: in YSH, this should be deprecated
759 case $SH in dash|ash) exit ;; esac
760
761 show_bytes() {
762 od -A n -t x1
763 }
764 twomu=$'\u03bc\u03bc'
765 printf '[%s]\n' "$twomu"
766
767 # Hm this cuts off a UTF-8 character?
768 printf '%c' "$twomu" | show_bytes
769
770 ## STDOUT:
771 [μμ]
772 ce
773 ## END
774 ## N-I dash/ash STDOUT:
775 ## END
776
777 #### printf invalid format
778 printf '%z' 42
779 echo status=$?
780 printf '%-z' 42
781 echo status=$?
782 ## STDOUT:
783 status=1
784 status=1
785 ## END
786 # osh emits parse errors
787 ## OK dash/osh STDOUT:
788 status=2
789 status=2
790 ## END
791
792 #### printf %q
793 x='a b'
794 printf '[%q]\n' "$x"
795 ## STDOUT:
796 ['a b']
797 ## END
798 ## OK bash/zsh STDOUT:
799 [a\ b]
800 ## END
801 ## N-I ash/dash stdout-json: "["
802 ## N-I ash status: 1
803 ## N-I dash status: 2
804
805 #### printf %6q (width)
806 # NOTE: coreutils /usr/bin/printf does NOT implement this %6q !!!
807 x='a b'
808 printf '[%6q]\n' "$x"
809 printf '[%1q]\n' "$x"
810 ## STDOUT:
811 [ 'a b']
812 ['a b']
813 ## END
814 ## OK bash/zsh STDOUT:
815 [ a\ b]
816 [a\ b]
817 ## END
818 ## N-I mksh/ash/dash stdout-json: "[["
819 ## N-I mksh/ash status: 1
820 ## N-I dash status: 2
821
822 #### printf negative numbers
823 printf '[%d] ' -42
824 echo status=$?
825 printf '[%i] ' -42
826 echo status=$?
827
828 # extra LEADING space too
829 printf '[%d] ' ' -42'
830 echo status=$?
831 printf '[%i] ' ' -42'
832 echo status=$?
833
834 # extra TRAILING space too
835 printf '[%d] ' ' -42 '
836 echo status=$?
837 printf '[%i] ' ' -42 '
838 echo status=$?
839
840 # extra TRAILING chars
841 printf '[%d] ' ' -42z'
842 echo status=$?
843 printf '[%i] ' ' -42z'
844 echo status=$?
845
846 exit 0 # ok
847
848 ## STDOUT:
849 [-42] status=0
850 [-42] status=0
851 [-42] status=0
852 [-42] status=0
853 [-42] status=1
854 [-42] status=1
855 [-42] status=1
856 [-42] status=1
857 ## END
858 # zsh is LESS STRICT
859 ## OK zsh 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 [0] status=1
867 [0] status=1
868 ## END
869
870 # osh is like zsh but has a hard failure (TODO: could be an option?)
871 ## OK osh STDOUT:
872 [-42] status=0
873 [-42] status=0
874 [-42] status=0
875 [-42] status=0
876 [-42] status=0
877 [-42] status=0
878 status=1
879 status=1
880 ## END
881
882 # ash is MORE STRICT
883 ## OK ash STDOUT:
884 [-42] status=0
885 [-42] status=0
886 [-42] status=0
887 [-42] status=0
888 [0] status=1
889 [0] status=1
890 [0] status=1
891 [0] status=1
892 ## END
893
894
895 #### printf + and space flags
896 # I didn't know these existed -- I only knew about - and 0 !
897 printf '[%+d]\n' 42
898 printf '[%+d]\n' -42
899 printf '[% d]\n' 42
900 printf '[% d]\n' -42
901 ## STDOUT:
902 [+42]
903 [-42]
904 [ 42]
905 [-42]
906 ## END
907 ## N-I osh stdout-json: ""
908 ## N-I osh status: 2
909
910 #### printf # flag
911 # I didn't know these existed -- I only knew about - and 0 !
912 # Note: '#' flag for integers outputs a prefix ONLY WHEN the value is non-zero
913 printf '[%#o][%#o]\n' 0 42
914 printf '[%#x][%#x]\n' 0 42
915 printf '[%#X][%#X]\n' 0 42
916 echo ---
917 # Note: '#' flag for %f, %g always outputs the decimal point.
918 printf '[%.0f][%#.0f]\n' 3 3
919 # Note: In addition, '#' flag for %g does not omit zeroes in fraction
920 printf '[%g][%#g]\n' 3 3
921 ## STDOUT:
922 [0][052]
923 [0][0x2a]
924 [0][0X2A]
925 ---
926 [3][3.]
927 [3][3.00000]
928 ## END
929 ## N-I osh STDOUT:
930 ---
931 ## END
932 ## N-I osh status: 2
933
934 #### Runtime error for invalid integer
935 x=3abc
936 printf '%d\n' $x
937 echo status=$?
938 printf '%d\n' xyz
939 echo status=$?
940 ## STDOUT:
941 3
942 status=1
943 0
944 status=1
945 ## END
946 # zsh should exit 1 in both cases
947 ## BUG zsh STDOUT:
948 0
949 status=1
950 0
951 status=0
952 ## END
953 # fails but also prints 0 instead of 3abc
954 ## BUG ash STDOUT:
955 0
956 status=1
957 0
958 status=1
959 ## END
960 # osh doesn't print anything invalid
961 ## OK osh STDOUT:
962 status=1
963 status=1
964 ## END
965
966 #### %(strftime format)T
967 # The result depends on timezone
968 export TZ=Asia/Tokyo
969 printf '%(%Y-%m-%d)T\n' 1557978599
970 export TZ=US/Eastern
971 printf '%(%Y-%m-%d)T\n' 1557978599
972 echo status=$?
973 ## STDOUT:
974 2019-05-16
975 2019-05-15
976 status=0
977 ## END
978 ## N-I mksh/zsh/ash STDOUT:
979 status=1
980 ## END
981 ## N-I dash STDOUT:
982 status=2
983 ## END
984
985 #### %(strftime format)T doesn't respect TZ if not exported
986
987 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
988
989 TZ=Portugal # NOT exported
990 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
991
992 # TZ is respected
993 export TZ=Portugal
994 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
995
996 #echo $localtime
997 #echo $tz
998
999 if ! test "$localtime" = "$tz"; then
1000 echo 'not equal'
1001 fi
1002 ## STDOUT:
1003 not equal
1004 ## END
1005 ## N-I mksh/zsh/ash/dash stdout-json: ""
1006
1007 #### %(strftime format)T TZ in environ but not in shell's memory
1008
1009 # note: this test leaks! It assumes that /etc/localtime is NOT Portugal.
1010
1011 # TZ is respected
1012 export TZ=Portugal
1013 tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1014
1015 unset TZ # unset in the shell, but still in the environment
1016
1017 localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1018
1019 if ! test "$localtime" = "$tz"; then
1020 echo 'not equal'
1021 fi
1022
1023 ## STDOUT:
1024 not equal
1025 ## END
1026 ## N-I mksh/zsh/ash/dash stdout-json: ""
1027
1028 #### %10.5(strftime format)T
1029 # The result depends on timezone
1030 export TZ=Asia/Tokyo
1031 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1032 export TZ=US/Eastern
1033 printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1034 echo status=$?
1035 ## STDOUT:
1036 [ 2019-]
1037 [ 2019-]
1038 status=0
1039 ## END
1040 ## N-I mksh/zsh/ash STDOUT:
1041 [[status=1
1042 ## END
1043 ## N-I dash STDOUT:
1044 [[status=2
1045 ## END
1046
1047 #### Regression for 'printf x y'
1048 printf x y
1049 printf '%s\n' z
1050 ## STDOUT:
1051 xz
1052 ## END
1053
1054 #### bash truncates long strftime string at 128
1055
1056 case $SH in ash|dash|mksh|zsh) exit ;; esac
1057
1058 strftime-format() {
1059 local n=$1
1060
1061 # Prints increasingly long format strings:
1062 # %(%Y)T %(%Y)T %(%Y%Y)T ...
1063
1064 echo -n '%('
1065 for i in $(seq $n); do
1066 echo -n '%Y'
1067 done
1068 echo -n ')T'
1069 }
1070
1071 printf $(strftime-format 1) | wc --bytes
1072 printf $(strftime-format 10) | wc --bytes
1073 printf $(strftime-format 30) | wc --bytes
1074 printf $(strftime-format 31) | wc --bytes
1075 printf $(strftime-format 32) | wc --bytes
1076
1077 case $SH in
1078 (*/_bin/cxx-dbg/*)
1079 # Ensure that oils-for-unix detects the truncation of a fixed buffer.
1080 # bash has a buffer of 128.
1081
1082 set +o errexit
1083 (
1084 printf $(strftime-format 1000)
1085 )
1086 status=$?
1087 if test $status -ne 1; then
1088 echo FAIL
1089 fi
1090 ;;
1091 esac
1092
1093 ## STDOUT:
1094 4
1095 40
1096 120
1097 124
1098 0
1099 ## END
1100 ## OK osh STDOUT:
1101 4
1102 40
1103 120
1104 124
1105 128
1106 ## END
1107
1108 ## N-I ash/dash/mksh/zsh STDOUT:
1109 ## END
1110
1111 #### printf positive integer overflow
1112
1113 # %i seems like a synonym for %d
1114
1115 for fmt in '%u\n' '%d\n'; do
1116 # bash considers this in range for %u
1117 # same with mksh
1118 # zsh cuts everything off after 19 digits
1119 # ash truncates everything
1120 printf "$fmt" '18446744073709551615'
1121 echo status=$?
1122 printf "$fmt" '18446744073709551616'
1123 echo status=$?
1124 echo
1125 done
1126
1127 ## STDOUT:
1128 status=1
1129 status=1
1130
1131 status=1
1132 status=1
1133
1134 ## END
1135
1136 ## OK bash status: 0
1137 ## OK bash STDOUT:
1138 18446744073709551615
1139 status=0
1140 18446744073709551615
1141 status=0
1142
1143 9223372036854775807
1144 status=0
1145 9223372036854775807
1146 status=0
1147
1148 ## END
1149
1150 ## OK dash/mksh status: 0
1151 ## OK dash/mksh STDOUT:
1152 18446744073709551615
1153 status=0
1154 18446744073709551615
1155 status=1
1156
1157 9223372036854775807
1158 status=1
1159 9223372036854775807
1160 status=1
1161
1162 ## END
1163
1164 ## BUG ash status: 0
1165 ## BUG ash STDOUT:
1166 18446744073709551615
1167 status=0
1168 0
1169 status=1
1170
1171 0
1172 status=1
1173 0
1174 status=1
1175
1176 ## END
1177
1178 ## BUG zsh status: 0
1179 ## BUG zsh STDOUT:
1180 1844674407370955161
1181 status=0
1182 1844674407370955161
1183 status=0
1184
1185 1844674407370955161
1186 status=0
1187 1844674407370955161
1188 status=0
1189
1190 ## END
1191
1192 #### printf negative integer overflow
1193
1194 # %i seems like a synonym for %d
1195
1196 for fmt in '%u\n' '%d\n'; do
1197
1198 printf "$fmt" '-18446744073709551615'
1199 echo status=$?
1200 printf "$fmt" '-18446744073709551616'
1201 echo status=$?
1202 echo
1203 done
1204
1205 ## STDOUT:
1206 status=1
1207 status=1
1208
1209 status=1
1210 status=1
1211
1212 ## END
1213
1214 ## OK bash status: 0
1215 ## OK bash STDOUT:
1216 1
1217 status=0
1218 18446744073709551615
1219 status=0
1220
1221 -9223372036854775808
1222 status=0
1223 -9223372036854775808
1224 status=0
1225
1226 ## END
1227
1228 ## OK dash/mksh status: 0
1229 ## OK dash/mksh STDOUT:
1230 1
1231 status=0
1232 18446744073709551615
1233 status=1
1234
1235 -9223372036854775808
1236 status=1
1237 -9223372036854775808
1238 status=1
1239
1240 ## END
1241
1242 ## BUG zsh status: 0
1243 ## BUG zsh STDOUT:
1244 16602069666338596455
1245 status=0
1246 16602069666338596455
1247 status=0
1248
1249 -1844674407370955161
1250 status=0
1251 -1844674407370955161
1252 status=0
1253
1254 ## END
1255
1256 ## BUG ash status: 0
1257 ## BUG ash STDOUT:
1258 0
1259 status=1
1260 0
1261 status=1
1262
1263 0
1264 status=1
1265 0
1266 status=1
1267
1268 ## END
1269
1270 #### printf %b does backslash escaping
1271
1272 printf '[%s]\n' '\044' # escapes not evaluated
1273 printf '[%b]\n' '\044' # YES, escapes evaluated
1274 echo
1275
1276 printf '[%s]\n' '\x7e' # escapes not evaluated
1277 printf '[%b]\n' '\x7e' # YES, escapes evaluated
1278 echo
1279
1280 # not a valid escape
1281 printf '[%s]\n' '\A'
1282 printf '[%b]\n' '\A'
1283
1284 ## STDOUT:
1285 [\044]
1286 [$]
1287
1288 [\x7e]
1289 [~]
1290
1291 [\A]
1292 [\A]
1293 ## END
1294
1295 ## N-I dash STDOUT:
1296 [\044]
1297 [$]
1298
1299 [\x7e]
1300 [\x7e]
1301
1302 [\A]
1303 [\A]
1304 ## END
1305
1306 #### printf %b unicode escapes
1307
1308 printf '[%s]\n' '\u03bc' # escapes not evaluated
1309 printf '[%b]\n' '\u03bc' # YES, escapes evaluated
1310
1311 ## STDOUT:
1312 [\u03bc]
1313 [μ]
1314 ## END
1315
1316 ## N-I dash/ash STDOUT:
1317 [\u03bc]
1318 [\u03bc]
1319 ## END
1320
1321 #### printf %b respects \c early return
1322 printf '[%b]\n' 'ab\ncd\cxy'
1323 echo $?
1324 ## STDOUT:
1325 [ab
1326 cd0
1327 ## END
1328
1329
1330 #### printf %b supports octal escapes, both \141 and \0141
1331
1332 printf 'three %b\n' '\141' # di
1333 printf 'four %b\n' '\0141'
1334 echo
1335
1336 # trailing 9
1337 printf '%b\n' '\1419'
1338 printf '%b\n' '\01419'
1339
1340 # Notes:
1341 #
1342 # - echo -e:
1343 # - NO 3 digit octal - echo -e '\141' does not work
1344 # - YES 4 digit octal
1345 # - printf %b
1346 # - YES 3 digit octal
1347 # - YES 4 digit octal
1348 # - printf string (outer)
1349 # - YES 3 digit octal
1350 # - NO 4 digit octal
1351 # - $'' and $PS1
1352 # - YES 3 digit octal
1353 # - NO 4 digit octal
1354
1355 ## STDOUT:
1356 three a
1357 four a
1358
1359 a9
1360 a9
1361 ## END
1362
1363 ## N-I zsh STDOUT:
1364 three \141
1365 four a
1366
1367 \1419
1368 a9
1369 ## END
1370
1371 #### printf %b with truncated octal escapes
1372
1373 # 8 is not a valid octal digit
1374
1375 printf '%b\n' '\558'
1376 printf '%b\n' '\0558'
1377 echo
1378
1379 show_bytes() {
1380 od -A n -t x1
1381 }
1382 printf '%b' '\7' | show_bytes
1383 printf '%b' '\07' | show_bytes
1384 printf '%b' '\007' | show_bytes
1385 printf '%b' '\0007' | show_bytes
1386
1387 ## STDOUT:
1388 -8
1389 -8
1390
1391 07
1392 07
1393 07
1394 07
1395 ## END
1396
1397 ## N-I zsh STDOUT:
1398 \558
1399 -8
1400
1401 5c 37
1402 07
1403 07
1404 07
1405 ## END
1406
1407 #### printf %d %X support hex 0x5 and octal 055
1408
1409 echo hex
1410 printf '%d\n' 0x55
1411 printf '%X\n' 0x55
1412
1413 echo hex CAPS
1414 printf '%d\n' 0X55
1415 printf '%X\n' 0X55
1416
1417 echo octal 3
1418 printf '%d\n' 055
1419 printf '%X\n' 055
1420
1421 echo octal 4
1422 printf '%d\n' 0055
1423 printf '%X\n' 0055
1424
1425 echo octal 5
1426 printf '%d\n' 00055
1427 printf '%X\n' 00055
1428
1429 ## STDOUT:
1430 hex
1431 85
1432 55
1433 hex CAPS
1434 85
1435 55
1436 octal 3
1437 45
1438 2D
1439 octal 4
1440 45
1441 2D
1442 octal 5
1443 45
1444 2D
1445 ## END
1446
1447 ## BUG zsh STDOUT:
1448 hex
1449 85
1450 55
1451 hex CAPS
1452 85
1453 55
1454 octal 3
1455 55
1456 37
1457 octal 4
1458 55
1459 37
1460 octal 5
1461 55
1462 37
1463 ## END
1464
1465 #### printf %d with + prefix (positive sign)
1466
1467 echo decimal
1468 printf '%d\n' +42
1469
1470 echo octal
1471 printf '%d\n' +077
1472
1473 echo hex lowercase
1474 printf '%d\n' +0xab
1475
1476 echo hex uppercase
1477 printf '%d\n' +0XAB
1478
1479 ## STDOUT:
1480 decimal
1481 42
1482 octal
1483 63
1484 hex lowercase
1485 171
1486 hex uppercase
1487 171
1488 ## END
1489
1490 ## BUG zsh STDOUT:
1491 decimal
1492 42
1493 octal
1494 77
1495 hex lowercase
1496 171
1497 hex uppercase
1498 171
1499 ## END
1500
1501 #### leading spaces are accepted in value given to %d %X, but not trailing spaces
1502
1503 case $SH in zsh) exit ;; esac
1504
1505 # leading space is allowed
1506 printf '%d\n' ' -123'
1507 echo status=$?
1508 printf '%d\n' ' -123 '
1509 echo status=$?
1510
1511 echo ---
1512
1513 printf '%d\n' ' +077'
1514 echo status=$?
1515
1516 printf '%d\n' ' +0xff'
1517 echo status=$?
1518
1519 printf '%X\n' ' +0xff'
1520 echo status=$?
1521
1522 printf '%x\n' ' +0xff'
1523 echo status=$?
1524
1525 ## STDOUT:
1526 -123
1527 status=0
1528 -123
1529 status=1
1530 ---
1531 63
1532 status=0
1533 255
1534 status=0
1535 FF
1536 status=0
1537 ff
1538 status=0
1539 ## END
1540
1541 ## OK osh STDOUT:
1542 -123
1543 status=0
1544 status=1
1545 ---
1546 63
1547 status=0
1548 255
1549 status=0
1550 FF
1551 status=0
1552 ff
1553 status=0
1554 ## END
1555
1556 ## BUG ash STDOUT:
1557 -123
1558 status=0
1559 0
1560 status=1
1561 ---
1562 63
1563 status=0
1564 255
1565 status=0
1566 FF
1567 status=0
1568 ff
1569 status=0
1570 ## END
1571
1572 ## BUG-2 zsh STDOUT:
1573 ## END
1574
1575
1576 #### Arbitrary base 64#a is rejected (unlike in shell arithmetic)
1577
1578 printf '%d\n' '64#a'
1579 echo status=$?
1580
1581 # bash, dash, and mksh print 64 and return status 1
1582 # zsh and ash print 0 and return status 1
1583 # OSH rejects it completely (prints nothing) and returns status 1
1584
1585 ## STDOUT:
1586 status=1
1587 ## END
1588
1589 ## OK dash/bash/mksh STDOUT:
1590 64
1591 status=1
1592 ## END
1593
1594 ## OK zsh/ash STDOUT:
1595 0
1596 status=1
1597 ## END