Skip to content

Commit aa5e76a

Browse files
authored
Merge pull request #37785 from JuliaLang/jq/37784
Fix escaped % printing in new Printf code
2 parents 4ccfd37 + d2f65db commit aa5e76a

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

stdlib/Printf/src/Printf.jl

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -80,10 +80,19 @@ function Format(f::AbstractString)
8080
len = length(bytes)
8181
pos = 1
8282
b = 0x00
83-
while true
83+
while pos <= len
8484
b = bytes[pos]
8585
pos += 1
86-
(pos > len || (b == UInt8('%') && pos <= len && bytes[pos] != UInt8('%'))) && break
86+
if b == UInt8('%')
87+
pos > len && throw(ArgumentError("invalid format string: '$f'"))
88+
if bytes[pos] == UInt8('%')
89+
# escaped '%'
90+
b = bytes[pos]
91+
pos += 1
92+
else
93+
break
94+
end
95+
end
8796
end
8897
strs = [1:pos - 1 - (b == UInt8('%'))]
8998
fmts = []
@@ -645,9 +654,16 @@ const UNROLL_UPTO = 16
645654
# if you have your own buffer + pos, write formatted args directly to it
646655
@inline function format(buf::Vector{UInt8}, pos::Integer, f::Format, args...)
647656
# write out first substring
657+
escapechar = false
648658
for i in f.substringranges[1]
649-
buf[pos] = f.str[i]
650-
pos += 1
659+
b = f.str[i]
660+
if !escapechar
661+
buf[pos] = b
662+
pos += 1
663+
escapechar = b === UInt8('%')
664+
else
665+
escapechar = false
666+
end
651667
end
652668
# for each format, write out arg and next substring
653669
# unroll up to 16 formats
@@ -656,17 +672,29 @@ const UNROLL_UPTO = 16
656672
if N >= i
657673
pos = fmt(buf, pos, args[i], f.formats[i])
658674
for j in f.substringranges[i + 1]
659-
buf[pos] = f.str[j]
660-
pos += 1
675+
b = f.str[j]
676+
if !escapechar
677+
buf[pos] = b
678+
pos += 1
679+
escapechar = b === UInt8('%')
680+
else
681+
escapechar = false
682+
end
661683
end
662684
end
663685
end
664686
if N > 16
665687
for i = 17:length(f.formats)
666688
pos = fmt(buf, pos, args[i], f.formats[i])
667689
for j in f.substringranges[i + 1]
668-
buf[pos] = f.str[j]
669-
pos += 1
690+
b = f.str[j]
691+
if !escapechar
692+
buf[pos] = b
693+
pos += 1
694+
escapechar = b === UInt8('%')
695+
else
696+
escapechar = false
697+
end
670698
end
671699
end
672700
end

stdlib/Printf/test/runtests.jl

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,12 @@ end
303303
@testset "basics" begin
304304

305305
@test Printf.@sprintf("%%") == "%"
306+
@test Printf.@sprintf("1%%") == "1%"
307+
@test Printf.@sprintf("%%1") == "%1"
308+
@test Printf.@sprintf("1%%2") == "1%2"
309+
@test Printf.@sprintf("1%%%d", 2) == "1%2"
310+
@test Printf.@sprintf("1%%2%%3") == "1%2%3"
311+
@test Printf.@sprintf("GAP[%%]") == "GAP[%]"
306312
@test Printf.@sprintf("hey there") == "hey there"
307313
@test_throws ArgumentError Printf.Format("")
308314
@test_throws ArgumentError Printf.Format("%+")
@@ -421,7 +427,7 @@ end
421427

422428
# escaped '%'
423429
@test_throws ArgumentError @sprintf("%s%%%s", "a")
424-
@test @sprintf("%s%%%s", "a", "b") == "a%%b"
430+
@test @sprintf("%s%%%s", "a", "b") == "a%b"
425431

426432
# print float as %d uses round(x)
427433
@test @sprintf("%d", 25.5) == "26"

0 commit comments

Comments
 (0)