OILS / spec / builtin-printf.test.sh View on Github | oils.pub

1597 lines, 873 significant
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
19printf
20## status: 2
21## OK mksh/zsh status: 1
22## stdout-json: ""
23
24#### printf -v %s
25var=foo
26printf -v $var %s 'hello there'
27argv.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
39val='"quoted" with spaces and \'
40
41# quote 'val' and store it in foo
42printf -v foo %q "$val"
43# then round trip back to eval
44eval "bar=$foo"
45
46# debugging:
47#echo foo="$foo"
48#echo bar="$bar"
49#echo val="$val"
50
51test "$bar" = "$val" && echo OK
52## STDOUT:
53OK
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]
61a=(a b c)
62printf -v 'a[1]' %s 'foo'
63echo status=$?
64argv.py "${a[@]}"
65## STDOUT:
66status=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
77printf -v 'a[' %s 'foo'
78echo status=$?
79## STDOUT:
80status=2
81## END
82## N-I ash/mksh/zsh stdout: -vstatus=0
83
84#### dynamic declare instead of %s
85var=foo
86declare $var='hello there'
87argv.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
96var=foo
97val='"quoted" with spaces and \'
98# I think this is bash 4.4 only.
99declare $var="${val@Q}"
100echo "$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
116case $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}
120dollar='dollar'
121f() {
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}
128echo dollar=$dollar
129echo --
130f
131echo --
132echo dollar=$dollar
133echo mylocal=$mylocal
134## STDOUT:
135dollar=dollar
136--
137dollar=\$
138mylocal=mylocal
139--
140dollar=\$
141mylocal=
142## END
143## OK osh STDOUT:
144dollar=dollar
145--
146dollar='$'
147mylocal=mylocal
148--
149dollar='$'
150mylocal=
151## END
152## N-I dash/ash/mksh/zsh STDOUT:
153not implemented
154## END
155
156#### printf with too few arguments
157printf -- '-%s-%s-%s-\n' 'a b' 'x y'
158## STDOUT:
159-a b-x y--
160## END
161
162#### printf with too many arguments
163printf -- '-%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
171printf '[%5s]\n' abc
172printf '[%-5s]\n' abc
173## STDOUT:
174[ abc]
175[abc ]
176## END
177
178#### printf integer
179printf '%d\n' 42
180printf '%i\n' 42 # synonym
181printf '%d\n' \'a # if first character is a quote, use character code
182printf '%d\n' \"a # double quotes work too
183printf '[%5d]\n' 42
184printf '[%-5d]\n' 42
185printf '[%05d]\n' 42
186#printf '[%-05d]\n' 42 # the leading 0 is meaningless
187#[42 ]
188## STDOUT:
18942
19042
19197
19297
193[ 42]
194[42 ]
195[00042]
196## END
197
198#### printf %6.4d -- "precision" does padding for integers
199printf '[%6.4d]\n' 42
200printf '[%.4d]\n' 42
201printf '[%6.d]\n' 42
202echo --
203printf '[%6.4d]\n' -42
204printf '[%.4d]\n' -42
205printf '[%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
217printf '[%6.4x]\n' 42
218printf '[%.4x]\n' 42
219printf '[%6.x]\n' 42
220echo --
221printf '[%6.4X]\n' 42
222printf '[%.4X]\n' 42
223printf '[%6.X]\n' 42
224echo --
225printf '[%6.4o]\n' 42
226printf '[%.4o]\n' 42
227printf '[%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
243printf '[%06d]\n' 42
244printf '[%06d]\n' -42 # 6 TOTAL
245echo --
246printf '[%6.6d]\n' 42
247printf '[%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
257printf '[%06x]\n' 42
258printf '[%06X]\n' 42
259printf '[%06o]\n' 42
260## STDOUT:
261[00002a]
262[00002A]
263[000052]
264## END
265
266#### %06s is no-op
267printf '(%6s)\n' 42
268printf '(%6s)\n' -42
269printf '(%06s)\n' 42
270printf '(%06s)\n' -42
271echo status=$?
272## STDOUT:
273( 42)
274( -42)
275( 42)
276( -42)
277status=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
287printf '[%6s]\n' foo
288printf '[%6.4s]\n' foo
289printf '[%-6.4s]\n' foo
290printf '[%6s]\n' spam-eggs
291printf '[%6.4s]\n' spam-eggs
292printf '[%-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
303printf '[%6.0s]\n' foo
304printf '[%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
313printf '[%6.s]\n' foo
314printf '[%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)
327printf '[%*s]\n' 9 hello
328printf '[%.*s]\n' 3 hello
329printf '[%*.3s]\n' 9 hello
330printf '[%9.*s]\n' 3 hello
331printf '[%*.*s]\n' 9 3 hello
332## STDOUT:
333[ hello]
334[hel]
335[ hel]
336[ hel]
337[ hel]
338## END
339
340#### unsigned / octal / hex
341printf '[%u]\n' 42
342printf '[%o]\n' 42
343printf '[%x]\n' 42
344printf '[%X]\n' 42
345echo
346
347printf '[%X]\n' \'a # if first character is a quote, use character code
348printf '[%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
362for 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
368done
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)
397printf '%d\n' ''
398## OK osh stdout-json: ""
399## OK osh status: 1
400## OK ash status: 1
401## STDOUT:
4020
403## END
404
405#### No char after ' => zero code point
406
407# most shells use 0 here
408printf '%d\n' \'
409printf '%d\n' \"
410
411## OK mksh status: 1
412## STDOUT:
4130
4140
415## END
416
417#### Unicode char with '
418case $SH in mksh) echo 'weird bug'; exit ;; esac
419
420# the mu character is U+03BC
421
422printf '%x\n' \'μ
423printf '%u\n' \'μ
424printf '%o\n' \'μ
425echo
426
427u3=三
428# u4=😘
429
430printf '%x\n' \'$u3
431printf '%u\n' \'$u3
432printf '%o\n' \'$u3
433echo
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:
4763bc
477956
4781674
479
4804e09
48119977
48247011
483
484## END
485## BUG dash/ash STDOUT:
486ce
487206
488316
489
490e4
491228
492344
493
494## END
495
496## BUG mksh STDOUT:
497weird bug
498## END
499
500#### Invalid UTF-8
501
502echo bytes1
503not_utf8=$(python2 -c 'print("\xce\xce")')
504
505printf '%x\n' \'$not_utf8
506printf '%u\n' \'$not_utf8
507printf '%o\n' \'$not_utf8
508echo
509
510echo bytes2
511not_utf8=$(python2 -c 'print("\xbc\xbc")')
512printf '%x\n' \'$not_utf8
513printf '%u\n' \'$not_utf8
514printf '%o\n' \'$not_utf8
515echo
516
517# Copied from data_lang/utf8_test.cc
518
519echo overlong2
520overlong2=$(python2 -c 'print("\xC1\x81")')
521printf '%x\n' \'$overlong2
522printf '%u\n' \'$overlong2
523printf '%o\n' \'$overlong2
524echo
525
526echo overlong3
527overlong3=$(python2 -c 'print("\xE0\x81\x81")')
528printf '%x\n' \'$overlong3
529printf '%u\n' \'$overlong3
530printf '%o\n' \'$overlong3
531echo
532
533## STDOUT:
534bytes1
535ce
536206
537316
538
539bytes2
540bc
541188
542274
543
544overlong2
545c1
546193
547301
548
549overlong3
550e0
551224
552340
553
554## END
555
556
557#### Too large
558case $SH in mksh) echo 'weird bug'; exit ;; esac
559
560echo too large
561too_large=$(python2 -c 'print("\xF4\x91\x84\x91")')
562printf '%x\n' \'$too_large
563printf '%u\n' \'$too_large
564printf '%o\n' \'$too_large
565echo
566
567## STDOUT:
568too large
569111111
5701118481
5714210421
572
573## END
574
575## BUG dash/ash STDOUT:
576too large
577f4
578244
579364
580
581## END
582
583## BUG mksh STDOUT:
584weird bug
585## END
586
587# osh rejects code points that are too large for a DIFFERENT reason
588
589## OK osh STDOUT:
590too large
591f4
592244
593364
594
595## END
596
597
598#### negative numbers with unsigned / octal / hex
599printf '[%u]\n' -42
600echo status=$?
601
602printf '[%o]\n' -42
603echo status=$?
604
605printf '[%x]\n' -42
606echo status=$?
607
608printf '[%X]\n' -42
609echo status=$?
610
611## STDOUT:
612[18446744073709551574]
613status=0
614[1777777777777777777726]
615status=0
616[ffffffffffffffd6]
617status=0
618[FFFFFFFFFFFFFFD6]
619status=0
620## END
621
622# osh DISALLOWS this because the output depends on the machine architecture.
623## N-I osh STDOUT:
624status=1
625status=1
626status=1
627status=1
628## END
629
630#### printf floating point (not required, but they all implement it)
631printf '[%f]\n' 3.14159
632printf '[%.2f]\n' 3.14159
633printf '[%8.2f]\n' 3.14159
634printf '[%-8.2f]\n' 3.14159
635printf '[%-f]\n' 3.14159
636printf '[%-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
649printf '[%8.4f]\n' 3.14
650printf '[%08.4f]\n' 3.14
651printf '[%8.04f]\n' 3.14 # meaning less 0
652printf '[%08.04f]\n' 3.14
653echo ---
654# these all boil down to the same thing. The -, 8, and 4 are respected, but
655# none of the 0 are.
656printf '[%-8.4f]\n' 3.14
657printf '[%-08.4f]\n' 3.14
658printf '[%-8.04f]\n' 3.14
659printf '[%-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
677printf '[%e]\n' 3.14
678printf '[%E]\n' 3.14
679printf '[%f]\n' 3.14
680# bash is the only one that implements %F? Is it a synonym?
681#printf '[%F]\n' 3.14
682printf '[%g]\n' 3.14
683printf '[%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
695argv.py "$(printf 'a\tb')"
696argv.py "$(printf '\xE2\x98\xA0')"
697argv.py "$(printf '\044e')"
698argv.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
713argv.py "$(printf '\0377')"
714argv.py "$(printf '\377')"
715## STDOUT:
716['\x1f7']
717['\xff']
718## END
719
720#### printf unicode backslash escapes
721argv.py "$(printf '\u2620')"
722argv.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)
733printf '[\Z]\n'
734## STDOUT:
735[\Z]
736## END
737
738#### printf % escapes
739printf '[%%]\n'
740## STDOUT:
741[%]
742## END
743
744#### printf %c ASCII
745
746printf '%c\n' a
747printf '%c\n' ABC
748printf '%cZ\n' ABC
749
750## STDOUT:
751a
752A
753AZ
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
759case $SH in dash|ash) exit ;; esac
760
761show_bytes() {
762 od -A n -t x1
763}
764twomu=$'\u03bc\u03bc'
765printf '[%s]\n' "$twomu"
766
767# Hm this cuts off a UTF-8 character?
768printf '%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
778printf '%z' 42
779echo status=$?
780printf '%-z' 42
781echo status=$?
782## STDOUT:
783status=1
784status=1
785## END
786# osh emits parse errors
787## OK dash/osh STDOUT:
788status=2
789status=2
790## END
791
792#### printf %q
793x='a b'
794printf '[%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 !!!
807x='a b'
808printf '[%6q]\n' "$x"
809printf '[%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
823printf '[%d] ' -42
824echo status=$?
825printf '[%i] ' -42
826echo status=$?
827
828# extra LEADING space too
829printf '[%d] ' ' -42'
830echo status=$?
831printf '[%i] ' ' -42'
832echo status=$?
833
834# extra TRAILING space too
835printf '[%d] ' ' -42 '
836echo status=$?
837printf '[%i] ' ' -42 '
838echo status=$?
839
840# extra TRAILING chars
841printf '[%d] ' ' -42z'
842echo status=$?
843printf '[%i] ' ' -42z'
844echo status=$?
845
846exit 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
878status=1
879status=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 !
897printf '[%+d]\n' 42
898printf '[%+d]\n' -42
899printf '[% d]\n' 42
900printf '[% 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
913printf '[%#o][%#o]\n' 0 42
914printf '[%#x][%#x]\n' 0 42
915printf '[%#X][%#X]\n' 0 42
916echo ---
917# Note: '#' flag for %f, %g always outputs the decimal point.
918printf '[%.0f][%#.0f]\n' 3 3
919# Note: In addition, '#' flag for %g does not omit zeroes in fraction
920printf '[%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
935x=3abc
936printf '%d\n' $x
937echo status=$?
938printf '%d\n' xyz
939echo status=$?
940## STDOUT:
9413
942status=1
9430
944status=1
945## END
946# zsh should exit 1 in both cases
947## BUG zsh STDOUT:
9480
949status=1
9500
951status=0
952## END
953# fails but also prints 0 instead of 3abc
954## BUG ash STDOUT:
9550
956status=1
9570
958status=1
959## END
960# osh doesn't print anything invalid
961## OK osh STDOUT:
962status=1
963status=1
964## END
965
966#### %(strftime format)T
967# The result depends on timezone
968export TZ=Asia/Tokyo
969printf '%(%Y-%m-%d)T\n' 1557978599
970export TZ=US/Eastern
971printf '%(%Y-%m-%d)T\n' 1557978599
972echo status=$?
973## STDOUT:
9742019-05-16
9752019-05-15
976status=0
977## END
978## N-I mksh/zsh/ash STDOUT:
979status=1
980## END
981## N-I dash STDOUT:
982status=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
989TZ=Portugal # NOT exported
990localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
991
992# TZ is respected
993export TZ=Portugal
994tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
995
996#echo $localtime
997#echo $tz
998
999if ! test "$localtime" = "$tz"; then
1000 echo 'not equal'
1001fi
1002## STDOUT:
1003not 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
1012export TZ=Portugal
1013tz=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1014
1015unset TZ # unset in the shell, but still in the environment
1016
1017localtime=$(printf '%(%Y-%m-%d %H:%M:%S)T\n' 1557978599)
1018
1019if ! test "$localtime" = "$tz"; then
1020 echo 'not equal'
1021fi
1022
1023## STDOUT:
1024not 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
1030export TZ=Asia/Tokyo
1031printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1032export TZ=US/Eastern
1033printf '[%10.5(%Y-%m-%d)T]\n' 1557978599
1034echo status=$?
1035## STDOUT:
1036[ 2019-]
1037[ 2019-]
1038status=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'
1048printf x y
1049printf '%s\n' z
1050## STDOUT:
1051xz
1052## END
1053
1054#### bash truncates long strftime string at 128
1055
1056case $SH in ash|dash|mksh|zsh) exit ;; esac
1057
1058strftime-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
1071printf $(strftime-format 1) | wc --bytes
1072printf $(strftime-format 10) | wc --bytes
1073printf $(strftime-format 30) | wc --bytes
1074printf $(strftime-format 31) | wc --bytes
1075printf $(strftime-format 32) | wc --bytes
1076
1077case $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 ;;
1091esac
1092
1093## STDOUT:
10944
109540
1096120
1097124
10980
1099## END
1100## OK osh STDOUT:
11014
110240
1103120
1104124
1105128
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
1115for 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
1125done
1126
1127## STDOUT:
1128status=1
1129status=1
1130
1131status=1
1132status=1
1133
1134## END
1135
1136## OK bash status: 0
1137## OK bash STDOUT:
113818446744073709551615
1139status=0
114018446744073709551615
1141status=0
1142
11439223372036854775807
1144status=0
11459223372036854775807
1146status=0
1147
1148## END
1149
1150## OK dash/mksh status: 0
1151## OK dash/mksh STDOUT:
115218446744073709551615
1153status=0
115418446744073709551615
1155status=1
1156
11579223372036854775807
1158status=1
11599223372036854775807
1160status=1
1161
1162## END
1163
1164## BUG ash status: 0
1165## BUG ash STDOUT:
116618446744073709551615
1167status=0
11680
1169status=1
1170
11710
1172status=1
11730
1174status=1
1175
1176## END
1177
1178## BUG zsh status: 0
1179## BUG zsh STDOUT:
11801844674407370955161
1181status=0
11821844674407370955161
1183status=0
1184
11851844674407370955161
1186status=0
11871844674407370955161
1188status=0
1189
1190## END
1191
1192#### printf negative integer overflow
1193
1194# %i seems like a synonym for %d
1195
1196for fmt in '%u\n' '%d\n'; do
1197
1198 printf "$fmt" '-18446744073709551615'
1199 echo status=$?
1200 printf "$fmt" '-18446744073709551616'
1201 echo status=$?
1202 echo
1203done
1204
1205## STDOUT:
1206status=1
1207status=1
1208
1209status=1
1210status=1
1211
1212## END
1213
1214## OK bash status: 0
1215## OK bash STDOUT:
12161
1217status=0
121818446744073709551615
1219status=0
1220
1221-9223372036854775808
1222status=0
1223-9223372036854775808
1224status=0
1225
1226## END
1227
1228## OK dash/mksh status: 0
1229## OK dash/mksh STDOUT:
12301
1231status=0
123218446744073709551615
1233status=1
1234
1235-9223372036854775808
1236status=1
1237-9223372036854775808
1238status=1
1239
1240## END
1241
1242## BUG zsh status: 0
1243## BUG zsh STDOUT:
124416602069666338596455
1245status=0
124616602069666338596455
1247status=0
1248
1249-1844674407370955161
1250status=0
1251-1844674407370955161
1252status=0
1253
1254## END
1255
1256## BUG ash status: 0
1257## BUG ash STDOUT:
12580
1259status=1
12600
1261status=1
1262
12630
1264status=1
12650
1266status=1
1267
1268## END
1269
1270#### printf %b does backslash escaping
1271
1272printf '[%s]\n' '\044' # escapes not evaluated
1273printf '[%b]\n' '\044' # YES, escapes evaluated
1274echo
1275
1276printf '[%s]\n' '\x7e' # escapes not evaluated
1277printf '[%b]\n' '\x7e' # YES, escapes evaluated
1278echo
1279
1280# not a valid escape
1281printf '[%s]\n' '\A'
1282printf '[%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
1308printf '[%s]\n' '\u03bc' # escapes not evaluated
1309printf '[%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
1322printf '[%b]\n' 'ab\ncd\cxy'
1323echo $?
1324## STDOUT:
1325[ab
1326cd0
1327## END
1328
1329
1330#### printf %b supports octal escapes, both \141 and \0141
1331
1332printf 'three %b\n' '\141' # di
1333printf 'four %b\n' '\0141'
1334echo
1335
1336# trailing 9
1337printf '%b\n' '\1419'
1338printf '%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:
1356three a
1357four a
1358
1359a9
1360a9
1361## END
1362
1363## N-I zsh STDOUT:
1364three \141
1365four a
1366
1367\1419
1368a9
1369## END
1370
1371#### printf %b with truncated octal escapes
1372
1373# 8 is not a valid octal digit
1374
1375printf '%b\n' '\558'
1376printf '%b\n' '\0558'
1377echo
1378
1379show_bytes() {
1380 od -A n -t x1
1381}
1382printf '%b' '\7' | show_bytes
1383printf '%b' '\07' | show_bytes
1384printf '%b' '\007' | show_bytes
1385printf '%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
1409echo hex
1410printf '%d\n' 0x55
1411printf '%X\n' 0x55
1412
1413echo hex CAPS
1414printf '%d\n' 0X55
1415printf '%X\n' 0X55
1416
1417echo octal 3
1418printf '%d\n' 055
1419printf '%X\n' 055
1420
1421echo octal 4
1422printf '%d\n' 0055
1423printf '%X\n' 0055
1424
1425echo octal 5
1426printf '%d\n' 00055
1427printf '%X\n' 00055
1428
1429## STDOUT:
1430hex
143185
143255
1433hex CAPS
143485
143555
1436octal 3
143745
14382D
1439octal 4
144045
14412D
1442octal 5
144345
14442D
1445## END
1446
1447## BUG zsh STDOUT:
1448hex
144985
145055
1451hex CAPS
145285
145355
1454octal 3
145555
145637
1457octal 4
145855
145937
1460octal 5
146155
146237
1463## END
1464
1465#### printf %d with + prefix (positive sign)
1466
1467echo decimal
1468printf '%d\n' +42
1469
1470echo octal
1471printf '%d\n' +077
1472
1473echo hex lowercase
1474printf '%d\n' +0xab
1475
1476echo hex uppercase
1477printf '%d\n' +0XAB
1478
1479## STDOUT:
1480decimal
148142
1482octal
148363
1484hex lowercase
1485171
1486hex uppercase
1487171
1488## END
1489
1490## BUG zsh STDOUT:
1491decimal
149242
1493octal
149477
1495hex lowercase
1496171
1497hex uppercase
1498171
1499## END
1500
1501#### leading spaces are accepted in value given to %d %X, but not trailing spaces
1502
1503case $SH in zsh) exit ;; esac
1504
1505# leading space is allowed
1506printf '%d\n' ' -123'
1507echo status=$?
1508printf '%d\n' ' -123 '
1509echo status=$?
1510
1511echo ---
1512
1513printf '%d\n' ' +077'
1514echo status=$?
1515
1516printf '%d\n' ' +0xff'
1517echo status=$?
1518
1519printf '%X\n' ' +0xff'
1520echo status=$?
1521
1522printf '%x\n' ' +0xff'
1523echo status=$?
1524
1525## STDOUT:
1526-123
1527status=0
1528-123
1529status=1
1530---
153163
1532status=0
1533255
1534status=0
1535FF
1536status=0
1537ff
1538status=0
1539## END
1540
1541## OK osh STDOUT:
1542-123
1543status=0
1544status=1
1545---
154663
1547status=0
1548255
1549status=0
1550FF
1551status=0
1552ff
1553status=0
1554## END
1555
1556## BUG ash STDOUT:
1557-123
1558status=0
15590
1560status=1
1561---
156263
1563status=0
1564255
1565status=0
1566FF
1567status=0
1568ff
1569status=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
1578printf '%d\n' '64#a'
1579echo 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:
1586status=1
1587## END
1588
1589## OK dash/bash/mksh STDOUT:
159064
1591status=1
1592## END
1593
1594## OK zsh/ash STDOUT:
15950
1596status=1
1597## END