Comparison

util/format.lua @ 12032:3db09eb4c43b

util.format: Ensure sanitation of strings passed to wrong format Ie. log("debug", "%d", "\1\2\3") should not result in garbage. Also optimizing for the common case of ASCII string passed to %s and early returns everywhere. Returning nil from a gsub callback keeps the original substring.
author Kim Alvefur <zash@zash.se>
date Sat, 11 Dec 2021 13:30:34 +0100
parent 12031:87bc26f23d9b
child 12033:161f8268c4b3
comparison
equal deleted inserted replaced
12031:87bc26f23d9b 12032:3db09eb4c43b
47 47
48 48
49 -- process each format specifier 49 -- process each format specifier
50 local i = 0; 50 local i = 0;
51 formatstring = formatstring:gsub("%%[^cdiouxXaAeEfgGqs%%]*[cdiouxXaAeEfgGqs%%]", function(spec) 51 formatstring = formatstring:gsub("%%[^cdiouxXaAeEfgGqs%%]*[cdiouxXaAeEfgGqs%%]", function(spec)
52 if spec ~= "%%" then 52 if spec == "%%" then return end
53 i = i + 1; 53 i = i + 1;
54 local arg = args[i]; 54 local arg = args[i];
55 55
56 local option = spec:sub(-1); 56 if arg == nil then
57 if arg == nil then 57 args[i] = "nil";
58 args[i] = "nil"; 58 return "(%s)";
59 spec = "(%s)"; 59 end
60 elseif option == "q" then 60
61 args[i] = dump(arg); 61 local option = spec:sub(-1);
62 spec = "%s"; 62 local t = type(arg);
63 elseif option == "s" then 63
64 if option == "s" and t == "string" and not arg:find("[%z\1-\31\128-\255]") then
65 -- No UTF-8 or control characters, assumed to be the common case.
66 return
67 end
68
69 if option ~= "s" and option ~= "q" then
70 -- all other options expect numbers
71 if t ~= "number" then
72 -- arg isn't number as expected?
64 arg = tostring(arg); 73 arg = tostring(arg);
65 if arg:find("[\128-\255]") and not valid_utf8(arg) then
66 args[i] = dump(arg);
67 else
68 args[i] = arg:gsub("[%z\1-\8\11-\31\127]", control_symbols):gsub("\n\t?", "\n\t");
69 end
70 elseif type(arg) ~= "number" then -- arg isn't number as expected?
71 args[i] = tostring(arg);
72 spec = "[%s]";
73 option = "s"; 74 option = "s";
74 spec = "[%s]"; 75 spec = "[%s]";
75 t = "string"; 76 t = "string";
76 elseif expects_integer[option] and num_type(arg) ~= "integer" then 77 elseif expects_integer[option] and num_type(arg) ~= "integer" then
77 args[i] = tostring(arg); 78 args[i] = tostring(arg);
78 spec = "[%s]"; 79 return "[%s]";
80 else
81 return -- acceptable number
79 end 82 end
80 end 83 end
81 return spec; 84
85 if t == "string" then
86 if not valid_utf8(arg) then
87 option = "q";
88 else
89 args[i] = arg:gsub("[%z\1-\8\11-\31\127]", control_symbols):gsub("\n\t?", "\n\t");
90 return spec;
91 end
92 end
93
94 if option == "q" then
95 args[i] = dump(arg);
96 return "%s";
97 end
82 end); 98 end);
83 99
84 -- process extra args 100 -- process extra args
85 while i < args_length do 101 while i < args_length do
86 i = i + 1; 102 i = i + 1;