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.
|
||||
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
|
||||
|
||||
local do_protected_call, call_callback
|
||||
|
@ -151,6 +168,49 @@ function utils.rtrim(s)
|
|||
return s
|
||||
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.
|
||||
-- @tparam string icon_file Short or full name of the icon.
|
||||
-- @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
|
||||
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
|
||||
|
@ -233,12 +297,32 @@ function utils.parse_desktop_file(file)
|
|||
-- Don't show program if NoDisplay attribute is false
|
||||
if program.NoDisplay and string.lower(program.NoDisplay) == "true" then
|
||||
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
|
||||
-- or if it's equal to utils.wm_name
|
||||
if program.OnlyShowIn ~= nil and not program.OnlyShowIn:match(utils.wm_name) then
|
||||
program.show = false
|
||||
-- Only show the program if there is no OnlyShowIn attribute
|
||||
-- or if it contains wm_name
|
||||
if program.OnlyShowIn then
|
||||
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
|
||||
|
||||
-- Look up for a icon.
|
||||
|
@ -246,13 +330,9 @@ function utils.parse_desktop_file(file)
|
|||
program.icon_path = utils.lookup_icon(program.Icon)
|
||||
end
|
||||
|
||||
-- Split categories into a table. Categories are written in one
|
||||
-- line separated by semicolon.
|
||||
-- Make the variable lower-case like the rest of them
|
||||
if program.Categories then
|
||||
program.categories = {}
|
||||
for category in program.Categories:gmatch('[^;]+') do
|
||||
table.insert(program.categories, category)
|
||||
end
|
||||
program.categories = program.Categories
|
||||
end
|
||||
|
||||
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