Unescape strings when parsing desktop entries (#2043)
* Unescape strings when parsing desktop entries * Fix unused value warning in menubar/utils.lua * Move menubar.utils.unescape() tests * Clean up menubar.utils.unescape() function * Fix warning for using "_" in a non-local context * Do not ignore trailing whitespace in menubar.utils.parse_list()
This commit is contained in:
parent
55dee1ea87
commit
8d34201ec3
|
@ -41,6 +41,23 @@ local default_icon = nil
|
||||||
--- Name of the WM for the OnlyShownIn entry in the .desktop file.
|
--- Name of the WM for the OnlyShownIn entry in the .desktop file.
|
||||||
utils.wm_name = "awesome"
|
utils.wm_name = "awesome"
|
||||||
|
|
||||||
|
--- Possible escape characters (not including the preceding backslash) in
|
||||||
|
--.desktop files and their equates.
|
||||||
|
local escape_sequences = {
|
||||||
|
[ [[\\]] ] = [[\]],
|
||||||
|
[ [[\n]] ] = '\n',
|
||||||
|
[ [[\r]] ] = '\r',
|
||||||
|
[ [[\s]] ] = ' ',
|
||||||
|
[ [[\t]] ] = '\t',
|
||||||
|
}
|
||||||
|
|
||||||
|
--- The keys in desktop entries whose values are lists of strings.
|
||||||
|
local list_keys = {
|
||||||
|
Categories = true,
|
||||||
|
OnlyShownIn = true,
|
||||||
|
NotShowIn = true,
|
||||||
|
}
|
||||||
|
|
||||||
-- Private section
|
-- Private section
|
||||||
|
|
||||||
local do_protected_call, call_callback
|
local do_protected_call, call_callback
|
||||||
|
@ -151,6 +168,49 @@ function utils.rtrim(s)
|
||||||
return s
|
return s
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Unescape strings read from desktop files.
|
||||||
|
-- Possible sequences are \n, \r, \s, \t, \\, which have the usual meanings.
|
||||||
|
-- @tparam string s String to unescape
|
||||||
|
-- @treturn string The unescaped string
|
||||||
|
function utils.unescape(s)
|
||||||
|
if not s then return end
|
||||||
|
|
||||||
|
-- Ignore the second return value of string.gsub() (number replaced)
|
||||||
|
s = string.gsub(s, "\\.", function(sequence)
|
||||||
|
return escape_sequences[sequence] or sequence
|
||||||
|
end)
|
||||||
|
return s
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Separate semi-colon separated lists.
|
||||||
|
-- Semi-colons in lists are escaped as '\;'.
|
||||||
|
-- @tparam string s String to parse
|
||||||
|
-- @treturn table A table containing the separated strings. Each element is
|
||||||
|
-- unescaped by utils.unescape().
|
||||||
|
function utils.parse_list(s)
|
||||||
|
if not s then return end
|
||||||
|
|
||||||
|
-- Append terminating semi-colon if not already there.
|
||||||
|
if string.sub(s, -1) ~= ';' then
|
||||||
|
s = s .. ';'
|
||||||
|
end
|
||||||
|
|
||||||
|
local segments = {}
|
||||||
|
local part = ""
|
||||||
|
-- Iterate over sub-strings between semicolons
|
||||||
|
for word, backslashes in string.gmatch(s, "([^;]-(\\*));") do
|
||||||
|
if #backslashes % 2 ~= 0 then
|
||||||
|
-- The semicolon was escaped, remember this part for later
|
||||||
|
part = part .. word:sub(1, -2) .. ";"
|
||||||
|
else
|
||||||
|
table.insert(segments, utils.unescape(part .. word))
|
||||||
|
part = ""
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return segments
|
||||||
|
end
|
||||||
|
|
||||||
--- Lookup an icon in different folders of the filesystem.
|
--- Lookup an icon in different folders of the filesystem.
|
||||||
-- @tparam string icon_file Short or full name of the icon.
|
-- @tparam string icon_file Short or full name of the icon.
|
||||||
-- @treturn string|boolean Full name of the icon, or false on failure.
|
-- @treturn string|boolean Full name of the icon, or false on failure.
|
||||||
|
@ -219,7 +279,11 @@ function utils.parse_desktop_file(file)
|
||||||
|
|
||||||
-- Grab the values
|
-- Grab the values
|
||||||
for key, value in line:gmatch("(%w+)%s*=%s*(.+)") do
|
for key, value in line:gmatch("(%w+)%s*=%s*(.+)") do
|
||||||
program[key] = value
|
if list_keys[key] then
|
||||||
|
program[key] = utils.parse_list(value)
|
||||||
|
else
|
||||||
|
program[key] = utils.unescape(value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -233,12 +297,32 @@ function utils.parse_desktop_file(file)
|
||||||
-- Don't show program if NoDisplay attribute is false
|
-- Don't show program if NoDisplay attribute is false
|
||||||
if program.NoDisplay and string.lower(program.NoDisplay) == "true" then
|
if program.NoDisplay and string.lower(program.NoDisplay) == "true" then
|
||||||
program.show = false
|
program.show = false
|
||||||
end
|
else
|
||||||
|
-- Only check these values is NoDisplay is true (or non-existent)
|
||||||
|
|
||||||
-- Only show the program if there is no OnlyShowIn attribute
|
-- Only show the program if there is no OnlyShowIn attribute
|
||||||
-- or if it's equal to utils.wm_name
|
-- or if it contains wm_name
|
||||||
if program.OnlyShowIn ~= nil and not program.OnlyShowIn:match(utils.wm_name) then
|
if program.OnlyShowIn then
|
||||||
program.show = false
|
program.show = false -- Assume false until found
|
||||||
|
for _, wm in ipairs(program.OnlyShowIn) do
|
||||||
|
if wm == utils.wm_name then
|
||||||
|
program.show = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
program.show = true
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Only need to check NotShowIn if the program is being shown
|
||||||
|
if program.show and program.NotShowIn then
|
||||||
|
for _, wm in ipairs(program.NotShowIn) do
|
||||||
|
if wm == utils.wm_name then
|
||||||
|
program.show = false
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Look up for a icon.
|
-- Look up for a icon.
|
||||||
|
@ -246,13 +330,9 @@ function utils.parse_desktop_file(file)
|
||||||
program.icon_path = utils.lookup_icon(program.Icon)
|
program.icon_path = utils.lookup_icon(program.Icon)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Split categories into a table. Categories are written in one
|
-- Make the variable lower-case like the rest of them
|
||||||
-- line separated by semicolon.
|
|
||||||
if program.Categories then
|
if program.Categories then
|
||||||
program.categories = {}
|
program.categories = program.Categories
|
||||||
for category in program.Categories:gmatch('[^;]+') do
|
|
||||||
table.insert(program.categories, category)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if program.Exec then
|
if program.Exec then
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
-- @author Zach Peltzer
|
||||||
|
-- @copyright 2017 Zach Peltzer
|
||||||
|
---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
local utils = require("menubar.utils")
|
||||||
|
|
||||||
|
describe("menubar.utils unescape", function()
|
||||||
|
local single_strings = {
|
||||||
|
[ [[\n:\r:\s:\t:\\]] ] = "\n:\r: :\t:\\",
|
||||||
|
-- Make sure escapes are not read recursively
|
||||||
|
[ [[\\s]] ] = [[\s]],
|
||||||
|
-- Make sure ';' is not escaped for non-list strings and other
|
||||||
|
-- characters are not escaped
|
||||||
|
[ [[ab\c\;1\23]] ] = [[ab\c\;1\23]],
|
||||||
|
}
|
||||||
|
|
||||||
|
for escaped, unescaped in pairs(single_strings) do
|
||||||
|
it(escaped, function()
|
||||||
|
assert.is.equal(unescaped, utils.unescape(escaped))
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local list_strings = {
|
||||||
|
-- Normal list
|
||||||
|
[ [[abc;123;xyz]] ] = { "abc", "123", "xyz" },
|
||||||
|
-- Optional terminating semicolon
|
||||||
|
[ [[abc;123;xyz;]] ] = { "abc", "123", "xyz" },
|
||||||
|
-- Blank item
|
||||||
|
[ [[abc;;123]] ] = { "abc", "", "123" },
|
||||||
|
-- Trailing whitespace
|
||||||
|
[ [[abc;123; ]] ] = { "abc", "123", " " },
|
||||||
|
-- Escape semicolon
|
||||||
|
[ [[abc\;;12\;3;\;xyz]] ] = { "abc;", "12;3", ";xyz" },
|
||||||
|
-- Normal escapes are parsed like normal
|
||||||
|
[ [[ab\c;1\s23;x\\yz]] ] = { "ab\\c", "1 23", "x\\yz" },
|
||||||
|
-- Escaped backslashes before semicolon
|
||||||
|
[ [[abc\\\;;12\\;3;xyz]] ] = { "abc\\;", "12\\", "3", "xyz" },
|
||||||
|
}
|
||||||
|
|
||||||
|
for escaped, unescaped in pairs(list_strings) do
|
||||||
|
it(escaped, function()
|
||||||
|
local returned = utils.parse_list(escaped)
|
||||||
|
assert.is.equal(#unescaped, #returned)
|
||||||
|
|
||||||
|
for i = 1, #unescaped do
|
||||||
|
assert.is.equal(unescaped[i], returned[i])
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- vim: filetype=lua:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:textwidth=80
|
Loading…
Reference in New Issue