diff --git a/stdlib/TOML/src/print.jl b/stdlib/TOML/src/print.jl index 0cbee990d8ae1..059414152f727 100644 --- a/stdlib/TOML/src/print.jl +++ b/stdlib/TOML/src/print.jl @@ -5,6 +5,34 @@ import Dates import Base: @invokelatest import ..isvalid_barekey_char +function print_toml_escaped(io::IO, s::AbstractString) + for c::AbstractChar in s + if !isvalid(c) + error("TOML print: invalid character $(repr(c)) encountered when printing string") + end + if c == '\b' + Base.print(io, '\\', 'b') + elseif c == '\t' + Base.print(io, '\\', 't') + elseif c == '\n' + Base.print(io, '\\', 'n') + elseif c == '\f' + Base.print(io, '\\', 'f') + elseif c == '\r' + Base.print(io, '\\', 'r') + elseif c == '"' + Base.print(io, '\\', '"') + elseif c == '\\' + Base.print(io, "\\", '\\') + elseif Base.iscntrl(c) + Base.print(io, "\\u") + Base.print(io, string(UInt32(c), base=16, pad=4)) + else + Base.print(io, c) + end + end +end + function printkey(io::IO, keys::Vector{String}) for (i, k) in enumerate(keys) i != 1 && Base.print(io, ".") @@ -13,7 +41,9 @@ function printkey(io::IO, keys::Vector{String}) Base.print(io, "\"\"") elseif any(!isvalid_barekey_char, k) # quoted key - Base.print(io, "\"", escape_string(k) ,"\"") + Base.print(io, "\"") + print_toml_escaped(io, k) + Base.print(io, "\"") else Base.print(io, k) end @@ -50,7 +80,11 @@ printvalue(f::MbyFunc, io::IO, value::AbstractFloat; _...) = Base.print(io, isnan(value) ? "nan" : isinf(value) ? string(value > 0 ? "+" : "-", "inf") : Float64(value)) # TOML specifies IEEE 754 binary64 for float -printvalue(f::MbyFunc, io::IO, value::AbstractString; _...) = Base.print(io, "\"", escape_string(value), "\"") +function printvalue(f::MbyFunc, io::IO, value::AbstractString; _...) + Base.print(io, "\"") + print_toml_escaped(io, value) + Base.print(io, "\"") +end is_table(value) = isa(value, AbstractDict) is_array_of_tables(value) = isa(value, AbstractArray) && diff --git a/stdlib/TOML/test/print.jl b/stdlib/TOML/test/print.jl index 10bb48ffae411..e1971ce5d4f92 100644 --- a/stdlib/TOML/test/print.jl +++ b/stdlib/TOML/test/print.jl @@ -60,3 +60,14 @@ end d = TOML.parse(s) @test toml_str(d) == "user = \"me\"\n\n[julia]\n\n[option]\n" end + +@testset "special characters" begin + s = """ + "\U1f355 \0 \x0 \x1 \t \b" = "\U1f355 \0 \x0 \x1 \t \b" + "\x7f" = "\x7f" + """ + @test roundtrip(s) + + d = Dict("str" => string(Char(0xd800))) + @test_throws ErrorException TOML.print(d) +end